summaryrefslogtreecommitdiff
path: root/snmplib/container_binary_array.c
diff options
context:
space:
mode:
Diffstat (limited to 'snmplib/container_binary_array.c')
-rw-r--r--snmplib/container_binary_array.c879
1 files changed, 879 insertions, 0 deletions
diff --git a/snmplib/container_binary_array.c b/snmplib/container_binary_array.c
new file mode 100644
index 0000000..249a3a9
--- /dev/null
+++ b/snmplib/container_binary_array.c
@@ -0,0 +1,879 @@
+/*
+ * container_binary_array.c
+ * $Id$
+ *
+ * see comments in header file.
+ *
+ */
+
+#include <net-snmp/net-snmp-config.h>
+
+#if HAVE_IO_H
+#include <io.h>
+#endif
+#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/container_binary_array.h>
+#include <net-snmp/library/tools.h>
+#include <net-snmp/library/snmp_assert.h>
+
+typedef struct binary_array_table_s {
+ size_t max_size; /* Size of the current data table */
+ size_t count; /* Index of the next free entry */
+ int dirty;
+ void **data; /* The table itself */
+} binary_array_table;
+
+typedef struct binary_array_iterator_s {
+ netsnmp_iterator base;
+
+ size_t pos;
+} binary_array_iterator;
+
+static netsnmp_iterator *_ba_iterator_get(netsnmp_container *c);
+
+/**********************************************************************
+ *
+ *
+ *
+ */
+static void
+array_qsort(void **data, int first, int last, netsnmp_container_compare *f)
+{
+ int i, j;
+ void *mid, *tmp;
+
+ i = first;
+ j = last;
+ mid = data[(first+last)/2];
+
+ do {
+ while (i < last && (*f)(data[i], mid) < 0)
+ ++i;
+ while (j > first && (*f)(mid, data[j]) < 0)
+ --j;
+
+ if(i < j) {
+ tmp = data[i];
+ data[i] = data[j];
+ data[j] = tmp;
+ ++i;
+ --j;
+ }
+ else if (i == j) {
+ ++i;
+ --j;
+ break;
+ }
+ } while(i <= j);
+
+ if (j > first)
+ array_qsort(data, first, j, f);
+
+ if (i < last)
+ array_qsort(data, i, last, f);
+}
+
+static int
+Sort_Array(netsnmp_container *c)
+{
+ binary_array_table *t = (binary_array_table*)c->container_data;
+ netsnmp_assert(t!=NULL);
+ netsnmp_assert(c->compare!=NULL);
+
+ if (c->flags & CONTAINER_KEY_UNSORTED)
+ return 0;
+
+ if (t->dirty) {
+ /*
+ * Sort the table
+ */
+ if (t->count > 1)
+ array_qsort(t->data, 0, t->count - 1, c->compare);
+ t->dirty = 0;
+
+ /*
+ * no way to know if it actually changed... just assume so.
+ */
+ ++c->sync;
+ }
+
+ return 1;
+}
+
+static int
+linear_search(const void *val, netsnmp_container *c)
+{
+ binary_array_table *t = (binary_array_table*)c->container_data;
+ size_t pos = 0;
+
+ if (!t->count)
+ return -1;
+
+ if (! (c->flags & CONTAINER_KEY_UNSORTED)) {
+ snmp_log(LOG_ERR, "linear search on sorted container %s?!?\n",
+ c->container_name);
+ return -1;
+ }
+
+ for (; pos < t->count; ++pos) {
+ if (c->compare(t->data[pos], val) == 0)
+ break;
+ }
+
+ if (pos >= t->count)
+ return -1;
+
+ return pos;
+}
+
+static int
+binary_search(const void *val, netsnmp_container *c, int exact)
+{
+ binary_array_table *t = (binary_array_table*)c->container_data;
+ size_t len = t->count;
+ size_t half;
+ size_t middle = 0;
+ size_t first = 0;
+ int result = 0;
+
+ if (!len)
+ return -1;
+
+ if (c->flags & CONTAINER_KEY_UNSORTED) {
+ if (!exact) {
+ snmp_log(LOG_ERR, "non-exact search on unsorted container %s?!?\n",
+ c->container_name);
+ return -1;
+ }
+ return linear_search(val, c);
+ }
+
+ if (t->dirty)
+ Sort_Array(c);
+
+ while (len > 0) {
+ half = len >> 1;
+ middle = first;
+ middle += half;
+ if ((result =
+ c->compare(t->data[middle], val)) < 0) {
+ first = middle;
+ ++first;
+ len = len - half - 1;
+ } else {
+ if(result == 0) {
+ first = middle;
+ break;
+ }
+ len = half;
+ }
+ }
+
+ if (first >= t->count)
+ return -1;
+
+ if(first != middle) {
+ /* last compare wasn't against first, so get actual result */
+ result = c->compare(t->data[first], val);
+ }
+
+ if(result == 0) {
+ if (!exact) {
+ if (++first == t->count)
+ first = -1;
+ }
+ }
+ else {
+ if(exact)
+ first = -1;
+ }
+
+ return first;
+}
+
+NETSNMP_STATIC_INLINE binary_array_table *
+netsnmp_binary_array_initialize(void)
+{
+ binary_array_table *t;
+
+ t = SNMP_MALLOC_TYPEDEF(binary_array_table);
+ if (t == NULL)
+ return NULL;
+
+ t->max_size = 0;
+ t->count = 0;
+ t->dirty = 0;
+ t->data = NULL;
+
+ return t;
+}
+
+void
+netsnmp_binary_array_release(netsnmp_container *c)
+{
+ binary_array_table *t = (binary_array_table*)c->container_data;
+ SNMP_FREE(t->data);
+ SNMP_FREE(t);
+ SNMP_FREE(c);
+}
+
+int
+netsnmp_binary_array_options_set(netsnmp_container *c, int set, u_int flags)
+{
+#define BA_FLAGS (CONTAINER_KEY_ALLOW_DUPLICATES|CONTAINER_KEY_UNSORTED)
+
+ if (set) {
+ if ((flags & BA_FLAGS) == flags)
+ c->flags = flags;
+ else
+ flags = (u_int)-1; /* unsupported flag */
+ }
+ else
+ return ((c->flags & flags) == flags);
+ return flags;
+}
+
+NETSNMP_STATIC_INLINE size_t
+netsnmp_binary_array_count(netsnmp_container *c)
+{
+ binary_array_table *t = (binary_array_table*)c->container_data;
+ /*
+ * return count
+ */
+ return t ? t->count : 0;
+}
+
+NETSNMP_STATIC_INLINE void *
+netsnmp_binary_array_get(netsnmp_container *c, const void *key, int exact)
+{
+ binary_array_table *t = (binary_array_table*)c->container_data;
+ int index = 0;
+
+ /*
+ * if there is no data, return NULL;
+ */
+ if (!t->count)
+ return NULL;
+
+ /*
+ * if the table is dirty, sort it.
+ */
+ if (t->dirty)
+ Sort_Array(c);
+
+ /*
+ * if there is a key, search. Otherwise default is 0;
+ */
+ if (key) {
+ if ((index = binary_search(key, c, exact)) == -1)
+ return NULL;
+ }
+
+ return t->data[index];
+}
+
+int
+netsnmp_binary_array_remove_at(netsnmp_container *c, size_t index, void **save)
+{
+ binary_array_table *t = (binary_array_table*)c->container_data;
+
+ if (save)
+ *save = NULL;
+
+ /*
+ * if there is no data, return NULL;
+ */
+ if (!t->count)
+ return 0;
+
+ /*
+ * find old data and save it, if ptr provided
+ */
+ if (save)
+ *save = t->data[index];
+
+ /*
+ * if entry was last item, just decrement count
+ */
+ --t->count;
+ if (index != t->count) {
+ /*
+ * otherwise, shift array down
+ */
+ memmove(&t->data[index], &t->data[index+1],
+ sizeof(void*) * (t->count - index));
+
+ ++c->sync;
+ }
+
+ return 0;
+}
+int
+netsnmp_binary_array_remove(netsnmp_container *c, const void *key, void **save)
+{
+ binary_array_table *t = (binary_array_table*)c->container_data;
+ int index = 0;
+
+ if (save)
+ *save = NULL;
+
+ /*
+ * if there is no data, return NULL;
+ */
+ if (!t->count)
+ return 0;
+
+ /*
+ * if the table is dirty, sort it.
+ */
+ if (t->dirty)
+ Sort_Array(c);
+
+ /*
+ * search
+ */
+ if ((index = binary_search(key, c, 1)) == -1)
+ return -1;
+
+ return netsnmp_binary_array_remove_at(c, (size_t)index, save);
+}
+
+NETSNMP_STATIC_INLINE void
+netsnmp_binary_array_for_each(netsnmp_container *c,
+ netsnmp_container_obj_func *fe,
+ void *context, int sort)
+{
+ binary_array_table *t = (binary_array_table*)c->container_data;
+ size_t i;
+
+ if (sort && t->dirty)
+ Sort_Array(c);
+
+ for (i = 0; i < t->count; ++i)
+ (*fe) (t->data[i], context);
+}
+
+NETSNMP_STATIC_INLINE void
+netsnmp_binary_array_clear(netsnmp_container *c,
+ netsnmp_container_obj_func *fe,
+ void *context)
+{
+ binary_array_table *t = (binary_array_table*)c->container_data;
+
+ if( NULL != fe ) {
+ size_t i;
+
+ for (i = 0; i < t->count; ++i)
+ (*fe) (t->data[i], context);
+ }
+
+ t->count = 0;
+ t->dirty = 0;
+ ++c->sync;
+}
+
+NETSNMP_STATIC_INLINE int
+netsnmp_binary_array_insert(netsnmp_container *c, const void *entry)
+{
+ binary_array_table *t = (binary_array_table*)c->container_data;
+ int was_dirty = 0;
+ /*
+ * check for duplicates
+ */
+ if (! (c->flags & CONTAINER_KEY_ALLOW_DUPLICATES)) {
+ was_dirty = t->dirty;
+ if (NULL != netsnmp_binary_array_get(c, entry, 1)) {
+ DEBUGMSGTL(("container","not inserting duplicate key\n"));
+ return -1;
+ }
+ }
+
+ /*
+ * check if we need to resize the array
+ */
+ if (t->max_size <= t->count) {
+ /*
+ * Table is full, so extend it to double the size, or use 10 elements
+ * if it is empty.
+ */
+ size_t const new_max = t->max_size > 0 ? 2 * t->max_size : 10;
+ void ** const new_data =
+ (void**) realloc(t->data, new_max * sizeof(void*));
+
+ if (new_data == NULL)
+ return -1;
+
+ memset(new_data + t->max_size, 0x0,
+ (new_max - t->max_size) * sizeof(void*));
+
+ t->data = new_data;
+ t->max_size = new_max;
+ }
+
+ /*
+ * Insert the new entry into the data array
+ */
+ t->data[t->count++] = NETSNMP_REMOVE_CONST(void *, entry);
+ t->dirty = 1;
+
+ /*
+ * if array was dirty before we called get, sync was incremented when
+ * get called SortArray. If we didn't call get or the array wasn't dirty,
+ * bump sync now.
+ */
+ if (!was_dirty)
+ ++c->sync;
+
+ return 0;
+}
+
+/**********************************************************************
+ *
+ * Special case support for subsets
+ *
+ */
+static int
+binary_search_for_start(netsnmp_index *val, netsnmp_container *c)
+{
+ binary_array_table *t = (binary_array_table*)c->container_data;
+ size_t len = t->count;
+ size_t half;
+ size_t middle;
+ size_t first = 0;
+ int result = 0;
+
+ if (!len)
+ return -1;
+
+ if (t->dirty)
+ Sort_Array(c);
+
+ while (len > 0) {
+ half = len >> 1;
+ middle = first;
+ middle += half;
+ if ((result = c->ncompare(t->data[middle], val)) < 0) {
+ first = middle;
+ ++first;
+ len = len - half - 1;
+ } else
+ len = half;
+ }
+
+ if ((first >= t->count) ||
+ c->ncompare(t->data[first], val) != 0)
+ return -1;
+
+ return first;
+}
+
+void **
+netsnmp_binary_array_get_subset(netsnmp_container *c, void *key, int *len)
+{
+ binary_array_table *t;
+ void **subset;
+ int start, end;
+ size_t i;
+
+ /*
+ * if there is no data, return NULL;
+ */
+ if (!c || !key)
+ return NULL;
+
+ t = (binary_array_table*)c->container_data;
+ netsnmp_assert(c->ncompare);
+ if (!t->count | !c->ncompare)
+ return NULL;
+
+ /*
+ * if the table is dirty, sort it.
+ */
+ if (t->dirty)
+ Sort_Array(c);
+
+ /*
+ * find matching items
+ */
+ start = end = binary_search_for_start((netsnmp_index *)key, c);
+ if (start == -1)
+ return NULL;
+
+ for (i = start + 1; i < t->count; ++i) {
+ if (0 != c->ncompare(t->data[i], key))
+ break;
+ ++end;
+ }
+
+ *len = end - start + 1;
+ subset = (void **)malloc((*len) * sizeof(void*));
+ if (subset)
+ memcpy(subset, &t->data[start], sizeof(void*) * (*len));
+
+ return subset;
+}
+
+/**********************************************************************
+ *
+ * container
+ *
+ */
+static void *
+_ba_find(netsnmp_container *container, const void *data)
+{
+ return netsnmp_binary_array_get(container, data, 1);
+}
+
+static void *
+_ba_find_next(netsnmp_container *container, const void *data)
+{
+ return netsnmp_binary_array_get(container, data, 0);
+}
+
+static int
+_ba_insert(netsnmp_container *container, const void *data)
+{
+ return netsnmp_binary_array_insert(container, data);
+}
+
+static int
+_ba_remove(netsnmp_container *container, const void *data)
+{
+ return netsnmp_binary_array_remove(container,data, NULL);
+}
+
+static int
+_ba_free(netsnmp_container *container)
+{
+ netsnmp_binary_array_release(container);
+ return 0;
+}
+
+static size_t
+_ba_size(netsnmp_container *container)
+{
+ return netsnmp_binary_array_count(container);
+}
+
+static void
+_ba_for_each(netsnmp_container *container, netsnmp_container_obj_func *f,
+ void *context)
+{
+ netsnmp_binary_array_for_each(container, f, context, 1);
+}
+
+static void
+_ba_clear(netsnmp_container *container, netsnmp_container_obj_func *f,
+ void *context)
+{
+ netsnmp_binary_array_clear(container, f, context);
+}
+
+static netsnmp_void_array *
+_ba_get_subset(netsnmp_container *container, void *data)
+{
+ netsnmp_void_array * va;
+ void ** rtn;
+ int len;
+
+ rtn = netsnmp_binary_array_get_subset(container, data, &len);
+ if ((NULL==rtn) || (len <=0))
+ return NULL;
+
+ va = SNMP_MALLOC_TYPEDEF(netsnmp_void_array);
+ if (NULL==va)
+ {
+ free (rtn);
+ return NULL;
+ }
+
+ va->size = len;
+ va->array = rtn;
+
+ return va;
+}
+
+static int _ba_options(netsnmp_container *c, int set, u_int flags)
+{
+ return netsnmp_binary_array_options_set(c, set, flags);
+}
+
+static netsnmp_container *
+_ba_duplicate(netsnmp_container *c, void *ctx, u_int flags)
+{
+ netsnmp_container *dup;
+ binary_array_table *dupt, *t;
+
+ if (flags) {
+ snmp_log(LOG_ERR, "binary arry duplicate does not supprt flags yet\n");
+ return NULL;
+ }
+
+ dup = netsnmp_container_get_binary_array();
+ if (NULL == dup) {
+ snmp_log(LOG_ERR," no memory for binary array duplicate\n");
+ return NULL;
+ }
+ /*
+ * deal with container stuff
+ */
+ if (netsnmp_container_data_dup(dup, c) != 0) {
+ netsnmp_binary_array_release(dup);
+ return NULL;
+ }
+
+ /*
+ * deal with data
+ */
+ dupt = (binary_array_table*)dup->container_data;
+ t = (binary_array_table*)c->container_data;
+
+ dupt->max_size = t->max_size;
+ dupt->count = t->count;
+ dupt->dirty = t->dirty;
+
+ /*
+ * shallow copy
+ */
+ dupt->data = (void**) malloc(dupt->max_size * sizeof(void*));
+ if (NULL == dupt->data) {
+ snmp_log(LOG_ERR, "no memory for binary array duplicate\n");
+ netsnmp_binary_array_release(dup);
+ return NULL;
+ }
+
+ memcpy(dupt->data, t->data, dupt->max_size * sizeof(void*));
+
+ return dup;
+}
+
+netsnmp_container *
+netsnmp_container_get_binary_array(void)
+{
+ /*
+ * allocate memory
+ */
+ netsnmp_container *c = SNMP_MALLOC_TYPEDEF(netsnmp_container);
+ if (NULL==c) {
+ snmp_log(LOG_ERR, "couldn't allocate memory\n");
+ return NULL;
+ }
+
+ c->container_data = netsnmp_binary_array_initialize();
+
+ /*
+ * NOTE: CHANGES HERE MUST BE DUPLICATED IN duplicate AS WELL!!
+ */
+ netsnmp_init_container(c, NULL, _ba_free, _ba_size, NULL, _ba_insert,
+ _ba_remove, _ba_find);
+ c->find_next = _ba_find_next;
+ c->get_subset = _ba_get_subset;
+ c->get_iterator = _ba_iterator_get;
+ c->for_each = _ba_for_each;
+ c->clear = _ba_clear;
+ c->options = _ba_options;
+ c->duplicate = _ba_duplicate;
+
+ return c;
+}
+
+netsnmp_factory *
+netsnmp_container_get_binary_array_factory(void)
+{
+ static netsnmp_factory f = { "binary_array",
+ (netsnmp_factory_produce_f*)
+ netsnmp_container_get_binary_array };
+
+ return &f;
+}
+
+void
+netsnmp_container_binary_array_init(void)
+{
+ netsnmp_container_register("binary_array",
+ netsnmp_container_get_binary_array_factory());
+}
+
+/**********************************************************************
+ *
+ * iterator
+ *
+ */
+NETSNMP_STATIC_INLINE binary_array_table *
+_ba_it2cont(binary_array_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(NULL == it->base.container->container_data) {
+ netsnmp_assert(NULL != it->base.container->container_data);
+ return NULL;
+ }
+
+ return (binary_array_table*)(it->base.container->container_data);
+}
+
+NETSNMP_STATIC_INLINE void *
+_ba_iterator_position(binary_array_iterator *it, size_t pos)
+{
+ binary_array_table *t = _ba_it2cont(it);
+ if (NULL == t)
+ return t; /* msg already logged */
+
+ if(it->base.container->sync != it->base.sync) {
+ DEBUGMSGTL(("container:iterator", "out of sync\n"));
+ return NULL;
+ }
+
+ if(0 == t->count) {
+ DEBUGMSGTL(("container:iterator", "empty\n"));
+ return NULL;
+ }
+ else if(pos >= t->count) {
+ DEBUGMSGTL(("container:iterator", "end of container\n"));
+ return NULL;
+ }
+
+ return t->data[ pos ];
+}
+
+static void *
+_ba_iterator_curr(binary_array_iterator *it)
+{
+ if(NULL == it) {
+ netsnmp_assert(NULL != it);
+ return NULL;
+ }
+
+ return _ba_iterator_position(it, it->pos);
+}
+
+static void *
+_ba_iterator_first(binary_array_iterator *it)
+{
+ return _ba_iterator_position(it, 0);
+}
+
+static void *
+_ba_iterator_next(binary_array_iterator *it)
+{
+ if(NULL == it) {
+ netsnmp_assert(NULL != it);
+ return NULL;
+ }
+
+ ++it->pos;
+
+ return _ba_iterator_position(it, it->pos);
+}
+
+static void *
+_ba_iterator_last(binary_array_iterator *it)
+{
+ binary_array_table* t = _ba_it2cont(it);
+ if(NULL == t) {
+ netsnmp_assert(NULL != t);
+ return NULL;
+ }
+
+ return _ba_iterator_position(it, t->count - 1 );
+}
+
+static int
+_ba_iterator_remove(binary_array_iterator *it)
+{
+ binary_array_table* t = _ba_it2cont(it);
+ if(NULL == t) {
+ netsnmp_assert(NULL != t);
+ return -1;
+ }
+
+ /*
+ * since this iterator was used for the remove, keep it in sync with
+ * the container. Also, back up one so that next will be the position
+ * that was just removed.
+ */
+ ++it->base.sync;
+ return netsnmp_binary_array_remove_at(it->base.container, it->pos--, NULL);
+
+}
+
+static int
+_ba_iterator_reset(binary_array_iterator *it)
+{
+ binary_array_table* t = _ba_it2cont(it);
+ if(NULL == t) {
+ netsnmp_assert(NULL != t);
+ return -1;
+ }
+
+ if (t->dirty)
+ Sort_Array(it->base.container);
+
+ /*
+ * save sync count, to make sure container doesn't change while
+ * iterator is in use.
+ */
+ it->base.sync = it->base.container->sync;
+
+ it->pos = 0;
+
+ return 0;
+}
+
+static int
+_ba_iterator_release(netsnmp_iterator *it)
+{
+ free(it);
+
+ return 0;
+}
+
+static netsnmp_iterator *
+_ba_iterator_get(netsnmp_container *c)
+{
+ binary_array_iterator* it;
+
+ if(NULL == c)
+ return NULL;
+
+ it = SNMP_MALLOC_TYPEDEF(binary_array_iterator);
+ if(NULL == it)
+ return NULL;
+
+ it->base.container = c;
+
+ it->base.first = (netsnmp_iterator_rtn*)_ba_iterator_first;
+ it->base.next = (netsnmp_iterator_rtn*)_ba_iterator_next;
+ it->base.curr = (netsnmp_iterator_rtn*)_ba_iterator_curr;
+ it->base.last = (netsnmp_iterator_rtn*)_ba_iterator_last;
+ it->base.remove = (netsnmp_iterator_rc*)_ba_iterator_remove;
+ it->base.reset = (netsnmp_iterator_rc*)_ba_iterator_reset;
+ it->base.release = (netsnmp_iterator_rc*)_ba_iterator_release;
+
+ (void)_ba_iterator_reset(it);
+
+ return (netsnmp_iterator *)it;
+}