>From c78a594c6743bb817f1cd76b4db9077049e25462 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 29 Jan 2016 17:53:20 +1300 Subject: [PATCH 1/2] util/binsearch: macro for greater than or equal search Sometimes you want to find the place where an item would be in a sorted list, whether or not it is actually there. The BINARY_ARRAY_SEARCH_GTE macro takes an extra 'next' pointer argument over the other binsearch macros. This will end up pointing to the next element in the case where there is not an exact match, or NULL when there is. That is, searching the list { 2, 3, 4, 4, 9} with a standard integer compare should give the following results: search term *result *next 1 - 2 3 3 - 4 4 [1] - 7 - 9 9 9 - 10 - - [2] Notes [1] There are two fours, but you will always get the first one. [2] The both NULL case means the search term is beyond the last list item. You can safely use the same pointer for both 'result' and 'next', if you don't care to distinguish between the 'greater-than' and 'equals' cases. There is a torture test for this. Signed-off-by: Douglas Bagnall --- lib/util/binsearch.h | 33 +++++++++ lib/util/tests/binsearch.c | 144 ++++++++++++++++++++++++++++++++++++ source4/torture/local/local.c | 1 + source4/torture/local/wscript_build | 2 +- 4 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 lib/util/tests/binsearch.c diff --git a/lib/util/binsearch.h b/lib/util/binsearch.h index f85d116..001f2c5 100644 --- a/lib/util/binsearch.h +++ b/lib/util/binsearch.h @@ -81,4 +81,37 @@ if (_r < 0) _e = _i - 1; else _b = _i + 1; \ }} } while (0) + +/* + like BINARY_ARRAY_SEARCH_V, but if an exact result is not found, the 'next' + argument will point to the element after the place where the exact result + would have been. If an exact result is found, 'next' will be NULL. If the + target is beyond the end of the list, both 'result' and 'next' will be NULL. + Unlike other binsearch macros, where there are several elements that compare + the same, the result will always point to the first one. + + If you don't care to distinguish between the 'greater than' and 'equals' + cases, you can use the same pointer for both 'result' and 'next'. + + As with all the binsearch macros, the comparison function is always called + with the search term first. + */ +#define BINARY_ARRAY_SEARCH_GTE(array, array_size, target, comparison_fn, \ + result, next) do { \ + int32_t _b, _e; \ + (result) = NULL; (next) = NULL; \ + if ((array_size) > 0) { \ + for (_b = 0, _e = (array_size)-1; _b <= _e; ) { \ + int32_t _i = (_b + _e) / 2; \ + int _r = comparison_fn(target, array[_i]); \ + if (_r == 0) { \ + (result) = &array[_i]; \ + _e = _i - 1; \ + } else if (_r < 0) { _e = _i - 1; \ + } else { _b = _i + 1; } \ + } \ + if ((result) == NULL &&_b < (array_size)) { \ + (next) = &array[_b]; \ + } } } while (0) + #endif diff --git a/lib/util/tests/binsearch.c b/lib/util/tests/binsearch.c new file mode 100644 index 0000000..f8a0227 --- /dev/null +++ b/lib/util/tests/binsearch.c @@ -0,0 +1,144 @@ +/* + Unix SMB/CIFS implementation. + + Tests for binsearch.h macros. + + Copyright Catalyst IT 2016. + + Written by Douglas Bagnall + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "includes.h" +#include "lib/util/binsearch.h" +#include "torture/torture.h" +#include "torture/local/proto.h" + +static int int_cmp(int a, int b) +{ + return a - b; +} + + +static bool test_binsearch_gte(struct torture_context *tctx) +{ + int array[] = { -11, -7, -7, -7, -1, 0, 0, 1, 723, 723, 723, + 724, 724, 10000}; + size_t a_len = ARRAY_SIZE(array); + int targets[] = { -121, -8, -7, -6, 17, -10, 10, -1, 723, + 724, 725, 10002, 10000, 0, -11, 1, 11}; + int i, j, target; + int *result = NULL, *next = NULL; + + for (i = 0; i < ARRAY_SIZE(targets); i++) { + target = targets[i]; + torture_comment(tctx, "looking for targets[%d] %d\n", + i, target); + + BINARY_ARRAY_SEARCH_GTE(array, a_len, target, + int_cmp, result, next); + + if (result == NULL) { + /* we think there is no exact match */ + for (j = 0; j < a_len; j++) { + if (target == array[j]) { + torture_comment(tctx, + "failed to find %d\n", + targets[i]); + torture_fail(tctx, + "result is wrongly NULL"); + } + } + if (next != NULL) { + torture_assert(tctx, (next >= array && + next < array + a_len), + "next is out of bounds"); + + torture_assert(tctx, *next > target, + "next <= target"); + if (target <= array[0]) { + torture_assert(tctx, next == array, + "search before start failed"); + } + if (next != array) { + torture_assert(tctx, next[-1] < target, + "next[-1] >= target"); + } + } + else { + torture_assert(tctx, array[a_len - 1] < target, + "next was not found\n"); + } + } else { + /* we think we found an exact match */ + torture_assert(tctx, *result == target, + "result has wrong value"); + + torture_assert(tctx, (result >= array && + result < array + a_len), + "result is out of bounds!"); + + torture_assert(tctx, next == NULL, + "next should be NULL on exact match\n"); + if (result != array) { + torture_assert(tctx, result[-1] != target, + "didn't find first target\n"); + } + } + if (target >= array[a_len - 1]) { + torture_assert(tctx, next == NULL, + "next is not NULL at array end\n"); + } + } + + /* try again, with result and next the same pointer */ + for (i = 0; i < ARRAY_SIZE(targets); i++) { + target = targets[i]; + torture_comment(tctx, "looking for targets[%d] %d\n", + i, target); + + BINARY_ARRAY_SEARCH_GTE(array, a_len, target, + int_cmp, result, result); + + if (result == NULL) { + /* we think the target is greater than all elements */ + torture_assert(tctx, array[a_len - 1] < target, + "element >= target not found\n"); + } else { + /* we think an element is >= target */ + torture_assert(tctx, *result >= target, + "result has wrong value"); + + torture_assert(tctx, (result >= array && + result < array + a_len), + "result is out of bounds!"); + + if (result != array) { + torture_assert(tctx, result[-1] < target, + "didn't find first target\n"); + } + } + } + + return true; +} + +struct torture_suite *torture_local_util_binsearch(TALLOC_CTX *mem_ctx) +{ + struct torture_suite *suite = torture_suite_create(mem_ctx, "binsearch"); + torture_suite_add_simple_test(suite, "binsearch_v", test_binsearch_v); + torture_suite_add_simple_test(suite, "binsearch_gte", test_binsearch_gte); + return suite; +} diff --git a/source4/torture/local/local.c b/source4/torture/local/local.c index c23df0d..79ab014 100644 --- a/source4/torture/local/local.c +++ b/source4/torture/local/local.c @@ -39,6 +39,7 @@ torture_local_util_str, torture_local_util_time, torture_local_util_data_blob, + torture_local_util_binsearch, torture_local_util_asn1, torture_local_util_anonymous_shared, torture_local_util_strv, diff --git a/source4/torture/local/wscript_build b/source4/torture/local/wscript_build index 545544c..707c7d1 100644 --- a/source4/torture/local/wscript_build +++ b/source4/torture/local/wscript_build @@ -5,7 +5,7 @@ TORTURE_LOCAL_SOURCE = '''../../../lib/util/charset/tests/iconv.c ../../lib/messaging/tests/irpc.c ../../librpc/tests/binding_string.c ../../../lib/util/tests/idtree.c ../../../lib/util/tests/dlinklist.c ../../lib/socket/testsuite.c ../../libcli/resolve/testsuite.c - ../../../lib/util/tests/strlist.c + ../../../lib/util/tests/strlist.c ../../../lib/util/tests/binsearch.c ../../../lib/util/tests/str.c ../../../lib/util/tests/time.c ../../../lib/util/tests/asn1_tests.c ../../../lib/util/tests/data_blob.c ../../../lib/util/tests/file.c ../../../lib/util/tests/genrand.c -- 2.5.0 >From 3573f1a19a3ac2313cfec6fa625b68bb0a8f5be7 Mon Sep 17 00:00:00 2001 From: Douglas Bagnall Date: Fri, 19 Feb 2016 15:39:38 +1300 Subject: [PATCH 2/2] util/tests: add test for BINARY_ARRAY_SEARCH_V macro Signed-off-by: Douglas Bagnall --- lib/util/tests/binsearch.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/util/tests/binsearch.c b/lib/util/tests/binsearch.c index f8a0227..c6ef47e 100644 --- a/lib/util/tests/binsearch.c +++ b/lib/util/tests/binsearch.c @@ -31,6 +31,30 @@ static int int_cmp(int a, int b) return a - b; } +static bool test_binsearch_v(struct torture_context *tctx) +{ + int array[] = { -11, -7, 0, 1, 723, 1000000}; + int misses[] = { -121, 17, -10, 10, -1, -723, 1000002}; + int i; + int *result = NULL; + + for (i = 0; i < ARRAY_SIZE(misses); i++) { + BINARY_ARRAY_SEARCH_V(array, ARRAY_SIZE(array), + misses[i], int_cmp, result); + torture_comment(tctx, "looking for misses[%d] == %d\n", i, misses[i]); + torture_assert(tctx, result == NULL, "failed to miss"); + } + + for (i = 0; i < ARRAY_SIZE(array); i++) { + BINARY_ARRAY_SEARCH_V(array, ARRAY_SIZE(array), + array[i], int_cmp, result); + torture_comment(tctx, "looking for array[%d] == %d, %p; got %p\n", + i, array[i], &array[i], result); + torture_assert(tctx, result == &array[i], + "failed to find element"); + } + return true; +} static bool test_binsearch_gte(struct torture_context *tctx) { -- 2.5.0