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