| /* |
| * container_list_sl.c |
| * $Id$ |
| * |
| */ |
| #include <net-snmp/net-snmp-config.h> |
| |
| #include <stdio.h> |
| #if HAVE_STDLIB_H |
| #include <stdlib.h> |
| #endif |
| #if HAVE_MALLOC_H |
| #include <malloc.h> |
| #endif |
| #include <sys/types.h> |
| #if HAVE_STRING_H |
| #include <string.h> |
| #else |
| #include <strings.h> |
| #endif |
| |
| #include <net-snmp/net-snmp-includes.h> |
| #include <net-snmp/types.h> |
| #include <net-snmp/library/snmp_api.h> |
| #include <net-snmp/library/container.h> |
| #include <net-snmp/library/tools.h> |
| #include <net-snmp/library/snmp_assert.h> |
| |
| #include <net-snmp/library/container_list_ssll.h> |
| |
| typedef struct sl_node { |
| void *data; |
| struct sl_node *next; |
| } sl_node; |
| |
| typedef struct sl_container_s { |
| netsnmp_container c; |
| |
| size_t count; /* Index of the next free entry */ |
| sl_node *head; /* head of list */ |
| |
| int unsorted; /* unsorted list? */ |
| int fifo; /* lifo or fifo? */ |
| |
| } sl_container; |
| |
| typedef struct ssll_iterator_s { |
| netsnmp_iterator base; |
| |
| sl_node *pos; |
| sl_node *last; |
| } ssll_iterator; |
| |
| static netsnmp_iterator *_ssll_iterator_get(netsnmp_container *c); |
| |
| |
| static void * |
| _get(netsnmp_container *c, const void *key, int exact) |
| { |
| sl_container *sl = (sl_container*)c; |
| sl_node *curr = sl->head; |
| int rc = 0; |
| |
| /* |
| * note: get-next on unsorted list is meaningless. we |
| * don't try to search whole array, looking for the next highest. |
| */ |
| if( (NULL != curr) && (NULL != key)) { |
| while (curr) { |
| rc = sl->c.compare(curr->data, key); |
| if (rc == 0) |
| break; |
| else if (rc > 0) { |
| if (0 == sl->unsorted) { |
| /* |
| * if sorted, we can stop. |
| */ |
| break; |
| } |
| } |
| curr = curr->next; |
| } |
| |
| if((curr) && (!exact) && (rc == 0)) { |
| curr = curr->next; |
| } |
| } |
| |
| return curr ? curr->data : NULL; |
| } |
| |
| /********************************************************************** |
| * |
| * |
| * |
| **********************************************************************/ |
| static int |
| _ssll_free(netsnmp_container *c) |
| { |
| if(c) { |
| free(c); |
| } |
| return 0; |
| } |
| |
| static void * |
| _ssll_find(netsnmp_container *c, const void *data) |
| { |
| if((NULL == c) || (NULL == data)) |
| return NULL; |
| |
| return _get(c, data, 1); |
| } |
| |
| static void * |
| _ssll_find_next(netsnmp_container *c, const void *data) |
| { |
| if(NULL == c) |
| return NULL; |
| |
| return _get(c, data, 0); |
| } |
| |
| static int |
| _ssll_insert(netsnmp_container *c, const void *data) |
| { |
| sl_container *sl = (sl_container*)c; |
| sl_node *new_node, *curr = sl->head; |
| |
| if(NULL == c) |
| return -1; |
| |
| new_node = SNMP_MALLOC_TYPEDEF(sl_node); |
| if(NULL == new_node) |
| return -1; |
| new_node->data = NETSNMP_REMOVE_CONST(void *, data); |
| ++sl->count; |
| ++c->sync; |
| |
| /* |
| * first node? |
| */ |
| if(NULL == sl->head) { |
| sl->head = new_node; |
| return 0; |
| } |
| |
| /* |
| * sorted or unsorted insert? |
| */ |
| if (1 == sl->unsorted) { |
| /* |
| * unsorted: fifo, or lifo? |
| */ |
| if (1 == sl->fifo) { |
| /* |
| * fifo: insert at tail |
| */ |
| while(NULL != curr->next) |
| curr = curr->next; |
| curr->next = new_node; |
| } |
| else { |
| /* |
| * lifo: insert at head |
| */ |
| new_node->next = sl->head; |
| sl->head = new_node; |
| } |
| } |
| else { |
| /* |
| * sorted |
| */ |
| sl_node *last = NULL; |
| for( ; curr; last = curr, curr = curr->next) { |
| if(sl->c.compare(curr->data, data) > 0) |
| break; |
| } |
| if(NULL == last) { |
| new_node->next = sl->head; |
| sl->head = new_node; |
| } |
| else { |
| new_node->next = last->next; |
| last->next = new_node; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| _ssll_remove(netsnmp_container *c, const void *data) |
| { |
| sl_container *sl = (sl_container*)c; |
| sl_node *curr = sl->head; |
| |
| if((NULL == c) || (NULL == curr)) |
| return -1; |
| |
| /* |
| * special case for NULL data, act like stack |
| */ |
| if ((NULL == data) || |
| (sl->c.compare(sl->head->data, data) == 0)) { |
| curr = sl->head; |
| sl->head = sl->head->next; |
| } |
| else { |
| sl_node *last = sl->head; |
| int rc; |
| for(curr = sl->head->next ; curr; last = curr, curr = curr->next) { |
| rc = sl->c.compare(curr->data, data); |
| if (rc == 0) { |
| last->next = curr->next; |
| break; |
| } |
| else if ((rc > 0) && (0 == sl->unsorted)) { |
| /* |
| * if sorted and rc > 0, didn't find entry |
| */ |
| curr = NULL; |
| break; |
| } |
| } |
| } |
| |
| if(NULL == curr) |
| return -2; |
| |
| /* |
| * free our node structure, but not the data |
| */ |
| free(curr); |
| --sl->count; |
| ++c->sync; |
| |
| return 0; |
| } |
| |
| static size_t |
| _ssll_size(netsnmp_container *c) |
| { |
| sl_container *sl = (sl_container*)c; |
| |
| if(NULL == c) |
| return 0; |
| |
| return sl->count; |
| } |
| |
| static void |
| _ssll_for_each(netsnmp_container *c, netsnmp_container_obj_func *f, |
| void *context) |
| { |
| sl_container *sl = (sl_container*)c; |
| sl_node *curr; |
| |
| if(NULL == c) |
| return; |
| |
| for(curr = sl->head; curr; curr = curr->next) |
| (*f) ((void *)curr->data, context); |
| } |
| |
| static void |
| _ssll_clear(netsnmp_container *c, netsnmp_container_obj_func *f, |
| void *context) |
| { |
| sl_container *sl = (sl_container*)c; |
| sl_node *curr, *next; |
| |
| if(NULL == c) |
| return; |
| |
| for(curr = sl->head; curr; curr = next) { |
| |
| next = curr->next; |
| |
| if( NULL != f ) { |
| curr->next = NULL; |
| (*f) ((void *)curr->data, context); |
| } |
| |
| /* |
| * free our node structure, but not the data |
| */ |
| free(curr); |
| } |
| sl->head = NULL; |
| sl->count = 0; |
| ++c->sync; |
| } |
| |
| /********************************************************************** |
| * |
| * |
| * |
| **********************************************************************/ |
| netsnmp_container * |
| netsnmp_container_get_ssll(void) |
| { |
| /* |
| * allocate memory |
| */ |
| sl_container *sl = SNMP_MALLOC_TYPEDEF(sl_container); |
| if (NULL==sl) { |
| snmp_log(LOG_ERR, "couldn't allocate memory\n"); |
| return NULL; |
| } |
| |
| sl->c.cfree = _ssll_free; |
| |
| sl->c.get_size = _ssll_size; |
| sl->c.init = NULL; |
| sl->c.insert = _ssll_insert; |
| sl->c.remove = _ssll_remove; |
| sl->c.find = _ssll_find; |
| sl->c.find_next = _ssll_find_next; |
| sl->c.get_subset = NULL; |
| sl->c.get_iterator =_ssll_iterator_get; |
| sl->c.for_each = _ssll_for_each; |
| sl->c.clear = _ssll_clear; |
| |
| |
| return (netsnmp_container*)sl; |
| } |
| |
| netsnmp_factory * |
| netsnmp_container_get_ssll_factory(void) |
| { |
| static netsnmp_factory f = {"sorted_singly_linked_list", |
| (netsnmp_factory_produce_f*) |
| netsnmp_container_get_ssll }; |
| |
| return &f; |
| } |
| |
| |
| netsnmp_container * |
| netsnmp_container_get_usll(void) |
| { |
| /* |
| * allocate memory |
| */ |
| sl_container *sl = (sl_container *)netsnmp_container_get_ssll(); |
| if (NULL==sl) |
| return NULL; /* msg already logged */ |
| |
| sl->unsorted = 1; |
| |
| return (netsnmp_container*)sl; |
| } |
| |
| netsnmp_container * |
| netsnmp_container_get_singly_linked_list(int fifo) |
| { |
| sl_container *sl = (sl_container *)netsnmp_container_get_usll(); |
| if (NULL == sl) |
| return NULL; /* error already logged */ |
| |
| sl->fifo = fifo; |
| |
| return (netsnmp_container *)sl; |
| } |
| |
| netsnmp_container * |
| netsnmp_container_get_fifo(void) |
| { |
| return netsnmp_container_get_singly_linked_list(1); |
| } |
| |
| netsnmp_factory * |
| netsnmp_container_get_usll_factory(void) |
| { |
| static netsnmp_factory f = {"unsorted_singly_linked_list-lifo", |
| (netsnmp_factory_produce_f*) |
| netsnmp_container_get_usll }; |
| |
| return &f; |
| } |
| |
| netsnmp_factory * |
| netsnmp_container_get_fifo_factory(void) |
| { |
| static netsnmp_factory f = {"unsorted_singly_linked_list-fifo", |
| (netsnmp_factory_produce_f*) |
| netsnmp_container_get_fifo }; |
| |
| return &f; |
| } |
| |
| void |
| netsnmp_container_ssll_init(void) |
| { |
| netsnmp_container_register("sorted_singly_linked_list", |
| netsnmp_container_get_ssll_factory()); |
| netsnmp_container_register("unsorted_singly_linked_list", |
| netsnmp_container_get_usll_factory()); |
| netsnmp_container_register("lifo", |
| netsnmp_container_get_usll_factory()); |
| netsnmp_container_register("fifo", |
| netsnmp_container_get_fifo_factory()); |
| } |
| |
| |
| /********************************************************************** |
| * |
| * iterator |
| * |
| */ |
| NETSNMP_STATIC_INLINE sl_container * |
| _ssll_it2cont(ssll_iterator *it) |
| { |
| if(NULL == it) { |
| netsnmp_assert(NULL != it); |
| return NULL; |
| } |
| |
| if(NULL == it->base.container) { |
| netsnmp_assert(NULL != it->base.container); |
| return NULL; |
| } |
| |
| if(it->base.container->sync != it->base.sync) { |
| DEBUGMSGTL(("container:iterator", "out of sync\n")); |
| return NULL; |
| } |
| |
| return (sl_container *)it->base.container; |
| } |
| |
| static void * |
| _ssll_iterator_curr(ssll_iterator *it) |
| { |
| sl_container *t = _ssll_it2cont(it); |
| if ((NULL == t) || (NULL == it->pos)) |
| return NULL; |
| |
| return it->pos->data; |
| } |
| |
| static void * |
| _ssll_iterator_first(ssll_iterator *it) |
| { |
| sl_container *t = _ssll_it2cont(it); |
| if ((NULL == t) || (NULL == t->head)) |
| return NULL; |
| |
| return t->head->data; |
| } |
| |
| static void * |
| _ssll_iterator_next(ssll_iterator *it) |
| { |
| sl_container *t = _ssll_it2cont(it); |
| if ((NULL == t) || (NULL == it->pos)) |
| return NULL; |
| |
| it->pos = it->pos->next; |
| if (NULL == it->pos) |
| return NULL; |
| |
| return it->pos->data; |
| } |
| |
| static void * |
| _ssll_iterator_last(ssll_iterator *it) |
| { |
| sl_node *n; |
| sl_container *t = _ssll_it2cont(it); |
| if(NULL == t) |
| return NULL; |
| |
| if (it->last) |
| return it->last; |
| |
| n = it->pos ? it->pos : t->head; |
| if (NULL == n) |
| return NULL; |
| |
| while(n->next) |
| n = n->next; |
| |
| if (NULL == n) |
| return NULL; |
| |
| it->last = n; |
| |
| return it->last->data; |
| } |
| |
| static int |
| _ssll_iterator_reset(ssll_iterator *it) |
| { |
| sl_container *t; |
| |
| /** can't use it2conf cuz we might be out of sync */ |
| if(NULL == it) { |
| netsnmp_assert(NULL != it); |
| return 0; |
| } |
| |
| if(NULL == it->base.container) { |
| netsnmp_assert(NULL != it->base.container); |
| return 0; |
| } |
| t = (sl_container *)it->base.container; |
| if(NULL == t) |
| return -1; |
| |
| it->last = NULL; |
| it->pos = t->head; |
| |
| /* |
| * save sync count, to make sure container doesn't change while |
| * iterator is in use. |
| */ |
| it->base.sync = it->base.container->sync; |
| |
| return 0; |
| } |
| |
| static int |
| _ssll_iterator_release(netsnmp_iterator *it) |
| { |
| free(it); |
| |
| return 0; |
| } |
| |
| static netsnmp_iterator * |
| _ssll_iterator_get(netsnmp_container *c) |
| { |
| ssll_iterator* it; |
| |
| if(NULL == c) |
| return NULL; |
| |
| it = SNMP_MALLOC_TYPEDEF(ssll_iterator); |
| if(NULL == it) |
| return NULL; |
| |
| it->base.container = c; |
| |
| it->base.first = (netsnmp_iterator_rtn*)_ssll_iterator_first; |
| it->base.next = (netsnmp_iterator_rtn*)_ssll_iterator_next; |
| it->base.curr = (netsnmp_iterator_rtn*)_ssll_iterator_curr; |
| it->base.last = (netsnmp_iterator_rtn*)_ssll_iterator_last; |
| it->base.reset = (netsnmp_iterator_rc*)_ssll_iterator_reset; |
| it->base.release = (netsnmp_iterator_rc*)_ssll_iterator_release; |
| |
| (void)_ssll_iterator_reset(it); |
| |
| return (netsnmp_iterator *)it; |
| } |