#include #define HASH_FUNCTION(keyptr, keylen, hashv) HASH_FUNCTION_OVERWRITE(keyptr, keylen, &hashv) #define HASH_KEYCMP(a, b, len) HASH_KEYCMP_OVERWRITE(a, b, len) #include "session_table.h" #include "session_private.h" struct session_table { struct session *root; session_free_cb free_cb; void *arg; uint64_t count; struct session *least_recently_unused; struct session *least_recently_used; }; /****************************************************************************** * Private API ******************************************************************************/ static void HASH_FUNCTION_OVERWRITE(const struct tuple6 *tuple, unsigned int keylen, uint32_t *hashv) { *hashv = tuple6_hash(tuple); } static int HASH_KEYCMP_OVERWRITE(const void *key_a, const void *key_b, size_t len) { struct tuple6 *tuple_a = (struct tuple6 *)key_a; struct tuple6 *tuple_b = (struct tuple6 *)key_b; if (tuple6_cmp(tuple_a, tuple_b) == 0) { return 0; } struct tuple6 reversed; tuple6_reverse(tuple_b, &reversed); if (tuple6_cmp(tuple_a, &reversed) == 0) { return 0; } return -1; } static void session_table_add_session_to_linklist(struct session_table *table, struct session *sess) { if (table == NULL || sess == NULL) { return; } if (table->least_recently_used == NULL) { table->least_recently_unused = sess; table->least_recently_used = sess; sess->prev_ptr = NULL; sess->next_ptr = NULL; } else { sess->next_ptr = table->least_recently_used; table->least_recently_used->prev_ptr = sess; sess->prev_ptr = NULL; table->least_recently_used = sess; } } static void session_table_del_session_from_linklist(struct session_table *table, struct session *sess) { if (table == NULL || sess == NULL) { return; } if (sess->prev_ptr == NULL && sess->next_ptr == NULL) { table->least_recently_unused = NULL; table->least_recently_used = NULL; } else if (sess->prev_ptr == NULL && sess->next_ptr != NULL) { table->least_recently_used = sess->next_ptr; sess->next_ptr->prev_ptr = NULL; } else if (sess->prev_ptr != NULL && sess->next_ptr == NULL) { table->least_recently_unused = sess->prev_ptr; sess->prev_ptr->next_ptr = NULL; } else { sess->prev_ptr->next_ptr = sess->next_ptr; sess->next_ptr->prev_ptr = sess->prev_ptr; } sess->prev_ptr = NULL; sess->next_ptr = NULL; } /****************************************************************************** * Public API ******************************************************************************/ struct session_table *session_table_new() { struct session_table *table = (struct session_table *)calloc(1, sizeof(struct session_table)); table->count = 0; table->least_recently_unused = NULL; table->least_recently_used = NULL; return table; } void session_table_free(struct session_table *table) { if (table) { struct session *node = NULL; struct session *tmp = NULL; HASH_ITER(hh, table->root, node, tmp) { HASH_DELETE(hh, table->root, node); if (table->free_cb && node) { table->free_cb(node, table->arg); } } table->count--; free(table); table = NULL; } } uint64_t session_table_get_count(struct session_table *table) { if (table == NULL) { return 0; } return table->count; } void session_table_set_freecb(struct session_table *table, session_free_cb free_cb, void *arg) { if (table) { table->free_cb = free_cb; table->arg = arg; } } int session_table_add_session(struct session_table *table, const struct tuple6 *tuple, struct session *sess) { if (table == NULL || sess == NULL) { return -1; } if (session_table_find_session(table, tuple)) { return -1; } HASH_ADD(hh, table->root, tuple, sizeof(sess->tuple), sess); session_table_add_session_to_linklist(table, sess); table->count++; return 0; } void session_table_del_session(struct session_table *table, const struct tuple6 *tuple) { if (table == NULL) { return; } struct session *sess = session_table_find_session(table, tuple); if (sess == NULL) { return; } HASH_DELETE(hh, table->root, sess); if (table->free_cb && sess) { table->free_cb(sess, table->arg); } session_table_del_session_from_linklist(table, sess); table->count--; } struct session *session_table_find_session(struct session_table *table, const struct tuple6 *tuple) { if (table == NULL) { return NULL; } struct session *sess = NULL; HASH_FIND(hh, table->root, tuple, sizeof(struct tuple6), sess); if (sess == NULL) { struct tuple6 out; memset(&out, 0, sizeof(struct tuple6)); tuple6_reverse(tuple, &out); HASH_FIND(hh, table->root, &out, sizeof(struct tuple6), sess); } if (sess) { session_table_del_session_from_linklist(table, sess); session_table_add_session_to_linklist(table, sess); } return sess; } struct session *session_table_find_least_recently_unused_session(struct session_table *table) { if (table == NULL) { return NULL; } return table->least_recently_unused; } struct session *session_table_find_least_recently_used_session(struct session_table *table) { if (table == NULL) { return NULL; } return table->least_recently_used; }