/*-
* Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef __NSCD_CACHELIB_H__
#define __NSCD_CACHELIB_H__
#include "hashtable.h"
#include "cacheplcs.h"
enum cache_entry_t {
CET_COMMON = 0, /* cache item is atomic */
CET_MULTIPART /* cache item is formed part by part */
};
enum cache_transformation_t {
CTT_FLUSH = 0, /* flush the cache - delete all obsolete items */
CTT_CLEAR = 1 /* delete all items in the cache */
};
/* cache deletion policy type enum */
enum cache_policy_t {
CPT_FIFO = 0, /* first-in first-out */
CPT_LRU = 1, /* least recently used */
CPT_LFU = 2 /* least frequently used */
};
/* multipart sessions can be used for reading and writing */
enum cache_mp_session_t {
CMPT_READ_SESSION,
CMPT_WRITE_SESSION
};
/*
* When doing partial transformations of entries (which are applied for
* elements with keys, that contain specified buffer in its left or
* right part), this enum will show the needed position of the key part.
*/
enum part_position_t {
KPPT_LEFT,
KPPT_RIGHT
};
/* num_levels attribute is obsolete, i think - user can always emulate it
* by using one entry.
* get_time_func is needed to have the clocks-independent counter
*/
struct cache_params {
void (*get_time_func)(struct timeval *);
};
/*
* base structure - normal_cache_entry_params and multipart_cache_entry_params
* are "inherited" from it
*/
struct cache_entry_params {
enum cache_entry_t entry_type;
char *entry_name;
};
/* params, used for most entries */
struct common_cache_entry_params {
struct cache_entry_params cep;
size_t cache_entries_size;
size_t max_elemsize; /* if 0 then no check is made */
size_t satisf_elemsize; /* if entry size is exceeded,
* this number of elements will be left,
* others will be deleted */
int confidence_threshold; /* number matching replies required */
struct timeval max_lifetime; /* if 0 then no check is made */
enum cache_policy_t policy; /* policy used for transformations */
};
/* params, used for multipart entries */
struct mp_cache_entry_params {
struct cache_entry_params cep;
/* unique fields */
size_t max_elemsize; /* if 0 then no check is made */
size_t max_sessions; /* maximum number of active sessions */
struct timeval max_lifetime; /* maximum elements lifetime */
};
struct cache_ht_item_data_ {
/* key is the bytes sequence only - not the null-terminated string */
char *key;
size_t key_size;
char *value;
size_t value_size;
struct cache_policy_item_ *fifo_policy_item;
int confidence; /* incremented for each verification */
};
struct cache_ht_item_ {
HASHTABLE_ENTRY_HEAD(ht_item_, struct cache_ht_item_data_) data;
};
struct cache_entry_ {
char *name;
struct cache_entry_params *params;
};
struct cache_common_entry_ {
char *name;
struct cache_entry_params *params;
struct common_cache_entry_params common_params;
HASHTABLE_HEAD(cache_ht_, cache_ht_item_) items;
size_t items_size;
/*
* Entry always has the FIFO policy, that is used to eliminate old
* elements (the ones, with lifetime more than max_lifetime). Besides,
* user can specify another policy to be applied, when there are too
* many elements in the entry. So policies_size can be 1 or 2.
*/
struct cache_policy_ **policies;
size_t policies_size;
void (*get_time_func)(struct timeval *);
};
struct cache_mp_data_item_ {
char *value;
size_t value_size;
TAILQ_ENTRY(cache_mp_data_item_) entries;
};
struct cache_mp_write_session_ {
struct cache_mp_entry_ *parent_entry;
/*
* All items are accumulated in this queue. When the session is
* committed, they all will be copied to the multipart entry.
*/
TAILQ_HEAD(cache_mp_data_item_head, cache_mp_data_item_) items;
size_t items_size;
TAILQ_ENTRY(cache_mp_write_session_) entries;
};
struct cache_mp_read_session_ {
struct cache_mp_entry_ *parent_entry;
struct cache_mp_data_item_ *current_item;
TAILQ_ENTRY(cache_mp_read_session_) entries;
};
struct cache_mp_entry_ {
char *name;
struct cache_entry_params *params;
struct mp_cache_entry_params mp_params;
/* All opened write sessions */
TAILQ_HEAD(write_sessions_head, cache_mp_write_session_) ws_head;
size_t ws_size;
/* All opened read sessions */
TAILQ_HEAD(read_sessions_head, cache_mp_read_session_) rs_head;
size_t rs_size;
/*
* completed_write_session is the committed write sessions. All read
* sessions use data from it. If the completed_write_session is out of
* date, but still in use by some of the read sessions, the newly
* committed write session is stored in the pending_write_session.
* In such a case, completed_write_session will be substituted with
* pending_write_session as soon as it won't be used by any of
* the read sessions.
*/
struct cache_mp_write_session_ *completed_write_session;
struct cache_mp_write_session_ *pending_write_session;
struct timeval creation_time;
struct timeval last_request_time;
void (*get_time_func)(struct timeval *);
};
struct cache_ {
struct cache_params params;
struct cache_entry_ **entries;
size_t entries_capacity;
size_t entries_size;
};
/* simple abstractions - for not to write "struct" every time */
typedef struct cache_ *cache;
typedef struct cache_entry_ *cache_entry;
typedef struct cache_mp_write_session_ *cache_mp_write_session;
typedef struct cache_mp_read_session_ *cache_mp_read_session;
#define INVALID_CACHE (NULL)
#define INVALID_CACHE_ENTRY (NULL)
#define INVALID_CACHE_MP_WRITE_SESSION (NULL)
#define INVALID_CACHE_MP_READ_SESSION (NULL)
/*
* NOTE: all cache operations are thread-unsafe. You must ensure thread-safety
* externally, by yourself.
*/
/* cache initialization/destruction routines */
cache init_cache(struct cache_params const *);
void destroy_cache(cache);
/* cache entries manipulation routines */
int register_cache_entry(cache, struct cache_entry_params const *);
int unregister_cache_entry(cache, const char *);
cache_entry find_cache_entry(cache, const char *);
/* read/write operations used on common entries */
int cache_read(cache_entry, const char *, size_t, char *, size_t *);
int cache_write(cache_entry, const char *, size_t, char const *, size_t);
/* read/write operations used on multipart entries */
cache_mp_write_session open_cache_mp_write_session(cache_entry);
int cache_mp_write(cache_mp_write_session, char *, size_t);
void abandon_cache_mp_write_session(cache_mp_write_session);
void close_cache_mp_write_session(cache_mp_write_session);
cache_mp_read_session open_cache_mp_read_session(cache_entry);
int cache_mp_read(cache_mp_read_session, char *, size_t *);
void close_cache_mp_read_session(cache_mp_read_session);
/* transformation routines */
int transform_cache_entry(cache_entry, enum cache_transformation_t);
int transform_cache_entry_part(cache_entry, enum cache_transformation_t,
const char *, size_t, enum part_position_t);
#endif