Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
ccan
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
mirror
ccan
Commits
8f7447e4
Commit
8f7447e4
authored
Jan 17, 2010
by
Rusty Russell
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Joey's btree module.
parent
5008de76
Changes
7
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
2468 additions
and
0 deletions
+2468
-0
ccan/btree/_info
ccan/btree/_info
+250
-0
ccan/btree/btree.c
ccan/btree/btree.c
+727
-0
ccan/btree/btree.h
ccan/btree/btree.h
+238
-0
ccan/btree/test/run-benchmark.c
ccan/btree/test/run-benchmark.c
+348
-0
ccan/btree/test/run-random-access.c
ccan/btree/test/run-random-access.c
+387
-0
ccan/btree/test/run-search-implement.c
ccan/btree/test/run-search-implement.c
+161
-0
ccan/btree/test/run-trivial.c
ccan/btree/test/run-trivial.c
+357
-0
No files found.
ccan/btree/_info
0 → 100644
View file @
8f7447e4
#include <string.h>
#include "config.h"
/**
* btree - Efficient sorted associative container based on B-trees.
*
* This module provides an efficient in-memory lookup container that keeps a
* set of pointers sorted using a user-defined search function.
*
* This module supports insertion, removal, lookup, and traversal using an
* iterator system. Note that insertion and removal into/from a btree will
* invalidate all iterators pointing to it (including the one passed to the
* insertion or removal function).
*
* btree currently doesn't have convenience functions for the simple tasks of
* "look up by key", "insert a key", and "remove a key". To insert or remove,
* you first have use btree_find* to position an iterator on the
* insertion/removal point, then call btree_insert_at or btree_remove_at using
* that iterator. Since a btree can hold multiple items with the same key,
* it isn't clear how the convenience functions should work yet. I'm open to
* suggestions.
*
* A B-tree (not to be confused with a binary tree) is a data structure that
* performs insertion, removal, and lookup in O(log n) time per operation.
* Although B-trees are typically used for databases and filesystems, this is
* an in-memory implementation.
*
* Unlike functions like qsort, bsearch, and tsearch, btree does not take a
* comparison function. It takes a binary search function, which is
* theoretically equivalent but faster. Writing a binary search function
* is more difficult than writing a comparison function, so a macro is provided
* to make it much easier than doing either manually.
*
* Example:
* #include <ccan/btree/btree.h>
*
* #include <errno.h>
* #include <stdlib.h>
* #include <stdio.h>
* #include <string.h>
*
* struct word {
* char *word;
* char *letter_set;
* };
*
* //Define the ordering function order_by_letter_set
* btree_search_implement
* (
* order_by_letter_set,
* struct word *,
* int c = strcmp(a->letter_set, b->letter_set),
* c == 0,
* c < 0
* )
*
* struct word *new_word(const char *line);
* char *chomp(char *str);
* char *make_letter_set(char *str);
*
* void insert_word(struct btree *btree, struct word *word)
* {
* btree_iterator iter;
*
* //Position iterator after existing words with this letter set.
* btree_find_last(btree, word, iter);
*
* //Insert new word at iterator position.
* btree_insert_at(iter, word);
* }
*
* void print_anagrams(struct btree *btree, char *line)
* {
* btree_iterator iter, end;
* struct word key = {
* NULL,
* make_letter_set(line)
* };
*
* //Find first matching word.
* if (!btree_find_first(btree, &key, iter)) {
* printf("\t(none)\n");
* return;
* }
*
* btree_find_last(btree, &key, end);
*
* //Traverse matching words.
* for (; btree_deref(iter) && btree_cmp_iters(iter, end) < 0;
* btree_next(iter))
* {
* struct word *word = iter->item;
* printf("\t%s\n", word->word);
* }
* }
*
* int destroy_word(struct word *word, void *ctx)
* {
* (void) ctx;
*
* free(word->word);
* free(word->letter_set);
* free(word);
*
* return 1;
* }
*
* struct btree *read_dictionary(const char *filename)
* {
* FILE *f;
* char line[256];
*
* //Construct new btree with the ordering function order_by_letter_set .
* struct btree *btree = btree_new(order_by_letter_set);
*
* f = fopen(filename, "r");
* if (!f)
* goto fail;
*
* //Set the destroy callback so btree_free will automatically
* //free our items. Setting btree->destroy is optional.
* btree->destroy = (btree_action_t)destroy_word;
*
* while (fgets(line, sizeof(line), f)) {
* struct word *word = new_word(line);
* if (!word)
* continue;
* insert_word(btree, word);
* }
*
* if (ferror(f)) {
* fclose(f);
* goto fail;
* }
* if (fclose(f))
* goto fail;
*
* return btree;
*
* fail:
* btree_delete(btree);
* fprintf(stderr, "%s: %s\n", filename, strerror(errno));
* return NULL;
* }
*
* int main(int argc, char *argv[])
* {
* struct btree *btree;
* char line[256];
*
* if (argc != 2) {
* fprintf(stderr,
* "Usage: %s dictionary-file\n"
* "Example:\n"
* "\t%s /usr/share/dict/words\n"
* "\n"
* , argv[0], argv[0]);
* return 1;
* }
*
* printf("Indexing...\n");
* btree = read_dictionary(argv[1]);
* printf("Dictionary has %ld words\n", (long)btree->count);
*
* for (;;) {
* printf("> ");
* if (!fgets(line, sizeof(line), stdin))
* break;
* chomp(line);
* if (!*line)
* break;
*
* printf("Anagrams of \"%s\":\n", line);
* print_anagrams(btree, line);
* }
*
* printf("Cleaning up...\n");
* btree_delete(btree);
*
* return 0;
* }
*
* struct word *new_word(const char *line)
* {
* struct word *word;
* char *letter_set = make_letter_set(strdup(line));
*
* //Discard lines with no letters
* if (!*letter_set) {
* free(letter_set);
* return NULL;
* }
*
* word = malloc(sizeof(struct word));
* word->word = chomp(strdup(line));
* word->letter_set = letter_set;
*
* return word;
* }
*
* //Remove trailing newline (if present).
* char *chomp(char *str)
* {
* char *end = strchr(str, '\0') - 1;
* if (*str && *end == '\n')
* *end = 0;
* return str;
* }
*
* //Remove all characters but letters, make letters lowercase, and sort them.
* char *make_letter_set(char *str)
* {
* size_t count[26] = {0};
* size_t i, j;
* char *o = str;
*
* for (i=0; str[i]; i++) {
* char c = str[i];
* if (c >= 'A' && c <= 'Z')
* c += 'a'-'A';
* if (c >= 'a' && c <= 'z')
* count[c - 'a']++;
* }
*
* for (i = 0; i < 26; i++) {
* for (j = 0; j < count[i]; j++)
* *o++ = 'a' + i;
* }
* *o = '\0';
*
* return str;
* }
*
* Author: Joey Adams
* Version: 0.1.0
* Licence: BSD
*/
int main(int argc, char *argv[])
{
/* Expect exactly one argument */
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
/* Nothing */
return 0;
}
return 1;
}
ccan/btree/btree.c
0 → 100644
View file @
8f7447e4
This diff is collapsed.
Click to expand it.
ccan/btree/btree.h
0 → 100644
View file @
8f7447e4
/*
Copyright (c) 2010 Joseph A. Adams
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CCAN_BTREE_H
#define CCAN_BTREE_H
/*
Note: The following should work but are not well-tested yet:
btree_walk...
btree_cmp_iters
*/
#include <stdint.h>
#include <stddef.h>
/*
* Maximum number of items per node.
* The maximum number of branches is BTREE_ITEM_MAX + 1.
*/
#define BTREE_ITEM_MAX 20
struct
btree_node
{
struct
btree_node
*
parent
;
/* Number of items (rather than branches). */
unsigned
char
count
;
/* 0 if node is a leaf, 1 if it has leaf children, etc. */
unsigned
char
depth
;
/* node->parent->branch[node->k] == this */
unsigned
char
k
;
const
void
*
item
[
BTREE_ITEM_MAX
];
/*
* Allocated to BTREE_ITEM_MAX+1 items if this is
* an internal node, 0 items if it is a leaf.
*/
struct
btree_node
*
branch
[];
};
typedef
struct
btree_iterator_s
{
struct
btree
*
btree
;
struct
btree_node
*
node
;
unsigned
int
k
;
/*
* The relationship between item and (node, k) depends on what function
* set it. It is mainly for convenience.
*/
void
*
item
;
}
btree_iterator
[
1
];
/*
* Instead of a compare function, this library accepts a binary search function
* to know how to order the items.
*/
typedef
unsigned
int
btree_search_proto
(
const
void
*
key
,
const
void
*
const
*
base
,
unsigned
int
count
,
int
lr
,
int
*
found
);
typedef
btree_search_proto
*
btree_search_t
;
/*
* Callback used by btree_delete() and btree_walk...().
*
* If it returns 0, it causes btree_walk...() to stop traversing and return 0.
* Thus, in normal circumstances, this callback should return 1.
*
* Callback shall not insert/remove items from the btree being traversed,
* nor shall anything modify it during a walk.
*/
typedef
int
(
*
btree_action_t
)(
void
*
item
,
void
*
ctx
);
struct
btree
{
struct
btree_node
*
root
;
size_t
count
;
/* Total number of items in B-tree */
btree_search_t
search
;
/*
* These are set to NULL by default.
*
* When destroy is not NULL, it is called on each item in order when
* btree_delete() is called.
*
* When destroy is NULL, btree_delete runs faster because it does not have
* to visit each and every item.
*/
btree_action_t
destroy
;
void
*
destroy_ctx
;
};
struct
btree
*
btree_new
(
btree_search_t
search
);
void
btree_delete
(
struct
btree
*
btree
);
/* lr must be 0 or 1, nothing else. */
int
btree_begin_end_lr
(
const
struct
btree
*
btree
,
btree_iterator
iter
,
int
lr
);
int
btree_find_lr
(
const
struct
btree
*
btree
,
const
void
*
key
,
btree_iterator
iter
,
int
lr
);
int
btree_walk_backward
(
const
struct
btree
*
btree
,
btree_action_t
action
,
void
*
ctx
);
int
btree_walk_forward
(
const
struct
btree
*
btree
,
btree_action_t
action
,
void
*
ctx
);
#define btree_begin(btree, iter) btree_begin_end_lr(btree, iter, 0)
#define btree_end(btree, iter) btree_begin_end_lr(btree, iter, 1)
int
btree_prev
(
btree_iterator
iter
);
int
btree_next
(
btree_iterator
iter
);
#define btree_walk(btree, action, ctx) btree_walk_forward(btree, action, ctx)
/*
* If key was found, btree_find_first will return 1, iter->item will be the
* first matching item, and iter will point to the beginning of the matching
* items.
*
* If key was not found, btree_find_first will return 0, iter->item will be
* undefined, and iter will point to where the key should go if inserted.
*/
#define btree_find_first(btree, key, iter) btree_find_lr(btree, key, iter, 0)
/*
* If key was found, btree_find_last will return 1, iter->item will be the
* last matching item, and iter will point to the end of the matching
* items.
*
* If key was not found, btree_find_last will return 0, iter->item will be
* undefined, and iter will point to where the key should go if inserted.
*/
#define btree_find_last(btree, key, iter) btree_find_lr(btree, key, iter, 1)
/* btree_find is an alias of btree_find_first. */
#define btree_find(btree, key, iter) btree_find_first(btree, key, iter)
/*
* If iter points to an item, btree_deref returns 1 and sets iter->item to the
* item it points to.
*
* Otherwise (if iter points to the end of the btree), btree_deref returns 0
* and leaves iter untouched.
*/
int
btree_deref
(
btree_iterator
iter
);
/*
* Inserts the item before the one pointed to by iter.
*
* Insertion invalidates all iterators to the btree, including the one
* passed to btree_insert_at. Nevertheless, iter->item will be set to
* the item inserted.
*/
void
btree_insert_at
(
btree_iterator
iter
,
const
void
*
item
);
/*
* Removes the item pointed to by iter. Returns 1 if iter pointed
* to an item. Returns 0 if iter pointed to the end, in which case
* it leaves iter intact.
*
* Removal invalidates all iterators to the btree, including the one
* passed to btree_remove_at. Nevertheless, iter->item will be set to
* the item removed.
*/
int
btree_remove_at
(
btree_iterator
iter
);
/*
* Compares positions of two iterators.
*
* Returns -1 if a is before b, 0 if a is at the same position as b,
* and +1 if a is after b.
*/
int
btree_cmp_iters
(
const
btree_iterator
iter_a
,
const
btree_iterator
iter_b
);
#define btree_search_implement(name, type, setup, equals, lessthan) \
unsigned int name(const void *__key, \
const void * const *__base, unsigned int __count, \
int __lr, int *__found) \
{ \
unsigned int __start = 0; \
while (__count) { \
unsigned int __middle = __count >> 1; \
type a = (type)__key; \
type b = (type)__base[__start + __middle]; \
{ \
setup; \
if (equals) \
goto __equals; \
if (lessthan) \
goto __lessthan; \
} \
__greaterthan: \
__start += __middle + 1; \
__count -= __middle + 1; \
continue; \
__equals: \
*__found = 1; \
if (__lr) \
goto __greaterthan; \
/* else, fall through to __lessthan */
\
__lessthan: \
__count = __middle; \
continue; \
} \
return __start; \
}
#endif
/* #ifndef CCAN_BTREE_H */
ccan/btree/test/run-benchmark.c
0 → 100644
View file @
8f7447e4
/* Include the main header first, to test it works */
#include <ccan/btree/btree.h>
/* Include the C files directly. */
#include <ccan/btree/btree.c>
#include <ccan/tap/tap.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
uint32_t
rand32_state
=
0
;
/*
* Finds a pseudorandom 32-bit number from 0 to 2^32-1 .
* Uses the BCPL linear congruential generator method.
*/
static
uint32_t
rand32
(
void
)
{
rand32_state
*=
(
uint32_t
)
0x7FF8A3ED
;
rand32_state
+=
(
uint32_t
)
0x2AA01D31
;
return
rand32_state
;
}
static
void
scramble
(
void
*
base
,
size_t
nmemb
,
size_t
size
)
{
char
*
i
=
base
;
char
*
o
;
size_t
sd
;
for
(;
nmemb
>
1
;
nmemb
--
)
{
o
=
i
+
size
*
(
rand32
()
%
nmemb
);
for
(
sd
=
size
;
sd
--
;)
{
char
tmp
=
*
o
;
*
o
++
=
*
i
;
*
i
++
=
tmp
;
}
}
}
struct
test_item
{
size_t
key
;
uint32_t
value
;
};
/* For ordering a btree of test_item pointers. */
static
btree_search_implement
(
order_by_key
,
const
struct
test_item
*
,
,
a
==
b
,
a
<
b
)
/* For qsorting an array of test_item pointers. */
static
int
compare_test_item
(
const
void
*
ap
,
const
void
*
bp
)
{
const
struct
test_item
*
a
=
*
(
const
struct
test_item
*
const
*
)
ap
;
const
struct
test_item
*
b
=
*
(
const
struct
test_item
*
const
*
)
bp
;
if
(
a
==
b
)
return
0
;
if
(
a
<
b
)
return
-
1
;
return
1
;
}
/*
* If lr == 0, make sure iter points to the item given.
* If lr == 1, make sure iter points to after the item given.
*/
static
int
check_iter
(
btree_iterator
iter_orig
,
const
void
*
item
,
int
lr
)
{
btree_iterator
iter
=
{
*
iter_orig
};
if
(
iter
->
item
!=
item
)
return
0
;
if
(
lr
)
{
if
(
!
btree_prev
(
iter
))
return
0
;
}
else
{
if
(
!
btree_deref
(
iter
))
return
0
;
}
if
(
iter
->
item
!=
item
)
return
0
;
if
(
iter
->
node
->
item
[
iter
->
k
]
!=
iter
->
item
)
return
0
;
return
1
;
}
/*
* Returns 1 on insert, 0 on duplicate,
* -1 on bad iterator returned by find, and
* -2 on bad iterator returned by insert.
*/
static
int
insert_test_item
(
struct
btree
*
btree
,
struct
test_item
*
item
)
{
btree_iterator
iter
;
int
lr
;
/* Find the first or last matching item, randomly choosing between the two. */
lr
=
rand32
()
&
1
;
if
(
btree_find_lr
(
btree
,
item
,
iter
,
lr
))
{
if
(
!
check_iter
(
iter
,
item
,
lr
))
return
-
1
;
return
0
;
}
btree_insert_at
(
iter
,
item
);
if
(
iter
->
item
!=
item
)
return
-
2
;
return
1
;
}
/*
* Returns 1 on remove, 0 on missing,
* -1 on bad iterator returned by find, and
* -2 on bad iterator returned by remove.
*/
static
int
remove_test_item
(
struct
btree
*
btree
,
struct
test_item
*
item
)
{
btree_iterator
iter
;
if
(
!
btree_find
(
btree
,
item
,
iter
))
return
0
;
if
(
!
check_iter
(
iter
,
item
,
0
))
return
-
1
;
btree_remove_at
(
iter
);
if
(
iter
->
item
!=
item
)
return
-
2
;
return
1
;
}
static
struct
{
size_t
success
;
size_t
excess
;
size_t
duplicate
;
size_t
missing
;
size_t
incorrect
;
size_t
failed
;
size_t
bad_iter_find
;
size_t
bad_iter_insert
;
size_t
bad_iter_remove
;
size_t
bad_iter_next
;
}
stats
;
static
void
clear_stats
(
void
)
{
memset
(
&
stats
,
0
,
sizeof
(
stats
));
}
static
int
print_stats
(
const
char
*
success_label
,
size_t
expected_success
)
{
int
failed
=
0
;
printf
(
" %s:
\t
%zu
\n
"
,
success_label
,
stats
.
success
);
if
(
stats
.
success
!=
expected_success
)
failed
=
1
;
if
(
stats
.
excess
)
failed
=
1
,
printf
(
" Excess:
\t
%zu
\n
"
,
stats
.
excess
);
if
(
stats
.
duplicate
)
failed
=
1
,
printf
(
" Duplicate:
\t
%zu
\n
"
,
stats
.
duplicate
);
if
(
stats
.
missing
)
failed
=
1
,
printf
(
" Missing:
\t
%zu
\n
"
,
stats
.
missing
);
if
(
stats
.
incorrect
)
failed
=
1
,
printf
(
" Incorrect:
\t
%zu
\n
"
,
stats
.
incorrect
);
if
(
stats
.
failed
)
failed
=
1
,
printf
(
" Failed:
\t
%zu
\n
"
,
stats
.
failed
);
if
(
stats
.
bad_iter_find
||
stats
.
bad_iter_insert
||
stats
.
bad_iter_remove
||
stats
.
bad_iter_next
)
{
failed
=
1
;
printf
(
" Bad iterators yielded by:
\n
"
);
if
(
stats
.
bad_iter_find
)
printf
(
" btree_find_lr(): %zu
\n
"
,
stats
.
bad_iter_find
);
if
(
stats
.
bad_iter_insert
)
printf
(
" btree_insert_at(): %zu
\n
"
,
stats
.
bad_iter_insert
);
if
(
stats
.
bad_iter_remove
)
printf
(
" btree_remove_at(): %zu
\n
"
,
stats
.
bad_iter_remove
);
if
(
stats
.
bad_iter_next
)
printf
(
" btree_next(): %zu
\n
"
,
stats
.
bad_iter_next
);
}
return
!
failed
;
}
static
void
benchmark
(
size_t
max_per_trial
,
size_t
round_count
,
int
random_counts
)
{
struct
test_item
**
test_item
;
struct
test_item
*
test_item_array
;
size_t
i
,
count
;
size_t
round
=
0
;
test_item
=
malloc
(
max_per_trial
*
sizeof
(
*
test_item
));
test_item_array
=
malloc
(
max_per_trial
*
sizeof
(
*
test_item_array
));
if
(
!
test_item
||
!
test_item_array
)
{
fail
(
"Not enough memory for %zu keys per trial
\n
"
,
max_per_trial
);
return
;
}
/* Initialize test_item pointers. */
for
(
i
=
0
;
i
<
max_per_trial
;
i
++
)
test_item
[
i
]
=
&
test_item_array
[
i
];
/*
* If round_count is not zero, run round_count trials.
* Otherwise, run forever.
*/
for
(
round
=
1
;
round_count
==
0
||
round
<=
round_count
;
round
++
)
{
struct
btree
*
btree
;
btree_iterator
iter
;
printf
(
"Round %zu
\n
"
,
round
);
if
(
random_counts
)
count
=
rand32
()
%
(
max_per_trial
+
1
);
else
count
=
max_per_trial
;
/*
* Initialize test items by giving them sequential keys and
* random values. Scramble them so the order of insertion
* will be random.
*/
for
(
i
=
0
;
i
<
count
;
i
++
)
{
test_item
[
i
]
->
key
=
i
;
test_item
[
i
]
->
value
=
rand32
();
}
scramble
(
test_item
,
count
,
sizeof
(
*
test_item
));
btree
=
btree_new
(
order_by_key
);
clear_stats
();
printf
(
" Inserting %zu items...
\n
"
,
count
);
for
(
i
=
0
;
i
<
count
;
i
++
)
{
switch
(
insert_test_item
(
btree
,
test_item
[
i
]))
{
case
1
:
stats
.
success
++
;
break
;
case
0
:
stats
.
duplicate
++
;
break
;
case
-
1
:
stats
.
bad_iter_find
++
;
break
;
case
-
2
:
stats
.
bad_iter_insert
++
;
break
;
default:
stats
.
failed
++
;
break
;
}
}
ok1
(
print_stats
(
"Inserted"
,
count
));
scramble
(
test_item
,
count
,
sizeof
(
*
test_item
));
printf
(
" Finding %zu items...
\n
"
,
count
);
clear_stats
();
for
(
i
=
0
;
i
<
count
;
i
++
)
{
int
lr
=
rand32
()
&
1
;
if
(
!
btree_find_lr
(
btree
,
test_item
[
i
],
iter
,
lr
))
{
stats
.
missing
++
;
continue
;
}
if
(
!
check_iter
(
iter
,
test_item
[
i
],
lr
))
{
stats
.
bad_iter_find
++
;
continue
;
}
stats
.
success
++
;
}
ok1
(
print_stats
(
"Retrieved"
,
count
));
qsort
(
test_item
,
count
,
sizeof
(
*
test_item
),
compare_test_item
);
printf
(
" Traversing forward through %zu items...
\n
"
,
count
);
clear_stats
();
i
=
0
;
for
(
btree_begin
(
btree
,
iter
);
btree_next
(
iter
);)
{
if
(
i
>=
count
)
{
stats
.
excess
++
;
continue
;
}
if
(
iter
->
item
==
test_item
[
i
])
stats
.
success
++
;
else
stats
.
incorrect
++
;
i
++
;
}
ok1
(
print_stats
(
"Retrieved"
,
count
));
printf
(
" Traversing backward through %zu items...
\n
"
,
count
);
clear_stats
();
i
=
count
;
for
(
btree_end
(
btree
,
iter
);
btree_prev
(
iter
);)
{
if
(
!
i
)
{
stats
.
excess
++
;
continue
;
}
i
--
;
if
(
iter
->
item
==
test_item
[
i
])
stats
.
success
++
;
else
stats
.
incorrect
++
;
}
ok1
(
print_stats
(
"Retrieved"
,
count
));
ok1
(
btree
->
count
==
count
);
//static int remove_test_item(struct btree *btree, struct test_item *item);
scramble
(
test_item
,
count
,
sizeof
(
*
test_item
));
printf
(
" Deleting %zu items...
\n
"
,
count
);
clear_stats
();
for
(
i
=
0
;
i
<
count
;
i
++
)
{
int
s
=
remove_test_item
(
btree
,
test_item
[
i
]);
if
(
s
!=
1
)
printf
(
"remove_test_item failed
\n
"
);
switch
(
s
)
{
case
1
:
stats
.
success
++
;
break
;
case
0
:
stats
.
missing
++
;
break
;
case
-
1
:
stats
.
bad_iter_find
++
;
break
;
case
-
2
:
stats
.
bad_iter_remove
++
;
break
;
default:
stats
.
failed
++
;
break
;
}
}
ok1
(
btree
->
count
==
0
);
ok1
(
print_stats
(
"Deleted"
,
count
));
ok1
(
btree
->
root
->
depth
==
0
&&
btree
->
root
->
count
==
0
);
btree_delete
(
btree
);
}
free
(
test_item
);
free
(
test_item_array
);
}
int
main
(
void
)
{
plan_tests
(
32
);
benchmark
(
300000
,
4
,
0
);
return
exit_status
();
}
ccan/btree/test/run-random-access.c
0 → 100644
View file @
8f7447e4
/* Include the main header first, to test it works */
#include <ccan/btree/btree.h>
/* Include the C files directly. */
#include <ccan/btree/btree.c>
#include <ccan/tap/tap.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static
uint32_t
rand32_state
=
0
;
/*
* Finds a pseudorandom 32-bit number from 0 to 2^32-1 .
* Uses the BCPL linear congruential generator method.
*/
static
uint32_t
rand32
(
void
)
{
rand32_state
*=
(
uint32_t
)
0x7FF8A3ED
;
rand32_state
+=
(
uint32_t
)
0x2AA01D31
;
return
rand32_state
;
}
/*
* Whether or not to add/remove multiple equal keys to the tree.
*
* Tests are run with multi set to 0 and 1.
*/
static
int
multi
=
0
;
static
struct
{
struct
{
size_t
success
;
size_t
failure
;
}
insert
,
remove
,
find
,
traverse
;
}
stats
;
static
int
check_stats
(
void
)
{
return
stats
.
insert
.
failure
==
0
&&
stats
.
remove
.
failure
==
0
&&
stats
.
find
.
failure
==
0
&&
stats
.
traverse
.
failure
==
0
;
}
static
int
print_stats
(
void
)
{
printf
(
"Insert: %zu succeeded, %zu failed
\n
"
,
stats
.
insert
.
success
,
stats
.
insert
.
failure
);
printf
(
"Remove: %zu succeeded, %zu failed
\n
"
,
stats
.
remove
.
success
,
stats
.
remove
.
failure
);
printf
(
"Find: %zu succeeded, %zu failed
\n
"
,
stats
.
find
.
success
,
stats
.
find
.
failure
);
printf
(
"Traverse: %zu succeeded, %zu failed
\n
"
,
stats
.
traverse
.
success
,
stats
.
traverse
.
failure
);
return
check_stats
();
}
static
void
clear_stats
(
void
)
{
memset
(
&
stats
,
0
,
sizeof
(
stats
));
}
static
int
test_node_consistency
(
struct
btree_node
*
node
,
struct
btree_node
*
parent
,
size_t
*
count
)
{
unsigned
int
i
,
j
,
e
=
node
->
count
;
/* Verify parent, depth, and k */
if
(
node
->
parent
!=
parent
)
return
0
;
if
(
parent
)
{
if
(
node
->
depth
!=
parent
->
depth
-
1
)
return
0
;
if
(
node
!=
parent
->
branch
[
node
->
k
])
return
0
;
}
/* Nodes must not be empty (unless the entire tree is empty). */
if
(
e
==
0
)
return
0
;
if
(
node
->
depth
)
{
/* Make sure child branches aren't duplicated or NULL. */
for
(
i
=
0
;
i
<=
e
;
i
++
)
{
if
(
node
->
branch
[
i
]
==
NULL
)
return
0
;
for
(
j
=
i
+
1
;
j
<=
e
;
j
++
)
if
(
node
->
branch
[
i
]
==
node
->
branch
[
j
])
return
0
;
}
/* Recursively check children. */
for
(
i
=
0
;
i
<=
e
;
i
++
)
{
if
(
!
test_node_consistency
(
node
->
branch
[
i
],
node
,
count
))
return
0
;
}
}
*
count
+=
node
->
count
;
return
1
;
}
static
int
test_consistency
(
const
struct
btree
*
btree
)
{
size_t
count
=
0
;
if
(
!
btree
->
root
)
return
0
;
if
(
btree
->
root
->
count
==
0
)
{
if
(
btree
->
count
!=
0
)
return
0
;
return
1
;
}
if
(
btree
->
count
==
0
)
return
0
;
if
(
!
test_node_consistency
(
btree
->
root
,
NULL
,
&
count
))
return
0
;
if
(
btree
->
count
!=
count
)
return
0
;
return
1
;
}
static
int
test_traverse
(
struct
btree
*
btree
,
size_t
key
[],
size_t
count
)
{
btree_iterator
iter
;
size_t
i
,
j
;
if
(
!
test_consistency
(
btree
))
return
0
;
/* Forward */
i
=
0
;
btree_begin
(
btree
,
iter
);
for
(;;)
{
while
(
i
<
count
&&
key
[
i
]
==
0
)
i
++
;
if
(
i
>=
count
)
{
if
(
btree_next
(
iter
))
return
0
;
break
;
}
for
(
j
=
0
;
j
<
key
[
i
]
&&
btree_next
(
iter
);
j
++
)
{
if
(
iter
->
item
!=
&
key
[
i
])
return
0
;
}
if
(
j
!=
key
[
i
])
return
0
;
i
++
;
}
/* Backward */
i
=
count
;
btree_end
(
btree
,
iter
);
for
(;;)
{
while
(
i
>
0
&&
key
[
i
-
1
]
==
0
)
i
--
;
if
(
i
<=
0
)
{
if
(
btree_prev
(
iter
))
return
0
;
break
;
}
for
(
j
=
0
;
j
<
key
[
i
-
1
]
&&
btree_prev
(
iter
);
j
++
)
{
if
(
iter
->
item
!=
&
key
[
i
-
1
])
return
0
;
}
if
(
j
!=
key
[
i
-
1
])
return
0
;
i
--
;
}
return
1
;
}
/*
* Finds and counts items matching &key[k], then makes sure the count
* equals key[k]. Also verifies that btree_find_... work as advertised.
*/
static
int
find
(
struct
btree
*
btree
,
size_t
key
[],
size_t
k
)
{
btree_iterator
iter
,
tmp
;
size_t
count
;
int
found
;
memset
(
iter
,
0
,
sizeof
(
iter
));
memset
(
tmp
,
0
,
sizeof
(
tmp
));
found
=
btree_find_first
(
btree
,
&
key
[
k
],
iter
);
if
(
iter
->
btree
!=
btree
)
return
0
;
if
(
found
!=
!!
key
[
k
])
return
0
;
if
(
key
[
k
]
&&
iter
->
item
!=
&
key
[
k
])
return
0
;
/* Make sure btree_find works exactly the same as btree_find_first. */
if
(
btree_find
(
btree
,
&
key
[
k
],
tmp
)
!=
found
)
return
0
;
if
(
memcmp
(
iter
,
tmp
,
sizeof
(
*
iter
)))
return
0
;
/* Make sure previous item doesn't match. */
*
tmp
=
*
iter
;
if
(
btree_prev
(
tmp
))
{
if
(
tmp
->
item
==
&
key
[
k
])
return
0
;
}
/* Count going forward. */
for
(
count
=
0
;
btree_deref
(
iter
)
&&
iter
->
item
==
&
key
[
k
];
count
++
,
btree_next
(
iter
))
{}
if
(
count
!=
key
[
k
])
return
0
;
/* Make sure next item doesn't match. */
*
tmp
=
*
iter
;
if
(
btree_deref
(
tmp
))
{
if
(
tmp
->
item
==
&
key
[
k
])
return
0
;
}
/* Make sure iter is now equal to the end of matching items. */
btree_find_last
(
btree
,
&
key
[
k
],
tmp
);
if
(
tmp
->
btree
!=
btree
)
return
0
;
if
(
btree_cmp_iters
(
iter
,
tmp
))
return
0
;
/* Count going backward. */
for
(
count
=
0
;
btree_prev
(
iter
);
count
++
)
{
if
(
iter
->
item
!=
&
key
[
k
])
{
btree_next
(
iter
);
break
;
}
}
if
(
count
!=
key
[
k
])
return
0
;
/* Make sure iter is now equal to the beginning of matching items. */
btree_find_first
(
btree
,
&
key
[
k
],
tmp
);
if
(
tmp
->
btree
!=
btree
)
return
0
;
if
(
btree_cmp_iters
(
iter
,
tmp
))
return
0
;
return
1
;
}
static
int
test_find
(
struct
btree
*
btree
,
size_t
key
[],
size_t
count
)
{
size_t
k
=
rand32
()
%
count
;
return
find
(
btree
,
key
,
k
);
}
static
int
test_remove
(
struct
btree
*
btree
,
size_t
key
[],
size_t
count
)
{
size_t
prev_count
=
btree
->
count
;
size_t
k
=
rand32
()
%
count
;
btree_iterator
iter
;
if
(
!
find
(
btree
,
key
,
k
))
return
0
;
btree_find
(
btree
,
&
key
[
k
],
iter
);
//remove (if present), and make sure removal status is correct
if
(
key
[
k
])
{
if
(
btree_remove_at
(
iter
)
!=
1
)
return
0
;
if
(
btree
->
count
!=
prev_count
-
1
)
return
0
;
key
[
k
]
--
;
if
(
!
find
(
btree
,
key
,
k
))
return
0
;
}
return
1
;
}
static
int
test_insert
(
struct
btree
*
btree
,
size_t
key
[],
size_t
count
)
{
size_t
k
=
rand32
()
%
count
;
btree_iterator
iter
;
size_t
prev_count
=
btree
->
count
;
int
found
;
if
(
!
find
(
btree
,
key
,
k
))
return
0
;
/* Make sure key's presense is consistent with our array. */
found
=
btree_find_first
(
btree
,
&
key
[
k
],
iter
);
if
(
key
[
k
])
{
if
(
!
found
||
iter
->
item
!=
&
key
[
k
])
return
0
;
if
(
!
btree_deref
(
iter
))
return
0
;
if
(
iter
->
k
>=
iter
->
node
->
count
||
iter
->
node
->
item
[
iter
->
k
]
!=
&
key
[
k
])
return
0
;
}
else
{
if
(
found
)
return
0
;
}
/* Insert if item hasn't been yet (or if we're in multi mode). */
if
(
!
key
[
k
]
||
multi
)
{
btree_insert_at
(
iter
,
&
key
[
k
]);
key
[
k
]
++
;
if
(
btree
->
count
!=
prev_count
+
1
)
return
0
;
}
/* Check result iterator's ->item field. */
if
(
iter
->
item
!=
&
key
[
k
])
return
0
;
if
(
!
find
(
btree
,
key
,
k
))
return
0
;
/* Make sure key is present and correct now. */
found
=
btree_find_first
(
btree
,
&
key
[
k
],
iter
);
if
(
!
found
||
iter
->
item
!=
&
key
[
k
])
return
0
;
return
1
;
}
static
btree_search_implement
(
order_by_ptr
,
size_t
*
,
,
a
==
b
,
a
<
b
)
static
void
stress
(
size_t
count
,
size_t
trials
)
{
struct
btree
*
btree
=
btree_new
(
order_by_ptr
);
size_t
*
key
=
calloc
(
count
,
sizeof
(
*
key
));
size_t
i
;
clear_stats
();
for
(
i
=
0
;
i
<
trials
;
i
++
)
{
unsigned
int
n
=
rand32
()
%
65536
;
unsigned
int
rib
=
btree
->
count
*
43688
/
count
;
//remove/insert boundary
if
(
n
>=
65534
)
{
if
(
test_traverse
(
btree
,
key
,
count
))
stats
.
traverse
.
success
++
;
else
stats
.
traverse
.
failure
++
;
}
else
if
(
n
>=
46388
)
{
if
(
test_find
(
btree
,
key
,
count
))
stats
.
find
.
success
++
;
else
stats
.
find
.
failure
++
;
}
else
if
(
n
<
rib
)
{
if
(
test_remove
(
btree
,
key
,
count
))
stats
.
remove
.
success
++
;
else
stats
.
remove
.
failure
++
;
}
else
{
if
(
test_insert
(
btree
,
key
,
count
))
stats
.
insert
.
success
++
;
else
stats
.
insert
.
failure
++
;
}
}
free
(
key
);
btree_delete
(
btree
);
print_stats
();
ok1
(
check_stats
());
}
int
main
(
void
)
{
plan_tests
(
2
);
multi
=
0
;
printf
(
"Running with multi = %d
\n
"
,
multi
);
stress
(
100000
,
500000
);
multi
=
1
;
printf
(
"Running with multi = %d
\n
"
,
multi
);
stress
(
100000
,
500000
);
return
exit_status
();
}
ccan/btree/test/run-search-implement.c
0 → 100644
View file @
8f7447e4
/* Include the main header first, to test it works */
#include <ccan/btree/btree.h>
/* Include the C files directly. */
#include <ccan/btree/btree.c>
#include <ccan/tap/tap.h>
#include <string.h>
#ifndef ARRAY_SIZE
#define ARRAY_SIZE(array) (sizeof(array) / sizeof(*(array)))
#endif
struct
foo
{
const
char
*
string
;
int
number
;
};
struct
foo
foo_structs
[]
=
{
{
"apple"
,
1
},
{
"banana"
,
2
},
{
"banana"
,
4
},
{
"cherry"
,
4
},
{
"doughnut"
,
5
},
};
struct
foo
*
foo_base
[
ARRAY_SIZE
(
foo_structs
)];
const
unsigned
int
foo_count
=
ARRAY_SIZE
(
foo_structs
);
static
void
init_foo_pointers
(
void
)
{
unsigned
int
i
;
for
(
i
=
0
;
i
<
foo_count
;
i
++
)
foo_base
[
i
]
=
&
foo_structs
[
i
];
}
/* Make sure forward declarations work */
btree_search_proto
order_by_string
,
order_by_number
;
static
void
test_order_by_string
(
void
)
{
struct
{
const
char
*
key
;
int
lr
;
unsigned
int
expect_offset
;
int
expect_found
;
}
test
[]
=
{
{
"anchovies"
,
0
,
0
,
0
},
{
"anchovies"
,
1
,
0
,
0
},
{
"apple"
,
0
,
0
,
1
},
{
"apple"
,
1
,
1
,
1
},
{
"banana"
,
0
,
1
,
1
},
{
"banana"
,
1
,
3
,
1
},
{
"blueberry"
,
0
,
3
,
0
},
{
"blueberry"
,
1
,
3
,
0
},
{
"doughnut"
,
0
,
4
,
1
},
{
"doughnut"
,
1
,
5
,
1
},
};
size_t
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
test
);
i
++
)
{
struct
foo
foo
=
{
test
[
i
].
key
,
0
};
unsigned
int
offset
;
int
found
=
0
;
offset
=
order_by_string
(
&
foo
,
(
void
*
)
foo_base
,
foo_count
,
test
[
i
].
lr
,
&
found
);
ok1
(
offset
==
test
[
i
].
expect_offset
&&
found
==
test
[
i
].
expect_found
);
}
}
static
void
test_empty
(
void
)
{
unsigned
int
offset
;
int
found
;
struct
foo
key
=
{
"apple"
,
-
1
};
offset
=
order_by_string
(
&
key
,
NULL
,
0
,
0
,
&
found
);
ok1
(
offset
==
0
);
offset
=
order_by_string
(
&
key
,
NULL
,
0
,
1
,
&
found
);
ok1
(
offset
==
0
);
offset
=
order_by_number
(
&
key
,
NULL
,
0
,
0
,
&
found
);
ok1
(
offset
==
0
);
offset
=
order_by_number
(
&
key
,
NULL
,
0
,
1
,
&
found
);
ok1
(
offset
==
0
);
}
static
void
test_order_by_number
(
void
)
{
struct
{
int
key
;
int
lr
;
unsigned
int
expect_offset
;
int
expect_found
;
}
test
[]
=
{
{
-
2
,
0
,
0
,
0
},
{
-
2
,
1
,
0
,
0
},
{
-
1
,
0
,
0
,
0
},
{
-
1
,
1
,
0
,
0
},
{
0
,
0
,
0
,
0
},
{
0
,
1
,
0
,
0
},
{
1
,
0
,
0
,
1
},
{
1
,
1
,
1
,
1
},
{
2
,
0
,
1
,
1
},
{
2
,
1
,
2
,
1
},
{
4
,
0
,
2
,
1
},
{
4
,
1
,
4
,
1
},
{
3
,
0
,
2
,
0
},
{
3
,
1
,
2
,
0
},
{
5
,
0
,
4
,
1
},
{
5
,
1
,
5
,
1
},
{
6
,
0
,
5
,
0
},
{
6
,
1
,
5
,
0
},
{
7
,
0
,
5
,
0
},
{
7
,
1
,
5
,
0
},
};
size_t
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
test
);
i
++
)
{
struct
foo
foo
=
{
""
,
test
[
i
].
key
};
unsigned
int
offset
;
int
found
=
0
;
offset
=
order_by_number
(
&
foo
,
(
void
*
)
foo_base
,
foo_count
,
test
[
i
].
lr
,
&
found
);
ok1
(
offset
==
test
[
i
].
expect_offset
&&
found
==
test
[
i
].
expect_found
);
}
}
int
main
(
void
)
{
plan_tests
(
34
);
init_foo_pointers
();
test_order_by_string
();
test_order_by_number
();
test_empty
();
return
exit_status
();
}
btree_search_implement
(
order_by_string
,
const
struct
foo
*
,
int
c
=
strcmp
(
a
->
string
,
b
->
string
),
c
==
0
,
c
<
0
)
btree_search_implement
(
order_by_number
,
const
struct
foo
*
,
,
a
->
number
==
b
->
number
,
a
->
number
<
b
->
number
)
ccan/btree/test/run-trivial.c
0 → 100644
View file @
8f7447e4
/* Include the main header first, to test it works */
#include <ccan/btree/btree.h>
/* Include the C files directly. */
#include <ccan/btree/btree.c>
#include <ccan/tap/tap.h>
#include <string.h>
struct
test_item
{
int
key
;
int
value
;
};
static
btree_search_implement
(
order_by_key
,
struct
test_item
*
,
,
a
->
key
==
b
->
key
,
a
->
key
<
b
->
key
)
static
int
insert_test_item
(
struct
btree
*
btree
,
int
key
,
int
value
)
{
struct
test_item
key_item
=
{
key
,
-
101
};
struct
test_item
*
item
;
btree_iterator
iter
;
if
(
btree_find_first
(
btree
,
&
key_item
,
iter
))
{
/* Don't insert new item, but do update its value. */
item
=
iter
->
item
;
item
->
value
=
value
;
return
0
;
}
item
=
malloc
(
sizeof
(
*
item
));
item
->
key
=
key
;
item
->
value
=
value
;
btree_insert_at
(
iter
,
item
);
return
1
;
}
static
int
lookup_test_item
(
const
struct
btree
*
btree
,
int
key
)
{
struct
test_item
key_item
=
{
key
,
-
102
};
struct
test_item
*
item
;
btree_iterator
iter
;
if
(
!
btree_find_first
(
btree
,
&
key_item
,
iter
))
return
-
100
;
item
=
iter
->
item
;
return
item
->
value
;
}
static
int
destroy_test_item
(
void
*
item
,
void
*
ctx
)
{
(
void
)
ctx
;
free
(
item
);
return
1
;
}
struct
test_insert_entry
{
int
key
;
int
value
;
int
expected_return
;
};
struct
test_traverse_entry
{
int
key
;
int
value
;
};
static
void
print_indent
(
unsigned
int
indent
)
{
while
(
indent
--
)
fputs
(
"
\t
"
,
stdout
);
}
static
void
btree_node_trace
(
struct
btree_node
*
node
,
unsigned
int
indent
)
{
unsigned
int
i
;
for
(
i
=
0
;
i
<
node
->
count
;
i
++
)
{
if
(
node
->
depth
)
btree_node_trace
(
node
->
branch
[
i
],
indent
+
1
);
print_indent
(
indent
);
puts
(
node
->
item
[
i
]);
}
if
(
node
->
depth
)
btree_node_trace
(
node
->
branch
[
node
->
count
],
indent
+
1
);
}
static
void
btree_trace
(
struct
btree
*
btree
)
{
btree_node_trace
(
btree
->
root
,
0
);
}
static
void
test_insert
(
struct
btree
*
btree
)
{
struct
test_insert_entry
ent
[]
=
{
{
3
,
1
,
1
},
{
4
,
1
,
1
},
{
5
,
9
,
1
},
{
2
,
6
,
1
},
{
5
,
3
,
0
},
{
5
,
8
,
0
},
{
9
,
7
,
1
},
{
9
,
3
,
0
},
{
2
,
3
,
0
},
{
8
,
4
,
1
},
{
6
,
2
,
1
},
{
6
,
4
,
0
},
{
3
,
3
,
0
},
{
8
,
3
,
0
},
{
2
,
7
,
0
},
{
9
,
5
,
0
},
{
0
,
2
,
1
},
{
8
,
8
,
0
},
{
4
,
1
,
0
},
{
9
,
7
,
0
},
{
1
,
6
,
1
},
{
9
,
3
,
0
},
{
9
,
9
,
0
},
{
3
,
7
,
0
},
{
5
,
1
,
0
},
{
0
,
5
,
0
},
{
8
,
2
,
0
},
{
0
,
9
,
0
},
{
7
,
4
,
1
},
{
9
,
4
,
0
},
{
4
,
5
,
0
},
{
9
,
2
,
0
}
};
size_t
i
,
count
=
sizeof
(
ent
)
/
sizeof
(
*
ent
);
for
(
i
=
0
;
i
<
count
;
i
++
)
{
int
ret
=
insert_test_item
(
btree
,
ent
[
i
].
key
,
ent
[
i
].
value
);
ok1
(
ret
==
ent
[
i
].
expected_return
);
}
}
static
void
test_find_traverse
(
struct
btree
*
btree
)
{
struct
test_traverse_entry
ent
[]
=
{
{
0
,
9
},
{
1
,
6
},
{
2
,
7
},
{
3
,
7
},
{
4
,
5
},
{
5
,
1
},
{
6
,
4
},
{
7
,
4
},
{
8
,
2
},
{
9
,
2
}
};
size_t
i
,
count
=
sizeof
(
ent
)
/
sizeof
(
*
ent
);
btree_iterator
iter
;
i
=
0
;
for
(
btree_begin
(
btree
,
iter
);
btree_next
(
iter
);)
{
struct
test_item
*
item
=
iter
->
item
;
if
(
i
>=
count
)
{
fail
(
"Too many items in btree according to forward traversal"
);
break
;
}
ok1
(
lookup_test_item
(
btree
,
item
->
key
)
==
item
->
value
);
ok1
(
item
->
key
==
ent
[
i
].
key
&&
item
->
value
==
ent
[
i
].
value
);
i
++
;
}
if
(
i
!=
count
)
fail
(
"Not enough items in btree according to forward traversal"
);
i
=
count
;
for
(
btree_end
(
btree
,
iter
);
btree_prev
(
iter
);)
{
struct
test_item
*
item
=
iter
->
item
;
if
(
!
i
--
)
{
fail
(
"Too many items in btree according to backward traversal"
);
break
;
}
ok1
(
lookup_test_item
(
btree
,
item
->
key
)
==
item
->
value
);
ok1
(
item
->
key
==
ent
[
i
].
key
&&
item
->
value
==
ent
[
i
].
value
);
}
if
(
i
!=
0
)
fail
(
"Not enough items in btree according to backward traversal"
);
}
static
btree_search_proto
order_by_string
;
static
btree_search_implement
(
order_by_string
,
//function name
const
char
*
,
//key type
int
c
=
strcmp
(
a
,
b
),
//setup
c
==
0
,
// a == b predicate
c
<
0
// a < b predicate
)
//used in the test case to sort the test strings
static
int
compare_by_string
(
const
void
*
ap
,
const
void
*
bp
)
{
const
char
*
const
*
a
=
ap
;
const
char
*
const
*
b
=
bp
;
return
strcmp
(
*
a
,
*
b
);
}
static
void
test_traverse
(
struct
btree
*
btree
,
const
char
*
sorted
[],
size_t
count
)
{
btree_iterator
iter
,
iter2
;
size_t
i
;
i
=
0
;
for
(
btree_begin
(
btree
,
iter
);
btree_next
(
iter
);)
{
if
(
i
>=
count
)
{
fail
(
"Too many items in btree according to forward traversal"
);
break
;
}
ok1
(
iter
->
item
==
sorted
[
i
]);
btree_find_first
(
btree
,
sorted
[
i
],
iter2
);
ok1
(
iter2
->
item
==
sorted
[
i
]);
i
++
;
}
if
(
i
!=
count
)
fail
(
"Not enough items in btree according to forward traversal"
);
i
=
count
;
for
(
btree_end
(
btree
,
iter
);
btree_prev
(
iter
);)
{
if
(
!
i
--
)
{
fail
(
"Too many items in btree according to backward traversal"
);
break
;
}
ok1
(
iter
->
item
==
sorted
[
i
]);
btree_find_first
(
btree
,
sorted
[
i
],
iter2
);
ok1
(
iter2
->
item
==
sorted
[
i
]);
}
if
(
i
!=
0
)
fail
(
"Not enough items in btree according to backward traversal"
);
}
#if 0
//(tries to) remove the key from both the btree and the test array. Returns 1 on success, 0 on failure.
//success is when an item is removed from the btree and the array, or when none is removed from either
//failure is when an item is removed from the btree but not the array or vice versa
//after removing, it tries removing again to make sure that removal returns 0
static int test_remove(struct btree *btree, struct btree_item items[], size_t *count, const char *key)
{
size_t i;
for (i = *count; i--;) {
if (!strcmp(items[i].key, key)) {
//item found in array
memmove(&items[i], &items[i+1], (*count-(i+1))*sizeof(*items));
(*count)--;
//puts("----------");
//btree_trace(btree);
//removal should succeed, as the key should be there
//this is not a contradiction; the test is performed twice
return btree_remove(btree, key) && !btree_remove(btree, key);
}
}
//removal should fail, as the key should not be there
//this is not redundant; the test is performed twice
return !btree_remove(btree, key) && !btree_remove(btree, key);
}
#endif
static
void
test_search_implement
(
void
)
{
struct
btree
*
btree
=
btree_new
(
order_by_string
);
size_t
i
;
const
char
*
unsorted
[]
=
{
"md4"
,
"isaac"
,
"noerr"
,
"talloc_link"
,
"asearch"
,
"tap"
,
"crcsync"
,
"wwviaudio"
,
"array_size"
,
"alignof"
,
"str"
,
"read_write_all"
,
"grab_file"
,
"out"
,
"daemonize"
,
"array"
,
"crc"
,
"str_talloc"
,
"build_assert"
,
"talloc"
,
"alloc"
,
"endian"
,
"btree"
,
"typesafe_cb"
,
"check_type"
,
"list"
,
"ciniparser"
,
"ilog"
,
"ccan_tokenizer"
,
"tdb"
,
"block_pool"
,
"sparse_bsearch"
,
"container_of"
,
"stringmap"
,
"hash"
,
"short_types"
,
"ogg_to_pcm"
,
"antithread"
,
};
size_t
count
=
sizeof
(
unsorted
)
/
sizeof
(
*
unsorted
);
const
char
*
sorted
[
count
];
memcpy
(
sorted
,
unsorted
,
sizeof
(
sorted
));
qsort
(
sorted
,
count
,
sizeof
(
*
sorted
),
compare_by_string
);
for
(
i
=
0
;
i
<
count
;
i
++
)
{
btree_iterator
iter
;
if
(
btree_find_first
(
btree
,
unsorted
[
i
],
iter
))
fail
(
"btree_insert thinks the test array has duplicates, but it doesn't"
);
else
btree_insert_at
(
iter
,
unsorted
[
i
]);
}
btree_trace
(
btree
);
test_traverse
(
btree
,
sorted
,
count
);
/*
//try removing items that should be in the tree
ok1(test_remove(btree, sorted, &count, "btree"));
ok1(test_remove(btree, sorted, &count, "ccan_tokenizer"));
ok1(test_remove(btree, sorted, &count, "endian"));
ok1(test_remove(btree, sorted, &count, "md4"));
ok1(test_remove(btree, sorted, &count, "asearch"));
ok1(test_remove(btree, sorted, &count, "alloc"));
ok1(test_remove(btree, sorted, &count, "build_assert"));
ok1(test_remove(btree, sorted, &count, "typesafe_cb"));
ok1(test_remove(btree, sorted, &count, "sparse_bsearch"));
ok1(test_remove(btree, sorted, &count, "stringmap"));
//try removing items that should not be in the tree
ok1(test_remove(btree, sorted, &count, "java"));
ok1(test_remove(btree, sorted, &count, "openoffice"));
ok1(test_remove(btree, sorted, &count, "firefox"));
ok1(test_remove(btree, sorted, &count, "linux"));
ok1(test_remove(btree, sorted, &count, ""));
//test the traversal again to make sure things are okay
test_traverse(btree, sorted, count);
//remove the rest of the items, then make sure tree is empty
while (count)
ok1(test_remove(btree, sorted, &count, sorted[count-1].key));
ok1(btree->root == NULL);
*/
btree_delete
(
btree
);
}
int
main
(
void
)
{
struct
btree
*
btree
;
plan_tests
(
224
);
btree
=
btree_new
(
order_by_key
);
btree
->
destroy
=
destroy_test_item
;
test_insert
(
btree
);
test_find_traverse
(
btree
);
btree_delete
(
btree
);
test_search_implement
();
return
exit_status
();
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment