/src/net-snmp/snmplib/container_binary_array.c
Line | Count | Source |
1 | | /* |
2 | | * container_binary_array.c |
3 | | * |
4 | | * see comments in header file. |
5 | | * |
6 | | * Portions of this file are subject to the following copyright(s). See |
7 | | * the Net-SNMP's COPYING file for more details and other copyrights |
8 | | * that may apply: |
9 | | * |
10 | | * Portions of this file are copyrighted by: |
11 | | * Copyright (c) 2016 VMware, Inc. All rights reserved. |
12 | | * Use is subject to license terms specified in the COPYING file |
13 | | * distributed with the Net-SNMP package. |
14 | | */ |
15 | | |
16 | | #include <net-snmp/net-snmp-config.h> |
17 | | |
18 | | #ifdef HAVE_IO_H |
19 | | #include <io.h> |
20 | | #endif |
21 | | #include <stdio.h> |
22 | | #ifdef HAVE_STDLIB_H |
23 | | #include <stdlib.h> |
24 | | #endif |
25 | | #ifdef HAVE_STDINT_H |
26 | | #include <stdint.h> |
27 | | #endif |
28 | | #ifdef HAVE_MALLOC_H |
29 | | #include <malloc.h> |
30 | | #endif |
31 | | #include <sys/types.h> |
32 | | #ifdef HAVE_STRING_H |
33 | | #include <string.h> |
34 | | #else |
35 | | #include <strings.h> |
36 | | #endif |
37 | | |
38 | | #include <net-snmp/net-snmp-includes.h> |
39 | | #include <net-snmp/types.h> |
40 | | #include <net-snmp/library/snmp_api.h> |
41 | | #include <net-snmp/library/container.h> |
42 | | #include <net-snmp/library/container_binary_array.h> |
43 | | #include <net-snmp/library/tools.h> |
44 | | #include <net-snmp/library/snmp_assert.h> |
45 | | #include "factory.h" |
46 | | |
47 | | typedef struct binary_array_table_s { |
48 | | size_t max_size; /* Size of the current data table */ |
49 | | size_t count; /* Index of the next free entry */ |
50 | | int dirty; |
51 | | void **data; /* The table itself */ |
52 | | } binary_array_table; |
53 | | |
54 | | typedef struct binary_array_iterator_s { |
55 | | netsnmp_iterator base; |
56 | | |
57 | | size_t pos; |
58 | | } binary_array_iterator; |
59 | | |
60 | | static netsnmp_iterator *_ba_iterator_get(netsnmp_container *c); |
61 | | |
62 | | static netsnmp_container *_ba_current_sort_container = NULL; |
63 | | |
64 | | static int |
65 | | _ba_qsort_wrapper(const void *p, const void *q) |
66 | 0 | { |
67 | 0 | const void *lhs = *(const void * const *)p; |
68 | 0 | const void *rhs = *(const void * const *)q; |
69 | 0 | return _ba_current_sort_container->compare(lhs, rhs); |
70 | 0 | } |
71 | | |
72 | | static int |
73 | | Sort_Array(netsnmp_container *c) |
74 | 0 | { |
75 | 0 | binary_array_table *t = (binary_array_table*)c->container_data; |
76 | 0 | netsnmp_assert(t!=NULL); |
77 | 0 | netsnmp_assert(c->compare!=NULL); |
78 | |
|
79 | 0 | if (c->flags & CONTAINER_KEY_UNSORTED) |
80 | 0 | return 0; |
81 | | |
82 | 0 | if (t->dirty) { |
83 | | /* |
84 | | * Sort the table |
85 | | */ |
86 | 0 | _ba_current_sort_container = c; |
87 | 0 | qsort(t->data, t->count, sizeof(t->data[0]), _ba_qsort_wrapper); |
88 | 0 | _ba_current_sort_container = NULL; |
89 | 0 | t->dirty = 0; |
90 | | |
91 | | /* |
92 | | * no way to know if it actually changed... just assume so. |
93 | | */ |
94 | 0 | ++c->sync; |
95 | 0 | } |
96 | |
|
97 | 0 | return 1; |
98 | 0 | } |
99 | | |
100 | | static int |
101 | | linear_search(const void *val, netsnmp_container *c) |
102 | 0 | { |
103 | 0 | binary_array_table *t = (binary_array_table*)c->container_data; |
104 | 0 | size_t pos = 0; |
105 | |
|
106 | 0 | if (!t->count) |
107 | 0 | return -1; |
108 | | |
109 | 0 | if (! (c->flags & CONTAINER_KEY_UNSORTED)) { |
110 | 0 | snmp_log(LOG_ERR, "linear search on sorted container %s?!?\n", |
111 | 0 | c->container_name); |
112 | 0 | return -1; |
113 | 0 | } |
114 | | |
115 | 0 | for (; pos < t->count; ++pos) { |
116 | 0 | if (c->compare(t->data[pos], val) == 0) |
117 | 0 | return pos; |
118 | 0 | } |
119 | | |
120 | 0 | return -1; |
121 | 0 | } |
122 | | |
123 | | static int |
124 | | binary_search(const void *val, netsnmp_container *c, int exact, size_t *next) |
125 | 170k | { |
126 | 170k | binary_array_table *t = (binary_array_table*)c->container_data; |
127 | 170k | size_t len = t->count; |
128 | 170k | size_t half; |
129 | 170k | size_t first = 0; |
130 | 170k | size_t middle = 0; /* init not needed; keeps compiler happy */ |
131 | 170k | int result = 0; /* init not needed; keeps compiler happy */ |
132 | | |
133 | 170k | if (!len) { |
134 | 0 | if (NULL != next) |
135 | 0 | *next = 0; |
136 | 0 | return -1; |
137 | 0 | } |
138 | | |
139 | 170k | if (c->flags & CONTAINER_KEY_UNSORTED) { |
140 | 0 | if (!exact) { |
141 | 0 | snmp_log(LOG_ERR, "non-exact search on unsorted container %s?!?\n", |
142 | 0 | c->container_name); |
143 | 0 | return -1; |
144 | 0 | } |
145 | 0 | return linear_search(val, c); |
146 | 0 | } |
147 | | |
148 | 170k | if (t->dirty) |
149 | 0 | Sort_Array(c); |
150 | | |
151 | 684k | while (len > 0) { |
152 | 569k | half = len >> 1; |
153 | 569k | middle = first + half; |
154 | 569k | if ((result = c->compare(t->data[middle], val)) < 0) { |
155 | 192k | first = middle + 1; |
156 | 192k | len = len - half - 1; |
157 | 376k | } else if (result == 0) { |
158 | 55.7k | first = middle; |
159 | 55.7k | break; |
160 | 320k | } else { |
161 | 320k | len = half; |
162 | 320k | } |
163 | 569k | } |
164 | | |
165 | 170k | if (first >= t->count) { |
166 | 13.9k | if (exact && NULL != next) |
167 | 6.97k | *next = t->count; |
168 | 13.9k | return -1; |
169 | 13.9k | } |
170 | | |
171 | 156k | if (first != middle) { |
172 | | /* last compare wasn't against first, so get actual result */ |
173 | 87.0k | result = c->compare(t->data[first], val); |
174 | 87.0k | } |
175 | | |
176 | 156k | if(result == 0) { |
177 | 55.7k | if (exact && NULL != next) |
178 | 148 | *next = first+1; |
179 | 55.5k | else if (!exact && ++first == t->count) { |
180 | 0 | if (NULL != next) |
181 | 0 | *next = first; |
182 | 0 | first = -1; |
183 | 0 | } |
184 | 101k | } else if(exact) { |
185 | 101k | if (NULL != next) { |
186 | 31.6k | if (result > 0) |
187 | 31.6k | *next = first; |
188 | 0 | else |
189 | 0 | *next = t->count; |
190 | 31.6k | } |
191 | 101k | first = -1; |
192 | 101k | } |
193 | | |
194 | 156k | return first; |
195 | 170k | } |
196 | | |
197 | | NETSNMP_STATIC_INLINE binary_array_table * |
198 | | netsnmp_binary_array_initialize(void) |
199 | 34.6k | { |
200 | 34.6k | binary_array_table *t; |
201 | | |
202 | 34.6k | t = SNMP_MALLOC_TYPEDEF(binary_array_table); |
203 | 34.6k | if (t == NULL) |
204 | 0 | return NULL; |
205 | | |
206 | 34.6k | t->max_size = 0; |
207 | 34.6k | t->count = 0; |
208 | 34.6k | t->dirty = 0; |
209 | 34.6k | t->data = NULL; |
210 | | |
211 | 34.6k | return t; |
212 | 34.6k | } |
213 | | |
214 | | void |
215 | | netsnmp_binary_array_release(netsnmp_container *c) |
216 | 34.6k | { |
217 | 34.6k | binary_array_table *t = (binary_array_table*)c->container_data; |
218 | 34.6k | SNMP_FREE(t->data); |
219 | 34.6k | SNMP_FREE(t); |
220 | 34.6k | SNMP_FREE(c->container_name); |
221 | 34.6k | SNMP_FREE(c); |
222 | 34.6k | } |
223 | | |
224 | | /** |
225 | | * Set or test the options of a binary array container. |
226 | | * @param c: Container. |
227 | | * @param set: Set (1) or test (0). |
228 | | * @param flags: Zero or more CONTAINER_KEY_* flags. |
229 | | */ |
230 | | int |
231 | | netsnmp_binary_array_options_set(netsnmp_container *c, int set, u_int flags) |
232 | 13.9k | { |
233 | 13.9k | #define BA_FLAGS (CONTAINER_KEY_ALLOW_DUPLICATES|CONTAINER_KEY_UNSORTED) |
234 | | |
235 | 13.9k | if (set) { |
236 | 13.9k | if ((flags & BA_FLAGS) == flags) { |
237 | | /** if turning off unsorted, do sort */ |
238 | 13.9k | int sort = ((c->flags & CONTAINER_KEY_UNSORTED) && |
239 | 0 | ! (flags & CONTAINER_KEY_UNSORTED)); |
240 | 13.9k | c->flags = flags; |
241 | 13.9k | if (sort) { |
242 | 0 | binary_array_table *t = (binary_array_table*)c->container_data; |
243 | 0 | t->dirty = 1; /* force sort */ |
244 | 0 | Sort_Array(c); |
245 | 0 | } |
246 | 13.9k | return flags; |
247 | 13.9k | } else { |
248 | 0 | return -1; /* unsupported flag */ |
249 | 0 | } |
250 | 13.9k | } else { |
251 | 0 | return ((c->flags & flags) == flags); |
252 | 0 | } |
253 | 13.9k | } |
254 | | |
255 | | NETSNMP_STATIC_INLINE size_t |
256 | | netsnmp_binary_array_count(netsnmp_container *c) |
257 | 3.44k | { |
258 | 3.44k | binary_array_table *t = (binary_array_table*)c->container_data; |
259 | | /* |
260 | | * return count |
261 | | */ |
262 | 3.44k | return t ? t->count : 0; |
263 | 3.44k | } |
264 | | |
265 | | NETSNMP_STATIC_INLINE void * |
266 | | netsnmp_binary_array_get(netsnmp_container *c, const void *key, int exact) |
267 | 135k | { |
268 | 135k | binary_array_table *t = (binary_array_table*)c->container_data; |
269 | 135k | int index = 0; |
270 | | |
271 | | /* |
272 | | * if there is no data, return NULL; |
273 | | */ |
274 | 135k | if (!t->count) |
275 | 3.48k | return NULL; |
276 | | |
277 | | /* |
278 | | * if the table is dirty, sort it. |
279 | | */ |
280 | 131k | if (t->dirty) |
281 | 0 | Sort_Array(c); |
282 | | |
283 | | /* |
284 | | * if there is a key, search. Otherwise default is 0; |
285 | | */ |
286 | 131k | if (key) { |
287 | 131k | if ((index = binary_search(key, c, exact, NULL)) == -1) |
288 | 76.4k | return NULL; |
289 | 55.5k | if (!exact && |
290 | 0 | c->flags & CONTAINER_KEY_ALLOW_DUPLICATES) { |
291 | 0 | int result; |
292 | | |
293 | | /* |
294 | | * If duplicates are allowed, we have to be extra |
295 | | * sure that we didn't just increment to a duplicate, |
296 | | * thus causing a getnext loop. |
297 | | */ |
298 | 0 | result = c->compare(t->data[index], key); |
299 | 0 | while (result == 0) { |
300 | 0 | DEBUGMSGTL(("container","skipping duplicate key in %s\n", |
301 | 0 | c->container_name)); |
302 | 0 | if (++index == t->count) |
303 | 0 | return NULL; |
304 | 0 | result = c->compare(t->data[index], key); |
305 | 0 | } |
306 | 0 | } |
307 | 55.5k | } |
308 | | |
309 | 55.5k | return t->data[index]; |
310 | 131k | } |
311 | | |
312 | | static int |
313 | | netsnmp_binary_array_get_at(netsnmp_container *c, size_t pos, void **entry) |
314 | 0 | { |
315 | 0 | binary_array_table *t = (binary_array_table*)c->container_data; |
316 | | |
317 | | /* |
318 | | * if there is no data, return NULL; |
319 | | */ |
320 | 0 | if (!t->count || pos >= t->count || NULL == entry) |
321 | 0 | return -1; |
322 | | |
323 | 0 | *entry = t->data[pos]; |
324 | |
|
325 | 0 | return 0; |
326 | 0 | } |
327 | | |
328 | | /** |
329 | | * Returns 1 if and only if the elements in @c are sorted in ascending order. |
330 | | * |
331 | | * To do: stop calling this function after |
332 | | * https://github.com/net-snmp/net-snmp/issues/107 and |
333 | | * https://github.com/net-snmp/net-snmp/issues/293 have been fixed. |
334 | | */ |
335 | | static int _ba_is_sorted(const netsnmp_container *c) |
336 | 42.1k | { |
337 | | /* |
338 | | * The code below has been commented out because it negatively affects |
339 | | * performance. |
340 | | */ |
341 | | #if 0 |
342 | | const binary_array_table *t = c->container_data; |
343 | | int i; |
344 | | |
345 | | for (i = 0; i + 1 < t->count; ++i) |
346 | | if (c->compare(t->data[i], t->data[i + 1]) > 0) |
347 | | return 0; |
348 | | #endif |
349 | | |
350 | 42.1k | return 1; |
351 | 42.1k | } |
352 | | |
353 | | int |
354 | | netsnmp_binary_array_remove_at(netsnmp_container *c, size_t index, void **save) |
355 | 0 | { |
356 | 0 | binary_array_table *t = (binary_array_table*)c->container_data; |
357 | |
|
358 | 0 | if (save) |
359 | 0 | *save = NULL; |
360 | | |
361 | | /* |
362 | | * if there is no data, return NULL; |
363 | | */ |
364 | 0 | if (!t->count) |
365 | 0 | return -1; |
366 | | |
367 | | /* |
368 | | * find old data and save it, if ptr provided |
369 | | */ |
370 | 0 | if (save) |
371 | 0 | *save = t->data[index]; |
372 | | |
373 | | /* |
374 | | * if entry was last item, just decrement count |
375 | | */ |
376 | 0 | --t->count; |
377 | 0 | if (index != t->count) { |
378 | | /* |
379 | | * otherwise, shift array down |
380 | | */ |
381 | 0 | memmove(&t->data[index], &t->data[index+1], |
382 | 0 | sizeof(void*) * (t->count - index)); |
383 | |
|
384 | 0 | ++c->sync; |
385 | 0 | } |
386 | |
|
387 | 0 | netsnmp_assert(t->dirty || _ba_is_sorted(c)); |
388 | |
|
389 | 0 | return 0; |
390 | 0 | } |
391 | | |
392 | | int |
393 | | netsnmp_binary_array_remove(netsnmp_container *c, const void *key, void **save) |
394 | 0 | { |
395 | 0 | binary_array_table *t = (binary_array_table*)c->container_data; |
396 | 0 | int index = 0; |
397 | |
|
398 | 0 | if (save) |
399 | 0 | *save = NULL; |
400 | | |
401 | | /* |
402 | | * if there is no data, return NULL; |
403 | | */ |
404 | 0 | if (!t->count) |
405 | 0 | return 0; |
406 | | |
407 | | /* |
408 | | * if the table is dirty, sort it. |
409 | | */ |
410 | 0 | if (t->dirty) |
411 | 0 | Sort_Array(c); |
412 | | |
413 | | /* |
414 | | * search |
415 | | */ |
416 | 0 | if ((index = binary_search(key, c, 1, NULL)) == -1) |
417 | 0 | return -1; |
418 | | |
419 | 0 | return netsnmp_binary_array_remove_at(c, (size_t)index, save); |
420 | 0 | } |
421 | | |
422 | | NETSNMP_STATIC_INLINE void |
423 | | netsnmp_binary_array_for_each(netsnmp_container *c, |
424 | | netsnmp_container_obj_func *fe, |
425 | | void *context, int sort) |
426 | 3.48k | { |
427 | 3.48k | binary_array_table *t = (binary_array_table*)c->container_data; |
428 | 3.48k | size_t i; |
429 | | |
430 | 3.48k | if (sort && t->dirty) |
431 | 0 | Sort_Array(c); |
432 | | |
433 | 45.2k | for (i = 0; i < t->count; ++i) |
434 | 41.7k | (*fe) (t->data[i], context); |
435 | 3.48k | } |
436 | | |
437 | | NETSNMP_STATIC_INLINE void |
438 | | netsnmp_binary_array_clear(netsnmp_container *c, |
439 | | netsnmp_container_obj_func *fe, |
440 | | void *context) |
441 | 31.1k | { |
442 | 31.1k | binary_array_table *t = (binary_array_table*)c->container_data; |
443 | | |
444 | 31.1k | if( NULL != fe ) { |
445 | 17.2k | size_t i; |
446 | | |
447 | 17.2k | for (i = 0; i < t->count; ++i) |
448 | 0 | (*fe) (t->data[i], context); |
449 | 17.2k | } |
450 | | |
451 | 31.1k | t->count = 0; |
452 | 31.1k | t->dirty = 0; |
453 | 31.1k | ++c->sync; |
454 | 31.1k | } |
455 | | |
456 | | static int |
457 | | _ba_resize_check(binary_array_table *t) |
458 | 42.1k | { |
459 | 42.1k | size_t new_max; |
460 | 42.1k | void ** new_data; |
461 | 42.1k | if (t->max_size > t->count) |
462 | 35.1k | return 0; /* resize not needed */ |
463 | | |
464 | | /* |
465 | | * Table is full, so extend it to double the size, or use 10 elements |
466 | | * if it is empty. |
467 | | */ |
468 | 6.97k | new_max = t->max_size > 0 ? 2 * t->max_size : 10; |
469 | 6.97k | new_data = (void**) realloc(t->data, new_max * sizeof(void*)); |
470 | 6.97k | if (new_data == NULL) { |
471 | 0 | snmp_log(LOG_ERR, "malloc failed in _ba_resize_check\n"); |
472 | 0 | return -1; /* error */ |
473 | 0 | } |
474 | | |
475 | 6.97k | memset(new_data + t->max_size, 0x0, |
476 | 6.97k | (new_max - t->max_size) * sizeof(void*)); |
477 | | |
478 | 6.97k | t->data = new_data; |
479 | 6.97k | t->max_size = new_max; |
480 | | |
481 | 6.97k | return 1; /* resized */ |
482 | 6.97k | } |
483 | | |
484 | | static int |
485 | | netsnmp_binary_array_insert_before(netsnmp_container *c, size_t index, |
486 | | const void *entry, int dirty) |
487 | 42.1k | { |
488 | 42.1k | binary_array_table *t = (binary_array_table*)c->container_data; |
489 | | |
490 | 42.1k | if (NULL == entry) |
491 | 0 | return -1; |
492 | | |
493 | 42.1k | if (index > t->count) { |
494 | 0 | DEBUGMSGTL(("container:insert:before", "index out of range\n")); |
495 | 0 | return -1; |
496 | 0 | } |
497 | | |
498 | | /* |
499 | | * check if we need to resize the array |
500 | | */ |
501 | 42.1k | _ba_resize_check(t); |
502 | | |
503 | 42.1k | netsnmp_assert(t->count < t->max_size); |
504 | | |
505 | | /* |
506 | | * shift array |
507 | | */ |
508 | 42.1k | memmove(&t->data[index+1], &t->data[index], |
509 | 42.1k | sizeof(void*) * (t->count - index)); |
510 | | |
511 | | /* |
512 | | * Insert the new entry into the data array |
513 | | */ |
514 | 42.1k | t->data[index] = NETSNMP_REMOVE_CONST(void *, entry); |
515 | 42.1k | ++t->count; |
516 | | |
517 | 42.1k | netsnmp_assert(index < t->count); |
518 | 42.1k | netsnmp_assert(t->count <= t->max_size); |
519 | | |
520 | 42.1k | if (dirty) |
521 | 0 | t->dirty = 1; |
522 | | |
523 | 42.1k | netsnmp_assert(t->dirty || _ba_is_sorted(c)); |
524 | | |
525 | 42.1k | ++c->sync; |
526 | | |
527 | 42.1k | return 0; |
528 | 42.1k | } |
529 | | |
530 | | NETSNMP_STATIC_INLINE int |
531 | | netsnmp_binary_array_insert(netsnmp_container *c, const void *const_entry) |
532 | 42.2k | { |
533 | 42.2k | binary_array_table *t = (binary_array_table*)c->container_data; |
534 | 42.2k | const int duplicates_allowed = c->flags & CONTAINER_KEY_ALLOW_DUPLICATES; |
535 | 42.2k | const int sorted = !(c->flags & CONTAINER_KEY_UNSORTED); |
536 | 42.2k | int i = -2; |
537 | 42.2k | size_t next, pos; |
538 | 42.2k | void *entry = NETSNMP_REMOVE_CONST(void *, const_entry); |
539 | | |
540 | 42.2k | if (NULL == entry) |
541 | 0 | return -1; |
542 | | |
543 | | /* |
544 | | * check key if we have at least 1 item and duplicates aren't allowed |
545 | | */ |
546 | 42.2k | if (!duplicates_allowed && t->count) { |
547 | 38.7k | i = binary_search(entry, c, 1, &next); |
548 | 38.7k | if (i >= 0) { |
549 | 148 | DEBUGMSGTL(("container","not inserting duplicate key\n")); |
550 | 148 | return -1; |
551 | 148 | } |
552 | 38.7k | } |
553 | | |
554 | | /* |
555 | | * if unsorted, just add at the end |
556 | | */ |
557 | 42.1k | if (!sorted) { |
558 | 0 | pos = t->count; |
559 | 42.1k | } else { |
560 | | /** if we haven't searched for key yet, do it now */ |
561 | 42.1k | if (-2 == i) { |
562 | 3.48k | if (0 == t->count) { |
563 | 3.48k | next = 0; |
564 | 3.48k | i = -1; |
565 | 3.48k | } else { |
566 | 0 | i = binary_search(entry, c, 1, &next); |
567 | 0 | } |
568 | 3.48k | } |
569 | | |
570 | 42.1k | pos = next; |
571 | | /* if key found, advance past any duplicates */ |
572 | 42.1k | if (duplicates_allowed && i >= 0) |
573 | 0 | while (pos < t->count && c->compare(t->data[pos], entry) == 0) |
574 | 0 | ++pos; |
575 | 42.1k | } |
576 | | |
577 | 42.1k | return netsnmp_binary_array_insert_before(c, pos, entry, !sorted); |
578 | 42.2k | } |
579 | | |
580 | | /********************************************************************** |
581 | | * |
582 | | * Special case support for subsets |
583 | | * |
584 | | */ |
585 | | static int |
586 | | binary_search_for_start(netsnmp_index *val, netsnmp_container *c) |
587 | 0 | { |
588 | 0 | binary_array_table *t = (binary_array_table*)c->container_data; |
589 | 0 | size_t len = t->count; |
590 | 0 | size_t half; |
591 | 0 | size_t middle; |
592 | 0 | size_t first = 0; |
593 | 0 | int result = 0; |
594 | |
|
595 | 0 | if (!len) |
596 | 0 | return -1; |
597 | | |
598 | 0 | if (t->dirty) |
599 | 0 | Sort_Array(c); |
600 | |
|
601 | 0 | while (len > 0) { |
602 | 0 | half = len >> 1; |
603 | 0 | middle = first + half; |
604 | 0 | if ((result = c->ncompare(t->data[middle], val)) < 0) { |
605 | 0 | first = middle + 1; |
606 | 0 | len = len - half - 1; |
607 | 0 | } else |
608 | 0 | len = half; |
609 | 0 | } |
610 | |
|
611 | 0 | if ((first >= t->count) || |
612 | 0 | c->ncompare(t->data[first], val) != 0) |
613 | 0 | return -1; |
614 | | |
615 | 0 | return first; |
616 | 0 | } |
617 | | |
618 | | void ** |
619 | | netsnmp_binary_array_get_subset(netsnmp_container *c, void *key, int *len) |
620 | 0 | { |
621 | 0 | binary_array_table *t; |
622 | 0 | void **subset; |
623 | 0 | int start, end, subset_size; |
624 | 0 | size_t i; |
625 | | |
626 | | /* |
627 | | * if there is no data, return NULL; |
628 | | */ |
629 | 0 | if (!c || !key || !len) |
630 | 0 | return NULL; |
631 | | |
632 | 0 | t = (binary_array_table*)c->container_data; |
633 | 0 | netsnmp_assert(c->ncompare); |
634 | 0 | if (!t->count || !c->ncompare) |
635 | 0 | return NULL; |
636 | | |
637 | | /* |
638 | | * if the table is dirty, sort it. |
639 | | */ |
640 | 0 | if (t->dirty) |
641 | 0 | Sort_Array(c); |
642 | | |
643 | | /* |
644 | | * find matching items |
645 | | */ |
646 | 0 | start = end = binary_search_for_start((netsnmp_index *)key, c); |
647 | | /* |
648 | | * Although start == end, Coverity doesn't seem to realize this. Hence |
649 | | * check both 'start' and 'end'. |
650 | | */ |
651 | 0 | if (start < 0 || end < 0 || start >= INT_MAX - 1 || end >= INT_MAX - 1) |
652 | 0 | return NULL; |
653 | | |
654 | 0 | for (i = start + 1; i < t->count; ++i) { |
655 | 0 | if (0 != c->ncompare(t->data[i], key)) |
656 | 0 | break; |
657 | 0 | if (end >= INT_MAX - 1) |
658 | 0 | break; |
659 | 0 | ++end; |
660 | 0 | } |
661 | |
|
662 | 0 | *len = end - start + 1; |
663 | 0 | if (*len <= 0 || *len > INT_MAX / sizeof(void*)) |
664 | 0 | return NULL; |
665 | | |
666 | 0 | subset_size = *len * sizeof(void *); |
667 | 0 | subset = malloc(subset_size); |
668 | 0 | if (subset) |
669 | 0 | memcpy(subset, &t->data[start], subset_size); |
670 | |
|
671 | 0 | return subset; |
672 | 0 | } |
673 | | |
674 | | /********************************************************************** |
675 | | * |
676 | | * container |
677 | | * |
678 | | */ |
679 | | static void * |
680 | | _ba_find(netsnmp_container *container, const void *data) |
681 | 135k | { |
682 | 135k | return netsnmp_binary_array_get(container, data, 1); |
683 | 135k | } |
684 | | |
685 | | static void * |
686 | | _ba_find_next(netsnmp_container *container, const void *data) |
687 | 0 | { |
688 | 0 | return netsnmp_binary_array_get(container, data, 0); |
689 | 0 | } |
690 | | |
691 | | static int |
692 | | _ba_insert(netsnmp_container *container, const void *data) |
693 | 42.2k | { |
694 | 42.2k | return netsnmp_binary_array_insert(container, data); |
695 | 42.2k | } |
696 | | |
697 | | static int |
698 | | _ba_insert_before(netsnmp_container *container, size_t index, void *data) |
699 | 0 | { |
700 | | /** don't trust users direct-acces inserts, mark array dirty */ |
701 | 0 | return netsnmp_binary_array_insert_before(container, index, data, 1); |
702 | 0 | } |
703 | | |
704 | | static int |
705 | | _ba_remove(netsnmp_container *container, const void *data) |
706 | 0 | { |
707 | 0 | return netsnmp_binary_array_remove(container,data, NULL); |
708 | 0 | } |
709 | | |
710 | | static int |
711 | | _ba_free(netsnmp_container *container) |
712 | 34.6k | { |
713 | 34.6k | netsnmp_binary_array_release(container); |
714 | 34.6k | return 0; |
715 | 34.6k | } |
716 | | |
717 | | static size_t |
718 | | _ba_size(netsnmp_container *container) |
719 | 3.44k | { |
720 | 3.44k | return netsnmp_binary_array_count(container); |
721 | 3.44k | } |
722 | | |
723 | | static void |
724 | | _ba_for_each(netsnmp_container *container, netsnmp_container_obj_func *f, |
725 | | void *context) |
726 | 3.48k | { |
727 | 3.48k | netsnmp_binary_array_for_each(container, f, context, 1); |
728 | 3.48k | } |
729 | | |
730 | | static void |
731 | | _ba_clear(netsnmp_container *container, netsnmp_container_obj_func *f, |
732 | | void *context) |
733 | 31.1k | { |
734 | 31.1k | netsnmp_binary_array_clear(container, f, context); |
735 | 31.1k | } |
736 | | |
737 | | static netsnmp_void_array * |
738 | | _ba_get_subset(netsnmp_container *container, void *data) |
739 | 0 | { |
740 | 0 | netsnmp_void_array * va; |
741 | 0 | void ** rtn; |
742 | 0 | int len; |
743 | |
|
744 | 0 | rtn = netsnmp_binary_array_get_subset(container, data, &len); |
745 | 0 | if (NULL==rtn) |
746 | 0 | return NULL; |
747 | | |
748 | 0 | va = SNMP_MALLOC_TYPEDEF(netsnmp_void_array); |
749 | 0 | if (va == NULL) { |
750 | 0 | free(rtn); |
751 | 0 | return NULL; |
752 | 0 | } |
753 | | |
754 | 0 | va->size = len; |
755 | 0 | va->array = rtn; |
756 | |
|
757 | 0 | return va; |
758 | 0 | } |
759 | | |
760 | | static int _ba_options(netsnmp_container *c, int set, u_int flags) |
761 | 13.9k | { |
762 | 13.9k | return netsnmp_binary_array_options_set(c, set, flags); |
763 | 13.9k | } |
764 | | |
765 | | static netsnmp_container * |
766 | | _ba_duplicate(netsnmp_container *c, void *ctx, u_int flags) |
767 | 0 | { |
768 | 0 | netsnmp_container *dup; |
769 | 0 | binary_array_table *dupt, *t; |
770 | |
|
771 | 0 | if (flags) { |
772 | 0 | snmp_log(LOG_ERR, "binary arry duplicate does not supprt flags yet\n"); |
773 | 0 | return NULL; |
774 | 0 | } |
775 | | |
776 | 0 | dup = netsnmp_container_get_binary_array(); |
777 | 0 | if (NULL == dup) { |
778 | 0 | snmp_log(LOG_ERR," no memory for binary array duplicate\n"); |
779 | 0 | return NULL; |
780 | 0 | } |
781 | | /* |
782 | | * deal with container stuff |
783 | | */ |
784 | 0 | if (netsnmp_container_data_dup(dup, c) != 0) { |
785 | 0 | netsnmp_binary_array_release(dup); |
786 | 0 | return NULL; |
787 | 0 | } |
788 | | |
789 | | /* |
790 | | * deal with data |
791 | | */ |
792 | 0 | dupt = (binary_array_table*)dup->container_data; |
793 | 0 | t = (binary_array_table*)c->container_data; |
794 | |
|
795 | 0 | dupt->max_size = t->max_size; |
796 | 0 | dupt->count = t->count; |
797 | 0 | dupt->dirty = t->dirty; |
798 | | |
799 | | /* |
800 | | * shallow copy |
801 | | */ |
802 | 0 | dupt->data = (void**) malloc(dupt->max_size * sizeof(void*)); |
803 | 0 | if (NULL == dupt->data) { |
804 | 0 | snmp_log(LOG_ERR, "no memory for binary array duplicate\n"); |
805 | 0 | netsnmp_binary_array_release(dup); |
806 | 0 | return NULL; |
807 | 0 | } |
808 | | |
809 | 0 | memcpy(dupt->data, t->data, dupt->max_size * sizeof(void*)); |
810 | |
|
811 | 0 | return dup; |
812 | 0 | } |
813 | | |
814 | | netsnmp_container * |
815 | | netsnmp_container_get_binary_array(void) |
816 | 34.6k | { |
817 | | /* |
818 | | * allocate memory |
819 | | */ |
820 | 34.6k | netsnmp_container *c = SNMP_MALLOC_TYPEDEF(netsnmp_container); |
821 | 34.6k | if (NULL==c) { |
822 | 0 | snmp_log(LOG_ERR, "couldn't allocate memory\n"); |
823 | 0 | return NULL; |
824 | 0 | } |
825 | | |
826 | 34.6k | c->container_data = netsnmp_binary_array_initialize(); |
827 | 34.6k | if (NULL == c->container_data) { |
828 | 0 | free(c); |
829 | 0 | snmp_log(LOG_ERR, "couldn't allocate memory for container_data\n"); |
830 | 0 | return NULL; |
831 | 0 | } |
832 | | |
833 | | /* |
834 | | * NOTE: CHANGES HERE MUST BE DUPLICATED IN duplicate AS WELL!! |
835 | | */ |
836 | 34.6k | netsnmp_init_container(c, NULL, _ba_free, _ba_size, NULL, _ba_insert, |
837 | 34.6k | _ba_remove, _ba_find); |
838 | 34.6k | c->find_next = _ba_find_next; |
839 | 34.6k | c->get_subset = _ba_get_subset; |
840 | 34.6k | c->get_iterator = _ba_iterator_get; |
841 | 34.6k | c->for_each = _ba_for_each; |
842 | 34.6k | c->clear = _ba_clear; |
843 | 34.6k | c->options = _ba_options; |
844 | 34.6k | c->duplicate = _ba_duplicate; |
845 | 34.6k | c->get_at = netsnmp_binary_array_get_at; |
846 | 34.6k | c->remove_at = netsnmp_binary_array_remove_at; |
847 | 34.6k | c->insert_before = _ba_insert_before; |
848 | | |
849 | 34.6k | return c; |
850 | 34.6k | } |
851 | | |
852 | | netsnmp_factory * |
853 | | netsnmp_container_get_binary_array_factory(void) |
854 | 3.48k | { |
855 | 3.48k | static netsnmp_factory f = { "binary_array", |
856 | 3.48k | netsnmp_container_get_binary_array }; |
857 | | |
858 | 3.48k | return &f; |
859 | 3.48k | } |
860 | | |
861 | | void |
862 | | netsnmp_container_binary_array_init(void) |
863 | 3.48k | { |
864 | 3.48k | netsnmp_container_register("binary_array", |
865 | 3.48k | netsnmp_container_get_binary_array_factory()); |
866 | 3.48k | } |
867 | | |
868 | | /********************************************************************** |
869 | | * |
870 | | * iterator |
871 | | * |
872 | | */ |
873 | | NETSNMP_STATIC_INLINE binary_array_table * |
874 | | _ba_it2cont(binary_array_iterator *it) |
875 | 10 | { |
876 | 10 | if(NULL == it) { |
877 | 0 | netsnmp_assert(NULL != it); |
878 | 0 | return NULL; |
879 | 0 | } |
880 | 10 | if(NULL == it->base.container) { |
881 | 0 | netsnmp_assert(NULL != it->base.container); |
882 | 0 | return NULL; |
883 | 0 | } |
884 | 10 | if(NULL == it->base.container->container_data) { |
885 | 0 | netsnmp_assert(NULL != it->base.container->container_data); |
886 | 0 | return NULL; |
887 | 0 | } |
888 | | |
889 | 10 | return (binary_array_table*)(it->base.container->container_data); |
890 | 10 | } |
891 | | |
892 | | NETSNMP_STATIC_INLINE void * |
893 | | _ba_iterator_position(binary_array_iterator *it, size_t pos) |
894 | 5 | { |
895 | 5 | binary_array_table *t = _ba_it2cont(it); |
896 | 5 | if (NULL == t) |
897 | 0 | return t; /* msg already logged */ |
898 | | |
899 | 5 | if(it->base.container->sync != it->base.sync) { |
900 | 0 | DEBUGMSGTL(("container:iterator", "out of sync\n")); |
901 | 0 | return NULL; |
902 | 0 | } |
903 | | |
904 | 5 | if(0 == t->count) { |
905 | 5 | DEBUGMSGTL(("container:iterator", "empty\n")); |
906 | 5 | return NULL; |
907 | 5 | } |
908 | 0 | else if(pos >= t->count) { |
909 | 0 | DEBUGMSGTL(("container:iterator", "end of container\n")); |
910 | 0 | return NULL; |
911 | 0 | } |
912 | | |
913 | 0 | return t->data[ pos ]; |
914 | 5 | } |
915 | | |
916 | | static void * |
917 | | _ba_iterator_curr(netsnmp_iterator *nit) |
918 | 0 | { |
919 | 0 | binary_array_iterator *it = (void *)nit; |
920 | |
|
921 | 0 | if(NULL == it) { |
922 | 0 | netsnmp_assert(NULL != it); |
923 | 0 | return NULL; |
924 | 0 | } |
925 | | |
926 | 0 | return _ba_iterator_position(it, it->pos); |
927 | 0 | } |
928 | | |
929 | | static void * |
930 | | _ba_iterator_first(netsnmp_iterator *nit) |
931 | 5 | { |
932 | 5 | binary_array_iterator *it = (void *)nit; |
933 | | |
934 | 5 | return _ba_iterator_position(it, 0); |
935 | 5 | } |
936 | | |
937 | | static void * |
938 | | _ba_iterator_next(netsnmp_iterator *nit) |
939 | 0 | { |
940 | 0 | binary_array_iterator *it = (void *)nit; |
941 | |
|
942 | 0 | if(NULL == it) { |
943 | 0 | netsnmp_assert(NULL != it); |
944 | 0 | return NULL; |
945 | 0 | } |
946 | | |
947 | 0 | ++it->pos; |
948 | |
|
949 | 0 | return _ba_iterator_position(it, it->pos); |
950 | 0 | } |
951 | | |
952 | | static void * |
953 | | _ba_iterator_last(netsnmp_iterator *nit) |
954 | 0 | { |
955 | 0 | binary_array_iterator *it = (void *)nit; |
956 | 0 | binary_array_table* t = _ba_it2cont(it); |
957 | 0 | if(NULL == t) { |
958 | 0 | netsnmp_assert(NULL != t); |
959 | 0 | return NULL; |
960 | 0 | } |
961 | | |
962 | 0 | return _ba_iterator_position(it, t->count - 1 ); |
963 | 0 | } |
964 | | |
965 | | static int |
966 | | _ba_iterator_remove(netsnmp_iterator *nit) |
967 | 0 | { |
968 | 0 | binary_array_iterator *it = (void *)nit; |
969 | 0 | binary_array_table* t = _ba_it2cont(it); |
970 | |
|
971 | 0 | if(NULL == t) { |
972 | 0 | netsnmp_assert(NULL != t); |
973 | 0 | return -1; |
974 | 0 | } |
975 | | |
976 | | /* |
977 | | * since this iterator was used for the remove, keep it in sync with |
978 | | * the container. Also, back up one so that next will be the position |
979 | | * that was just removed. |
980 | | */ |
981 | 0 | ++it->base.sync; |
982 | 0 | return netsnmp_binary_array_remove_at(it->base.container, it->pos--, NULL); |
983 | |
|
984 | 0 | } |
985 | | |
986 | | static int |
987 | | _ba_iterator_reset(netsnmp_iterator *nit) |
988 | 5 | { |
989 | 5 | binary_array_iterator *it = (void *)nit; |
990 | 5 | binary_array_table* t = _ba_it2cont(it); |
991 | 5 | if(NULL == t) { |
992 | 0 | netsnmp_assert(NULL != t); |
993 | 0 | return -1; |
994 | 0 | } |
995 | | |
996 | 5 | if (t->dirty) |
997 | 0 | Sort_Array(it->base.container); |
998 | | |
999 | | /* |
1000 | | * save sync count, to make sure container doesn't change while |
1001 | | * iterator is in use. |
1002 | | */ |
1003 | 5 | it->base.sync = it->base.container->sync; |
1004 | | |
1005 | 5 | it->pos = 0; |
1006 | | |
1007 | 5 | return 0; |
1008 | 5 | } |
1009 | | |
1010 | | static int |
1011 | | _ba_iterator_release(netsnmp_iterator *it) |
1012 | 5 | { |
1013 | 5 | free(it); |
1014 | | |
1015 | 5 | return 0; |
1016 | 5 | } |
1017 | | |
1018 | | static netsnmp_iterator * |
1019 | | _ba_iterator_get(netsnmp_container *c) |
1020 | 5 | { |
1021 | 5 | binary_array_iterator* it; |
1022 | | |
1023 | 5 | if(NULL == c) |
1024 | 0 | return NULL; |
1025 | | |
1026 | 5 | it = SNMP_MALLOC_TYPEDEF(binary_array_iterator); |
1027 | 5 | if(NULL == it) |
1028 | 0 | return NULL; |
1029 | | |
1030 | 5 | it->base.container = c; |
1031 | | |
1032 | 5 | it->base.first = _ba_iterator_first; |
1033 | 5 | it->base.next = _ba_iterator_next; |
1034 | 5 | it->base.curr = _ba_iterator_curr; |
1035 | 5 | it->base.last = _ba_iterator_last; |
1036 | 5 | it->base.remove = _ba_iterator_remove; |
1037 | 5 | it->base.reset = _ba_iterator_reset; |
1038 | 5 | it->base.release = _ba_iterator_release; |
1039 | | |
1040 | 5 | (void)_ba_iterator_reset(&it->base); |
1041 | | |
1042 | 5 | return &it->base; |
1043 | 5 | } |