add libdict to repo

This commit is contained in:
Christian Giese
2023-12-21 11:03:22 +00:00
parent 8927250b1e
commit 4b28ba190d
54 changed files with 11389 additions and 53 deletions
-8
View File
@@ -21,14 +21,6 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Download RtBrick libdict
shell: bash
run: wget https://github.com/rtbrick/libdict/releases/download/1.0.3/libdict-${{matrix.os}}.zip
- name: Install RtBrick libdict
shell: bash
run: unzip libdict-${{matrix.os}}.zip; sudo dpkg -i libdict_1.0.3_amd64.deb; sudo dpkg -i libdict-dev_1.0.3_amd64.deb
- name: Install Dependencies
shell: bash
run: sudo apt install -y libcunit1-dev libncurses-dev libssl-dev libjansson-dev libcmocka-dev libpcap-dev
-8
View File
@@ -47,14 +47,6 @@ jobs:
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Download RtBrick libdict
shell: bash
run: wget https://github.com/rtbrick/libdict/releases/download/1.0.3/libdict-ubuntu-22.04.zip
- name: Install RtBrick libdict
shell: bash
run: unzip libdict-ubuntu-22.04.zip; sudo dpkg -i libdict_1.0.3_amd64.deb; sudo dpkg -i libdict-dev_1.0.3_amd64.deb
- name: Install Dependencies
shell: bash
run: sudo apt install -y libcunit1-dev libncurses5-dev libssl-dev libjansson-dev libcmocka-dev libpcap-dev
-8
View File
@@ -38,14 +38,6 @@ jobs:
steps:
- uses: actions/checkout@v3
- name: Download RtBrick libdict
shell: bash
run: wget https://github.com/rtbrick/libdict/releases/download/1.0.3/libdict-${{matrix.os}}.zip
- name: Install RtBrick libdict
shell: bash
run: unzip libdict-${{matrix.os}}.zip; sudo dpkg -i libdict_1.0.3_amd64.deb; sudo dpkg -i libdict-dev_1.0.3_amd64.deb
- name: Install Dependencies
shell: bash
run: sudo apt install -y libcunit1-dev libncurses-dev libssl-dev libjansson-dev libcmocka-dev libpcap-dev
-3
View File
@@ -38,9 +38,6 @@ if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
set(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "..." FORCE)
endif()
# libdict will be statically linked
find_library(libdict NAMES libdict.a REQUIRED)
if(BNGBLASTER_TESTS)
include(CTest)
endif()
+2
View File
@@ -1,7 +1,9 @@
include_directories("common/src/")
include_directories("libdict/include/")
add_subdirectory(common)
add_subdirectory(lwip)
add_subdirectory(libdict)
add_subdirectory(bngblaster)
add_subdirectory(lspgen)
+6
View File
@@ -14,6 +14,12 @@ support.
https://savannah.nongnu.org/projects/lwip/
### libdict
Local fork of libdict:
https://github.com/fmela/libdict
### bngblaster
BNG Blaster directory.
+1 -1
View File
@@ -8,7 +8,7 @@ foreach(_file ${BBL_SOURCES})
set_property(SOURCE ${_file} APPEND_STRING PROPERTY COMPILE_FLAGS "-frandom-seed=0x${checksum}")
endforeach()
add_executable(bngblaster ${COMMON_SOURCES} ${BBL_SOURCES})
add_executable(bngblaster ${LIBDICT_SOURCES} ${COMMON_SOURCES} ${BBL_SOURCES})
set(PLATFORM_SPECIFIC_LIBS "-lpthread")
string(APPEND CMAKE_C_FLAGS "-pthread")
+1 -2
View File
@@ -8,8 +8,7 @@ set(PROPERTIES
include_directories("../src")
include_directories("../../common/src")
find_library(libdict NAMES libdict.a REQUIRED)
set(LINK_LIBS ${libdict} m)
set(LINK_LIBS m)
add_executable(fuzz-protocols-decode protocols_decode.c ../src/bbl_protocols.c)
target_link_libraries(fuzz-protocols-decode ${LINK_LIBS})
target_compile_options(fuzz-protocols-decode PRIVATE -Wall -Wextra -pedantic)
+1 -2
View File
@@ -34,8 +34,7 @@
#include <sys/types.h>
#include <common.h>
#include "libdict/dict.h"
#include <dict.h>
/* LwIP */
#ifdef BNGBLASTER_LWIP
+1
View File
@@ -0,0 +1 @@
BasedOnStyle: None
+9
View File
@@ -0,0 +1,9 @@
words-*
shuffle.pl
bin/*
build/
*.log
*.pdf
*.aux
.*.swp
*.pdf
+13
View File
@@ -0,0 +1,13 @@
# list of source files.
FILE(GLOB LIBDICT_SOURCES src/*.c)
# Deterministic randomness for symbol name creation
list(SORT LIBDICT_SOURCES)
foreach(_file ${LIBDICT_SOURCES})
file(SHA1 ${_file} checksum)
string(SUBSTRING ${checksum} 0 8 checksum)
set_property(SOURCE ${_file} APPEND_STRING PROPERTY COMPILE_FLAGS "-frandom-seed=0x${checksum}")
endforeach()
# Export variable one level up
set(LIBDICT_SOURCES ${LIBDICT_SOURCES} PARENT_SCOPE)
File diff suppressed because it is too large Load Diff
+19
View File
@@ -0,0 +1,19 @@
# CUNIT_INCLUDE_DIRS - where to find <CUnit/CUnit.h>, etc.
# CUNIT_LIBRARIES - List of libraries when using libcunit.
# CUNIT_FOUND - True if libcunit found.
if(CUNIT_INCLUDE_DIRS)
# Already in cache, be silent
set(CUNIT_FIND_QUIETLY YES)
endif()
find_path(CUNIT_INCLUDE_DIRS CUnit/CUnit.h)
find_library(CUNIT_LIBRARY NAMES cunit libcunit)
# handle the QUIETLY and REQUIRED arguments and set CUNIT_FOUND to TRUE if
# all listed variables are TRUE
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(CUnit DEFAULT_MSG CUNIT_LIBRARY CUNIT_INCLUDE_DIRS)
if(CUNIT_FOUND)
set(CUNIT_LIBRARIES ${CUNIT_LIBRARY})
endif()
+123
View File
@@ -0,0 +1,123 @@
SOURCE_DIR := src
HEADER_DIR := include
OUTPUT_DIR := bin
UNAME := $(shell uname)
ifeq ($(UNAME), Darwin)
HOMEBREW_PREFIX := $(shell dirname `which brew`)
HOMEBREW_PREFIX := $(shell dirname $(HOMEBREW_PREFIX))
TEST_CFLAGS := -I$(HOMEBREW_PREFIX)/include
TEST_LDFLAGS := -L$(HOMEBREW_PREFIX)/lib
endif
SOURCE := $(wildcard $(SOURCE_DIR)/*.c)
HEADER := $(wildcard $(HEADER_DIR)/*.h $(SOURCE_DIR)/*.h)
STATIC_OBJ := $(SOURCE:$(SOURCE_DIR)/%.c=$(OUTPUT_DIR)/%.o)
PROFIL_OBJ := $(SOURCE:$(SOURCE_DIR)/%.c=$(OUTPUT_DIR)/%.po)
SHARED_OBJ := $(SOURCE:$(SOURCE_DIR)/%.c=$(OUTPUT_DIR)/%.So)
PROG_SRC := anagram.c benchmark.c demo.c
PROGRAMS := $(PROG_SRC:%.c=$(OUTPUT_DIR)/%)
LIB := dict
STATIC_LIB_NAME := lib$(LIB).a
PROFIL_LIB_NAME := lib$(LIB)_p.a
SHARED_LIB_NAME := lib$(LIB).so
STATIC_LIB := $(OUTPUT_DIR)/$(STATIC_LIB_NAME)
PROFIL_LIB := $(OUTPUT_DIR)/$(PROFIL_LIB_NAME)
SHARED_LIB := $(OUTPUT_DIR)/$(SHARED_LIB_NAME)
ifeq ($(CC),cc)
CC := $(shell which clang || which gcc)
endif
INCLUDES := -I$(HEADER_DIR) -I$(SOURCE_DIR)
ifeq ($(findstring clang,$(CC)),clang)
WARNINGS := -Weverything -Wno-padded -Wno-format-nonliteral -Wno-disabled-macro-expansion
else # gcc
WARNINGS := -Wall -Wextra
endif
WARNINGS := $(WARNINGS) -Werror -ansi -pedantic
CFLAGS := $(WARNINGS) -std=c11 -O2 -pipe $(INCLUDES)
LDFLAGS :=
AR ?= ar
ARFLAGS ?= cru
INSTALL_PREFIX ?= /usr/local
INSTALL_BINDIR = $(INSTALL_PREFIX)/bin
INSTALL_LIBDIR = $(INSTALL_PREFIX)/lib
INSTALL_INCDIR = $(INSTALL_PREFIX)/include/dict
INSTALL_MANDIR = $(INSTALL_PREFIX)/man
INSTALL ?= install
INSTALL_LIBVER = 2
INSTALL_SHLIB = $(SHARED_LIB_NAME).$(INSTALL_LIBVER)
INSTALL_USER ?= 0
INSTALL_GROUP ?= 0
INSTALL_EXTRA_ARGS ?= -s
all: $(OUTPUT_DIR) $(STATIC_LIB) $(SHARED_LIB) $(PROGRAMS)
@echo && echo "Don't forget to run 'make test'"
shared: $(SHARED_LIB)
static: $(STATIC_LIB) $(PROFIL_LIB)
$(OUTPUT_DIR):
[ -d $(OUTPUT_DIR) ] || mkdir -m 755 $(OUTPUT_DIR)
$(STATIC_LIB): $(OUTPUT_DIR) $(STATIC_OBJ)
$(AR) $(ARFLAGS) $(STATIC_LIB) $(STATIC_OBJ)
$(PROFIL_LIB): $(OUTPUT_DIR) $(PROFIL_OBJ)
$(AR) $(ARFLAGS) $(PROFIL_LIB) $(PROFIL_OBJ)
$(SHARED_LIB): $(OUTPUT_DIR) $(SHARED_OBJ)
$(CC) -shared -o $(SHARED_LIB) $(SHARED_OBJ)
$(OUTPUT_DIR)/%.o: $(SOURCE_DIR)/%.c $(HEADER) GNUmakefile $(OUTPUT_DIR)
$(CC) $(CFLAGS) -c -o $(@) $(<)
$(OUTPUT_DIR)/%.po: $(SOURCE_DIR)/%.c $(HEADER) GNUmakefile $(OUTPUT_DIR)
$(CC) $(CFLAGS) -pg -c -o $(@) $(<)
$(OUTPUT_DIR)/%.So: $(SOURCE_DIR)/%.c $(HEADER) GNUmakefile $(OUTPUT_DIR)
$(CC) $(CFLAGS) -fPIC -DPIC -c -o $(@) $(<)
$(OUTPUT_DIR)/unit_tests: unit_tests.c $(STATIC_OBJ) GNUmakefile
$(CC) $(CFLAGS) $(TEST_CFLAGS) -o $(@) $(<) $(STATIC_OBJ) $(LDFLAGS) $(TEST_LDFLAGS) -lcunit
$(OUTPUT_DIR)/%: %.c $(STATIC_OBJ) GNUmakefile
$(CC) $(CFLAGS) -o $(@) $(<) $(STATIC_OBJ) $(LDFLAGS)
test: $(OUTPUT_DIR)/unit_tests
./$(OUTPUT_DIR)/unit_tests
.PHONY: clean
clean:
if test -d $(OUTPUT_DIR); then rm -r $(OUTPUT_DIR); fi
.PHONY: analyze
analyze:
@for x in $(SOURCE) $(PROG_SRC); \
do echo Analyzing $$x ...; \
clang --analyze $(INCLUDES) $$x -o /dev/null; \
done
.PHONY: install
install: $(STATIC_LIB) $(PROFIL_LIB) $(SHARED_LIB)
[ -d $(INSTALL_INCDIR) ] || mkdir -p -m 755 $(INSTALL_INCDIR)
$(INSTALL) -o $(INSTALL_USER) -g $(INSTALL_GROUP) -m 644 $(HEADER) $(INSTALL_INCDIR)
[ -d $(INSTALL_LIBDIR) ] || mkdir -p -m 755 $(INSTALL_LIBDIR)
$(INSTALL) $(INSTALL_EXTRA_ARGS) -o $(INSTALL_USER) -g $(INSTALL_GROUP) -m 644 $(STATIC_LIB) $(INSTALL_LIBDIR)/$(STATIC_LIB_NAME)
$(INSTALL) $(INSTALL_EXTRA_ARGS) -o $(INSTALL_USER) -g $(INSTALL_GROUP) -m 644 $(PROFIL_LIB) $(INSTALL_LIBDIR)/$(PROFIL_LIB_NAME)
$(INSTALL) $(INSTALL_EXTRA_ARGS) -o $(INSTALL_USER) -g $(INSTALL_GROUP) -m 755 $(SHARED_LIB) $(INSTALL_LIBDIR)/$(INSTALL_SHLIB)
$(SHELL) -ec 'cd $(INSTALL_LIBDIR) && ln -sf $(INSTALL_SHLIB) $(SHARED_LIB_NAME)'
.PHONY: uninstall
uninstall :
-rm -rf $(INSTALL_INCDIR)
-rm -f $(INSTALL_LIBDIR)/$(STATIC_LIB)
-rm -f $(INSTALL_LIBDIR)/$(PROFIL_LIB)
-rm -f $(INSTALL_LIBDIR)/$(INSTALL_SHLIB)
-rm -f $(INSTALL_LIBDIR)/$(SHARED_LIB)
+22
View File
@@ -0,0 +1,22 @@
Copyright (c) 2001-2014, Farooq Mela
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.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
+62
View File
@@ -0,0 +1,62 @@
# libdict
[![Test](https://github.com/rtbrick/libdict/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/rtbrick/libdict/actions/workflows/test.yml)
[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/rtbrick/libdict?color=green)](https://github.com/rtbrick/libdict/releases)
[![Doxygen documentation](https://img.shields.io/badge/docs-doxygen-green)](https://rtbrick.github.io/libdict/html)
[![GitHub](https://img.shields.io/github/license/rtbrick/libdict)](LICENSE)
libdict is a C library that provides the following data structures with efficient insert, lookup, and delete routines:
* [height-balanced (AVL) tree](http://en.wikipedia.org/wiki/AVL_tree)
* [red-black tree](http://en.wikipedia.org/wiki/Red-black_tree)
* [splay tree](http://en.wikipedia.org/wiki/Splay_tree)
* [weight-balanced tree](https://en.wikipedia.org/wiki/Weight-balanced_tree)
* [path-reduction tree](https://cs.uwaterloo.ca/research/tr/1982/CS-82-07.pdf)
* [treap](http://en.wikipedia.org/wiki/Treap)
* [hashtable using separate chaining](http://en.wikipedia.org/wiki/Hashtable#Separate_chaining)
* [hashtable using open addressing with linear probing](http://en.wikipedia.org/wiki/Hashtable#Open_addressing)
* [skip list](https://en.wikipedia.org/wiki/Skip_list)
All data structures in this library support insert, search, and remove, and have bidirectional iterators. The sorted data structures (everything but hash tables) support near-search operations: searching for the key greater or equal to, strictly greater than, lesser or equal to, or strictly less than, a given key. The tree data structures also support the selecting the nth element; this takes linear time, except in path-reduction and weight-balanced trees, where it only takes logarithmic time.
The API and code are written with efficiency as a primary concern. For example, an insert call returns a boolean indicating whether or not the key was already present in the dictionary (i.e. whether there was an insertion or a collision), and a pointer to the location of the associated data. Thus, an insert-or-update operation can be supported with a single traversal of the data structure. In addition, almost all recursive algorithms have been rewritten to use iteration instead.
Documentation is generated by Doxygen on every commit to master and is available [here](https://rtbrick.github.io/libdict/html).
## Installing
On every release, Debian packages are automatically built. You can download the
latest packages from [here](https://github.com/rtbrick/libdict/releases/latest/download/libdict-debian.zip).
To install, run this:
wget https://github.com/rtbrick/libdict/releases/latest/download/libdict-debian.zip
unzip libdict-debian.zip
sudo apt install -y ./*.deb
This will install the library, along with headers and tools.
## Building
Both GNU Make and CMake are supported. To use the CMake build system, ensure that you have
a recent version of CMake installed (version 3.10 or greater). Then, from within this
repository, run:
mkdir build
cd build
cmake ..
make -j16
If you want to run unit tests, you can run:
make test
If you want to generate Debian packages, you can run:
cpack -G DEB
To build with the included GNU Makefile, simply run `make` in the repository.
## License
libdict is released under the simplified BSD [license](LICENSE).
+21
View File
@@ -0,0 +1,21 @@
Aragon and Seidel, "Randomized Search Trees", Algorithmica 16:464-497, 1996
Cormen, Leiserson and Rivest, _Introduction to Algorithms_, MIT Press, 1990
Gonnet, "Balancing Binary Trees by Internal Path Reduction", Communications of the ACM 26(12):1074-1081, 1983
Gonnet, _Handbook of Algorithms and Data Structures_, Addison-Wesley, 1984
Guibas and Sedgewick, "A Dichromatic Framework for Balanced Trees", Proceedings of the 19th Annual Symposium on Foundations of Computer Science (FOCS) 19:8-21, IEEE Computer Science Society, 1978
Knuth, _The Art of Computer Programming, Volume 3: Sorting and Searching_, 2nd Edition, Addison-Wesley, 1998
Nievergelt and Reingold, "Binary Search Trees of Bounded Balance", SIAM Journal of Computing 2 1:33-43, 1973
Pugh, "Skip lists: a probabilistic alternative to balanced trees", Communications of the ACM 33:668-676, 1990
Sleator and Tarjan, "Self-Adjusting Binary Search Trees", Journal of the ACM 32(3):652-686, 1985
Tarjan, "Amortized Computational Complexity", SIAM Journal on Algebraic and Discrete Methods, 6(2):306-318, 1985
Tarjan, _Data Structures and Network Algorithms_, Society of Industrial and Applied Mathematics (SIAM), 1983
+7
View File
@@ -0,0 +1,7 @@
[ ] Use restrict keyword wherever appropriate.
[X] Fix skiplist prev pointers.
[ ] Implement incomplete functionality, e.g. iterator remove & compare.
[X] Reformat to 80 columns.
[ ] Optimize double-rotations.
[X] Fix bugs in weight balanced tree.
[ ] Optimize skiplist.
+114
View File
@@ -0,0 +1,114 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "dict.h"
#include "dict_private.h"
void *xmalloc(size_t size);
char *xstrdup(const char *s);
typedef struct WordList WordList;
struct WordList {
char *word;
WordList *next;
};
int
main(int argc, char *argv[])
{
if (argc != 2) {
printf("Expected filename argument.\n");
exit(1);
}
FILE *fp = fopen(argv[1], "r");
if (!fp) {
printf("Unable to open file '%s'.\n", argv[1]);
exit(1);
}
rb_tree *tree = rb_tree_new(dict_str_cmp);
char buf[512];
while (fgets(buf, sizeof(buf), fp)) {
if (isupper(buf[0])) /* Disregard proper nouns. */
continue;
strtok(buf, "\r\n");
int freq[256] = { 0 };
memset(freq, 0, sizeof(freq));
ASSERT(buf[0] != '\0');
for (char *p = buf; *p; p++)
freq[tolower(*p)]++;
char name[1024];
char *p = name;
for (int i=1; i<256; i++) {
if (freq[i]) {
ASSERT(freq[i] < 10);
*p++ = (char) i;
*p++ = '0' + (char) freq[i];
}
}
*p = 0;
WordList* word = xmalloc(sizeof(*word));
word->word = xstrdup(buf);
WordList** wordp = (WordList**) rb_tree_insert(tree, xstrdup(name)).datum_ptr;
word->next = *wordp;
*wordp = word;
}
rb_itor *itor = rb_itor_new(tree);
for (rb_itor_first(itor); rb_itor_valid(itor); rb_itor_next(itor)) {
WordList *word = *rb_itor_datum(itor);
ASSERT(word != NULL);
if (word->next) {
int count = 1;
for (; word->next; count++)
word = word->next;
printf("%2d:[", count);
word = *rb_itor_datum(itor);
while (word) {
printf("%s%c", word->word, word->next ? ',' : ']');
word = word->next;
}
printf("\n");
}
word = *rb_itor_datum(itor);
while (word) {
WordList *next = word->next;
free(word->word);
free(word);
word = next;
}
} while (rb_itor_next(itor));
rb_itor_free(itor);
rb_tree_free(tree, NULL);
return 0;
}
char *
xstrdup(const char *s)
{
size_t len = strlen(s) + 1;
return memcpy(xmalloc(len), s, len);
}
void *
xmalloc(size_t size)
{
ASSERT(size > 0);
void *p = malloc(size);
if (!p) {
fprintf(stderr, "Out of memory\n");
abort();
}
return p;
}
+456
View File
@@ -0,0 +1,456 @@
/* benchmark.c
* Some timing tests for libdict
* Copyright (C) 2001-2011 Farooq Mela */
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "dict.h"
#include "dict_private.h"
#include "tree_common.h"
#include "util.h"
static const char appname[] = "benchmark";
char *xstrdup(const char *str);
#ifdef __GNUC__
# define NORETURN __attribute__((__noreturn__))
#else
# define NORETURN
#endif
void quit(const char *, ...) NORETURN;
void warn(const char *fmt, ...);
void *xmalloc(size_t size);
static size_t hash_count = 0, comp_count = 0;
unsigned str_hash(const void *p);
int my_strcmp(const void *k1, const void *k2);
unsigned ptr_hash(const void *p);
int my_ptrcmp(const void *k1, const void *k2);
void key_str_free(void *key, void *datum);
void timer_start(struct rusage* start);
void timer_end(const struct rusage* start, struct rusage *end,
struct timeval *total);
dict *create_dictionary(char type, const char **container_name);
#define HASHTABLE_SIZE 97
/* #define HASHTABLE_SIZE 997 */
/* #define HASHTABLE_SIZE 9973 */
static size_t malloced = 0;
int
main(int argc, char **argv)
{
bool shuffle_keys = true;
if (argc != 3) {
fprintf(stderr, "usage: %s [type] [input]\n", appname);
fprintf(stderr, "type: specifies the dictionary type:\n");
fprintf(stderr, " h: height-balanced tree\n");
fprintf(stderr, " p: path-reduction tree\n");
fprintf(stderr, " r: red-black tree\n");
fprintf(stderr, " t: treap\n");
fprintf(stderr, " s: splay tree\n");
fprintf(stderr, " w: weight-balanced tree\n");
fprintf(stderr, " S: skiplist\n");
fprintf(stderr, " H: hashtable\n");
fprintf(stderr, " 2: hashtable 2\n");
fprintf(stderr, "input: text file consisting of newline-separated keys\n");
exit(EXIT_FAILURE);
}
srand(0xdeadbeef);
dict_malloc_func = xmalloc;
const char type = argv[1][0];
const char *container_name = NULL;
dict *dct = create_dictionary(type, &container_name);
if (!dct)
quit("can't create container");
ASSERT(dict_verify(dct));
ASSERT(comp_count == 0);
ASSERT(hash_count == 0);
const size_t malloced_save = malloced;
FILE *fp = fopen(argv[2], "r");
if (fp == NULL)
quit("cant open file '%s': %s", argv[2], strerror(errno));
size_t nwords = 0;
char buf[512];
while (fgets(buf, sizeof(buf), fp))
++nwords;
if (!nwords)
quit("nothing read from file");
char **words = xmalloc(sizeof(*words) * nwords);
rewind(fp);
size_t words_read = 0;
while (words_read < nwords && fgets(buf, sizeof(buf), fp)) {
strtok(buf, "\n");
words[words_read++] = xstrdup(buf);
}
fclose(fp);
if (words_read < nwords)
quit("Only read %zu/%zu words!", words_read, nwords);
printf("Loaded %zu keys from %s.\n", nwords, argv[2]);
malloced = malloced_save;
size_t total_comp = 0, total_hash = 0, total_rotations = 0;
struct rusage start, end;
struct timeval total = { 0, 0 };
timer_start(&start);
for (unsigned i = 0; i < nwords; i++) {
dict_insert_result result = dict_insert(dct, words[i]);
if (!result.inserted)
quit("insert #%d failed for '%s'", i, words[i]);
ASSERT(result.datum_ptr != NULL);
ASSERT(*result.datum_ptr == NULL);
*result.datum_ptr = words[i];
}
timer_end(&start, &end, &total);
printf(" %s container: %.02fkB\n", container_name, malloced_save * 1e-3);
printf(" %s memory: %.02fkB\n", container_name, malloced * 1e-3);
printf(" %s insert: %6.03fs %9zu cmp (%.02f/insert)",
container_name,
(end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6,
comp_count, comp_count / (double) nwords);
if (hash_count)
printf(" %9zu hash", hash_count);
printf("\n");
total_comp += comp_count; comp_count = 0;
total_hash += hash_count; hash_count = 0;
if (dict_is_sorted(dct) && type != 'S') {
tree_base *tree = dict_private(dct);
printf(" min path length: %zu\n", tree_min_path_length(tree));
printf(" max path length: %zu\n", tree_max_path_length(tree));
printf(" tot path length: %zu\n", tree_total_path_length(tree));
printf("insert rotations: %zu\n", tree->rotation_count);
total_rotations += tree->rotation_count;
tree->rotation_count = 0;
} else if (type == 'S') {
size_t counts[16] = { 0 };
size_t num_counts = skiplist_link_count_histogram(dict_private(dct), counts, sizeof(counts) / sizeof(counts[0]));
size_t count_sum = 0;
for (size_t i = 0; i <= num_counts; ++i) {
printf("skiplist %zu-node(s): %zu\n", i, counts[i]);
count_sum += counts[i];
}
ASSERT(count_sum == nwords);
}
ASSERT(dict_verify(dct));
comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */
size_t n = dict_count(dct);
if (n != nwords)
quit("bad count (%u - should be %u)!", n, nwords);
dict_itor *itor = dict_itor_new(dct);
timer_start(&start);
n = 0;
ASSERT(dict_itor_first(itor));
do {
ASSERT(dict_itor_valid(itor));
ASSERT(dict_itor_key(itor) == *dict_itor_datum(itor));
++n;
} while (dict_itor_next(itor));
timer_end(&start, &end, &total);
printf(" %s fwd iterate: %6.03fs\n",
container_name,
(end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6);
if (n != nwords)
warn("Fwd iteration returned %u items - should be %u", n, nwords);
ASSERT(dict_verify(dct));
comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */
timer_start(&start);
n = 0;
ASSERT(dict_itor_last(itor));
do {
ASSERT(dict_itor_valid(itor));
ASSERT(dict_itor_key(itor) == *dict_itor_datum(itor));
++n;
} while (dict_itor_prev(itor));
timer_end(&start, &end, &total);
printf(" %s rev iterate: %6.03fs\n",
container_name,
(end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6);
if (n != nwords)
warn("Rev iteration returned %u items - should be %u", n, nwords);
dict_itor_free(itor);
if (shuffle_keys) shuffle(words, nwords);
ASSERT(dict_verify(dct));
comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */
timer_start(&start);
for (unsigned i = 0; i < nwords; i++) {
void **p = dict_search(dct, words[i]);
if (!p)
quit("lookup failed for '%s'", buf);
if (*p != words[i])
quit("bad data for '%s', got '%s' instead", words[i], *(char **)p);
}
timer_end(&start, &end, &total);
printf(" %s good search: %6.03fs %9zu cmp (%.02f/search)",
container_name,
(end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6,
comp_count, comp_count / (double) nwords);
if (hash_count)
printf(" %9zu hash", hash_count);
printf("\n");
total_comp += comp_count; comp_count = 0;
total_hash += hash_count; hash_count = 0;
if (type != 'H' && type != '2' && type != 'S') {
tree_base *tree = dict_private(dct);
printf("search rotations: %zu\n", tree->rotation_count);
total_rotations += tree->rotation_count;
tree->rotation_count = 0;
}
ASSERT(dict_verify(dct));
comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */
timer_start(&start);
for (unsigned i = 0; i < nwords; i++) {
unsigned rv = dict_rand() % strlen(words[i]);
words[i][rv]++;
dict_search(dct, words[i]);
words[i][rv]--;
}
timer_end(&start, &end, &total);
printf(" %s bad search: %6.03fs %9zu cmp (%.02f/search)",
container_name,
(end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6,
comp_count, comp_count / (double) nwords);
if (hash_count)
printf(" %9zu hash", hash_count);
printf("\n");
total_comp += comp_count; comp_count = 0;
total_hash += hash_count; hash_count = 0;
ASSERT(dict_verify(dct));
comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */
if (shuffle_keys) shuffle(words, nwords);
timer_start(&start);
for (unsigned i = 0; i < nwords; i++) {
dict_remove_result result = dict_remove(dct, words[i]);
if (!result.removed)
quit("removing #%d '%s' failed!\n", i, words[i]);
ASSERT(result.key == words[i]);
ASSERT(result.datum == words[i]);
}
timer_end(&start, &end, &total);
printf(" %s remove: %6.03fs %9zu cmp (%.2f/remove)",
container_name,
(end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6,
comp_count, comp_count / (double)nwords);
if (hash_count)
printf(" %9zu hash", hash_count);
printf("\n");
total_comp += comp_count; comp_count = 0;
total_hash += hash_count; hash_count = 0;
if (type != 'H' && type != '2' && type != 'S') {
tree_base *tree = dict_private(dct);
printf("remove rotations: %zu\n", tree->rotation_count);
total_rotations += tree->rotation_count;
tree->rotation_count = 0;
}
ASSERT(dict_verify(dct));
comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */
if ((n = dict_count(dct)) != 0)
quit("error - count not zero (%u)!", n);
dict_free(dct, key_str_free);
printf(" %s total: %6.03fs %9zu cmp",
container_name,
(total.tv_sec * 1000000 + total.tv_usec) * 1e-6,
total_comp);
if (total_hash)
printf(" %9zu hash", total_hash);
printf("\n");
if (type != 'H' && type != '2' && type != 'S') {
printf(" total rotations: %zu\n", total_rotations);
}
FREE(words);
exit(EXIT_SUCCESS);
}
dict *
create_dictionary(char type, const char **container_name)
{
dict_compare_func cmp_func = my_strcmp;
dict_hash_func hash_func = str_hash;
switch (type) {
case 'h':
*container_name = "hb";
return hb_dict_new(cmp_func);
case 'p':
*container_name = "pr";
return pr_dict_new(cmp_func);
case 'r':
*container_name = "rb";
return rb_dict_new(cmp_func);
case 't':
*container_name = "tr";
return tr_dict_new(cmp_func, NULL);
case 's':
*container_name = "sp";
return sp_dict_new(cmp_func);
case 'S':
*container_name = "sk";
return skiplist_dict_new(cmp_func, 12);
case 'w':
*container_name = "wb";
return wb_dict_new(cmp_func);
case 'H':
*container_name = "ht";
return hashtable_dict_new(cmp_func, hash_func, HASHTABLE_SIZE);
case '2':
*container_name = "h2";
return hashtable2_dict_new(cmp_func, hash_func, HASHTABLE_SIZE);
default:
quit("type must be one of h, p, r, t, s, w or H");
}
}
char *
xstrdup(const char *str)
{
size_t len = strlen(str) + 1;
return memcpy(xmalloc(len), str, len);
}
void
quit(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "%s: ", appname);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
exit(EXIT_FAILURE);
}
void
warn(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "warning: %s: ", appname);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
}
void *
xmalloc(size_t size)
{
void *p = malloc(size);
if (!p)
quit("out of memory");
malloced += size;
return p;
}
unsigned
str_hash(const void *p)
{
++hash_count;
return dict_str_hash(p);
}
int
my_strcmp(const void *k1, const void *k2)
{
++comp_count;
return strcmp(k1, k2);
}
unsigned
ptr_hash(const void *p)
{
++hash_count;
return (unsigned) ((2166136261U ^ (uintptr_t)p) * 16777619U);
}
int
my_ptrcmp(const void *k1, const void *k2)
{
++comp_count;
return (k1 < k2) ? -1 : (k1 > k2);
}
void
key_str_free(void *key, void *datum)
{
ASSERT(key == datum);
free(key);
}
void
timer_start(struct rusage *start)
{
getrusage(RUSAGE_SELF, start);
}
void
timer_end(const struct rusage *start, struct rusage *end,
struct timeval *total) {
getrusage(RUSAGE_SELF, end);
if (end->ru_utime.tv_usec < start->ru_utime.tv_usec) {
end->ru_utime.tv_usec += 1000000; end->ru_utime.tv_sec--;
}
end->ru_utime.tv_usec -= start->ru_utime.tv_usec;
end->ru_utime.tv_sec -= start->ru_utime.tv_sec;
total->tv_sec += end->ru_utime.tv_sec;
if ((total->tv_usec += end->ru_utime.tv_usec) > 1000000) {
total->tv_usec -= 1000000; total->tv_sec++;
}
}
+295
View File
@@ -0,0 +1,295 @@
/* demo.c
* Interactive demo of libdict.
* Copyright (C) 2001-2011 Farooq Mela */
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include "dict.h"
static const char appname[] = "demo";
char *xstrdup(const char *str);
#if defined(__GNUC__) || defined(__clang__)
# define NORETURN __attribute__((__noreturn__))
#else
# define NORETURN
#endif
void quit(const char *, ...) NORETURN;
void *xmalloc(size_t size);
void *xrealloc(void *ptr, size_t size);
void *xdup(const void *ptr, size_t size);
static void
key_val_free(void *key, void *datum)
{
free(key);
free(datum);
}
#define HSIZE 997
#define SKIPLINKS 10
int
main(int argc, char **argv)
{
if (argc != 2)
quit("usage: %s [type]", appname);
srand((unsigned)time(NULL));
dict_malloc_func = xmalloc;
dict *dct = NULL;
++argv;
switch (argv[0][0]) {
case 'h':
dct = hb_dict_new((dict_compare_func)strcmp);
break;
case 'p':
dct = pr_dict_new((dict_compare_func)strcmp);
break;
case 'r':
dct = rb_dict_new((dict_compare_func)strcmp);
break;
case 't':
dct = tr_dict_new((dict_compare_func)strcmp, NULL);
break;
case 's':
dct = sp_dict_new((dict_compare_func)strcmp);
break;
case 'w':
dct = wb_dict_new((dict_compare_func)strcmp);
break;
case 'S':
dct = skiplist_dict_new((dict_compare_func)strcmp, SKIPLINKS);
break;
case 'H':
dct = hashtable_dict_new((dict_compare_func)strcmp, dict_str_hash, HSIZE);
break;
case '2':
dct = hashtable2_dict_new((dict_compare_func)strcmp, dict_str_hash, HSIZE);
break;
default:
quit("type must be one of h, p, r, t, s, w, S, H, or 2");
}
if (!dct)
quit("can't create container");
for (;;) {
dict_verify(dct);
printf("> ");
fflush(stdout);
char buf[512];
if (fgets(buf, sizeof(buf), stdin) == NULL)
break;
char *p, *ptr, *ptr2;
if ((p = strchr(buf, '\n')) != NULL)
*p = 0;
for (p = buf; *p && isspace(*p); p++)
/* void */;
if (buf != p) {
strcpy(buf, p);
}
ptr2 = (ptr = strtok(buf, " ") ? strtok(NULL, " ") : NULL) ?
strtok(NULL, " ") : NULL;
if (*buf == 0)
continue;
if (strcmp(buf, "insert") == 0) {
if (!ptr2) {
printf("usage: insert <key> <data>\n");
continue;
}
dict_insert_result result = dict_insert(dct, xstrdup(ptr));
if (result.inserted) {
*result.datum_ptr = xstrdup(ptr2);
printf("inserted '%s': '%s'\n",
ptr, (char *)*result.datum_ptr);
} else {
printf("'%s' already in dict: '%s'\n",
ptr, (char *)*result.datum_ptr);
}
} else if (strcmp(buf, "search") == 0) {
if (ptr2) {
printf("usage: search <key>\n");
continue;
}
void** search = dict_search(dct, ptr);
if (search)
printf("found '%s': '%s'\n", ptr, *(char **)search);
else
printf("'%s' not found!\n", ptr);
} else if (strcmp(buf, "searchle") == 0) {
if (ptr2) {
printf("usage: searchle <key>\n");
continue;
}
if (!dict_is_sorted(dct)) {
printf("dict does not support that operation!");
continue;
}
void** search = dict_search_le(dct, ptr);
if (search)
printf("le '%s': '%s'\n", ptr, *(char **)search);
else
printf("le '%s': no result.\n", ptr);
} else if (strcmp(buf, "searchlt") == 0) {
if (ptr2) {
printf("usage: searchlt <key>\n");
continue;
}
if (!dict_is_sorted(dct)) {
printf("dict does not support that operation!");
continue;
}
void** search = dict_search_lt(dct, ptr);
if (search)
printf("lt '%s': '%s'\n", ptr, *(char **)search);
else
printf("lt '%s': no result.\n", ptr);
} else if (strcmp(buf, "searchge") == 0) {
if (ptr2) {
printf("usage: searchge <key>\n");
continue;
}
if (!dict_is_sorted(dct)) {
printf("dict does not support that operation!");
continue;
}
void** search = dict_search_ge(dct, ptr);
if (search)
printf("ge '%s': '%s'\n", ptr, *(char **)search);
else
printf("ge '%s': no result.\n", ptr);
} else if (strcmp(buf, "searchgt") == 0) {
if (ptr2) {
printf("usage: searchgt <key>\n");
continue;
}
if (!dict_is_sorted(dct)) {
printf("dict does not support that operation!");
continue;
}
void** search = dict_search_gt(dct, ptr);
if (search)
printf("gt '%s': '%s'\n", ptr, *(char **)search);
else
printf("gt '%s': no result.\n", ptr);
} else if (strcmp(buf, "remove") == 0) {
if (!ptr || ptr2) {
printf("usage: remove <key>\n");
continue;
}
dict_remove_result result = dict_remove(dct, ptr);
if (result.removed) {
printf("removed '%s' from dict: %s\n", (char *)result.key, (char *)result.datum);
free(result.key);
free(result.datum);
} else
printf("key '%s' not in dict!\n", ptr);
} else if (strcmp(buf, "show") == 0) {
if (ptr) {
printf("usage: show\n");
continue;
}
dict_itor *itor = dict_itor_new(dct);
dict_itor_first(itor);
for (; dict_itor_valid(itor); dict_itor_next(itor))
printf("'%s': '%s'\n",
(char *)dict_itor_key(itor),
(char *)*dict_itor_datum(itor));
dict_itor_free(itor);
} else if (strcmp(buf, "reverse") == 0) {
if (ptr) {
printf("usage: reverse\n");
continue;
}
dict_itor *itor = dict_itor_new(dct);
dict_itor_last(itor);
for (; dict_itor_valid(itor); dict_itor_prev(itor))
printf("'%s': '%s'\n",
(char *)dict_itor_key(itor),
(char *)*dict_itor_datum(itor));
dict_itor_free(itor);
} else if (strcmp(buf, "clear") == 0) {
if (ptr) {
printf("usage: clear\n");
continue;
}
dict_clear(dct, key_val_free);
} else if (strcmp(buf, "count") == 0) {
if (ptr) {
printf("usage: count\n");
continue;
}
printf("count = %zu\n", dict_count(dct));
} else if (strcmp(buf, "quit") == 0) {
break;
} else {
printf("Usage summary:\n");
printf(" insert <key> <data>\n");
printf(" search <key>\n");
printf(" searchle <key>\n");
printf(" searchlt <key>\n");
printf(" searchge <key>\n");
printf(" searchgt <key>\n");
printf(" remove <key>\n");
printf(" clear\n");
printf(" count\n");
printf(" show\n");
printf(" reverse\n");
printf(" quit\n");
}
}
dict_free(dct, key_val_free);
exit(0);
}
char *
xstrdup(const char *str)
{
return xdup(str, strlen(str) + 1);
}
void
quit(const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
fprintf(stderr, "%s: ", appname);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\n");
va_end(args);
exit(EXIT_FAILURE);
}
void *
xmalloc(size_t size)
{
void *p = malloc(size);
if (!p) {
fprintf(stderr, "out of memory\n");
abort();
}
return p;
}
void *
xdup(const void *ptr, size_t size)
{
return memcpy(xmalloc(size), ptr, size);
}
+218
View File
@@ -0,0 +1,218 @@
/*
* libdict -- generic interface definitions.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_DICT_H__
#define LIBDICT_DICT_H__
#if defined(__cplusplus) || defined(c_plusplus)
# define BEGIN_DECL extern "C" {
# define END_DECL }
#else
# define BEGIN_DECL
# define END_DECL
#endif
BEGIN_DECL
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#define DICT_VERSION_MAJOR 0
#define DICT_VERSION_MINOR 3
#define DICT_VERSION_PATCH 0
extern const char* const kDictVersionString;
/* A pointer to a function that compares two keys. It needs to return a
* negative value if k1<k2, a positive value if k1>k2, and zero if the keys are
* equal. The comparison should be reflexive (k1>k2 implies k1<k2, etc.),
* symmetric (k1=k1) and transitive (k1>k2 and k2>k3 implies k1>k3). */
typedef int (*dict_compare_func)(const void*, const void*);
/* A pointer to a function that is called when a key-value pair gets removed
* from a dictionary. */
typedef void (*dict_delete_func)(void*, void*);
/* A pointer to a function used for iterating over dictionary contents. */
typedef bool (*dict_visit_func)(const void*, void*, void*);
/* A pointer to a function that returns the hash value of a key. */
typedef unsigned (*dict_hash_func)(const void*);
/* A pointer to a function that returns the priority of a key. */
typedef unsigned (*dict_prio_func)(const void*);
/* A pointer to a function that libdict will use to allocate memory. */
extern void* (*dict_malloc_func)(size_t);
/* A pointer to a function that libdict will use to deallocate memory. */
extern void (*dict_free_func)(void*);
/* Forward declarations for transparent type dict_itor. */
typedef struct dict_itor dict_itor;
typedef struct {
void** datum_ptr;
bool inserted;
} dict_insert_result;
typedef struct {
void* key;
void* datum;
bool removed;
} dict_remove_result;
typedef dict_itor* (*dict_inew_func)(void* obj);
typedef size_t (*dict_dfree_func)(void* obj, dict_delete_func delete_func);
typedef dict_insert_result
(*dict_insert_func)(void* obj, void* key);
typedef void** (*dict_search_func)(void* obj, const void* key);
typedef dict_remove_result
(*dict_remove_func)(void* obj, const void* key);
typedef size_t (*dict_clear_func)(void* obj, dict_delete_func delete_func);
typedef size_t (*dict_traverse_func)(void* obj, dict_visit_func visit, void* user_data);
typedef bool (*dict_select_func)(void *obj, size_t n, const void** key, void** datum);
typedef size_t (*dict_count_func)(const void* obj);
typedef bool (*dict_verify_func)(const void* obj);
typedef struct {
const bool sorted;
dict_inew_func inew;
dict_dfree_func dfree;
dict_insert_func insert;
dict_search_func search;
dict_search_func search_le;
dict_search_func search_lt;
dict_search_func search_ge;
dict_search_func search_gt;
dict_remove_func remove;
dict_clear_func clear;
dict_traverse_func traverse;
dict_select_func select;
dict_count_func count;
dict_verify_func verify;
} dict_vtable;
typedef void (*dict_ifree_func)(void* itor);
typedef bool (*dict_valid_func)(const void* itor);
typedef void (*dict_invalidate_func)(void* itor);
typedef bool (*dict_next_func)(void* itor);
typedef bool (*dict_prev_func)(void* itor);
typedef bool (*dict_nextn_func)(void* itor, size_t count);
typedef bool (*dict_prevn_func)(void* itor, size_t count);
typedef bool (*dict_first_func)(void* itor);
typedef bool (*dict_last_func)(void* itor);
typedef void* (*dict_key_func)(void* itor);
typedef void** (*dict_datum_func)(void* itor);
typedef bool (*dict_isearch_func)(void* itor, const void* key);
typedef bool (*dict_iremove_func)(void* itor);
typedef int (*dict_icompare_func)(void* itor1, void* itor2);
typedef struct {
dict_ifree_func ifree;
dict_valid_func valid;
dict_invalidate_func invalid;
dict_next_func next;
dict_prev_func prev;
dict_nextn_func nextn;
dict_prevn_func prevn;
dict_first_func first;
dict_last_func last;
dict_key_func key;
dict_datum_func datum;
dict_isearch_func search;
dict_isearch_func search_le;
dict_isearch_func search_lt;
dict_isearch_func search_ge;
dict_isearch_func search_gt;
dict_iremove_func remove;
dict_icompare_func compare;
} itor_vtable;
typedef struct {
void* _object;
const dict_vtable* _vtable;
} dict;
#define dict_private(dct) ((dct)->_object)
#define dict_insert(dct,key) ((dct)->_vtable->insert((dct)->_object, (key)))
#define dict_search(dct,key) ((dct)->_vtable->search((dct)->_object, (key)))
#define dict_is_sorted(dct) ((dct)->_vtable->sorted)
#define dict_search_le(dct,key) ((dct)->_vtable->search_le ? (dct)->_vtable->search_le((dct)->_object, (key)) : NULL)
#define dict_search_lt(dct,key) ((dct)->_vtable->search_lt ? (dct)->_vtable->search_lt((dct)->_object, (key)) : NULL)
#define dict_search_ge(dct,key) ((dct)->_vtable->search_ge ? (dct)->_vtable->search_ge((dct)->_object, (key)) : NULL)
#define dict_search_gt(dct,key) ((dct)->_vtable->search_gt ? (dct)->_vtable->search_gt((dct)->_object, (key)) : NULL)
#define dict_remove(dct,key) ((dct)->_vtable->remove((dct)->_object, (key)))
#define dict_clear(dct,func) ((dct)->_vtable->clear((dct)->_object, (func)))
#define dict_traverse(dct,func,ud) ((dct)->_vtable->traverse((dct)->_object, (func), (ud)))
#define dict_select(dct,n,key,d) ((dct)->_vtable->select && (dct)->_vtable->select((dct)->_object, (n), (key), (d)))
#define dict_count(dct) ((dct)->_vtable->count((dct)->_object))
#define dict_verify(dct) ((dct)->_vtable->verify((dct)->_object))
#define dict_itor_new(dct) (dct)->_vtable->inew((dct)->_object)
size_t dict_free(dict* dct, dict_delete_func delete_func);
struct dict_itor {
void* _itor;
const itor_vtable* _vtable;
};
#define dict_itor_private(i) ((i)->_itor)
#define dict_itor_valid(i) ((i)->_vtable->valid((i)->_itor))
#define dict_itor_invalidate(i) ((i)->_vtable->invalid((i)->_itor))
#define dict_itor_next(i) ((i)->_vtable->next((i)->_itor))
#define dict_itor_prev(i) ((i)->_vtable->prev((i)->_itor))
#define dict_itor_nextn(i,n) ((i)->_vtable->nextn((i)->_itor, (n)))
#define dict_itor_prevn(i,n) ((i)->_vtable->prevn((i)->_itor, (n)))
#define dict_itor_first(i) ((i)->_vtable->first((i)->_itor))
#define dict_itor_last(i) ((i)->_vtable->last((i)->_itor))
#define dict_itor_search(i,k) ((i)->_vtable->search((i)->_itor, (k)))
#define dict_itor_search_le(i,k) ((i)->_vtable->search_le && (i)->_vtable->search_le((i)->_itor, (k)))
#define dict_itor_search_lt(i,k) ((i)->_vtable->search_lt && (i)->_vtable->search_lt((i)->_itor, (k)))
#define dict_itor_search_ge(i,k) ((i)->_vtable->search_ge && (i)->_vtable->search_ge((i)->_itor, (k)))
#define dict_itor_search_gt(i,k) ((i)->_vtable->search_gt && (i)->_vtable->search_gt((i)->_itor, (k)))
#define dict_itor_key(i) ((i)->_vtable->key((i)->_itor))
#define dict_itor_datum(i) ((i)->_vtable->datum((i)->_itor))
#define dict_itor_compare(i1,i2) ((i1)->_vtable->compare((i1)->_itor, (i2)->_itor))
#define dict_itor_remove(i) ((i)->_vtable->remove && (i)->_vtable->remove((i)->_itor))
void dict_itor_free(dict_itor* itor);
int dict_int_cmp(const void* k1, const void* k2);
int dict_uint_cmp(const void* k1, const void* k2);
int dict_long_cmp(const void* k1, const void* k2);
int dict_ulong_cmp(const void* k1, const void* k2);
int dict_ptr_cmp(const void* k1, const void* k2);
int dict_str_cmp(const void* k1, const void* k2);
unsigned dict_str_hash(const void* str);
END_DECL
#include "hashtable.h"
#include "hashtable2.h"
#include "hb_tree.h"
#include "pr_tree.h"
#include "rb_tree.h"
#include "skiplist.h"
#include "sp_tree.h"
#include "tr_tree.h"
#include "wb_tree.h"
#endif /* !LIBDICT_DICT_H__ */
+75
View File
@@ -0,0 +1,75 @@
/*
* libdict -- hash-value sorted, chained hash-table interface.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_HASHTABLE_H__
#define LIBDICT_HASHTABLE_H__
#include "dict.h"
BEGIN_DECL
typedef struct hashtable hashtable;
hashtable* hashtable_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned size);
dict* hashtable_dict_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned size);
size_t hashtable_free(hashtable* table, dict_delete_func delete_func);
dict_insert_result
hashtable_insert(hashtable* table, void* key);
void** hashtable_search(hashtable* table, const void* key);
dict_remove_result
hashtable_remove(hashtable* table, const void* key);
size_t hashtable_clear(hashtable* table, dict_delete_func delete_func);
size_t hashtable_traverse(hashtable* table, dict_visit_func visit, void* user_data);
size_t hashtable_count(const hashtable* table);
size_t hashtable_size(const hashtable* table);
size_t hashtable_slots_used(const hashtable* table);
bool hashtable_verify(const hashtable* table);
bool hashtable_resize(hashtable* table, unsigned size);
typedef struct hashtable_itor hashtable_itor;
hashtable_itor* hashtable_itor_new(hashtable* table);
dict_itor* hashtable_dict_itor_new(hashtable* table);
void hashtable_itor_free(hashtable_itor* itor);
bool hashtable_itor_valid(const hashtable_itor* itor);
void hashtable_itor_invalidate(hashtable_itor* itor);
bool hashtable_itor_next(hashtable_itor* itor);
bool hashtable_itor_prev(hashtable_itor* itor);
bool hashtable_itor_nextn(hashtable_itor* itor, size_t count);
bool hashtable_itor_prevn(hashtable_itor* itor, size_t count);
bool hashtable_itor_first(hashtable_itor* itor);
bool hashtable_itor_last(hashtable_itor* itor);
bool hashtable_itor_search(hashtable_itor* itor, const void* key);
const void* hashtable_itor_key(const hashtable_itor* itor);
void** hashtable_itor_datum(hashtable_itor* itor);
bool hashtable_itor_remove(hashtable_itor* itor);
END_DECL
#endif /* !LIBDICT_HASHTABLE_H__ */
+75
View File
@@ -0,0 +1,75 @@
/*
* libdict -- open-addressing hash-table interface.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_HASHTABLE2_H__
#define LIBDICT_HASHTABLE2_H__
#include "dict.h"
BEGIN_DECL
typedef struct hashtable2 hashtable2;
hashtable2* hashtable2_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned initial_size);
dict* hashtable2_dict_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned initial_size);
size_t hashtable2_free(hashtable2* table, dict_delete_func delete_func);
dict_insert_result
hashtable2_insert(hashtable2* table, void* key);
void** hashtable2_search(hashtable2* table, const void* key);
dict_remove_result
hashtable2_remove(hashtable2* table, const void* key);
size_t hashtable2_clear(hashtable2* table, dict_delete_func delete_func);
size_t hashtable2_traverse(hashtable2* table, dict_visit_func visit, void* user_data);
size_t hashtable2_count(const hashtable2* table);
size_t hashtable2_size(const hashtable2* table);
size_t hashtable2_slots_used(const hashtable2* table);
bool hashtable2_verify(const hashtable2* table);
bool hashtable2_resize(hashtable2* table, unsigned size);
typedef struct hashtable2_itor hashtable2_itor;
hashtable2_itor* hashtable2_itor_new(hashtable2* table);
dict_itor* hashtable2_dict_itor_new(hashtable2* table);
void hashtable2_itor_free(hashtable2_itor* itor);
bool hashtable2_itor_valid(const hashtable2_itor* itor);
void hashtable2_itor_invalidate(hashtable2_itor* itor);
bool hashtable2_itor_next(hashtable2_itor* itor);
bool hashtable2_itor_prev(hashtable2_itor* itor);
bool hashtable2_itor_nextn(hashtable2_itor* itor, size_t count);
bool hashtable2_itor_prevn(hashtable2_itor* itor, size_t count);
bool hashtable2_itor_first(hashtable2_itor* itor);
bool hashtable2_itor_last(hashtable2_itor* itor);
bool hashtable2_itor_search(hashtable2_itor* itor, const void* key);
const void* hashtable2_itor_key(const hashtable2_itor* itor);
void** hashtable2_itor_datum(hashtable2_itor* itor);
bool hashtable2_itor_remove(hashtable2_itor* itor);
END_DECL
#endif /* !LIBDICT_HASHTABLE2_H__ */
+85
View File
@@ -0,0 +1,85 @@
/*
* libdict -- height-balanced (AVL) tree interface.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_HB_TREE_H__
#define LIBDICT_HB_TREE_H__
#include "dict.h"
BEGIN_DECL
typedef struct hb_tree hb_tree;
hb_tree* hb_tree_new(dict_compare_func cmp_func);
dict* hb_dict_new(dict_compare_func cmp_func);
size_t hb_tree_free(hb_tree* tree, dict_delete_func delete_func);
dict_insert_result
hb_tree_insert(hb_tree* tree, void* key);
void** hb_tree_search(hb_tree* tree, const void* key);
void** hb_tree_search_le(hb_tree* tree, const void* key);
void** hb_tree_search_lt(hb_tree* tree, const void* key);
void** hb_tree_search_ge(hb_tree* tree, const void* key);
void** hb_tree_search_gt(hb_tree* tree, const void* key);
dict_remove_result
hb_tree_remove(hb_tree* tree, const void* key);
size_t hb_tree_clear(hb_tree* tree, dict_delete_func delete_func);
size_t hb_tree_traverse(hb_tree* tree, dict_visit_func visit, void* user_data);
bool hb_tree_select(hb_tree* tree, size_t n, const void** key, void** datum);
size_t hb_tree_count(const hb_tree* tree);
size_t hb_tree_min_path_length(const hb_tree* tree);
size_t hb_tree_max_path_length(const hb_tree* tree);
size_t hb_tree_total_path_length(const hb_tree* tree);
bool hb_tree_verify(const hb_tree* tree);
typedef struct hb_itor hb_itor;
hb_itor* hb_itor_new(hb_tree* tree);
dict_itor* hb_dict_itor_new(hb_tree* tree);
void hb_itor_free(hb_itor* tree);
bool hb_itor_valid(const hb_itor* itor);
void hb_itor_invalidate(hb_itor* itor);
bool hb_itor_next(hb_itor* itor);
bool hb_itor_prev(hb_itor* itor);
bool hb_itor_nextn(hb_itor* itor, size_t count);
bool hb_itor_prevn(hb_itor* itor, size_t count);
bool hb_itor_first(hb_itor* itor);
bool hb_itor_last(hb_itor* itor);
bool hb_itor_search(hb_itor* itor, const void* key);
bool hb_itor_search_le(hb_itor* itor, const void* key);
bool hb_itor_search_lt(hb_itor* itor, const void* key);
bool hb_itor_search_ge(hb_itor* itor, const void* key);
bool hb_itor_search_gt(hb_itor* itor, const void* key);
const void* hb_itor_key(const hb_itor* itor);
void** hb_itor_datum(hb_itor* itor);
int hb_itor_compare(const hb_itor* i1, const hb_itor* i2);
bool hb_itor_remove(hb_itor* itor);
END_DECL
#endif /* !LIBDICT_HB_TREE_H__ */
+85
View File
@@ -0,0 +1,85 @@
/*
* libdict -- internal path reduction tree interface.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_PR_TREE_H__
#define LIBDICT_PR_TREE_H__
#include "dict.h"
BEGIN_DECL
typedef struct pr_tree pr_tree;
pr_tree* pr_tree_new(dict_compare_func cmp_func);
dict* pr_dict_new(dict_compare_func cmp_func);
size_t pr_tree_free(pr_tree* tree, dict_delete_func delete_func);
dict_insert_result
pr_tree_insert(pr_tree* tree, void* key);
void** pr_tree_search(pr_tree* tree, const void* key);
void** pr_tree_search_le(pr_tree* tree, const void* key);
void** pr_tree_search_lt(pr_tree* tree, const void* key);
void** pr_tree_search_ge(pr_tree* tree, const void* key);
void** pr_tree_search_gt(pr_tree* tree, const void* key);
dict_remove_result
pr_tree_remove(pr_tree* tree, const void* key);
size_t pr_tree_clear(pr_tree* tree, dict_delete_func delete_func);
size_t pr_tree_traverse(pr_tree* tree, dict_visit_func visit, void* user_data);
bool pr_tree_select(pr_tree* tree, size_t n, const void** key, void** datum);
size_t pr_tree_count(const pr_tree* tree);
size_t pr_tree_min_path_length(const pr_tree* tree);
size_t pr_tree_max_path_length(const pr_tree* tree);
size_t pr_tree_total_path_length(const pr_tree* tree);
bool pr_tree_verify(const pr_tree* tree);
typedef struct pr_itor pr_itor;
pr_itor* pr_itor_new(pr_tree* tree);
dict_itor* pr_dict_itor_new(pr_tree* tree);
void pr_itor_free(pr_itor* tree);
bool pr_itor_valid(const pr_itor* itor);
void pr_itor_invalidate(pr_itor* itor);
bool pr_itor_next(pr_itor* itor);
bool pr_itor_prev(pr_itor* itor);
bool pr_itor_nextn(pr_itor* itor, size_t count);
bool pr_itor_prevn(pr_itor* itor, size_t count);
bool pr_itor_first(pr_itor* itor);
bool pr_itor_last(pr_itor* itor);
bool pr_itor_search(pr_itor* itor, const void* key);
bool pr_itor_search_le(pr_itor* itor, const void* key);
bool pr_itor_search_lt(pr_itor* itor, const void* key);
bool pr_itor_search_ge(pr_itor* itor, const void* key);
bool pr_itor_search_gt(pr_itor* itor, const void* key);
const void* pr_itor_key(const pr_itor* itor);
void** pr_itor_datum(pr_itor* itor);
int pr_itor_compare(const pr_itor* i1, const pr_itor* i2);
bool pr_itor_remove(pr_itor* itor);
END_DECL
#endif /* !LIBDICT_PR_TREE_H__ */
+85
View File
@@ -0,0 +1,85 @@
/*
* libdict -- red-black tree interface.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_RB_TREE_H__
#define LIBDICT_RB_TREE_H__
#include "dict.h"
BEGIN_DECL
typedef struct rb_tree rb_tree;
rb_tree* rb_tree_new(dict_compare_func cmp_func);
dict* rb_dict_new(dict_compare_func cmp_func);
size_t rb_tree_free(rb_tree* tree, dict_delete_func delete_func);
dict_insert_result
rb_tree_insert(rb_tree* tree, void* key);
void** rb_tree_search(rb_tree* tree, const void* key);
void** rb_tree_search_le(rb_tree* tree, const void* key);
void** rb_tree_search_lt(rb_tree* tree, const void* key);
void** rb_tree_search_ge(rb_tree* tree, const void* key);
void** rb_tree_search_gt(rb_tree* tree, const void* key);
dict_remove_result
rb_tree_remove(rb_tree* tree, const void* key);
size_t rb_tree_clear(rb_tree* tree, dict_delete_func delete_func);
size_t rb_tree_traverse(rb_tree* tree, dict_visit_func visit, void* user_data);
bool rb_tree_select(rb_tree* tree, size_t n, const void** key, void** datum);
size_t rb_tree_count(const rb_tree* tree);
size_t rb_tree_min_path_length(const rb_tree* tree);
size_t rb_tree_max_path_length(const rb_tree* tree);
size_t rb_tree_total_path_length(const rb_tree* tree);
bool rb_tree_verify(const rb_tree* tree);
typedef struct rb_itor rb_itor;
rb_itor* rb_itor_new(rb_tree* tree);
dict_itor* rb_dict_itor_new(rb_tree* tree);
void rb_itor_free(rb_itor* tree);
bool rb_itor_valid(const rb_itor* itor);
void rb_itor_invalidate(rb_itor* itor);
bool rb_itor_next(rb_itor* itor);
bool rb_itor_prev(rb_itor* itor);
bool rb_itor_nextn(rb_itor* itor, size_t count);
bool rb_itor_prevn(rb_itor* itor, size_t count);
bool rb_itor_first(rb_itor* itor);
bool rb_itor_last(rb_itor* itor);
bool rb_itor_search(rb_itor* itor, const void* key);
bool rb_itor_search_le(rb_itor* itor, const void* key);
bool rb_itor_search_lt(rb_itor* itor, const void* key);
bool rb_itor_search_ge(rb_itor* itor, const void* key);
bool rb_itor_search_gt(rb_itor* itor, const void* key);
const void* rb_itor_key(const rb_itor* itor);
void** rb_itor_datum(rb_itor* itor);
int rb_itor_compare(const rb_itor* i1, const rb_itor* i2);
bool rb_itor_remove(rb_itor* itor);
END_DECL
#endif /* !LIBDICT_RB_TREE_H__ */
+89
View File
@@ -0,0 +1,89 @@
/*
* libdict -- skiplist interface.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_SKIPLIST_H__
#define LIBDICT_SKIPLIST_H__
#include "dict.h"
BEGIN_DECL
typedef struct skiplist skiplist;
skiplist* skiplist_new(dict_compare_func cmp_func, unsigned max_link);
dict* skiplist_dict_new(dict_compare_func cmp_func, unsigned max_link);
size_t skiplist_free(skiplist* list, dict_delete_func delete_func);
dict_insert_result
skiplist_insert(skiplist* list, void* key);
void** skiplist_search(skiplist* list, const void* key);
void** skiplist_search_le(skiplist* list, const void* key);
void** skiplist_search_lt(skiplist* list, const void* key);
void** skiplist_search_ge(skiplist* list, const void* key);
void** skiplist_search_gt(skiplist* list, const void* key);
dict_remove_result
skiplist_remove(skiplist* list, const void* key);
size_t skiplist_clear(skiplist* list, dict_delete_func delete_func);
size_t skiplist_traverse(skiplist* list, dict_visit_func visit, void* user_data);
size_t skiplist_count(const skiplist* list);
bool skiplist_verify(const skiplist* list);
/* Compute the histogram of link counts of the skiplist.
* For 0 < x < |ncounts|, |counts|[x] will be set to the number of nodes with x
* links, and the maximal link count will be returned. If the return value is
* greater than or equal to |ncounts|, not all link counts could be stored in
* |counts| (i.e. the array was not large enough). */
size_t skiplist_link_count_histogram(const skiplist* list,
size_t counts[], size_t ncounts);
typedef struct skiplist_itor skiplist_itor;
skiplist_itor* skiplist_itor_new(skiplist* list);
dict_itor* skiplist_dict_itor_new(skiplist* list);
void skiplist_itor_free(skiplist_itor* );
bool skiplist_itor_valid(const skiplist_itor* itor);
void skiplist_itor_invalidate(skiplist_itor* itor);
bool skiplist_itor_next(skiplist_itor* itor);
bool skiplist_itor_prev(skiplist_itor* itor);
bool skiplist_itor_nextn(skiplist_itor* itor, size_t count);
bool skiplist_itor_prevn(skiplist_itor* itor, size_t count);
bool skiplist_itor_first(skiplist_itor* itor);
bool skiplist_itor_last(skiplist_itor* itor);
bool skiplist_itor_search(skiplist_itor* itor, const void* key);
bool skiplist_itor_search_le(skiplist_itor* itor, const void* key);
bool skiplist_itor_search_lt(skiplist_itor* itor, const void* key);
bool skiplist_itor_search_ge(skiplist_itor* itor, const void* key);
bool skiplist_itor_search_gt(skiplist_itor* itor, const void* key);
const void* skiplist_itor_key(const skiplist_itor* itor);
void** skiplist_itor_datum(skiplist_itor* itor);
int skiplist_itor_compare(const skiplist_itor* it1, const skiplist_itor* it2);
bool skiplist_itor_remove(skiplist_itor* itor);
END_DECL
#endif /* !LIBDICT_SKIPLIST_H__ */
+85
View File
@@ -0,0 +1,85 @@
/*
* libdict -- splay tree interface.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_SP_TREE_H__
#define LIBDICT_SP_TREE_H__
#include "dict.h"
BEGIN_DECL
typedef struct sp_tree sp_tree;
sp_tree* sp_tree_new(dict_compare_func cmp_func);
dict* sp_dict_new(dict_compare_func cmp_func);
size_t sp_tree_free(sp_tree* tree, dict_delete_func delete_func);
dict_insert_result
sp_tree_insert(sp_tree* tree, void* key);
void** sp_tree_search(sp_tree* tree, const void* key);
void** sp_tree_search_le(sp_tree* tree, const void* key);
void** sp_tree_search_lt(sp_tree* tree, const void* key);
void** sp_tree_search_ge(sp_tree* tree, const void* key);
void** sp_tree_search_gt(sp_tree* tree, const void* key);
dict_remove_result
sp_tree_remove(sp_tree* tree, const void* key);
size_t sp_tree_clear(sp_tree* tree, dict_delete_func delete_func);
size_t sp_tree_traverse(sp_tree* tree, dict_visit_func visit, void* user_data);
bool sp_tree_select(sp_tree* tree, size_t n, const void** key, void** datum);
size_t sp_tree_count(const sp_tree* tree);
size_t sp_tree_min_path_length(const sp_tree* tree);
size_t sp_tree_max_path_length(const sp_tree* tree);
size_t sp_tree_total_path_length(const sp_tree* tree);
bool sp_tree_verify(const sp_tree* tree);
typedef struct sp_itor sp_itor;
sp_itor* sp_itor_new(sp_tree* tree);
dict_itor* sp_dict_itor_new(sp_tree* tree);
void sp_itor_free(sp_itor* tree);
bool sp_itor_valid(const sp_itor* itor);
void sp_itor_invalidate(sp_itor* itor);
bool sp_itor_next(sp_itor* itor);
bool sp_itor_prev(sp_itor* itor);
bool sp_itor_nextn(sp_itor* itor, size_t count);
bool sp_itor_prevn(sp_itor* itor, size_t count);
bool sp_itor_first(sp_itor* itor);
bool sp_itor_last(sp_itor* itor);
bool sp_itor_search(sp_itor* itor, const void* key);
bool sp_itor_search_le(sp_itor* itor, const void* key);
bool sp_itor_search_lt(sp_itor* itor, const void* key);
bool sp_itor_search_ge(sp_itor* itor, const void* key);
bool sp_itor_search_gt(sp_itor* itor, const void* key);
const void* sp_itor_key(const sp_itor* itor);
void** sp_itor_datum(sp_itor* itor);
int sp_itor_compare(const sp_itor* i1, const sp_itor* i2);
bool sp_itor_remove(sp_itor* itor);
END_DECL
#endif /* !LIBDICT_SP_TREE_H__ */
+85
View File
@@ -0,0 +1,85 @@
/*
* libdict -- treap interface.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_TR_TREE_H__
#define LIBDICT_TR_TREE_H__
#include "dict.h"
BEGIN_DECL
typedef struct tr_tree tr_tree;
tr_tree* tr_tree_new(dict_compare_func compare_func, dict_prio_func prio_func);
dict* tr_dict_new(dict_compare_func compare_func, dict_prio_func prio_func);
size_t tr_tree_free(tr_tree* tree, dict_delete_func delete_func);
dict_insert_result
tr_tree_insert(tr_tree* tree, void* key);
void** tr_tree_search(tr_tree* tree, const void* key);
void** tr_tree_search_le(tr_tree* tree, const void* key);
void** tr_tree_search_lt(tr_tree* tree, const void* key);
void** tr_tree_search_ge(tr_tree* tree, const void* key);
void** tr_tree_search_gt(tr_tree* tree, const void* key);
dict_remove_result
tr_tree_remove(tr_tree* tree, const void* key);
size_t tr_tree_clear(tr_tree* tree, dict_delete_func delete_func);
size_t tr_tree_traverse(tr_tree* tree, dict_visit_func visit, void* user_data);
bool tr_tree_select(tr_tree* tree, size_t n, const void** key, void** datum);
size_t tr_tree_count(const tr_tree* tree);
size_t tr_tree_min_path_length(const tr_tree* tree);
size_t tr_tree_max_path_length(const tr_tree* tree);
size_t tr_tree_total_path_length(const tr_tree* tree);
bool tr_tree_verify(const tr_tree* tree);
typedef struct tr_itor tr_itor;
tr_itor* tr_itor_new(tr_tree* tree);
dict_itor* tr_dict_itor_new(tr_tree* tree);
void tr_itor_free(tr_itor* tree);
bool tr_itor_valid(const tr_itor* itor);
void tr_itor_invalidate(tr_itor* itor);
bool tr_itor_next(tr_itor* itor);
bool tr_itor_prev(tr_itor* itor);
bool tr_itor_nextn(tr_itor* itor, size_t count);
bool tr_itor_prevn(tr_itor* itor, size_t count);
bool tr_itor_first(tr_itor* itor);
bool tr_itor_last(tr_itor* itor);
bool tr_itor_search(tr_itor* itor, const void* key);
bool tr_itor_search_le(tr_itor* itor, const void* key);
bool tr_itor_search_lt(tr_itor* itor, const void* key);
bool tr_itor_search_ge(tr_itor* itor, const void* key);
bool tr_itor_search_gt(tr_itor* itor, const void* key);
const void* tr_itor_key(const tr_itor* itor);
void** tr_itor_datum(tr_itor* itor);
int tr_itor_compare(const tr_itor* i1, const tr_itor* i2);
bool tr_itor_remove(tr_itor* itor);
END_DECL
#endif /* !LIBDICT_TR_TREE_H__ */
+85
View File
@@ -0,0 +1,85 @@
/*
* libdict -- weight balanced tree interface.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_WB_TREE_H__
#define LIBDICT_WB_TREE_H__
#include "dict.h"
BEGIN_DECL
typedef struct wb_tree wb_tree;
wb_tree* wb_tree_new(dict_compare_func cmp_func);
dict* wb_dict_new(dict_compare_func cmp_func);
size_t wb_tree_free(wb_tree* tree, dict_delete_func delete_func);
dict_insert_result
wb_tree_insert(wb_tree* tree, void* key);
void** wb_tree_search(wb_tree* tree, const void* key);
void** wb_tree_search_le(wb_tree* tree, const void* key);
void** wb_tree_search_lt(wb_tree* tree, const void* key);
void** wb_tree_search_ge(wb_tree* tree, const void* key);
void** wb_tree_search_gt(wb_tree* tree, const void* key);
dict_remove_result
wb_tree_remove(wb_tree* tree, const void* key);
size_t wb_tree_clear(wb_tree* tree, dict_delete_func delete_func);
size_t wb_tree_traverse(wb_tree* tree, dict_visit_func visit, void* user_data);
bool wb_tree_select(wb_tree* tree, size_t n, const void** key, void** datum);
size_t wb_tree_count(const wb_tree* tree);
size_t wb_tree_min_path_length(const wb_tree* tree);
size_t wb_tree_max_path_length(const wb_tree* tree);
size_t wb_tree_total_path_length(const wb_tree* tree);
bool wb_tree_verify(const wb_tree* tree);
typedef struct wb_itor wb_itor;
wb_itor* wb_itor_new(wb_tree* tree);
dict_itor* wb_dict_itor_new(wb_tree* tree);
void wb_itor_free(wb_itor* tree);
bool wb_itor_valid(const wb_itor* itor);
void wb_itor_invalidate(wb_itor* itor);
bool wb_itor_next(wb_itor* itor);
bool wb_itor_prev(wb_itor* itor);
bool wb_itor_nextn(wb_itor* itor, size_t count);
bool wb_itor_prevn(wb_itor* itor, size_t count);
bool wb_itor_first(wb_itor* itor);
bool wb_itor_last(wb_itor* itor);
bool wb_itor_search(wb_itor* itor, const void* key);
bool wb_itor_search_le(wb_itor* itor, const void* key);
bool wb_itor_search_lt(wb_itor* itor, const void* key);
bool wb_itor_search_ge(wb_itor* itor, const void* key);
bool wb_itor_search_gt(wb_itor* itor, const void* key);
const void* wb_itor_key(const wb_itor* itor);
void** wb_itor_datum(wb_itor* itor);
int wb_itor_compare(const wb_itor* i1, const wb_itor* i2);
bool wb_itor_remove(wb_itor* itor);
END_DECL
#endif /* !LIBDICT_WB_TREE_H__ */
+119
View File
@@ -0,0 +1,119 @@
/*
* libdict -- generic dictionary implementation.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
#include "dict_private.h"
#define XSTRINGIFY(x) STRINGIFY(x)
#define STRINGIFY(x) #x
const char* const kDictVersionString = XSTRINGIFY(DICT_VERSION_MAJOR) "."
XSTRINGIFY(DICT_VERSION_MINOR) "."
XSTRINGIFY(DICT_VERSION_PATCH);
void* (*dict_malloc_func)(size_t) = malloc;
void (*dict_free_func)(void*) = free;
int
dict_int_cmp(const void* k1, const void* k2)
{
const int a = *(const int*)k1;
const int b = *(const int*)k2;
return (a > b) - (a < b);
}
int
dict_uint_cmp(const void* k1, const void* k2)
{
const unsigned int a = *(const unsigned int*)k1;
const unsigned int b = *(const unsigned int*)k2;
return (a > b) - (a < b);
}
int
dict_long_cmp(const void* k1, const void* k2)
{
const long a = *(const long*)k1;
const long b = *(const long*)k2;
return (a > b) - (a < b);
}
int
dict_ulong_cmp(const void* k1, const void* k2)
{
const unsigned long a = *(const unsigned long*)k1;
const unsigned long b = *(const unsigned long*)k2;
return (a > b) - (a < b);
}
int
dict_ptr_cmp(const void* k1, const void* k2)
{
return (k1 > k2) - (k1 < k2);
}
int
dict_str_cmp(const void* k1, const void* k2)
{
const char* a = k1;
const char* b = k2;
for (;;) {
char p = *a++, q = *b++;
if (!p || p != q)
return (p > q) - (p < q);
}
}
unsigned
dict_str_hash(const void* k)
{
/* FNV 1-a string hash. */
unsigned hash = 2166136261U;
for (const uint8_t* ptr = k; *ptr;) {
hash = (hash ^ *ptr++) * 16777619U;
}
return hash;
}
size_t
dict_free(dict* dct, dict_delete_func delete_func)
{
ASSERT(dct != NULL);
size_t count = dct->_vtable->dfree(dct->_object, delete_func);
FREE(dct);
return count;
}
void
dict_itor_free(dict_itor* itor)
{
ASSERT(itor != NULL);
itor->_vtable->ifree(itor->_itor);
FREE(itor);
}
+107
View File
@@ -0,0 +1,107 @@
/*
* libdict - private definitions.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_DICT_PRIVATE_H__
#define LIBDICT_DICT_PRIVATE_H__
#include <stdio.h>
#include <stdlib.h>
#include "dict.h"
/* A feature (or bug) of this macro is that the expression is always evaluated,
* regardless of whether NDEBUG is defined or not. This is intentional and
* sometimes useful. */
#ifndef NDEBUG
# undef ASSERT
# if defined(__GNUC__) || defined(__clang__)
# define ASSERT(expr) \
do { \
if (!__builtin_expect((expr), 1)) { \
fprintf(stderr, "\n%s:%d (%s) assertion failed: %s\n", \
__FILE__, __LINE__, __func__, #expr); \
abort(); \
} \
} while (0)
# else
# define ASSERT(expr) \
do { \
if (!(expr)) { \
fprintf(stderr, "\n%s:%d assertion failed: %s\n", \
__FILE__, __LINE__, #expr); \
abort(); \
} \
} while (0)
# endif
#else
# define ASSERT(expr) (void)(expr)
#endif
#if defined(__GNUC__) || defined(__clang__)
# define LIKELY(expr) __builtin_expect((expr), 1)
# define UNLIKELY(expr) __builtin_expect((expr), 0)
# define VERIFY(expr) \
do { \
if (!__builtin_expect((expr), 1)) { \
fprintf(stderr, "\n%s:%d (%s) verification failed: %s\n", \
__FILE__, __LINE__, __func__, #expr); \
return false; \
} \
} while (0)
#else
# define LIKELY(expr) (expr)
# define UNLIKELY(expr) (expr)
# define VERIFY(expr) \
do { \
if (!(expr)) { \
fprintf(stderr, "\n%s:%d verification failed: %s\n", \
__FILE__, __LINE__, #expr); \
return false; \
} \
} while (0)
#endif
#define MALLOC(n) (*dict_malloc_func)(n)
#define FREE(p) (*dict_free_func)(p)
#define ABS(a) ((a) < 0 ? -(a) : (a))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define SWAP(a,b,v) do { v = (a); (a) = (b); (b) = v; } while (0)
#if defined(__GNUC__) || defined(__clang__)
# define GCC_INLINE __inline__
# define GCC_CONST __attribute__((__const__))
#else
# define GCC_INLINE
# define GCC_CONST
#endif
extern long random(void);
static inline unsigned dict_rand() { return (unsigned) random(); }
#endif /* !LIBDICT_DICT_PRIVATE_H__ */
+545
View File
@@ -0,0 +1,545 @@
/*
* libdict -- chained hash-table, with chains sorted by hash, implementation.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
/*
* cf. [Gonnet 1984], [Knuth 1998]
*/
#include "hashtable.h"
#include <string.h> /* For memset() */
#include "dict_private.h"
#include "hashtable_common.h"
/* TODO: make this configurable in the constructor methods */
#define LOADFACTOR_NUMERATOR 2
#define LOADFACTOR_DENOMINATOR 3
#if LOADFACTOR_NUMERATOR > LOADFACTOR_DENOMINATOR
# error LOADFACTOR_NUMERATOR must be less than LOADFACTOR_DENOMINATOR
#endif
typedef struct hash_node hash_node;
struct hash_node {
void* key;
void* datum;
hash_node* next;
/* Only because iterators are bidirectional: */
hash_node* prev;
unsigned hash; /* Untruncated hash value. */
};
struct hashtable {
hash_node** table;
dict_compare_func cmp_func;
dict_hash_func hash_func;
size_t count;
unsigned size;
};
struct hashtable_itor {
hashtable* table;
hash_node* node;
unsigned slot;
};
static const dict_vtable hashtable_vtable = {
false,
(dict_inew_func) hashtable_dict_itor_new,
(dict_dfree_func) hashtable_free,
(dict_insert_func) hashtable_insert,
(dict_search_func) hashtable_search,
(dict_search_func) NULL,/* search_le: not supported */
(dict_search_func) NULL,/* search_lt: not supported */
(dict_search_func) NULL,/* search_ge: not supported */
(dict_search_func) NULL,/* search_gt: not supported */
(dict_remove_func) hashtable_remove,
(dict_clear_func) hashtable_clear,
(dict_traverse_func) hashtable_traverse,
(dict_select_func) NULL,
(dict_count_func) hashtable_count,
(dict_verify_func) hashtable_verify,
};
static const itor_vtable hashtable_itor_vtable = {
(dict_ifree_func) hashtable_itor_free,
(dict_valid_func) hashtable_itor_valid,
(dict_invalidate_func) hashtable_itor_invalidate,
(dict_next_func) hashtable_itor_next,
(dict_prev_func) hashtable_itor_prev,
(dict_nextn_func) hashtable_itor_nextn,
(dict_prevn_func) hashtable_itor_prevn,
(dict_first_func) hashtable_itor_first,
(dict_last_func) hashtable_itor_last,
(dict_key_func) hashtable_itor_key,
(dict_datum_func) hashtable_itor_datum,
(dict_isearch_func) hashtable_itor_search,
(dict_isearch_func) NULL,/* itor_search_le: not supported */
(dict_isearch_func) NULL,/* itor_search_lt: not supported */
(dict_isearch_func) NULL,/* itor_search_ge: not supported */
(dict_isearch_func) NULL,/* itor_search_gt: not supported */
(dict_iremove_func) hashtable_itor_remove,/* hashtable_itor_remove not implemented yet */
(dict_icompare_func) NULL,/* hashtable_itor_compare not implemented yet */
};
hashtable*
hashtable_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned size)
{
ASSERT(cmp_func != NULL);
ASSERT(hash_func != NULL);
ASSERT(size > 0);
hashtable* table = MALLOC(sizeof(*table));
if (table) {
table->size = dict_prime_geq(size);
table->table = MALLOC(table->size * sizeof(hash_node*));
if (!table->table) {
FREE(table);
return NULL;
}
memset(table->table, 0, table->size * sizeof(hash_node*));
table->cmp_func = cmp_func;
table->hash_func = hash_func;
table->count = 0;
}
return table;
}
dict*
hashtable_dict_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned size)
{
ASSERT(hash_func != NULL);
ASSERT(size > 0);
dict* dct = MALLOC(sizeof(*dct));
if (dct) {
dct->_object = hashtable_new(cmp_func, hash_func, size);
if (!dct->_object) {
FREE(dct);
return NULL;
}
dct->_vtable = &hashtable_vtable;
}
return dct;
}
size_t
hashtable_free(hashtable* table, dict_delete_func delete_func)
{
ASSERT(table != NULL);
size_t count = hashtable_clear(table, delete_func);
FREE(table->table);
FREE(table);
return count;
}
dict_insert_result
hashtable_insert(hashtable* table, void* key)
{
if (LOADFACTOR_DENOMINATOR * table->count >= LOADFACTOR_NUMERATOR * table->size) {
/* Load factor too high. */
hashtable_resize(table, table->size + 1);
}
const unsigned hash = table->hash_func(key);
const unsigned mhash = hash % table->size;
hash_node* node = table->table[mhash];
hash_node* prev = NULL;
while (node && hash >= node->hash) {
if (hash == node->hash && table->cmp_func(key, node->key) == 0)
return (dict_insert_result) { &node->datum, false };
prev = node;
node = node->next;
}
hash_node* add = MALLOC(sizeof(*add));
if (!add)
return (dict_insert_result) { NULL, false };
add->key = key;
add->datum = NULL;
add->hash = hash;
add->prev = prev;
if (prev)
prev->next = add;
else
table->table[mhash] = add;
add->next = node;
if (node)
node->prev = add;
table->count++;
return (dict_insert_result) { &add->datum, true };
}
void**
hashtable_search(hashtable* table, const void* key)
{
const unsigned hash = table->hash_func(key);
hash_node* node = table->table[hash % table->size];
while (node && hash >= node->hash) {
if (hash == node->hash && table->cmp_func(key, node->key) == 0)
return &node->datum;
node = node->next;
}
return NULL;
}
static void
remove_node(hashtable* table, hash_node* node, unsigned mhash)
{
if (node->prev)
node->prev->next = node->next;
else
table->table[mhash] = node->next;
if (node->next)
node->next->prev = node->prev;
FREE(node);
table->count--;
}
dict_remove_result
hashtable_remove(hashtable* table, const void* key)
{
const unsigned hash = table->hash_func(key);
const unsigned mhash = hash % table->size;
hash_node* node = table->table[mhash];
while (node && hash >= node->hash) {
if (hash == node->hash && table->cmp_func(key, node->key) == 0) {
dict_remove_result result = { node->key, node->datum, true };
remove_node(table, node, mhash);
return result;
}
node = node->next;
}
return (dict_remove_result) { NULL, NULL, false };
}
size_t
hashtable_clear(hashtable* table, dict_delete_func delete_func)
{
for (unsigned slot = 0; slot < table->size; slot++) {
hash_node* node = table->table[slot];
while (node != NULL) {
hash_node* next = node->next;
if (delete_func)
delete_func(node->key, node->datum);
FREE(node);
node = next;
}
table->table[slot] = NULL;
}
const size_t count = table->count;
table->count = 0;
return count;
}
size_t
hashtable_traverse(hashtable* table, dict_visit_func visit, void* user_data)
{
size_t count = 0;
for (unsigned i = 0; i < table->size; i++)
for (hash_node* node = table->table[i]; node; node = node->next) {
++count;
if (!visit(node->key, node->datum, user_data))
return count;
}
return count;
}
size_t
hashtable_count(const hashtable* table)
{
return table->count;
}
size_t
hashtable_size(const hashtable* table)
{
return table->size;
}
size_t
hashtable_slots_used(const hashtable* table)
{
size_t count = 0;
for (unsigned i = 0; i < table->size; i++)
if (table->table[i])
count++;
return count;
}
bool
hashtable_resize(hashtable* table, unsigned new_size)
{
ASSERT(new_size > 0);
new_size = dict_prime_geq(new_size);
if (table->size == new_size)
return true;
/* TODO: investigate whether using realloc would be advantageous. */
hash_node** ntable = MALLOC(new_size * sizeof(hash_node*));
if (!ntable)
return false;
memset(ntable, 0, new_size * sizeof(hash_node*));
for (unsigned i = 0; i < table->size; i++) {
for (hash_node* node = table->table[i]; node;) {
hash_node* const next = node->next;
const unsigned mhash = node->hash % new_size;
hash_node* search = ntable[mhash];
hash_node* prev = NULL;
while (search && node->hash >= search->hash) {
prev = search;
search = search->next;
}
if ((node->next = search) != NULL)
search->prev = node;
if ((node->prev = prev) != NULL)
prev->next = node;
else
ntable[mhash] = node;
node = next;
}
}
FREE(table->table);
table->table = ntable;
table->size = new_size;
return true;
}
bool
hashtable_verify(const hashtable* table)
{
for (unsigned slot = 0; slot < table->size; ++slot) {
for (hash_node* n = table->table[slot]; n; n = n->next) {
if (n == table->table[slot]) {
VERIFY(n->prev == NULL);
} else {
VERIFY(n->prev != NULL);
VERIFY(n->prev->next == n);
}
if (n->next) {
VERIFY(n->next->prev == n);
VERIFY(n->hash <= n->next->hash);
}
VERIFY(n->hash % table->size == slot);
}
}
return true;
}
hashtable_itor*
hashtable_itor_new(hashtable* table)
{
hashtable_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
itor->table = table;
itor->node = NULL;
itor->slot = 0;
}
return itor;
}
dict_itor*
hashtable_dict_itor_new(hashtable* table)
{
dict_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
if (!(itor->_itor = hashtable_itor_new(table))) {
FREE(itor);
return NULL;
}
itor->_vtable = &hashtable_itor_vtable;
}
return itor;
}
void
hashtable_itor_free(hashtable_itor* itor)
{
FREE(itor);
}
bool
hashtable_itor_valid(const hashtable_itor* itor)
{
return itor->node != NULL;
}
void
hashtable_itor_invalidate(hashtable_itor* itor)
{
itor->node = NULL;
itor->slot = 0;
}
bool
hashtable_itor_next(hashtable_itor* itor)
{
if (!itor->node)
return false;
if ((itor->node = itor->node->next) != NULL)
return true;
unsigned slot = itor->slot;
while (++slot < itor->table->size) {
if (itor->table->table[slot]) {
itor->node = itor->table->table[slot];
itor->slot = slot;
return true;
}
}
itor->node = NULL;
itor->slot = 0;
return itor->node != NULL;
}
bool
hashtable_itor_prev(hashtable_itor* itor)
{
if (!itor->node)
return false;
if ((itor->node = itor->node->prev) != NULL)
return true;
unsigned slot = itor->slot;
while (slot > 0) {
hash_node* node = itor->table->table[--slot];
if (node) {
while (node->next)
node = node->next;
itor->node = node;
itor->slot = slot;
return true;
}
}
itor->node = NULL;
itor->slot = 0;
return false;
}
bool
hashtable_itor_nextn(hashtable_itor* itor, size_t count)
{
while (count--)
if (!hashtable_itor_next(itor))
return false;
return itor->node != NULL;
}
bool
hashtable_itor_prevn(hashtable_itor* itor, size_t count)
{
while (count--)
if (!hashtable_itor_prev(itor))
return false;
return itor->node != NULL;
}
bool
hashtable_itor_first(hashtable_itor* itor)
{
for (unsigned slot = 0; slot < itor->table->size; ++slot)
if (itor->table->table[slot]) {
itor->node = itor->table->table[slot];
itor->slot = slot;
return true;
}
itor->node = NULL;
itor->slot = 0;
return false;
}
bool
hashtable_itor_last(hashtable_itor* itor)
{
for (unsigned slot = itor->table->size; slot > 0;)
if (itor->table->table[--slot]) {
hash_node* node = itor->table->table[slot];
while (node->next)
node = node->next;
itor->node = node;
itor->slot = slot;
return true;
}
itor->node = NULL;
itor->slot = 0;
return false;
}
bool
hashtable_itor_search(hashtable_itor* itor, const void* key)
{
const unsigned hash = itor->table->hash_func(key);
const unsigned mhash = hash % itor->table->size;
hash_node* node = itor->table->table[mhash];
while (node && hash >= node->hash) {
if (hash == node->hash && itor->table->cmp_func(key, node->key) == 0) {
itor->node = node;
itor->slot = mhash;
return true;
}
node = node->next;
}
itor->node = NULL;
itor->slot = 0;
return false;
}
const void*
hashtable_itor_key(const hashtable_itor* itor)
{
return itor->node ? itor->node->key : NULL;
}
void**
hashtable_itor_datum(hashtable_itor* itor)
{
return itor->node ? &itor->node->datum : NULL;
}
bool
hashtable_itor_remove(hashtable_itor* itor)
{
if (!itor->node)
return false;
remove_node(itor->table, itor->node, itor->node->hash % itor->table->size);
itor->node = NULL;
return true;
}
+579
View File
@@ -0,0 +1,579 @@
/*
* libdict -- chained hash-table, with chains sorted by hash, implementation.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
/*
* cf. [Gonnet 1984], [Knuth 1998]
*/
#include "hashtable2.h"
#include <string.h> /* For memset() */
#include "dict_private.h"
#include "hashtable_common.h"
/* TODO: make this configurable in the constructor methods */
#define LOADFACTOR_NUMERATOR 2
#define LOADFACTOR_DENOMINATOR 3
#if LOADFACTOR_NUMERATOR > LOADFACTOR_DENOMINATOR
# error LOADFACTOR_NUMERATOR must be less than LOADFACTOR_DENOMINATOR
#endif
typedef struct hash_node hash_node;
struct hash_node {
void* key;
void* datum;
unsigned hash; /* Untruncated hash value; 0 iff unoccupied. */
};
struct hashtable2 {
size_t count;
dict_compare_func cmp_func;
dict_hash_func hash_func;
hash_node* table;
unsigned size;
};
struct hashtable2_itor {
hashtable2* table;
int slot;
};
static const dict_vtable hashtable2_vtable = {
false,
(dict_inew_func) hashtable2_dict_itor_new,
(dict_dfree_func) hashtable2_free,
(dict_insert_func) hashtable2_insert,
(dict_search_func) hashtable2_search,
(dict_search_func) NULL,/* search_le: not supported */
(dict_search_func) NULL,/* search_lt: not supported */
(dict_search_func) NULL,/* search_ge: not supported */
(dict_search_func) NULL,/* search_gt: not supported */
(dict_remove_func) hashtable2_remove,
(dict_clear_func) hashtable2_clear,
(dict_traverse_func) hashtable2_traverse,
(dict_select_func) NULL,
(dict_count_func) hashtable2_count,
(dict_verify_func) hashtable2_verify,
};
static const itor_vtable hashtable2_itor_vtable = {
(dict_ifree_func) hashtable2_itor_free,
(dict_valid_func) hashtable2_itor_valid,
(dict_invalidate_func) hashtable2_itor_invalidate,
(dict_next_func) hashtable2_itor_next,
(dict_prev_func) hashtable2_itor_prev,
(dict_nextn_func) hashtable2_itor_nextn,
(dict_prevn_func) hashtable2_itor_prevn,
(dict_first_func) hashtable2_itor_first,
(dict_last_func) hashtable2_itor_last,
(dict_key_func) hashtable2_itor_key,
(dict_datum_func) hashtable2_itor_datum,
(dict_isearch_func) hashtable2_itor_search,
(dict_isearch_func) NULL,/* itor_search_le: not supported */
(dict_isearch_func) NULL,/* itor_search_lt: not supported */
(dict_isearch_func) NULL,/* itor_search_ge: not supported */
(dict_isearch_func) NULL,/* itor_search_gt: not supported */
(dict_iremove_func) hashtable2_itor_remove,
(dict_icompare_func) NULL,/* hashtable2_itor_compare not implemented yet */
};
hashtable2*
hashtable2_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned initial_size)
{
ASSERT(cmp_func != NULL);
ASSERT(hash_func != NULL);
ASSERT(initial_size > 0);
hashtable2* table = MALLOC(sizeof(*table));
if (table) {
table->size = dict_prime_geq(initial_size);
table->table = MALLOC(table->size * sizeof(hash_node));
if (!table->table) {
FREE(table);
return NULL;
}
memset(table->table, 0, table->size * sizeof(hash_node));
table->cmp_func = cmp_func;
table->hash_func = hash_func;
table->count = 0;
}
return table;
}
dict*
hashtable2_dict_new(dict_compare_func cmp_func, dict_hash_func hash_func, unsigned initial_size)
{
ASSERT(hash_func != NULL);
ASSERT(initial_size > 0);
dict* dct = MALLOC(sizeof(*dct));
if (dct) {
dct->_object = hashtable2_new(cmp_func, hash_func, initial_size);
if (!dct->_object) {
FREE(dct);
return NULL;
}
dct->_vtable = &hashtable2_vtable;
}
return dct;
}
size_t
hashtable2_free(hashtable2* table, dict_delete_func delete_func)
{
size_t count = hashtable2_clear(table, delete_func);
FREE(table->table);
FREE(table);
return count;
}
static inline dict_insert_result
insert(hashtable2* table, void *key, unsigned hash)
{
hash_node* const first = table->table + (hash % table->size);
hash_node* const table_end = table->table + table->size;
hash_node* node = first;
do {
if (!node->hash) {
node->hash = hash;
node->key = key;
ASSERT(node->datum == NULL);
return (dict_insert_result) { &node->datum, true };
}
if (node->hash == hash && table->cmp_func(key, node->key) == 0)
return (dict_insert_result) { &node->datum, false };
if (++node == table_end)
node = table->table;
} while (node != first);
/* No room for new element! */
return (dict_insert_result) { NULL, false };
}
static inline unsigned
nonzero_hash(dict_hash_func hash_func, const void *key)
{
const unsigned hash = hash_func(key);
return hash ? hash : ~(unsigned)0;
}
dict_insert_result
hashtable2_insert(hashtable2* table, void* key)
{
if (LOADFACTOR_DENOMINATOR * table->count >= LOADFACTOR_NUMERATOR * table->size) {
/* Load factor too high: increase the table size. */
if (!hashtable2_resize(table, table->size + 1)) {
/* No memory for a bigger table, but let the insert proceed anyway. */
}
}
const unsigned hash = nonzero_hash(table->hash_func, key);
dict_insert_result result = insert(table, key, hash);
if (result.inserted)
table->count++;
return result;
}
void**
hashtable2_search(hashtable2* table, const void* key)
{
const unsigned hash = nonzero_hash(table->hash_func, key);
hash_node* const first = table->table + (hash % table->size);
hash_node* const table_end = table->table + table->size;
hash_node* node = first;
do {
if (!node->hash) /* Not occupied. */
break;
if (node->hash == hash && table->cmp_func(key, node->key) == 0)
return &node->datum;
if (++node == table_end)
node = table->table;
} while (node != first);
return NULL;
}
#if 0
static int
index_of_node_to_shift(hashtable2* table, unsigned truncated_hash, unsigned index)
{
int last_index = -1;
do {
hash_node* node = &table->table[index];
if (!node->hash) {
break;
}
if (node->hash % table->size == truncated_hash) {
last_index = index;
}
if (++index == table->size) {
index = 0;
}
} while (index != truncated_hash);
return last_index;
}
#endif
static void
remove_cleanup(hashtable2* table, hash_node* const first, hash_node* node)
{
hash_node* const table_end = table->table + table->size;
do {
const unsigned hash = node->hash;
if (!hash) /* Not occupied. */
break;
void* const datum = node->datum;
node->datum = NULL;
node->hash = 0;
dict_insert_result result = insert(table, node->key, hash);
ASSERT(result.inserted);
ASSERT(result.datum_ptr != NULL);
ASSERT(*result.datum_ptr == NULL);
*result.datum_ptr = datum;
if (++node == table_end)
node = table->table;
} while (node != first);
}
static void
remove_node(hashtable2* table, hash_node* first, hash_node* node)
{
ASSERT(node->hash != 0);
node->key = node->datum = NULL;
node->hash = 0;
table->count--;
if (++node == table->table + table->size)
node = table->table;
remove_cleanup(table, first, node);
}
dict_remove_result
hashtable2_remove(hashtable2* table, const void* key)
{
const unsigned hash = nonzero_hash(table->hash_func, key);
hash_node* const first = table->table + (hash % table->size);
hash_node* const table_end = table->table + table->size;
hash_node* node = first;
do {
if (!node->hash) /* Not occupied. */
break;
if (node->hash == hash && table->cmp_func(key, node->key) == 0) {
dict_remove_result result = { node->key, node->datum, true };
remove_node(table, first, node);
return result;
}
if (++node == table_end)
node = table->table;
} while (node != first);
return (dict_remove_result) { NULL, NULL, false };
}
size_t
hashtable2_clear(hashtable2* table, dict_delete_func delete_func)
{
hash_node *node = table->table;
hash_node *const end = table->table + table->size;
for (; node != end; ++node) {
if (node->hash) {
if (delete_func)
delete_func(node->key, node->datum);
node->key = node->datum = NULL;
node->hash = 0;
}
}
const size_t count = table->count;
table->count = 0;
return count;
}
size_t
hashtable2_traverse(hashtable2* table, dict_visit_func visit, void* user_data)
{
size_t count = 0;
hash_node *node = table->table;
for (hash_node *const end = table->table + table->size; node != end; ++node) {
if (node->hash) {
++count;
if (!visit(node->key, node->datum, user_data))
break;
}
}
return count;
}
size_t
hashtable2_count(const hashtable2* table)
{
return table->count;
}
size_t
hashtable2_size(const hashtable2* table)
{
return table->size;
}
size_t
hashtable2_slots_used(const hashtable2* table)
{
return table->count;
}
bool
hashtable2_resize(hashtable2* table, unsigned new_size)
{
ASSERT(new_size > 0);
new_size = dict_prime_geq(new_size);
if (table->size == new_size)
return true;
if (table->count > new_size) {
/* The number of records already in hashtable will not fit (must be a reduction in size). */
return false;
}
const unsigned old_size = table->size;
const size_t old_count = table->count;
hash_node* const old_table = table->table;
table->table = MALLOC(new_size * sizeof(hash_node));
if (!table->table) {
table->table = old_table;
return false;
}
memset(table->table, 0, new_size * sizeof(hash_node));
table->size = new_size;
for (unsigned i = 0; i < old_size; ++i) {
if (old_table[i].hash) {
dict_insert_result result =
insert(table, old_table[i].key, old_table[i].hash);
if (!result.inserted || !result.datum_ptr) {
FREE(table->table);
table->table = old_table;
table->size = old_size;
table->count = old_count;
return false;
}
*result.datum_ptr = old_table[i].datum;
}
}
FREE(old_table);
return true;
}
bool
hashtable2_verify(const hashtable2* table)
{
size_t count = 0;
const hash_node *node = table->table;
const hash_node *end = table->table + table->size;
for (; node != end; ++node) {
if (node->hash) {
++count;
} else {
VERIFY(node->datum == NULL);
}
}
VERIFY(table->count == count);
return true;
}
hashtable2_itor*
hashtable2_itor_new(hashtable2* table)
{
hashtable2_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
itor->table = table;
itor->slot = -1;
}
return itor;
}
dict_itor*
hashtable2_dict_itor_new(hashtable2* table)
{
dict_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
if (!(itor->_itor = hashtable2_itor_new(table))) {
FREE(itor);
return NULL;
}
itor->_vtable = &hashtable2_itor_vtable;
}
return itor;
}
void
hashtable2_itor_free(hashtable2_itor* itor)
{
FREE(itor);
}
bool
hashtable2_itor_valid(const hashtable2_itor* itor)
{
if (itor->slot < 0)
return false;
ASSERT(itor->table->table[itor->slot].hash != 0);
return true;
}
void
hashtable2_itor_invalidate(hashtable2_itor* itor)
{
itor->slot = -1;
}
bool
hashtable2_itor_next(hashtable2_itor* itor)
{
if (itor->slot < 0)
return false;
while (++itor->slot < (int) itor->table->size) {
if (itor->table->table[itor->slot].hash)
return true;
}
itor->slot = -1;
return false;
}
bool
hashtable2_itor_prev(hashtable2_itor* itor)
{
if (itor->slot < 0)
return false;
while (itor->slot-- > 0) {
if (itor->table->table[itor->slot].hash)
return true;
}
ASSERT(itor->slot == -1);
return false;
}
bool
hashtable2_itor_nextn(hashtable2_itor* itor, size_t count)
{
while (count--)
if (!hashtable2_itor_next(itor))
return false;
return itor->slot >= 0;
}
bool
hashtable2_itor_prevn(hashtable2_itor* itor, size_t count)
{
while (count--)
if (!hashtable2_itor_prev(itor))
return false;
return itor->slot >= 0;
}
bool
hashtable2_itor_first(hashtable2_itor* itor)
{
for (unsigned slot = 0; slot < itor->table->size; ++slot) {
if (itor->table->table[slot].hash) {
itor->slot = (int) slot;
return true;
}
}
itor->slot = -1;
return false;
}
bool
hashtable2_itor_last(hashtable2_itor* itor)
{
for (unsigned slot = itor->table->size; slot > 0;) {
if (itor->table->table[--slot].hash) {
itor->slot = (int) slot;
return true;
}
}
itor->slot = -1;
return false;
}
bool
hashtable2_itor_search(hashtable2_itor* itor, const void* key)
{
const unsigned hash = nonzero_hash(itor->table->hash_func, key);
const unsigned truncated_hash = hash % itor->table->size;
unsigned index = truncated_hash;
do {
hash_node *node = &itor->table->table[index];
if (!node->hash)
break;
if (node->hash == hash && itor->table->cmp_func(key, node->key) == 0) {
itor->slot = (int) index;
return true;
}
if (++index == itor->table->size)
index = 0;
} while (index != truncated_hash);
itor->slot = -1;
return NULL;
}
const void*
hashtable2_itor_key(const hashtable2_itor* itor)
{
return (itor->slot >= 0) ? itor->table->table[itor->slot].key : NULL;
}
void**
hashtable2_itor_datum(hashtable2_itor* itor)
{
return (itor->slot >= 0) ? &itor->table->table[itor->slot].datum : NULL;
}
bool
hashtable2_itor_remove(hashtable2_itor* itor)
{
if (itor->slot < 0)
return false;
remove_node(itor->table,
itor->table->table + itor->table->table[itor->slot].hash % itor->table->size,
itor->table->table + itor->slot);
itor->slot = -1;
return true;
}
+48
View File
@@ -0,0 +1,48 @@
/*
* libdict - common definitions for hash tables.
*
* Copyright (c) 2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
#include "hashtable_common.h"
static const unsigned kPrimes[] = {
11, 17, 37, 67, 131,
257, 521, 1031, 2053, 4099,
8209, 16411, 32771, 65537, 131101,
262147, 524309, 1048583, 2097169, 4194319,
8388617, 16777259, 33554467, 67108879, 134217757,
268435459, 536870923, 1073741827, 2147483659, 4294967291
};
static const unsigned kNumPrimes = sizeof(kPrimes) / sizeof(kPrimes[0]);
unsigned
dict_prime_geq(unsigned n)
{
/* TODO(farooq): use binary search */
for (unsigned index = 0; index < kNumPrimes; ++index)
if (kPrimes[index] >= n)
return kPrimes[index];
return kPrimes[kNumPrimes - 1];
}
+33
View File
@@ -0,0 +1,33 @@
/*
* libdict - common definitions for hash tables.
*
* Copyright (c) 2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_HASHTABLE_COMMON_H__
#define LIBDICT_HASHTABLE_COMMON_H__
unsigned dict_prime_geq(unsigned n);
#endif /* !LIBDICT_HASHTABLE_COMMON_H__ */
+665
View File
@@ -0,0 +1,665 @@
/*
* libdict -- height-balanced (AVL) tree implementation.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
#include "hb_tree.h"
#include "dict_private.h"
#include "tree_common.h"
typedef struct hb_node hb_node;
struct hb_node {
void* key;
void* datum;
union {
intptr_t bal;
hb_node* pptr;
};
hb_node* llink;
hb_node* rlink;
};
#define BAL_MASK ((intptr_t)3)
#define PARENT(node) ((hb_node*) ((node)->bal & ~BAL_MASK))
#define BAL_POS(node) ((node)->bal & 1)
#define BAL_NEG(node) ((node)->bal & 2)
struct hb_tree {
TREE_FIELDS(hb_node);
};
struct hb_itor {
TREE_ITERATOR_FIELDS(hb_tree, hb_node);
};
static const dict_vtable hb_tree_vtable = {
true,
(dict_inew_func) hb_dict_itor_new,
(dict_dfree_func) hb_tree_free,
(dict_insert_func) hb_tree_insert,
(dict_search_func) tree_search,
(dict_search_func) tree_search_le,
(dict_search_func) tree_search_lt,
(dict_search_func) tree_search_ge,
(dict_search_func) tree_search_gt,
(dict_remove_func) hb_tree_remove,
(dict_clear_func) hb_tree_clear,
(dict_traverse_func) hb_tree_traverse,
(dict_select_func) hb_tree_select,
(dict_count_func) tree_count,
(dict_verify_func) hb_tree_verify,
};
static const itor_vtable hb_tree_itor_vtable = {
(dict_ifree_func) tree_iterator_free,
(dict_valid_func) tree_iterator_valid,
(dict_invalidate_func) tree_iterator_invalidate,
(dict_next_func) hb_itor_next,
(dict_prev_func) hb_itor_prev,
(dict_nextn_func) hb_itor_nextn,
(dict_prevn_func) hb_itor_prevn,
(dict_first_func) tree_iterator_first,
(dict_last_func) tree_iterator_last,
(dict_key_func) tree_iterator_key,
(dict_datum_func) tree_iterator_datum,
(dict_isearch_func) tree_iterator_search,
(dict_isearch_func) tree_iterator_search_le,
(dict_isearch_func) tree_iterator_search_lt,
(dict_isearch_func) tree_iterator_search_ge,
(dict_isearch_func) tree_iterator_search_gt,
(dict_iremove_func) hb_itor_remove,
(dict_icompare_func) tree_iterator_compare,
};
static hb_node* node_prev(hb_node* node);
static hb_node* node_next(hb_node* node);
static hb_node* node_new(void* key);
static bool node_verify(const hb_tree* tree, const hb_node* parent, const hb_node* node,
unsigned* height, size_t *count);
hb_tree*
hb_tree_new(dict_compare_func cmp_func)
{
ASSERT(cmp_func != NULL);
hb_tree* tree = MALLOC(sizeof(*tree));
if (tree) {
tree->root = NULL;
tree->count = 0;
tree->cmp_func = cmp_func;
tree->rotation_count = 0;
}
return tree;
}
dict*
hb_dict_new(dict_compare_func cmp_func)
{
dict* dct = MALLOC(sizeof(*dct));
if (dct) {
if (!(dct->_object = hb_tree_new(cmp_func))) {
FREE(dct);
return NULL;
}
dct->_vtable = &hb_tree_vtable;
}
return dct;
}
size_t
hb_tree_free(hb_tree* tree, dict_delete_func delete_func) {
const size_t count = hb_tree_clear(tree, delete_func);
FREE(tree);
return count;
}
size_t
hb_tree_clear(hb_tree* tree, dict_delete_func delete_func)
{
const size_t count = tree->count;
hb_node* node = tree->root;
while (node) {
if (node->llink || node->rlink) {
node = node->llink ? node->llink : node->rlink;
continue;
}
if (delete_func)
delete_func(node->key, node->datum);
hb_node* const parent = PARENT(node);
FREE(node);
*(parent ? (parent->llink == node ? &parent->llink : &parent->rlink) : &tree->root) = NULL;
node = parent;
}
ASSERT(tree->root == NULL);
tree->count = 0;
return count;
}
/* L: rotate |q| left */
static bool
rotate_l(hb_tree* restrict const tree, hb_node* restrict const q)
{
hb_node* restrict const qp = PARENT(q);
hb_node* restrict const qr = q->rlink;
ASSERT(BAL_POS(q));
hb_node *restrict const qrl = qr->llink;
const int qr_bal = qr->bal & BAL_MASK;
/* q->parent <- qr; q->bal <- (qr_bal == 0); */
q->bal = (intptr_t)qr | (qr_bal == 0);
if ((q->rlink = qrl) != NULL)
qrl->bal = (intptr_t)q | (qrl->bal & BAL_MASK); /* qrl->parent <- q; */
/* qr->parent <- qp; qr->bal <- -(qr_bal == 0); */
qr->bal = (intptr_t)qp | ((qr_bal == 0) << 1);
qr->llink = q;
*(qp == NULL ? &tree->root : qp->llink == q ? &qp->llink : &qp->rlink) = qr;
return (qr_bal == 0);
}
/* R: rotate |q| right. */
static bool
rotate_r(hb_tree* restrict const tree, hb_node* restrict const q)
{
hb_node* restrict const qp = PARENT(q);
hb_node* restrict const ql = q->llink;
ASSERT(BAL_NEG(q));
hb_node* restrict const qlr = ql->rlink;
const int ql_bal = ql->bal & BAL_MASK;
/* q->parent <- ql; q->bal <- -(ql_bal == 0); */
q->bal = (intptr_t)ql | ((ql_bal == 0) << 1);
if ((q->llink = qlr) != NULL)
qlr->bal = (intptr_t)q | (qlr->bal & BAL_MASK); /* qlr->parent <- q; */
/* ql->parent <- qp; ql->bal <- (ql_bal == 0); */
ql->bal = (intptr_t)qp | (ql_bal == 0);
ql->rlink = q;
*(qp == NULL ? &tree->root : qp->llink == q ? &qp->llink : &qp->rlink) = ql;
return (ql_bal == 0);
}
/* RL: Rotate |q->rlink| right, then |q| left. */
static void
rotate_rl(hb_tree* restrict const tree, hb_node* restrict const q)
{
hb_node* restrict const qp = PARENT(q);
hb_node* restrict const qr = q->rlink;
hb_node* restrict const qrl = qr->llink;
ASSERT(BAL_POS(q));
ASSERT(BAL_NEG(qr));
const int qrl_bal = qrl->bal & BAL_MASK;
hb_node* restrict const qrll = qrl->llink;
hb_node* restrict const qrlr = qrl->rlink;
/* qrl->parent <- qp; qrl->bal <- 0; */
qrl->bal = (intptr_t)qp;
*(qp == NULL ? &tree->root : qp->llink == q ? &qp->llink : &qp->rlink) = qrl;
qrl->llink = q;
qrl->rlink = qr;
/* q->parent <- qrl; q->bal <- -(qrl_bal == 1); */
q->bal = (intptr_t)qrl | ((qrl_bal == 1) << 1);
if ((q->rlink = qrll) != NULL)
qrll->bal = (intptr_t)q | (qrll->bal & BAL_MASK);
/* qr->parent <- qrl; qr->bal <- (qrl_bal == -1); */
qr->bal = (intptr_t)qrl | (qrl_bal == 2);
if ((qr->llink = qrlr) != NULL)
qrlr->bal = (intptr_t)qr | (qrlr->bal & BAL_MASK);
}
/* LR: rotate |q->llink| left, then rotate |q| right. */
static void
rotate_lr(hb_tree* restrict const tree, hb_node* restrict const q)
{
hb_node* restrict const qp = PARENT(q);
hb_node* restrict const ql = q->llink;
hb_node* restrict const qlr = ql->rlink;
ASSERT(BAL_NEG(q));
ASSERT(BAL_POS(ql));
const int qlr_bal = qlr->bal & BAL_MASK;
hb_node* restrict const qlrl = qlr->llink;
hb_node* restrict const qlrr = qlr->rlink;
/* qlr->parent <- qp; qlr->bal <- 0; */
qlr->bal = (intptr_t)qp;
*(qp == NULL ? &tree->root : qp->llink == q ? &qp->llink : &qp->rlink) = qlr;
qlr->llink = ql;
qlr->rlink = q;
/* q->parent <- qlr; q->bal = (qlr_bal == -1); */
q->bal = (intptr_t)qlr | (qlr_bal == 2);
if ((q->llink = qlrr) != NULL)
qlrr->bal = (intptr_t)q | (qlrr->bal & BAL_MASK);
/* ql->parent <- qlr; ql->bal <- -(qlr_bal == 1); */
ql->bal = (intptr_t)qlr | ((qlr_bal == 1) << 1);
if ((ql->rlink = qlrl) != NULL)
qlrl->bal = (intptr_t)ql | (qlrl->bal & BAL_MASK);
}
dict_insert_result
hb_tree_insert(hb_tree* tree, void* key)
{
int cmp = 0;
hb_node* node = tree->root;
hb_node* parent = NULL;
hb_node* q = NULL;
while (node) {
cmp = tree->cmp_func(key, node->key);
if (cmp < 0) {
parent = node; node = node->llink;
} else if (cmp > 0) {
parent = node; node = node->rlink;
} else
return (dict_insert_result) { &node->datum, false };
if (parent->bal & BAL_MASK)
q = parent;
}
hb_node* const add = node = node_new(key);
if (!node)
return (dict_insert_result) { NULL, false };
if (!(node->pptr = parent)) {
ASSERT(tree->count == 0);
ASSERT(tree->root == NULL);
tree->root = node;
} else {
if (cmp < 0)
parent->llink = node;
else
parent->rlink = node;
while (parent != q) {
ASSERT((parent->bal & BAL_MASK) == 0);
if (parent->llink == node)
parent->bal |= 2;
else
parent->bal |= 1;
node = parent;
parent = PARENT(parent);
}
if (q) {
ASSERT(q->bal & BAL_MASK);
if (q->llink == node) {
if (BAL_NEG(q)) { /* if q->balance == -1 */
if (BAL_POS(q->llink)) { /* if q->llink->balance == +1 */
tree->rotation_count += 2;
rotate_lr(tree, q);
} else {
tree->rotation_count += 1;
ASSERT(!rotate_r(tree, q));
}
} else { /* else, q->balance == +1 */
ASSERT(BAL_POS(q));
q->bal &= ~BAL_MASK; /* q->balance <- 0 */
}
} else {
ASSERT(q->rlink == node);
if (BAL_POS(q)) { /* if q->balance == +1 */
if (BAL_NEG(q->rlink)) { /* if q->rlink->balance == -1 */
tree->rotation_count += 2;
rotate_rl(tree, q);
} else {
tree->rotation_count += 1;
ASSERT(!rotate_l(tree, q));
}
} else { /* else, q->balance == -1 */
ASSERT(BAL_NEG(q));
q->bal &= ~BAL_MASK; /* q->balance <- 0 */
}
}
}
}
tree->count++;
return (dict_insert_result) { &add->datum, true };
}
void** hb_tree_search(hb_tree* tree, const void* key) { return tree_search(tree, key); }
void** hb_tree_search_le(hb_tree* tree, const void* key) { return tree_search_le(tree, key); }
void** hb_tree_search_lt(hb_tree* tree, const void* key) { return tree_search_lt(tree, key); }
void** hb_tree_search_ge(hb_tree* tree, const void* key) { return tree_search_ge(tree, key); }
void** hb_tree_search_gt(hb_tree* tree, const void* key) { return tree_search_gt(tree, key); }
static void
remove_node(hb_tree* tree, hb_node* node)
{
if (node->llink && node->rlink) {
hb_node* restrict out =
BAL_POS(node) ? tree_node_min(node->rlink) : tree_node_max(node->llink);
void* tmp;
SWAP(node->key, out->key, tmp);
SWAP(node->datum, out->datum, tmp);
node = out;
}
hb_node* p = PARENT(node);
hb_node* child = node->llink ? node->llink : node->rlink;
FREE(node);
tree->count--;
if (child)
child->bal = (intptr_t)p | (child->bal & BAL_MASK);
if (!p) {
tree->root = child;
return;
}
bool left = (p->llink == node);
if (left) {
p->llink = child;
} else {
ASSERT(p->rlink == node);
p->rlink = child;
}
node = child;
unsigned rotations = 0;
for (;;) {
if (left) {
ASSERT(p->llink == node);
if (BAL_POS(p)) { /* if p->balance == +1 */
if (BAL_NEG(p->rlink)) { /* if p->rlink->balance == -1 */
rotations += 2;
rotate_rl(tree, p);
} else {
rotations += 1;
if (rotate_l(tree, p))
break;
}
node = PARENT(p);
} else if (BAL_NEG(p)) { /* else if p->balance == -1 */
p->bal &= ~BAL_MASK; /* p->balance <- 0 */
node = p;
} else { /* else, p->balance == 0 */
ASSERT((p->bal & BAL_MASK) == 0);
p->bal |= 1; /* p->balance <- +1 */
break;
}
} else {
ASSERT(p->rlink == node);
if (BAL_NEG(p)) { /* if p->balance == -1 */
if (BAL_POS(p->llink)) { /* if p->llink->balance == +1 */
rotations += 2;
rotate_lr(tree, p);
} else {
rotations += 1;
if (rotate_r(tree, p))
break;
}
node = PARENT(p);
} else if (BAL_POS(p)) { /* else if p->balance == +1 */
p->bal &= ~BAL_MASK; /* p->balance <- 0 */
node = p;
} else { /* else, p->balance == 0 */
ASSERT((p->bal & BAL_MASK) == 0);
p->bal |= 2; /* p->balance <- -1 */
break;
}
}
if (!(p = PARENT(node)))
break;
if (p->llink == node) {
left = true;
} else {
ASSERT(p->rlink == node);
left = false;
}
}
tree->rotation_count += rotations;
}
dict_remove_result
hb_tree_remove(hb_tree* tree, const void* key)
{
hb_node* node = tree_search_node(tree, key);
if (!node)
return (dict_remove_result) { NULL, NULL, false };
const dict_remove_result result = { node->key, node->datum, true };
remove_node(tree, node);
return result;
}
size_t
hb_tree_traverse(hb_tree* tree, dict_visit_func visit, void* user_data)
{
return tree_traverse(tree, visit, user_data);
}
bool
hb_tree_select(hb_tree *tree, size_t n, const void **key, void **datum)
{
if (n >= tree->count) {
if (key)
*key = NULL;
if (datum)
*datum = NULL;
return false;
}
hb_node* node;
if (n >= tree->count / 2) {
node = tree_node_max(tree->root);
n = tree->count - 1 - n;
while (n--)
node = node_prev(node);
} else {
node = tree_node_min(tree->root);
while (n--)
node = node_next(node);
}
if (key)
*key = node->key;
if (datum)
*datum = node->datum;
return true;
}
size_t hb_tree_count(const hb_tree* tree) { return tree_count(tree); }
size_t hb_tree_min_path_length(const hb_tree* tree) { return tree_min_path_length(tree); }
size_t hb_tree_max_path_length(const hb_tree* tree) { return tree_max_path_length(tree); }
size_t hb_tree_total_path_length(const hb_tree* tree) { return tree_total_path_length(tree); }
static hb_node*
node_new(void* key)
{
hb_node* node = MALLOC(sizeof(*node));
if (node) {
ASSERT((((intptr_t)node) & 3) == 0); /* Ensure malloc returns aligned result. */
node->key = key;
node->datum = NULL;
node->bal = 0; /* also initializes parent to NULL */
node->llink = NULL;
node->rlink = NULL;
}
return node;
}
static hb_node*
node_prev(hb_node* node)
{
if (node->llink)
return tree_node_max(node->llink);
hb_node* parent = PARENT(node);
while (parent && parent->llink == node) {
node = parent;
parent = PARENT(parent);
}
return parent;
}
static hb_node*
node_next(hb_node* node)
{
if (node->rlink)
return tree_node_min(node->rlink);
hb_node* parent = PARENT(node);
while (parent && parent->rlink == node) {
node = parent;
parent = PARENT(parent);
}
return parent;
}
static bool
node_verify(const hb_tree* tree, const hb_node* parent, const hb_node* node,
unsigned* height, size_t *count)
{
if (!parent) {
VERIFY(tree->root == node);
} else {
if (parent->llink == node) {
if (node)
VERIFY(tree->cmp_func(parent->key, node->key) > 0);
} else {
ASSERT(parent->rlink == node);
if (node)
VERIFY(tree->cmp_func(parent->key, node->key) < 0);
}
}
if (node) {
int bal = node->bal & BAL_MASK;
VERIFY(bal >= 0);
VERIFY(bal <= 2);
if (bal == 2) {
VERIFY(node->llink != NULL);
bal = -1;
} else if (bal == 1) {
VERIFY(node->rlink != NULL);
}
VERIFY(PARENT(node) == parent);
unsigned lheight, rheight;
if (!node_verify(tree, node, node->llink, &lheight, count) ||
!node_verify(tree, node, node->rlink, &rheight, count))
return false;
VERIFY(bal == (int)rheight - (int)lheight);
if (height)
*height = MAX(lheight, rheight) + 1;
*count += 1;
} else {
if (height)
*height = 0;
}
return true;
}
bool
hb_tree_verify(const hb_tree* tree)
{
size_t count = 0;
bool verified = node_verify(tree, NULL, tree->root, NULL, &count);
VERIFY(tree->count == count);
return verified;
}
hb_itor*
hb_itor_new(hb_tree* tree)
{
hb_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
itor->tree = tree;
itor->node = NULL;
}
return itor;
}
dict_itor*
hb_dict_itor_new(hb_tree* tree)
{
dict_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
if (!(itor->_itor = hb_itor_new(tree))) {
FREE(itor);
return NULL;
}
itor->_vtable = &hb_tree_itor_vtable;
}
return itor;
}
void hb_itor_free(hb_itor* itor) { tree_iterator_free(itor); }
bool hb_itor_valid(const hb_itor* itor) { return tree_iterator_valid(itor); }
void hb_itor_invalidate(hb_itor* itor) { tree_iterator_invalidate(itor); }
bool hb_itor_next(hb_itor* itor) {
if (itor->node)
itor->node = node_next(itor->node);
return itor->node != NULL;
}
bool hb_itor_prev(hb_itor* itor) {
if (itor->node)
itor->node = node_prev(itor->node);
return itor->node != NULL;
}
bool hb_itor_nextn(hb_itor* itor, size_t count) {
while (itor->node && count--)
itor->node = node_next(itor->node);
return itor->node != NULL;
}
bool hb_itor_prevn(hb_itor* itor, size_t count) {
while (itor->node && count--)
itor->node = node_prev(itor->node);
return itor->node != NULL;
}
bool hb_itor_first(hb_itor* itor) { return tree_iterator_first(itor); }
bool hb_itor_last(hb_itor* itor) { return tree_iterator_last(itor); }
bool hb_itor_search(hb_itor* itor, const void* key) { return tree_iterator_search_ge(itor, key); }
bool hb_itor_search_le(hb_itor* itor, const void* key) { return tree_iterator_search_le(itor, key); }
bool hb_itor_search_lt(hb_itor* itor, const void* key) { return tree_iterator_search_lt(itor, key); }
bool hb_itor_search_ge(hb_itor* itor, const void* key) { return tree_iterator_search_ge(itor, key); }
bool hb_itor_search_gt(hb_itor* itor, const void* key) { return tree_iterator_search_gt(itor, key); }
int hb_itor_compare(const hb_itor* i1, const hb_itor* i2) { return tree_iterator_compare(i1, i2); }
const void* hb_itor_key(const hb_itor* itor) { return tree_iterator_key(itor); }
void** hb_itor_datum(hb_itor* itor) { return tree_iterator_datum(itor); }
bool
hb_itor_remove(hb_itor* itor)
{
if (!itor->node)
return false;
remove_node(itor->tree, itor->node);
itor->node = NULL;
return true;
}
+543
View File
@@ -0,0 +1,543 @@
/*
* libdict -- internal path reduction tree implementation.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
/*
* cf. [Gonnet 1983], [Gonnet 1984]
*/
#include "pr_tree.h"
#include "dict_private.h"
#include "tree_common.h"
typedef struct pr_node pr_node;
struct pr_node {
TREE_NODE_FIELDS(pr_node);
unsigned weight;
};
#define WEIGHT(n) ((n) ? (n)->weight : 1)
struct pr_tree {
TREE_FIELDS(pr_node);
};
struct pr_itor {
TREE_ITERATOR_FIELDS(pr_tree, pr_node);
};
static const dict_vtable pr_tree_vtable = {
true,
(dict_inew_func) pr_dict_itor_new,
(dict_dfree_func) tree_free,
(dict_insert_func) pr_tree_insert,
(dict_search_func) tree_search,
(dict_search_func) tree_search_le,
(dict_search_func) tree_search_lt,
(dict_search_func) tree_search_ge,
(dict_search_func) tree_search_gt,
(dict_remove_func) pr_tree_remove,
(dict_clear_func) tree_clear,
(dict_traverse_func) tree_traverse,
(dict_select_func) pr_tree_select,
(dict_count_func) tree_count,
(dict_verify_func) pr_tree_verify,
};
static const itor_vtable pr_tree_itor_vtable = {
(dict_ifree_func) tree_iterator_free,
(dict_valid_func) tree_iterator_valid,
(dict_invalidate_func) tree_iterator_invalidate,
(dict_next_func) tree_iterator_next,
(dict_prev_func) tree_iterator_prev,
(dict_nextn_func) tree_iterator_nextn,
(dict_prevn_func) tree_iterator_prevn,
(dict_first_func) tree_iterator_first,
(dict_last_func) tree_iterator_last,
(dict_key_func) tree_iterator_key,
(dict_datum_func) tree_iterator_datum,
(dict_isearch_func) tree_iterator_search,
(dict_isearch_func) tree_iterator_search_le,
(dict_isearch_func) tree_iterator_search_lt,
(dict_isearch_func) tree_iterator_search_ge,
(dict_isearch_func) tree_iterator_search_gt,
(dict_iremove_func) pr_itor_remove,
(dict_icompare_func) tree_iterator_compare,
};
static unsigned fixup(pr_tree* tree, pr_node* node);
static void rot_left(pr_tree* tree, pr_node* node);
static void rot_right(pr_tree* tree, pr_node* node);
static pr_node* node_new(void* key);
pr_tree*
pr_tree_new(dict_compare_func cmp_func)
{
ASSERT(cmp_func != NULL);
pr_tree* tree = MALLOC(sizeof(*tree));
if (tree) {
tree->root = NULL;
tree->count = 0;
tree->cmp_func = cmp_func;
tree->rotation_count = 0;
}
return tree;
}
dict*
pr_dict_new(dict_compare_func cmp_func)
{
dict* dct = MALLOC(sizeof(*dct));
if (dct) {
if (!(dct->_object = pr_tree_new(cmp_func))) {
FREE(dct);
return NULL;
}
dct->_vtable = &pr_tree_vtable;
}
return dct;
}
size_t pr_tree_free(pr_tree* tree, dict_delete_func delete_func) { return tree_free(tree, delete_func); }
void** pr_tree_search(pr_tree* tree, const void* key) { return tree_search(tree, key); }
void** pr_tree_search_le(pr_tree* tree, const void* key) { return tree_search_le(tree, key); }
void** pr_tree_search_lt(pr_tree* tree, const void* key) { return tree_search_lt(tree, key); }
void** pr_tree_search_ge(pr_tree* tree, const void* key) { return tree_search_ge(tree, key); }
void** pr_tree_search_gt(pr_tree* tree, const void* key) { return tree_search_gt(tree, key); }
static unsigned
fixup(pr_tree* tree, pr_node* node)
{
/*
* The internal path length of a tree is defined as the sum of levels of
* all the tree's internal nodes. Path-reduction trees are similar to
* weight-balanced trees, except that rotations are only made when they can
* reduce the total internal path length of the tree. These particular
* trees are in the class BB[1/3], but because of these restrictions their
* performance is superior to BB[1/3] trees.
*
* Consider a node N.
* A single left rotation is performed when
* WEIGHT(n->llink) < WEIGHT(n->rlink->rlink)
* A right-left rotation is performed when
* WEIGHT(n->llink) < WEIGHT(n->rlink->llink)
*
* A single right rotation is performed when
* WEIGHT(n->rlink) > WEIGHT(n->llink->llink)
* A left-right rotation is performed when
* WEIGHT(n->rlink) > WEIGHT(n->llink->rlink)
*
* Although the worst case number of rotations for a single insertion or
* deletion is O(n), the amortized worst-case number of rotations is
* .44042lg(n) + O(1) for insertion, and .42062lg(n) + O(1) for deletion.
*
* We use tail recursion to minimize the number of recursive calls. For
* single rotations, no recursive call is made, and we tail recurse to
* continue checking for out-of-balance conditions. For double, we make one
* recursive call and then tail recurse.
*/
unsigned rotations = 0;
const unsigned lweight = WEIGHT(node->llink);
const unsigned rweight = WEIGHT(node->rlink);
if (lweight < rweight) {
pr_node* r = node->rlink;
ASSERT(r != NULL);
if (WEIGHT(r->rlink) > lweight) { /* LL */
rot_left(tree, node);
rotations += 1;
rotations += fixup(tree, node);
/* Commenting the next line means we must make the weight
* verification condition in node_verify() less stringent. */
rotations += fixup(tree, r);
} else if (WEIGHT(r->llink) > lweight) { /* RL */
/* Rotate |r| right, then |node| left, with |rl| taking the place
* of |node| and having |node| and |r| as left and right children,
* respectively. */
pr_node* const rl = r->llink;
pr_node* const parent = node->parent;
pr_node* const a = rl->llink;
rl->llink = node;
node->parent = rl;
if ((node->rlink = a) != NULL)
a->parent = node;
pr_node* const b = rl->rlink;
rl->rlink = r;
r->parent = rl;
if ((r->llink = b) != NULL)
b->parent = r;
rl->parent = parent;
*(parent ? (parent->llink == node ? &parent->llink : &parent->rlink) : &tree->root) = rl;
node->weight += WEIGHT(a) - r->weight;
r->weight += WEIGHT(b) - rl->weight;
rl->weight = node->weight + r->weight;
rotations += 1;
rotations += fixup(tree, r);
rotations += fixup(tree, node);
}
} else if (lweight > rweight) {
pr_node* l = node->llink;
ASSERT(l != NULL);
if (WEIGHT(l->llink) > rweight) { /* RR */
rot_right(tree, node);
rotations += 1;
rotations += fixup(tree, node);
/* Commenting the next line means we must turn the weight
* verification condition in node_verify() less stringent. */
rotations += fixup(tree, l);
} else if (WEIGHT(l->rlink) > rweight) { /* LR */
/* Rotate |l| left, then |node| right, with |lr| taking the place of
* |node| and having |l| and |node| as left and right children,
* respectively. */
pr_node* const lr = l->rlink;
pr_node* const parent = node->parent;
pr_node* const a = lr->llink;
lr->llink = l;
l->parent = lr;
if ((l->rlink = a) != NULL)
a->parent = l;
pr_node* const b = lr->rlink;
lr->rlink = node;
node->parent = lr;
if ((node->llink = b) != NULL)
b->parent = node;
lr->parent = parent;
*(parent ? (parent->llink == node ? &parent->llink : &parent->rlink) : &tree->root) = lr;
node->weight += WEIGHT(b) - l->weight;
l->weight += WEIGHT(a) - lr->weight;
lr->weight = node->weight + l->weight;
rotations += 2;
rotations += fixup(tree, l);
rotations += fixup(tree, node);
}
}
return rotations;
}
dict_insert_result
pr_tree_insert(pr_tree* tree, void* key)
{
int cmp = 0;
pr_node* node = tree->root;
pr_node* parent = NULL;
while (node) {
cmp = tree->cmp_func(key, node->key);
if (cmp < 0) {
parent = node; node = node->llink;
} else if (cmp > 0) {
parent = node; node = node->rlink;
} else
return (dict_insert_result) { &node->datum, false };
}
pr_node* add = node_new(key);
if (!add)
return (dict_insert_result) { NULL, false };
if (!(add->parent = parent)) {
ASSERT(tree->count == 0);
ASSERT(tree->root == NULL);
tree->root = add;
} else {
if (cmp < 0)
parent->llink = add;
else
parent->rlink = add;
unsigned rotations = 0;
while ((node = parent) != NULL) {
parent = parent->parent;
++node->weight;
rotations += fixup(tree, node);
}
tree->rotation_count += rotations;
}
tree->count++;
return (dict_insert_result) { &add->datum, true };
}
static void
remove_node(pr_tree* tree, pr_node* node)
{
if (node->llink && node->rlink) {
pr_node* out;
if (node->llink->weight > node->rlink->weight) {
out = tree_node_max(node->llink);
} else {
out = tree_node_min(node->rlink);
}
void* tmp;
SWAP(node->key, out->key, tmp);
SWAP(node->datum, out->datum, tmp);
node = out;
}
ASSERT(!node->llink || !node->rlink);
/* Splice in the successor, if any. */
pr_node* child = node->llink ? node->llink : node->rlink;
pr_node* parent = node->parent;
if (child)
child->parent = parent;
*(parent ? (parent->llink == node ? &parent->llink : &parent->rlink) : &tree->root) = child;
FREE(node);
tree->count--;
/* Now move up the tree, decrementing weights. */
unsigned rotations = 0;
while (parent) {
--parent->weight;
pr_node* up = parent->parent;
rotations += fixup(tree, parent);
parent = up;
}
tree->rotation_count += rotations;
}
dict_remove_result
pr_tree_remove(pr_tree* tree, const void* key)
{
pr_node* node = tree_search_node(tree, key);
if (!node)
return (dict_remove_result) { NULL, NULL, false };
dict_remove_result result = { node->key, node->datum, true };
remove_node(tree, node);
return result;
}
size_t pr_tree_clear(pr_tree* tree, dict_delete_func delete_func) { return tree_clear(tree, delete_func); }
size_t
pr_tree_traverse(pr_tree* tree, dict_visit_func visit, void* user_data)
{
return tree_traverse(tree, visit, user_data);
}
bool
pr_tree_select(pr_tree* tree, size_t n, const void** key, void** datum)
{
if (n >= tree->count) {
if (key)
*key = NULL;
if (datum)
*datum = NULL;
return false;
}
pr_node* node = tree->root;
for (;;) {
const unsigned nw = WEIGHT(node->llink);
if (n + 1 >= nw) {
if (n + 1 == nw) {
if (key)
*key = node->key;
if (datum)
*datum = node->datum;
return true;
}
n -= nw;
node = node->rlink;
} else {
node = node->llink;
}
}
}
size_t pr_tree_count(const pr_tree* tree) { return tree_count(tree); }
size_t pr_tree_max_path_length(const pr_tree* tree) { return tree_max_path_length(tree); }
size_t pr_tree_min_path_length(const pr_tree* tree) { return tree_min_path_length(tree); }
size_t pr_tree_total_path_length(const pr_tree* tree) { return tree_total_path_length(tree); }
static pr_node*
node_new(void* key)
{
pr_node* node = MALLOC(sizeof(*node));
if (node) {
node->key = key;
node->datum = NULL;
node->parent = NULL;
node->llink = NULL;
node->rlink = NULL;
node->weight = 2;
}
return node;
}
/*
* rot_left(T, B):
*
* / /
* B D
* / \ / \
* A D ==> B E
* / \ / \
* C E A C
*
* Only the weights of B and B's right child to be readjusted.
*/
static void
rot_left(pr_tree* tree, pr_node *node)
{
ASSERT(node->rlink != NULL);
pr_node* rlink = node->rlink;
tree_node_rot_left(tree, node);
node->weight = WEIGHT(node->llink) + WEIGHT(node->rlink);
rlink->weight = node->weight + WEIGHT(rlink->rlink);
}
/*
* rot_right(T, D):
*
* / /
* D B
* / \ / \
* B E ==> A D
* / \ / \
* A C C E
*
* Only the weights of D and D's left child need to be readjusted.
*/
static void
rot_right(pr_tree* tree, pr_node* node)
{
ASSERT(node->llink != NULL);
pr_node* llink = node->llink;
tree_node_rot_right(tree, node);
node->weight = WEIGHT(node->llink) + WEIGHT(node->rlink);
llink->weight = WEIGHT(llink->llink) + node->weight;
}
static bool
node_verify(const pr_tree* tree, const pr_node* parent, const pr_node* node)
{
if (!parent) {
VERIFY(tree->root == node);
} else {
VERIFY(parent->llink == node || parent->rlink == node);
}
if (node) {
VERIFY(node->parent == parent);
if (parent) {
if (parent->llink == node) {
VERIFY(tree->cmp_func(parent->key, node->key) > 0);
} else {
ASSERT(parent->rlink == node);
VERIFY(tree->cmp_func(parent->key, node->key) < 0);
}
}
pr_node* l = node->llink;
pr_node* r = node->rlink;
if (!node_verify(tree, node, l) ||
!node_verify(tree, node, r))
return false;
unsigned lweight = WEIGHT(l);
unsigned rweight = WEIGHT(r);
VERIFY(node->weight == lweight + rweight);
if (rweight > lweight) {
VERIFY(WEIGHT(r->rlink) <= lweight);
VERIFY(WEIGHT(r->llink) <= lweight);
} else if (lweight > rweight) {
VERIFY(WEIGHT(l->llink) <= rweight);
VERIFY(WEIGHT(l->rlink) <= rweight);
}
}
return true;
}
bool
pr_tree_verify(const pr_tree* tree)
{
if (tree->root) {
VERIFY(tree->count > 0);
} else {
VERIFY(tree->count == 0);
}
return node_verify(tree, NULL, tree->root);
}
pr_itor*
pr_itor_new(pr_tree* tree)
{
pr_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
itor->tree = tree;
itor->node = NULL;
}
return itor;
}
dict_itor*
pr_dict_itor_new(pr_tree* tree)
{
dict_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
if (!(itor->_itor = pr_itor_new(tree))) {
FREE(itor);
return NULL;
}
itor->_vtable = &pr_tree_itor_vtable;
}
return itor;
}
void pr_itor_free(pr_itor* itor) { tree_iterator_free(itor); }
bool pr_itor_valid(const pr_itor* itor) { return tree_iterator_valid(itor); }
void pr_itor_invalidate(pr_itor* itor) { tree_iterator_invalidate(itor); }
bool pr_itor_next(pr_itor* itor) { return tree_iterator_next(itor); }
bool pr_itor_prev(pr_itor* itor) { return tree_iterator_prev(itor); }
bool pr_itor_nextn(pr_itor* itor, size_t count) { return tree_iterator_nextn(itor, count); } /* TODO: speed up */
bool pr_itor_prevn(pr_itor* itor, size_t count) { return tree_iterator_prevn(itor, count); } /* TODO: speed up */
bool pr_itor_first(pr_itor* itor) { return tree_iterator_first(itor); }
bool pr_itor_last(pr_itor* itor) { return tree_iterator_last(itor); }
bool pr_itor_search(pr_itor* itor, const void* key) { return tree_iterator_search(itor, key); }
bool pr_itor_search_le(pr_itor* itor, const void* key) { return tree_iterator_search_le(itor, key); }
bool pr_itor_search_lt(pr_itor* itor, const void* key) { return tree_iterator_search_lt(itor, key); }
bool pr_itor_search_ge(pr_itor* itor, const void* key) { return tree_iterator_search_ge(itor, key); }
bool pr_itor_search_gt(pr_itor* itor, const void* key) { return tree_iterator_search_gt(itor, key); }
const void* pr_itor_key(const pr_itor* itor) { return tree_iterator_key(itor); }
void** pr_itor_datum(pr_itor* itor) { return tree_iterator_datum(itor); }
int pr_itor_compare(const pr_itor* i1, const pr_itor* i2) { return tree_iterator_compare(i1, i2); }
bool
pr_itor_remove(pr_itor* it)
{
if (!it->node)
return false;
remove_node(it->tree, it->node);
it->node = NULL;
return true;
}
+647
View File
@@ -0,0 +1,647 @@
/*
* libdict -- red-black tree implementation.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
/*
* cf. [Cormen, Leiserson, and Rivest 1990], [Guibas and Sedgewick, 1978]
*/
#include "rb_tree.h"
#include <string.h>
#include "dict_private.h"
#include "tree_common.h"
typedef struct rb_node rb_node;
struct rb_node {
void* key;
void* datum;
intptr_t color;
rb_node* llink;
rb_node* rlink;
};
#define RB_RED 0
#define RB_BLACK 1
#define PARENT(node) ((rb_node*)((node)->color & ~RB_BLACK))
#define COLOR(node) ((node)->color & RB_BLACK)
#define SET_RED(node) (node)->color &= (~(intptr_t)RB_BLACK)
#define SET_BLACK(node) (node)->color |= ((intptr_t)RB_BLACK)
#define SET_PARENT(node,p) (node)->color = COLOR(node) | (intptr_t)(p)
struct rb_tree {
TREE_FIELDS(rb_node);
};
struct rb_itor {
TREE_ITERATOR_FIELDS(rb_tree, rb_node);
};
static const dict_vtable rb_tree_vtable = {
true,
(dict_inew_func) rb_dict_itor_new,
(dict_dfree_func) rb_tree_free,
(dict_insert_func) rb_tree_insert,
(dict_search_func) tree_search,
(dict_search_func) tree_search_le,
(dict_search_func) tree_search_lt,
(dict_search_func) tree_search_ge,
(dict_search_func) tree_search_gt,
(dict_remove_func) rb_tree_remove,
(dict_clear_func) rb_tree_clear,
(dict_traverse_func) rb_tree_traverse,
(dict_select_func) rb_tree_select,
(dict_count_func) tree_count,
(dict_verify_func) rb_tree_verify,
};
static const itor_vtable rb_tree_itor_vtable = {
(dict_ifree_func) rb_itor_free,
(dict_valid_func) rb_itor_valid,
(dict_invalidate_func) rb_itor_invalidate,
(dict_next_func) rb_itor_next,
(dict_prev_func) rb_itor_prev,
(dict_nextn_func) rb_itor_nextn,
(dict_prevn_func) rb_itor_prevn,
(dict_first_func) rb_itor_first,
(dict_last_func) rb_itor_last,
(dict_key_func) rb_itor_key,
(dict_datum_func) rb_itor_datum,
(dict_isearch_func) rb_itor_search,
(dict_isearch_func) rb_itor_search_le,
(dict_isearch_func) rb_itor_search_lt,
(dict_isearch_func) rb_itor_search_ge,
(dict_isearch_func) rb_itor_search_gt,
(dict_iremove_func) rb_itor_remove,
(dict_icompare_func) tree_iterator_compare
};
static void rot_left(rb_tree* tree, rb_node* node);
static void rot_right(rb_tree* tree, rb_node* node);
static unsigned insert_fixup(rb_tree* tree, rb_node* node);
static unsigned delete_fixup(rb_tree* tree, rb_node* node, rb_node* parent, bool left);
static rb_node* node_new(void* key);
static rb_node* node_next(rb_node* node);
static rb_node* node_prev(rb_node* node);
rb_tree*
rb_tree_new(dict_compare_func cmp_func)
{
ASSERT(cmp_func != NULL);
rb_tree* tree = MALLOC(sizeof(*tree));
if (tree) {
tree->root = NULL;
tree->count = 0;
tree->cmp_func = cmp_func;
tree->rotation_count = 0;
}
return tree;
}
dict*
rb_dict_new(dict_compare_func cmp_func)
{
dict* dct = MALLOC(sizeof(*dct));
if (dct) {
if (!(dct->_object = rb_tree_new(cmp_func))) {
FREE(dct);
return NULL;
}
dct->_vtable = &rb_tree_vtable;
}
return dct;
}
size_t
rb_tree_free(rb_tree* tree, dict_delete_func delete_func) {
const size_t count = rb_tree_clear(tree, delete_func);
FREE(tree);
return count;
}
size_t
rb_tree_clear(rb_tree* tree, dict_delete_func delete_func)
{
const size_t count = tree->count;
rb_node* node = tree->root;
while (node) {
if (node->llink || node->rlink) {
node = node->llink ? node->llink : node->rlink;
continue;
}
if (delete_func)
delete_func(node->key, node->datum);
rb_node* const parent = PARENT(node);
FREE(node);
*(parent ? (parent->llink == node ? &parent->llink : &parent->rlink) : &tree->root) = NULL;
node = parent;
}
ASSERT(tree->root == NULL);
tree->count = 0;
return count;
}
void** rb_tree_search(rb_tree* tree, const void* key) { return tree_search(tree, key); }
void** rb_tree_search_le(rb_tree* tree, const void* key) { return tree_search_le(tree, key); }
void** rb_tree_search_lt(rb_tree* tree, const void* key) { return tree_search_lt(tree, key); }
void** rb_tree_search_ge(rb_tree* tree, const void* key) { return tree_search_ge(tree, key); }
void** rb_tree_search_gt(rb_tree* tree, const void* key) { return tree_search_gt(tree, key); }
dict_insert_result
rb_tree_insert(rb_tree* tree, void* key)
{
int cmp = 0; /* Quell GCC warning about uninitialized usage. */
rb_node* node = tree->root;
rb_node* parent = NULL;
while (node) {
cmp = tree->cmp_func(key, node->key);
if (cmp < 0) {
parent = node; node = node->llink;
} else if (cmp > 0) {
parent = node; node = node->rlink;
} else
return (dict_insert_result) { &node->datum, false };
}
if (!(node = node_new(key)))
return (dict_insert_result) { NULL, false };
SET_PARENT(node, parent);
if (parent == NULL) {
ASSERT(tree->root == NULL);
tree->root = node;
ASSERT(tree->count == 0);
SET_BLACK(node);
} else {
if (cmp < 0)
parent->llink = node;
else
parent->rlink = node;
tree->rotation_count += insert_fixup(tree, node);
}
tree->count++;
return (dict_insert_result) { &node->datum, true };
}
static unsigned
insert_fixup(rb_tree* tree, rb_node* node)
{
unsigned rotations = 0;
while (node != tree->root && COLOR(PARENT(node)) == RB_RED) {
if (PARENT(node) == PARENT(PARENT(node))->llink) {
rb_node* temp = PARENT(PARENT(node))->rlink;
if (temp && COLOR(temp) == RB_RED) {
SET_BLACK(temp);
node = PARENT(node);
SET_BLACK(node);
node = PARENT(node);
SET_RED(node);
} else {
if (node == PARENT(node)->rlink) {
node = PARENT(node);
rot_left(tree, node);
++rotations;
}
temp = PARENT(node);
SET_BLACK(temp);
temp = PARENT(temp);
SET_RED(temp);
rot_right(tree, temp);
++rotations;
}
} else {
rb_node* temp = PARENT(PARENT(node))->llink;
if (temp && COLOR(temp) == RB_RED) {
SET_BLACK(temp);
node = PARENT(node);
SET_BLACK(node);
node = PARENT(node);
SET_RED(node);
} else {
if (node == PARENT(node)->llink) {
node = PARENT(node);
rot_right(tree, node);
++rotations;
}
temp = PARENT(node);
SET_BLACK(temp);
temp = PARENT(temp);
SET_RED(temp);
rot_left(tree, temp);
++rotations;
}
}
}
SET_BLACK(tree->root);
return rotations;
}
static void
remove_node(rb_tree* tree, rb_node* node)
{
rb_node* out;
if (!node->llink || !node->rlink) {
out = node;
} else {
out = tree_node_min(node->rlink);
void *tmp;
SWAP(node->key, out->key, tmp);
SWAP(node->datum, out->datum, tmp);
}
rb_node* const x = out->llink ? out->llink : out->rlink;
rb_node* const xp = PARENT(out);
if (x)
SET_PARENT(x, xp);
bool left = xp && xp->llink == out;
*(xp ? (xp->llink == out ? &xp->llink : &xp->rlink) : &tree->root) = x;
if (COLOR(out) == RB_BLACK && tree->root)
tree->rotation_count += delete_fixup(tree, x, xp, left);
FREE(out);
tree->count--;
}
dict_remove_result
rb_tree_remove(rb_tree* tree, const void* key)
{
rb_node* node = tree_search_node(tree, key);
if (!node)
return (dict_remove_result) { NULL, NULL, false };
dict_remove_result result = { node->key, node->datum, true };
remove_node(tree, node);
return result;
}
static unsigned
delete_fixup(rb_tree* tree, rb_node* node, rb_node* parent, bool left)
{
unsigned rotations = 0;
while (node != tree->root && (node == NULL || COLOR(node) == RB_BLACK)) {
if (left) {
rb_node* w = parent->rlink;
if (COLOR(w) == RB_RED) {
SET_BLACK(w);
SET_RED(parent);
rot_left(tree, parent); ++rotations;
w = parent->rlink;
}
if ((w->llink == NULL || COLOR(w->llink) == RB_BLACK) &&
(w->rlink == NULL || COLOR(w->rlink) == RB_BLACK)) {
SET_RED(w);
node = parent;
parent = PARENT(parent);
left = parent && parent->llink == node;
} else {
if (w->rlink == NULL || COLOR(w->rlink) == RB_BLACK) {
SET_BLACK(w->llink);
SET_RED(w);
rot_right(tree, w); ++rotations;
w = parent->rlink;
}
if (COLOR(parent) == RB_RED)
SET_RED(w);
else
SET_BLACK(w);
if (w->rlink)
SET_BLACK(w->rlink);
SET_BLACK(parent);
rot_left(tree, parent); ++rotations;
break;
}
} else { // left == false
rb_node* w = parent->llink;
if (COLOR(w) == RB_RED) {
SET_BLACK(w);
SET_RED(parent);
rot_right(tree, parent); ++rotations;
w = parent->llink;
}
if ((w->llink == NULL || COLOR(w->llink) == RB_BLACK) &&
(w->rlink == NULL || COLOR(w->rlink) == RB_BLACK)) {
SET_RED(w);
node = parent;
parent = PARENT(parent);
left = parent && parent->llink == node;
} else {
if (w->llink == NULL || COLOR(w->llink) == RB_BLACK) {
SET_BLACK(w->rlink);
SET_RED(w);
rot_left(tree, w); ++rotations;
w = parent->llink;
}
if (COLOR(parent) == RB_RED)
SET_RED(w);
else
SET_BLACK(w);
if (w->llink)
SET_BLACK(w->llink);
SET_BLACK(parent);
rot_right(tree, parent); ++rotations;
break;
}
}
}
if (node)
SET_BLACK(node);
return rotations;
}
size_t rb_tree_count(const rb_tree* tree) { return tree_count(tree); }
size_t rb_tree_min_path_length(const rb_tree* tree) { return tree_min_path_length(tree); }
size_t rb_tree_max_path_length(const rb_tree* tree) { return tree_max_path_length(tree); }
size_t rb_tree_total_path_length(const rb_tree* tree) { return tree_total_path_length(tree); }
size_t
rb_tree_traverse(rb_tree* tree, dict_visit_func visit, void* user_data)
{
if (tree->root == NULL)
return 0;
size_t count = 0;
rb_node* node = tree_node_min(tree->root);
for (; node != NULL; node = node_next(node)) {
++count;
if (!visit(node->key, node->datum, user_data))
break;
}
return count;
}
bool
rb_tree_select(rb_tree *tree, size_t n, const void **key, void **datum)
{
if (n >= tree->count) {
if (key)
*key = NULL;
if (datum)
*datum = NULL;
return false;
}
rb_node *node;
if (n >= tree->count / 2) {
node = tree_node_max(tree->root);
n = tree->count - 1 - n;
while (n--)
node = node_prev(node);
} else {
node = tree_node_min(tree->root);
while (n--)
node = node_next(node);
}
if (key)
*key = node->key;
if (datum)
*datum = node->datum;
return true;
}
static void
rot_left(rb_tree* tree, rb_node* node)
{
rb_node* const rlink = node->rlink;
if ((node->rlink = rlink->llink) != NULL)
SET_PARENT(rlink->llink, node);
rb_node* const parent = PARENT(node);
SET_PARENT(rlink, parent);
*(parent ? (parent->llink == node ? &parent->llink : &parent->rlink) : &tree->root) = rlink;
rlink->llink = node;
SET_PARENT(node, rlink);
}
static void
rot_right(rb_tree* tree, rb_node* node)
{
rb_node* const llink = node->llink;
if ((node->llink = llink->rlink) != NULL)
SET_PARENT(llink->rlink, node);
rb_node* const parent = PARENT(node);
SET_PARENT(llink, parent);
*(parent ? (parent->llink == node ? &parent->llink : &parent->rlink) : &tree->root) = llink;
llink->rlink = node;
SET_PARENT(node, llink);
}
static rb_node*
node_new(void* key)
{
rb_node* node = MALLOC(sizeof(*node));
if (node) {
ASSERT((((intptr_t)node) & 1) == 0); /* Ensure malloc returns aligned result. */
node->key = key;
node->datum = NULL;
node->color = RB_RED; /* Also initializes parent to NULL */
node->llink = NULL;
node->rlink = NULL;
}
return node;
}
static rb_node*
node_next(rb_node* node)
{
if (node->rlink) {
for (node = node->rlink; node->llink; node = node->llink)
/* void */;
} else {
rb_node* temp = PARENT(node);
while (temp && temp->rlink == node) {
node = temp;
temp = PARENT(temp);
}
node = temp;
}
return node;
}
static rb_node*
node_prev(rb_node* node)
{
if (node->llink) {
for (node = node->llink; node->rlink; node = node->rlink)
/* void */;
} else {
rb_node* temp = PARENT(node);
while (temp && temp->llink == node) {
node = temp;
temp = PARENT(temp);
}
node = temp;
}
return node;
}
static bool
node_verify(const rb_tree* tree, const rb_node* parent, const rb_node* node,
unsigned black_node_count, unsigned leaf_black_node_count)
{
if (parent == NULL) {
VERIFY(tree->root == node);
VERIFY(node == NULL || COLOR(node) == RB_BLACK);
} else {
VERIFY(parent->llink == node || parent->rlink == node);
}
if (node) {
VERIFY(PARENT(node) == parent);
if (parent) {
if (parent->llink == node) {
VERIFY(tree->cmp_func(parent->key, node->key) > 0);
} else {
ASSERT(parent->rlink == node);
VERIFY(tree->cmp_func(parent->key, node->key) < 0);
}
}
if (COLOR(node) == RB_RED) {
/* Verify that every child of a red node is black. */
if (node->llink)
VERIFY(COLOR(node->llink) == RB_BLACK);
if (node->rlink)
VERIFY(COLOR(node->rlink) == RB_BLACK);
} else {
black_node_count++;
}
if (!node->llink && !node->rlink) {
/* Verify that each path to a leaf contains the same number of black nodes. */
VERIFY(black_node_count == leaf_black_node_count);
}
bool l = node_verify(tree, node, node->llink, black_node_count, leaf_black_node_count);
bool r = node_verify(tree, node, node->rlink, black_node_count, leaf_black_node_count);
return l && r;
}
return true;
}
bool
rb_tree_verify(const rb_tree* tree)
{
if (tree->root)
VERIFY(COLOR(tree->root) == RB_BLACK);
unsigned leaf_black_node_count = 0;
if (tree->root) {
VERIFY(tree->count > 0);
for (rb_node* node = tree->root; node; node = node->llink)
if (COLOR(node) == RB_BLACK)
leaf_black_node_count++;
} else {
VERIFY(tree->count == 0);
}
return node_verify(tree, NULL, tree->root, 0, leaf_black_node_count);
}
rb_itor*
rb_itor_new(rb_tree* tree)
{
rb_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
itor->tree = tree;
itor->node = NULL;
}
return itor;
}
dict_itor*
rb_dict_itor_new(rb_tree* tree)
{
dict_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
if (!(itor->_itor = rb_itor_new(tree))) {
FREE(itor);
return NULL;
}
itor->_vtable = &rb_tree_itor_vtable;
}
return itor;
}
void rb_itor_free(rb_itor* itor) { tree_iterator_free(itor); }
bool rb_itor_valid(const rb_itor* itor) { return tree_iterator_valid(itor); }
void rb_itor_invalidate(rb_itor* itor) { tree_iterator_invalidate(itor); }
bool
rb_itor_next(rb_itor* itor)
{
if (itor->node)
itor->node = node_next(itor->node);
return itor->node != NULL;
}
bool
rb_itor_prev(rb_itor* itor)
{
if (itor->node)
itor->node = node_prev(itor->node);
return itor->node != NULL;
}
bool
rb_itor_nextn(rb_itor* itor, size_t count)
{
while (count--)
if (!rb_itor_next(itor))
return false;
return itor->node != NULL;
}
bool
rb_itor_prevn(rb_itor* itor, size_t count)
{
while (count--)
if (!rb_itor_prev(itor))
return false;
return itor->node != NULL;
}
bool rb_itor_first(rb_itor* itor) { return tree_iterator_first(itor); }
bool rb_itor_last(rb_itor* itor) { return tree_iterator_last(itor); }
bool rb_itor_search(rb_itor* itor, const void* key) { return tree_iterator_search(itor, key); }
bool rb_itor_search_le(rb_itor* itor, const void* key) { return tree_iterator_search_le(itor, key); }
bool rb_itor_search_lt(rb_itor* itor, const void* key) { return tree_iterator_search_lt(itor, key); }
bool rb_itor_search_ge(rb_itor* itor, const void* key) { return tree_iterator_search_ge(itor, key); }
bool rb_itor_search_gt(rb_itor* itor, const void* key) { return tree_iterator_search_gt(itor, key); }
const void* rb_itor_key(const rb_itor* itor) { return tree_iterator_key(itor); }
void** rb_itor_datum(rb_itor* itor) { return tree_iterator_datum(itor); }
int rb_itor_compare(const rb_itor* i1, const rb_itor* i2) { return tree_iterator_compare(i1, i2); }
bool
rb_itor_remove(rb_itor* it)
{
if (!it->node)
return false;
remove_node(it->tree, it->node);
it->node = NULL;
return true;
}
+689
View File
@@ -0,0 +1,689 @@
/*
* libdict -- skiplist implementation.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
/*
* cf. [Pugh 1990], [Sedgewick 1998]
*/
#include "skiplist.h"
#include <string.h> /* For memset() */
#include "dict_private.h"
typedef struct skip_node skip_node;
struct skip_node {
void* key;
void* datum;
skip_node* prev;
unsigned link_count;
skip_node* link[];
};
#define MAX_LINK 32
struct skiplist {
skip_node* head;
unsigned max_link;
unsigned top_link;
dict_compare_func cmp_func;
size_t count;
};
struct skiplist_itor {
skiplist* list;
skip_node* node;
};
static const dict_vtable skiplist_vtable = {
true,
(dict_inew_func) skiplist_dict_itor_new,
(dict_dfree_func) skiplist_free,
(dict_insert_func) skiplist_insert,
(dict_search_func) skiplist_search,
(dict_search_func) skiplist_search_le,
(dict_search_func) skiplist_search_lt,
(dict_search_func) skiplist_search_ge,
(dict_search_func) skiplist_search_gt,
(dict_remove_func) skiplist_remove,
(dict_clear_func) skiplist_clear,
(dict_traverse_func) skiplist_traverse,
(dict_select_func) NULL,
(dict_count_func) skiplist_count,
(dict_verify_func) skiplist_verify,
};
static const itor_vtable skiplist_itor_vtable = {
(dict_ifree_func) skiplist_itor_free,
(dict_valid_func) skiplist_itor_valid,
(dict_invalidate_func) skiplist_itor_invalidate,
(dict_next_func) skiplist_itor_next,
(dict_prev_func) skiplist_itor_prev,
(dict_nextn_func) skiplist_itor_nextn,
(dict_prevn_func) skiplist_itor_prevn,
(dict_first_func) skiplist_itor_first,
(dict_last_func) skiplist_itor_last,
(dict_key_func) skiplist_itor_key,
(dict_datum_func) skiplist_itor_datum,
(dict_isearch_func) skiplist_itor_search,
(dict_isearch_func) skiplist_itor_search_le,
(dict_isearch_func) skiplist_itor_search_lt,
(dict_isearch_func) skiplist_itor_search_ge,
(dict_isearch_func) skiplist_itor_search_gt,
(dict_iremove_func) skiplist_itor_remove,
(dict_icompare_func) skiplist_itor_compare,
};
static inline skip_node* node_new(void* key, unsigned link_count);
static inline void node_insert(skiplist* list, skip_node* x, skip_node** update);
static inline skip_node* node_search(skiplist* list, const void* key);
static inline skip_node* node_search_le(skiplist* list, const void* key);
static inline skip_node* node_search_lt(skiplist* list, const void* key);
static inline skip_node* node_search_ge(skiplist* list, const void* key);
static inline skip_node* node_search_gt(skiplist* list, const void* key);
static inline unsigned rand_link_count(skiplist* list);
skiplist*
skiplist_new(dict_compare_func cmp_func, unsigned max_link)
{
ASSERT(cmp_func != NULL);
ASSERT(max_link > 0);
if (max_link > MAX_LINK)
max_link = MAX_LINK;
skiplist* list = MALLOC(sizeof(*list));
if (list) {
if (!(list->head = node_new(NULL, max_link))) {
FREE(list);
return NULL;
}
list->max_link = max_link;
list->top_link = 0;
list->cmp_func = cmp_func;
list->count = 0;
}
return list;
}
dict*
skiplist_dict_new(dict_compare_func cmp_func, unsigned max_link)
{
dict* dct = MALLOC(sizeof(*dct));
if (dct) {
if (!(dct->_object = skiplist_new(cmp_func, max_link))) {
FREE(dct);
return NULL;
}
dct->_vtable = &skiplist_vtable;
}
return dct;
}
size_t
skiplist_free(skiplist* list, dict_delete_func delete_func)
{
size_t count = skiplist_clear(list, delete_func);
FREE(list->head);
FREE(list);
return count;
}
static inline void
node_insert(skiplist* list, skip_node* x, skip_node** update)
{
const unsigned nlinks = x->link_count;
ASSERT(nlinks < list->max_link);
if (list->top_link < nlinks) {
for (unsigned k = list->top_link+1; k <= nlinks; k++) {
ASSERT(!update[k]);
update[k] = list->head;
}
list->top_link = nlinks;
}
x->prev = (update[0] == list->head) ? NULL : update[0];
if (update[0]->link[0])
update[0]->link[0]->prev = x;
for (unsigned k = 0; k < nlinks; k++) {
ASSERT(update[k]->link_count > k);
x->link[k] = update[k]->link[k];
update[k]->link[k] = x;
}
++list->count;
}
dict_insert_result
skiplist_insert(skiplist* list, void* key)
{
skip_node* x = list->head;
skip_node* update[MAX_LINK] = { 0 };
for (unsigned k = list->top_link+1; k-->0; ) {
ASSERT(x->link_count > k);
for (;;) {
skip_node* const y = x->link[k];
if (!y)
break;
const int cmp = list->cmp_func(key, y->key);
if (cmp < 0) {
while (k > 0 && x->link[k - 1] == y)
update[k--] = x;
break;
} else if (cmp == 0)
return (dict_insert_result) { &y->datum, false };
x = y;
}
update[k] = x;
}
x = node_new(key, rand_link_count(list));
if (!x)
return (dict_insert_result) { NULL, false };
node_insert(list, x, update);
return (dict_insert_result) { &x->datum, true };
}
static inline skip_node*
node_search(skiplist* list, const void* key)
{
skip_node* x = list->head;
for (unsigned k = list->top_link+1; k-->0;) {
for (;;) {
skip_node* const y = x->link[k];
if (!y)
break;
const int cmp = list->cmp_func(key, y->key);
if (cmp < 0) {
while (k > 0 && x->link[k - 1] == y)
k--;
break;
} else if (cmp == 0)
return y;
x = y;
}
}
return NULL;
}
static inline skip_node*
node_search_le(skiplist* list, const void* key)
{
skip_node* x = list->head;
for (unsigned k = list->top_link+1; k-->0;) {
for (;;) {
skip_node* const y = x->link[k];
if (!y)
break;
const int cmp = list->cmp_func(key, y->key);
if (cmp < 0) {
while (k > 0 && x->link[k - 1] == y)
k--;
break;
} else if (cmp == 0)
return y;
x = y;
}
}
return x == list->head ? NULL : x;
}
static inline skip_node*
node_search_lt(skiplist* list, const void* key)
{
skip_node* x = list->head;
for (unsigned k = list->top_link+1; k-->0;) {
for (;;) {
skip_node* const y = x->link[k];
if (!y)
break;
const int cmp = list->cmp_func(key, y->key);
if (cmp < 0) {
while (k > 0 && x->link[k - 1] == y)
k--;
break;
} else if (cmp == 0)
return y->prev;
x = y;
}
}
return x == list->head ? NULL : x;
}
static inline skip_node*
node_search_ge(skiplist* list, const void* key)
{
skip_node* x = list->head;
skip_node* ret = NULL;
for (unsigned k = list->top_link+1; k-->0;) {
for (;;) {
skip_node* const y = x->link[k];
if (!y)
break;
const int cmp = list->cmp_func(key, y->key);
if (cmp < 0) {
ret = y;
while (k > 0 && x->link[k - 1] == y)
k--;
break;
} else if (cmp == 0)
return y;
x = y;
}
}
return ret;
}
static inline skip_node*
node_search_gt(skiplist* list, const void* key)
{
skip_node* x = list->head;
skip_node* ret = NULL;
for (unsigned k = list->top_link+1; k-->0;) {
for (;;) {
skip_node* const y = x->link[k];
if (!y)
break;
const int cmp = list->cmp_func(key, y->key);
if (cmp < 0) {
ret = y;
while (k > 0 && x->link[k - 1] == y)
k--;
break;
} else if (cmp == 0)
return y->link[0];
x = y;
}
}
return ret;
}
void**
skiplist_search(skiplist* list, const void* key)
{
skip_node* x = node_search(list, key);
return x ? &x->datum : NULL;
}
void**
skiplist_search_le(skiplist* list, const void* key)
{
skip_node* x = node_search_le(list, key);
return x ? &x->datum : NULL;
}
void**
skiplist_search_lt(skiplist* list, const void* key)
{
skip_node* x = node_search_lt(list, key);
return x ? &x->datum : NULL;
}
void**
skiplist_search_ge(skiplist* list, const void* key)
{
skip_node* x = node_search_ge(list, key);
return x ? &x->datum : NULL;
}
void**
skiplist_search_gt(skiplist* list, const void* key)
{
skip_node* x = node_search_gt(list, key);
return x ? &x->datum : NULL;
}
dict_remove_result
skiplist_remove(skiplist* list, const void* key)
{
skip_node* x = list->head;
skip_node* update[MAX_LINK] = { 0 };
bool found = false;
for (unsigned k = list->top_link+1; k-->0;) {
ASSERT(x->link_count > k);
for (;;) {
skip_node* const y = x->link[k];
if (!y)
break;
const int cmp = list->cmp_func(key, y->key);
if (cmp > 0)
x = y;
else {
while (k > 0 && x->link[k - 1] == y)
update[k--] = x;
if (cmp == 0)
found = true;
break;
}
}
update[k] = x;
}
if (!found)
return (dict_remove_result) { NULL, NULL, false };
x = x->link[0];
for (unsigned k = 0; k <= list->top_link; k++) {
ASSERT(update[k] != NULL);
ASSERT(update[k]->link_count > k);
if (update[k]->link[k] != x)
break;
update[k]->link[k] = x->link[k];
}
if (x->prev)
x->prev->link[0] = x->link[0];
if (x->link[0])
x->link[0]->prev = x->prev;
dict_remove_result result = { x->key, x->datum, true };
FREE(x);
while (list->top_link > 0 && !list->head->link[list->top_link-1])
list->top_link--;
list->count--;
return result;
}
size_t
skiplist_clear(skiplist* list, dict_delete_func delete_func)
{
skip_node* node = list->head->link[0];
while (node) {
skip_node* next = node->link[0];
if (delete_func)
delete_func(node->key, node->datum);
FREE(node);
node = next;
}
const size_t count = list->count;
list->count = 0;
list->head->link[list->top_link] = NULL;
while (list->top_link)
list->head->link[--list->top_link] = NULL;
return count;
}
size_t
skiplist_traverse(skiplist* list, dict_visit_func visit, void* user_data)
{
size_t count = 0;
for (skip_node* node = list->head->link[0]; node; node = node->link[0]) {
++count;
if (!visit(node->key, node->datum, user_data))
break;
}
return count;
}
size_t
skiplist_count(const skiplist* list)
{
return list->count;
}
bool
skiplist_verify(const skiplist* list)
{
if (list->count == 0) {
VERIFY(list->top_link == 0);
} else {
VERIFY(list->top_link > 0);
}
VERIFY(list->top_link < list->max_link);
for (unsigned i = 0; i < list->top_link; ++i) {
VERIFY(list->head->link[i] != NULL);
}
for (unsigned i = list->top_link; i < list->max_link; ++i) {
VERIFY(list->head->link[i] == NULL);
}
unsigned observed_top_link = 0;
skip_node* prev = NULL;
skip_node* node = list->head->link[0];
while (node) {
if (observed_top_link < node->link_count)
observed_top_link = node->link_count;
VERIFY(node->prev == prev);
VERIFY(node->link_count >= 1);
VERIFY(node->link_count <= list->top_link);
for (unsigned k = 0; k < node->link_count; k++) {
if (node->link[k]) {
VERIFY(node->link[k]->link_count >= k);
}
}
prev = node;
node = node->link[0];
}
VERIFY(list->top_link == observed_top_link);
return true;
}
size_t
skiplist_link_count_histogram(const skiplist* list, size_t counts[], size_t ncounts)
{
for (size_t i = 0; i < ncounts; ++i)
counts[i] = 0;
size_t max_num_links = 0;
for (const skip_node* node = list->head->link[0]; node; node = node->link[0]) {
if (max_num_links < node->link_count)
max_num_links = node->link_count;
if (ncounts > node->link_count)
counts[node->link_count]++;
}
return max_num_links;
}
skiplist_itor*
skiplist_itor_new(skiplist* list)
{
skiplist_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
itor->list = list;
itor->node = NULL;
}
return itor;
}
dict_itor*
skiplist_dict_itor_new(skiplist* list)
{
dict_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
if (!(itor->_itor = skiplist_itor_new(list))) {
FREE(itor);
return NULL;
}
itor->_vtable = &skiplist_itor_vtable;
}
return itor;
}
void
skiplist_itor_free(skiplist_itor* itor)
{
FREE(itor);
}
bool
skiplist_itor_valid(const skiplist_itor* itor)
{
return itor->node != NULL;
}
void
skiplist_itor_invalidate(skiplist_itor* itor)
{
itor->node = NULL;
}
bool
skiplist_itor_next(skiplist_itor* itor)
{
if (itor->node)
itor->node = itor->node->link[0];
return itor->node != NULL;
}
bool
skiplist_itor_prev(skiplist_itor* itor)
{
if (itor->node)
itor->node = itor->node->prev;
return itor->node != NULL;
}
bool
skiplist_itor_nextn(skiplist_itor* itor, size_t count)
{
while (count--)
if (!skiplist_itor_next(itor))
return false;
return itor->node != NULL;
}
bool
skiplist_itor_prevn(skiplist_itor* itor, size_t count)
{
while (count--)
if (!skiplist_itor_prev(itor))
return false;
return itor->node != NULL;
}
bool
skiplist_itor_first(skiplist_itor* itor)
{
return (itor->node = itor->list->head->link[0]) != NULL;
}
bool
skiplist_itor_last(skiplist_itor* itor)
{
skip_node* x = itor->list->head;
for (unsigned k = itor->list->top_link; k-->0;) {
while (x->link[k])
x = x->link[k];
}
if (x == itor->list->head) {
itor->node = NULL;
return false;
} else {
itor->node = x;
return true;
}
}
bool
skiplist_itor_search(skiplist_itor* itor, const void* key)
{
return (itor->node = node_search(itor->list, key)) != NULL;
}
bool
skiplist_itor_search_le(skiplist_itor* itor, const void* key)
{
return (itor->node = node_search_le(itor->list, key)) != NULL;
}
bool
skiplist_itor_search_lt(skiplist_itor* itor, const void* key)
{
return (itor->node = node_search_lt(itor->list, key)) != NULL;
}
bool
skiplist_itor_search_ge(skiplist_itor* itor, const void* key)
{
return (itor->node = node_search_ge(itor->list, key)) != NULL;
}
bool
skiplist_itor_search_gt(skiplist_itor* itor, const void* key)
{
return (itor->node = node_search_gt(itor->list, key)) != NULL;
}
const void*
skiplist_itor_key(const skiplist_itor* itor)
{
return itor->node ? itor->node->key : NULL;
}
void**
skiplist_itor_datum(skiplist_itor* itor)
{
return itor->node ? &itor->node->datum : NULL;
}
int
skiplist_itor_compare(const skiplist_itor* itor1, const skiplist_itor* itor2)
{
ASSERT(itor1->list == itor2->list);
if (!itor1->node)
return !itor2->node ? 0 : -1;
if (!itor2->node)
return 1;
return itor1->list->cmp_func(itor1->node->key, itor2->node->key);
}
bool
skiplist_itor_remove(skiplist_itor* itor)
{
if (!itor->node)
return false;
/* XXX make this smarter */
dict_remove_result result = skiplist_remove(itor->list, itor->node->key);
ASSERT(result.removed);
itor->node = NULL;
return true;
}
static inline skip_node*
node_new(void* key, unsigned link_count)
{
ASSERT(link_count >= 1);
skip_node* node = MALLOC(sizeof(*node) +
sizeof(node->link[0]) * link_count);
if (node) {
node->key = key;
node->datum = NULL;
node->prev = NULL;
node->link_count = link_count;
memset(node->link, 0, sizeof(node->link[0]) * link_count);
}
return node;
}
static inline unsigned
rand_link_count(skiplist* list)
{
unsigned count = (unsigned) __builtin_ctz(dict_rand()) / 2 + 1;
return (count >= list->max_link) ? list->max_link - 1 : count;
}
+499
View File
@@ -0,0 +1,499 @@
/*
* libdict -- splay tree implementation.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
/*
* cf. [Sleator and Tarjan, 1985], [Tarjan 1985], [Tarjan 1983]
*
* A single operation on a splay tree has a worst-case time complexity of O(N),
* but a series of M operations have a time complexity of O(M lg N), and thus
* the amortized time complexity of an operation is O(lg N). More specifically,
* a series of M operations on a tree with N nodes will runs in O((N+M)lg(N+M))
* time. Splay trees work by "splaying" a node up the tree using a series of
* rotations until it is the root each time it is accessed. They are much
* simpler to code than most balanced trees, because there is no strict
* requirement about maintaining a balance scheme among nodes. When inserting
* and searching, we simply splay the node in question up until it becomes the
* root of the tree.
*
* This implementation is a bottom-up, move-to-root splay tree.
*/
/* TODO: rather than splay after the fact, use the splay operation to traverse
* the tree during insert, search, delete, etc. */
#include "sp_tree.h"
#include "dict_private.h"
#include "tree_common.h"
typedef struct sp_node sp_node;
struct sp_node {
TREE_NODE_FIELDS(sp_node);
};
struct sp_tree {
TREE_FIELDS(sp_node);
};
struct sp_itor {
TREE_ITERATOR_FIELDS(sp_tree, sp_node);
};
static const dict_vtable sp_tree_vtable = {
true,
(dict_inew_func) sp_dict_itor_new,
(dict_dfree_func) tree_free,
(dict_insert_func) sp_tree_insert,
(dict_search_func) sp_tree_search,
(dict_search_func) sp_tree_search_le,
(dict_search_func) sp_tree_search_lt,
(dict_search_func) sp_tree_search_ge,
(dict_search_func) sp_tree_search_gt,
(dict_remove_func) sp_tree_remove,
(dict_clear_func) tree_clear,
(dict_traverse_func) tree_traverse,
(dict_select_func) tree_select,
(dict_count_func) tree_count,
(dict_verify_func) sp_tree_verify,
};
static const itor_vtable sp_tree_itor_vtable = {
(dict_ifree_func) tree_iterator_free,
(dict_valid_func) tree_iterator_valid,
(dict_invalidate_func) tree_iterator_invalidate,
(dict_next_func) tree_iterator_next,
(dict_prev_func) tree_iterator_prev,
(dict_nextn_func) tree_iterator_nextn,
(dict_prevn_func) tree_iterator_prevn,
(dict_first_func) tree_iterator_first,
(dict_last_func) tree_iterator_last,
(dict_key_func) tree_iterator_key,
(dict_datum_func) tree_iterator_datum,
(dict_isearch_func) sp_itor_search,
(dict_isearch_func) tree_iterator_search_le,
(dict_isearch_func) tree_iterator_search_lt,
(dict_isearch_func) tree_iterator_search_ge,
(dict_isearch_func) tree_iterator_search_gt,
(dict_iremove_func) sp_itor_remove,
(dict_icompare_func) tree_iterator_compare
};
static sp_node* node_new(void* key);
static void splay(sp_tree* t, sp_node* n);
sp_tree*
sp_tree_new(dict_compare_func cmp_func)
{
ASSERT(cmp_func != NULL);
sp_tree* tree = MALLOC(sizeof(*tree));
if (tree) {
tree->root = NULL;
tree->count = 0;
tree->cmp_func = cmp_func;
tree->rotation_count = 0;
}
return tree;
}
dict*
sp_dict_new(dict_compare_func cmp_func)
{
dict* dct = MALLOC(sizeof(*dct));
if (dct) {
if (!(dct->_object = sp_tree_new(cmp_func))) {
FREE(dct);
return NULL;
}
dct->_vtable = &sp_tree_vtable;
}
return dct;
}
size_t sp_tree_free(sp_tree* tree, dict_delete_func delete_func) { return tree_free(tree, delete_func); }
size_t sp_tree_clear(sp_tree* tree, dict_delete_func delete_func) { return tree_clear(tree, delete_func); }
static void
splay(sp_tree* t, sp_node* n)
{
unsigned rotations = 0;
for (;;) {
sp_node* p = n->parent;
if (!p)
break;
sp_node* pp = p->parent;
if (!pp) {
/* Parent is the root; simply rotate root left or right so that |n|
* becomes new root. */
if (p->llink == n) {
if ((p->llink = n->rlink) != NULL)
p->llink->parent = p;
n->rlink = p;
++rotations;
} else {
if ((p->rlink = n->llink) != NULL)
p->rlink->parent = p;
n->llink = p;
}
p->parent = n;
t->root = n;
n->parent = NULL;
rotations += 1;
break;
}
rotations += 2;
sp_node* ppp = pp->parent;
if (p->llink == n) {
if (pp->llink == p) {
/* Rotate parent right, then node right. */
sp_node* const pr = p->rlink;
p->rlink = pp;
pp->parent = p;
if ((pp->llink = pr) != NULL)
pr->parent = pp;
sp_node* const nr = n->rlink;
n->rlink = p;
p->parent = n;
if ((p->llink = nr) != NULL)
nr->parent = p;
} else {
/* Rotate node right, then parent left. */
sp_node* const nr = n->rlink;
n->rlink = p;
p->parent = n;
if ((p->llink = nr) != NULL)
nr->parent = p;
sp_node* const nl = n->llink;
n->llink = pp;
pp->parent = n;
if ((pp->rlink = nl) != NULL)
nl->parent = pp;
}
} else {
if (pp->rlink == p) {
/* Rotate parent left, then node left. */
sp_node* const pl = p->llink;
p->llink = pp;
pp->parent = p;
if ((pp->rlink = pl) != NULL)
pl->parent = pp;
sp_node* const nl = n->llink;
n->llink = p;
p->parent = n;
if ((p->rlink = nl) != NULL)
nl->parent = p;
} else {
/* Rotate node left, then parent right. */
sp_node* const nl = n->llink;
n->llink = p;
p->parent = n;
if ((p->rlink = nl) != NULL)
nl->parent = p;
sp_node* const nr = n->rlink;
n->rlink = pp;
pp->parent = n;
if ((pp->llink = nr) != NULL)
nr->parent = pp;
}
}
n->parent = ppp;
if (ppp) {
if (ppp->llink == pp)
ppp->llink = n;
else
ppp->rlink = n;
} else {
t->root = n;
break;
}
}
t->rotation_count += rotations;
}
dict_insert_result
sp_tree_insert(sp_tree* tree, void* key)
{
int cmp = 0;
sp_node* node = tree->root;
sp_node* parent = NULL;
while (node) {
cmp = tree->cmp_func(key, node->key);
if (cmp < 0) {
parent = node; node = node->llink;
} else if (cmp > 0) {
parent = node; node = node->rlink;
} else
return (dict_insert_result) { &node->datum, false };
}
if (!(node = node_new(key)))
return (dict_insert_result) { NULL, false };
if (!(node->parent = parent)) {
ASSERT(tree->count == 0);
ASSERT(tree->root == NULL);
tree->root = node;
tree->count = 1;
} else {
if (cmp < 0)
parent->llink = node;
else
parent->rlink = node;
splay(tree, node);
tree->count++;
}
ASSERT(tree->root == node);
return (dict_insert_result) { &node->datum, true };
}
void**
sp_tree_search(sp_tree* tree, const void* key)
{
sp_node* parent = NULL;
for (sp_node* node = tree->root; node;) {
parent = node;
const int cmp = tree->cmp_func(key, node->key);
if (cmp < 0)
node = node->llink;
else if (cmp)
node = node->rlink;
else {
splay(tree, node);
ASSERT(tree->root == node);
return &node->datum;
}
}
if (parent)
splay(tree, parent);
return NULL;
}
void**
sp_tree_search_le(sp_tree* tree, const void* key)
{
sp_node* node = tree_search_le_node(tree, key);
if (node) {
splay(tree, node);
ASSERT(tree->root == node);
return &node->datum;
}
return NULL;
}
void**
sp_tree_search_lt(sp_tree* tree, const void* key)
{
sp_node* node = tree_search_lt_node(tree, key);
if (node) {
splay(tree, node);
ASSERT(tree->root == node);
return &node->datum;
}
return NULL;
}
void**
sp_tree_search_ge(sp_tree* tree, const void* key)
{
sp_node* node = tree_search_ge_node(tree, key);
if (node) {
splay(tree, node);
ASSERT(tree->root == node);
return &node->datum;
}
return NULL;
}
void**
sp_tree_search_gt(sp_tree* tree, const void* key)
{
sp_node* node = tree_search_gt_node(tree, key);
if (node) {
splay(tree, node);
ASSERT(tree->root == node);
return &node->datum;
}
return NULL;
}
static void
remove_node(sp_tree* tree, sp_node* node)
{
sp_node* out;
if (!node->llink || !node->rlink) {
out = node;
} else {
out = tree_node_min(node->rlink);
void* tmp;
SWAP(node->key, out->key, tmp);
SWAP(node->datum, out->datum, tmp);
}
sp_node* const temp = out->llink ? out->llink : out->rlink;
sp_node* const parent = out->parent;
FREE(out);
if (temp)
temp->parent = parent;
*(parent ? (parent->llink == out ? &parent->llink : &parent->rlink) : &tree->root) = temp;
if (parent)
splay(tree, parent);
tree->count--;
}
dict_remove_result
sp_tree_remove(sp_tree* tree, const void* key)
{
sp_node* node = tree_search_node(tree, key);
if (!node)
return (dict_remove_result) { NULL, NULL, false };
dict_remove_result result = { node->key, node->datum, true };
remove_node(tree, node);
return result;
}
size_t sp_tree_traverse(sp_tree* tree, dict_visit_func visit, void* user_data) { return tree_traverse(tree, visit, user_data); }
bool sp_tree_select(sp_tree* tree, size_t n, const void** key, void** datum) { return tree_select(tree, n, key, datum); }
size_t sp_tree_count(const sp_tree* tree) { return tree_count(tree); }
size_t sp_tree_min_path_length(const sp_tree* tree) { return tree_min_path_length(tree); }
size_t sp_tree_max_path_length(const sp_tree* tree) { return tree_max_path_length(tree); }
size_t sp_tree_total_path_length(const sp_tree* tree) { return tree_total_path_length(tree); }
static sp_node*
node_new(void* key)
{
sp_node* node = MALLOC(sizeof(*node));
if (node) {
node->key = key;
node->datum = NULL;
node->parent = NULL;
node->llink = NULL;
node->rlink = NULL;
}
return node;
}
static bool
node_verify(const sp_tree* tree, const sp_node* parent, const sp_node* node)
{
if (!parent) {
VERIFY(tree->root == node);
} else {
VERIFY(parent->llink == node || parent->rlink == node);
}
if (node) {
VERIFY(node->parent == parent);
if (parent) {
if (parent->llink == node) {
VERIFY(tree->cmp_func(parent->key, node->key) > 0);
} else {
ASSERT(parent->rlink == node);
VERIFY(tree->cmp_func(parent->key, node->key) < 0);
}
}
if (!node_verify(tree, node, node->llink) ||
!node_verify(tree, node, node->rlink))
return false;
}
return true;
}
bool
sp_tree_verify(const sp_tree* tree)
{
if (tree->root) {
VERIFY(tree->count > 0);
} else {
VERIFY(tree->count == 0);
}
return node_verify(tree, NULL, tree->root);
}
sp_itor*
sp_itor_new(sp_tree* tree)
{
sp_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
itor->tree = tree;
itor->node = NULL;
}
return itor;
}
dict_itor*
sp_dict_itor_new(sp_tree* tree)
{
dict_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
if (!(itor->_itor = sp_itor_new(tree))) {
FREE(itor);
return NULL;
}
itor->_vtable = &sp_tree_itor_vtable;
}
return itor;
}
void sp_itor_free(sp_itor* itor) { tree_iterator_free(itor); }
bool sp_itor_valid(const sp_itor* itor) { return tree_iterator_valid(itor); }
void sp_itor_invalidate(sp_itor* itor) { tree_iterator_invalidate(itor); }
bool sp_itor_next(sp_itor* itor) { return tree_iterator_next(itor); }
bool sp_itor_prev(sp_itor* itor) { return tree_iterator_prev(itor); }
bool sp_itor_nextn(sp_itor* itor, size_t count) { return tree_iterator_nextn(itor, count); }
bool sp_itor_prevn(sp_itor* itor, size_t count) { return tree_iterator_prevn(itor, count); }
bool sp_itor_first(sp_itor* itor) { return tree_iterator_first(itor); }
bool sp_itor_last(sp_itor* itor) { return tree_iterator_last(itor); }
/* TODO: use algorithm from sp_tree_search() */
bool sp_itor_search(sp_itor* itor, const void* key) { return tree_iterator_search(itor, key); }
bool sp_itor_search_le(sp_itor* itor, const void* key) { return tree_iterator_search_le(itor, key); }
bool sp_itor_search_lt(sp_itor* itor, const void* key) { return tree_iterator_search_lt(itor, key); }
bool sp_itor_search_ge(sp_itor* itor, const void* key) { return tree_iterator_search_ge(itor, key); }
bool sp_itor_search_gt(sp_itor* itor, const void* key) { return tree_iterator_search_gt(itor, key); }
const void* sp_itor_key(const sp_itor* itor) { return tree_iterator_key(itor); }
void** sp_itor_datum(sp_itor* itor) { return tree_iterator_datum(itor); }
int
sp_itor_compare(const sp_itor* i1, const sp_itor* i2)
{
return tree_iterator_compare(i1, i2);
}
bool
sp_itor_remove(sp_itor* itor)
{
if (!itor->node)
return false;
remove_node(itor->tree, itor->node);
itor->node = NULL;
return true;
}
+332
View File
@@ -0,0 +1,332 @@
/*
* libdict -- treap implementation.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
/*
* cf. [Aragon and Seidel, 1996], [Knuth 1998]
*
* A treap is a randomized data structure in which each node of tree has an
* associated key and priority. The priority is chosen at random when the node
* is inserted into the tree. Each node is inserted so that the lexicographic
* order of the keys is preserved, and the priority of any node is less than
* the priority of either of its child nodes; in this way the treap is a
* combination of a tree and a min-heap. In this implementation, this is
* accomplished by first inserting the node according to lexigraphical order of
* keys as in a normal binary tree, and then, if needed, sifting the node
* upwards using a series of rotations until the heap property of the tree is
* restored.
*/
#include "tr_tree.h"
#include <limits.h>
#include "dict_private.h"
#include "tree_common.h"
typedef struct tr_node tr_node;
struct tr_node {
TREE_NODE_FIELDS(tr_node);
uint32_t prio;
};
struct tr_tree {
TREE_FIELDS(tr_node);
dict_prio_func prio_func;
};
struct tr_itor {
TREE_ITERATOR_FIELDS(tr_tree, tr_node);
};
static const dict_vtable tr_tree_vtable = {
true,
(dict_inew_func) tr_dict_itor_new,
(dict_dfree_func) tree_free,
(dict_insert_func) tr_tree_insert,
(dict_search_func) tree_search,
(dict_search_func) tree_search_le,
(dict_search_func) tree_search_lt,
(dict_search_func) tree_search_ge,
(dict_search_func) tree_search_gt,
(dict_remove_func) tr_tree_remove,
(dict_clear_func) tree_clear,
(dict_traverse_func) tree_traverse,
(dict_select_func) tree_select,
(dict_count_func) tree_count,
(dict_verify_func) tr_tree_verify,
};
static const itor_vtable tr_tree_itor_vtable = {
(dict_ifree_func) tree_iterator_free,
(dict_valid_func) tree_iterator_valid,
(dict_invalidate_func) tree_iterator_invalidate,
(dict_next_func) tree_iterator_next,
(dict_prev_func) tree_iterator_prev,
(dict_nextn_func) tree_iterator_nextn,
(dict_prevn_func) tree_iterator_prevn,
(dict_first_func) tree_iterator_first,
(dict_last_func) tree_iterator_last,
(dict_key_func) tree_iterator_key,
(dict_datum_func) tree_iterator_datum,
(dict_isearch_func) tree_iterator_search,
(dict_isearch_func) tree_iterator_search_le,
(dict_isearch_func) tree_iterator_search_lt,
(dict_isearch_func) tree_iterator_search_ge,
(dict_isearch_func) tree_iterator_search_gt,
(dict_iremove_func) tr_itor_remove,
(dict_icompare_func) tree_iterator_compare,
};
static tr_node* node_new(void* key);
tr_tree*
tr_tree_new(dict_compare_func cmp_func, dict_prio_func prio_func)
{
ASSERT(cmp_func != NULL);
tr_tree* tree = MALLOC(sizeof(*tree));
if (tree) {
tree->root = NULL;
tree->count = 0;
tree->cmp_func = cmp_func;
tree->rotation_count = 0;
tree->prio_func = prio_func;
}
return tree;
}
dict*
tr_dict_new(dict_compare_func cmp_func, dict_prio_func prio_func)
{
dict* dct = MALLOC(sizeof(*dct));
if (dct) {
if (!(dct->_object = tr_tree_new(cmp_func, prio_func))) {
FREE(dct);
return NULL;
}
dct->_vtable = &tr_tree_vtable;
}
return dct;
}
size_t tr_tree_free(tr_tree* tree, dict_delete_func delete_func) { return tree_free(tree, delete_func); }
size_t tr_tree_clear(tr_tree* tree, dict_delete_func delete_func) { return tree_clear(tree, delete_func); }
dict_insert_result
tr_tree_insert(tr_tree* tree, void* key)
{
int cmp = 0;
tr_node* node = tree->root;
tr_node* parent = NULL;
while (node) {
cmp = tree->cmp_func(key, node->key);
if (cmp < 0) {
parent = node; node = node->llink;
} else if (cmp > 0) {
parent = node; node = node->rlink;
} else
return (dict_insert_result) { &node->datum, false };
}
if (!(node = node_new(key)))
return (dict_insert_result) { NULL, false };
node->prio = tree->prio_func ? tree->prio_func(key) : dict_rand();
if (!(node->parent = parent)) {
ASSERT(tree->root == NULL);
ASSERT(tree->count == 0);
tree->root = node;
} else {
if (cmp < 0)
parent->llink = node;
else
parent->rlink = node;
unsigned rotations = 0;
while (parent->prio < node->prio) {
++rotations;
if (parent->llink == node)
tree_node_rot_right(tree, parent);
else
tree_node_rot_left(tree, parent);
if (!(parent = node->parent))
break;
}
tree->rotation_count += rotations;
}
tree->count++;
return (dict_insert_result) { &node->datum, true };
}
static void
remove_node(tr_tree* tree, tr_node* node)
{
unsigned rotations = 0;
while (node->llink && node->rlink) {
++rotations;
if (node->llink->prio > node->rlink->prio)
tree_node_rot_right(tree, node);
else
tree_node_rot_left(tree, node);
}
tree->rotation_count += rotations;
tr_node* const out = node->llink ? node->llink : node->rlink;
tr_node* const parent = node->parent;
if (out)
out->parent = parent;
*(parent ? (parent->llink == node ? &parent->llink : &parent->rlink) : &tree->root) = out;
FREE(node);
tree->count--;
}
dict_remove_result
tr_tree_remove(tr_tree* tree, const void* key)
{
tr_node* node = tree_search_node(tree, key);
if (!node)
return (dict_remove_result) { NULL, NULL, false };
dict_remove_result result = { node->key, node->datum, true };
remove_node(tree, node);
return result;
}
void** tr_tree_search(tr_tree* tree, const void* key) { return tree_search(tree, key); }
void** tr_tree_search_le(tr_tree* tree, const void* key) { return tree_search_le(tree, key); }
void** tr_tree_search_lt(tr_tree* tree, const void* key) { return tree_search_lt(tree, key); }
void** tr_tree_search_ge(tr_tree* tree, const void* key) { return tree_search_ge(tree, key); }
void** tr_tree_search_gt(tr_tree* tree, const void* key) { return tree_search_gt(tree, key); }
size_t tr_tree_traverse(tr_tree* tree, dict_visit_func visit, void* user_data) { return tree_traverse(tree, visit, user_data); }
bool tr_tree_select(tr_tree* tree, size_t n, const void** key, void** datum) { return tree_select(tree, n, key, datum); }
size_t tr_tree_count(const tr_tree* tree) { return tree_count(tree); }
size_t tr_tree_min_path_length(const tr_tree* tree) { return tree_min_path_length(tree); }
size_t tr_tree_max_path_length(const tr_tree* tree) { return tree_max_path_length(tree); }
size_t tr_tree_total_path_length(const tr_tree* tree) { return tree_total_path_length(tree); }
static tr_node*
node_new(void* key)
{
tr_node* node = MALLOC(sizeof(*node));
if (node) {
node->key = key;
node->datum = NULL;
node->parent = NULL;
node->llink = NULL;
node->rlink = NULL;
}
return node;
}
static bool
node_verify(const tr_tree* tree, const tr_node* parent, const tr_node* node)
{
if (!parent) {
VERIFY(tree->root == node);
} else {
VERIFY(parent->llink == node || parent->rlink == node);
}
if (node) {
VERIFY(node->parent == parent);
if (parent) {
VERIFY(node->prio <= parent->prio);
if (parent->llink == node) {
VERIFY(tree->cmp_func(parent->key, node->key) > 0);
} else {
ASSERT(parent->rlink == node);
VERIFY(tree->cmp_func(parent->key, node->key) < 0);
}
}
if (!node_verify(tree, node, node->llink) ||
!node_verify(tree, node, node->rlink))
return false;
}
return true;
}
bool
tr_tree_verify(const tr_tree* tree)
{
if (tree->root) {
VERIFY(tree->count > 0);
} else {
VERIFY(tree->count == 0);
}
return node_verify(tree, NULL, tree->root);
}
tr_itor*
tr_itor_new(tr_tree* tree)
{
tr_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
itor->tree = tree;
itor->node = NULL;
}
return itor;
}
dict_itor*
tr_dict_itor_new(tr_tree* tree)
{
dict_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
if (!(itor->_itor = tr_itor_new(tree))) {
FREE(itor);
return NULL;
}
itor->_vtable = &tr_tree_itor_vtable;
}
return itor;
}
void tr_itor_free(tr_itor* itor) { tree_iterator_free(itor); }
bool tr_itor_valid(const tr_itor* itor) { return tree_iterator_valid(itor); }
void tr_itor_invalidate(tr_itor* itor) { tree_iterator_invalidate(itor); }
bool tr_itor_next(tr_itor* itor) { return tree_iterator_next(itor); }
bool tr_itor_prev(tr_itor* itor) { return tree_iterator_prev(itor); }
bool tr_itor_nextn(tr_itor* itor, size_t count) { return tree_iterator_nextn(itor, count); }
bool tr_itor_prevn(tr_itor* itor, size_t count) { return tree_iterator_prevn(itor, count); }
bool tr_itor_first(tr_itor* itor) { return tree_iterator_first(itor); }
bool tr_itor_last(tr_itor* itor) { return tree_iterator_last(itor); }
bool tr_itor_search(tr_itor* itor, const void* key) { return tree_iterator_search(itor, key); }
bool tr_itor_search_le(tr_itor* itor, const void* key) { return tree_iterator_search_le(itor, key); }
bool tr_itor_search_lt(tr_itor* itor, const void* key) { return tree_iterator_search_lt(itor, key); }
bool tr_itor_search_ge(tr_itor* itor, const void* key) { return tree_iterator_search_ge(itor, key); }
bool tr_itor_search_gt(tr_itor* itor, const void* key) { return tree_iterator_search_gt(itor, key); }
const void* tr_itor_key(const tr_itor* itor) { return tree_iterator_key(itor); }
void** tr_itor_datum(tr_itor* itor) { return tree_iterator_datum(itor); }
int tr_itor_compare(const tr_itor* i1, const tr_itor* i2) { return tree_iterator_compare(i1, i2); }
bool
tr_itor_remove(tr_itor* it)
{
if (!it->node)
return false;
remove_node(it->tree, it->node);
it->node = NULL;
return true;
}
+506
View File
@@ -0,0 +1,506 @@
/*
* libdict - common definitions for binary search trees.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
#include "tree_common.h"
#include <string.h>
#include "dict_private.h"
typedef struct tree_node {
TREE_NODE_FIELDS(struct tree_node);
} tree_node;
typedef struct {
TREE_FIELDS(tree_node);
} tree;
typedef struct {
TREE_ITERATOR_FIELDS(tree, tree_node);
} tree_iterator;
void
tree_node_rot_left(void* Tree, void* Node)
{
tree_node* const n = Node;
tree_node* const nr = n->rlink;
ASSERT(nr != NULL);
if ((n->rlink = nr->llink) != NULL)
n->rlink->parent = n;
nr->llink = n;
tree_node* p = n->parent;
n->parent = nr;
nr->parent = p;
*(p == NULL ? &((tree*)Tree)->root : p->llink == n ? &p->llink : &p->rlink) = nr;
}
void
tree_node_rot_right(void* Tree, void* Node)
{
tree_node* const n = Node;
tree_node* const nl = n->llink;
ASSERT(nl != NULL);
if ((n->llink = nl->rlink) != NULL)
n->llink->parent = n;
nl->rlink = n;
tree_node* const p = n->parent;
n->parent = nl;
nl->parent = p;
*(p == NULL ? &((tree*)Tree)->root : p->llink == n ? &p->llink : &p->rlink) = nl;
}
void*
tree_node_prev(void* Node)
{
tree_node* node = Node;
if (node->llink)
return tree_node_max(node->llink);
tree_node* parent = node->parent;
while (parent && parent->llink == node) {
node = parent;
parent = parent->parent;
}
return parent;
}
void*
tree_node_next(void* Node)
{
tree_node* node = Node;
if (node->rlink)
return tree_node_min(node->rlink);
tree_node* parent = node->parent;
while (parent && parent->rlink == node) {
node = parent;
parent = parent->parent;
}
return parent;
}
void*
tree_node_min(void* Node)
{
tree_node* node = Node;
if (!node)
return NULL;
while (node->llink)
node = node->llink;
return node;
}
void*
tree_node_max(void* Node)
{
tree_node* node = Node;
if (!node)
return NULL;
while (node->rlink)
node = node->rlink;
return node;
}
void*
tree_search_node(void* Tree, const void* key)
{
tree* t = Tree;
for (tree_node* node = t->root; node;) {
const int cmp = t->cmp_func(key, node->key);
if (cmp < 0)
node = node->llink;
else if (cmp)
node = node->rlink;
else
return node;
}
return NULL;
}
void**
tree_search(void* Tree, const void* key)
{
tree_node* node = tree_search_node(Tree, key);
return node ? &node->datum : NULL;
}
void*
tree_search_le_node(void* Tree, const void* key)
{
tree* t = Tree;
tree_node* node = t->root, *ret = NULL;
while (node) {
const int cmp = t->cmp_func(key, node->key);
if (cmp == 0)
return node;
if (cmp < 0) {
node = node->llink;
} else {
ret = node;
node = node->rlink;
}
}
return ret;
}
void**
tree_search_le(void* Tree, const void* key)
{
tree_node* node = tree_search_le_node(Tree, key);
return node ? &node->datum : NULL;
}
void*
tree_search_lt_node(void* Tree, const void* key)
{
tree* t = Tree;
tree_node* node = t->root, *ret = NULL;
while (node) {
const int cmp = t->cmp_func(key, node->key);
if (cmp <= 0) {
node = node->llink;
} else {
ret = node;
node = node->rlink;
}
}
return ret;
}
void**
tree_search_lt(void* Tree, const void* key)
{
tree_node* node = tree_search_lt_node(Tree, key);
return node ? &node->datum : NULL;
}
void*
tree_search_ge_node(void* Tree, const void* key)
{
tree* t = Tree;
tree_node* node = t->root, *ret = NULL;
while (node) {
const int cmp = t->cmp_func(key, node->key);
if (cmp == 0) {
return node;
}
if (cmp < 0) {
ret = node;
node = node->llink;
} else {
node = node->rlink;
}
}
return ret;
}
void**
tree_search_ge(void* Tree, const void* key)
{
tree_node* node = tree_search_ge_node(Tree, key);
return node ? &node->datum : NULL;
}
void*
tree_search_gt_node(void* Tree, const void* key)
{
tree* t = Tree;
tree_node* node = t->root, *ret = NULL;
while (node) {
const int cmp = t->cmp_func(key, node->key);
if (cmp < 0) {
ret = node;
node = node->llink;
} else {
node = node->rlink;
}
}
return ret;
}
void**
tree_search_gt(void* Tree, const void* key)
{
tree_node* node = tree_search_gt_node(Tree, key);
return node ? &node->datum : NULL;
}
size_t
tree_traverse(void* Tree, dict_visit_func visit, void* user_data)
{
ASSERT(visit != NULL);
tree* t = Tree;
size_t count = 0;
if (t->root) {
tree_node* node = tree_node_min(t->root);
do {
++count;
if (!visit(node->key, node->datum, user_data))
break;
node = tree_node_next(node);
} while (node);
}
return count;
}
bool
tree_select(void *Tree, size_t n, const void **key, void **datum)
{
tree* t = Tree;
if (n >= t->count) {
*key = NULL;
*datum = NULL;
return false;
}
tree_node* node;
if (n >= t->count / 2) {
node = tree_node_max(t->root);
n = t->count - 1 - n;
while (n--)
node = tree_node_prev(node);
} else {
node = tree_node_min(t->root);
while (n--)
node = tree_node_next(node);
}
*key = node->key;
*datum = node->datum;
return true;
}
size_t
tree_count(const void* Tree)
{
return ((const tree*)Tree)->count;
}
size_t
tree_clear(void* Tree, dict_delete_func delete_func)
{
tree* t = Tree;
const size_t count = t->count;
tree_node* node = t->root;
while (node) {
if (node->llink || node->rlink) {
node = node->llink ? node->llink : node->rlink;
continue;
}
if (delete_func)
delete_func(node->key, node->datum);
tree_node* const parent = node->parent;
FREE(node);
*(parent ? (parent->llink == node ? &parent->llink : &parent->rlink) : &t->root) = NULL;
node = parent;
}
ASSERT(t->root == NULL);
t->count = 0;
return count;
}
size_t
tree_free(void* Tree, dict_delete_func delete_func)
{
const size_t count = tree_clear(Tree, delete_func);
FREE(Tree);
return count;
}
static size_t
node_min_path_length(const tree_node* node)
{
size_t l = node->llink ? node_min_path_length(node->llink) : 0;
size_t r = node->rlink ? node_min_path_length(node->rlink) : 0;
return 1 + MIN(l, r);
}
size_t
tree_min_path_length(const void* Tree)
{
const tree* t = Tree;
return t->root ? node_min_path_length(t->root) : 0;
}
static size_t
node_max_path_length(const tree_node* node)
{
size_t l = node->llink ? node_max_path_length(node->llink) : 0;
size_t r = node->rlink ? node_max_path_length(node->rlink) : 0;
return 1 + MAX(l, r);
}
size_t
tree_max_path_length(const void* Tree)
{
const tree* t = Tree;
return t->root ? node_max_path_length(t->root) : 0;
}
static size_t
node_path_length(const tree_node* node, size_t level)
{
return level
+ (node->llink ? node_path_length(node->llink, level + 1) : 0)
+ (node->rlink ? node_path_length(node->rlink, level + 1) : 0);
}
size_t
tree_total_path_length(const void* Tree)
{
const tree* t = Tree;
return t->root ? node_path_length(t->root, 1) : 0;
}
bool
tree_iterator_valid(const void* Iterator)
{
return ((const tree_iterator*)Iterator)->node != NULL;
}
void
tree_iterator_invalidate(void* Iterator)
{
((tree_iterator*)Iterator)->node = NULL;
}
void
tree_iterator_free(void* Iterator)
{
tree_iterator* iterator = Iterator;
iterator->node = NULL;
FREE(iterator);
}
bool
tree_iterator_next(void* Iterator)
{
tree_iterator* iterator = Iterator;
return (iterator->node != NULL) && (iterator->node = tree_node_next(iterator->node)) != NULL;
}
bool
tree_iterator_prev(void* Iterator)
{
tree_iterator* iterator = Iterator;
return (iterator->node != NULL) && (iterator->node = tree_node_prev(iterator->node)) != NULL;
}
bool
tree_iterator_nextn(void* Iterator, size_t count)
{
tree_iterator* iterator = Iterator;
while (iterator->node && count--)
iterator->node = tree_node_next(iterator->node);
return iterator->node != NULL;
}
bool
tree_iterator_prevn(void* Iterator, size_t count)
{
tree_iterator* iterator = Iterator;
while (iterator->node && count--)
iterator->node = tree_node_prev(iterator->node);
return iterator->node != NULL;
}
bool
tree_iterator_first(void* Iterator)
{
tree_iterator* iterator = Iterator;
return (iterator->node = tree_node_min(iterator->tree->root)) != NULL;
}
bool
tree_iterator_last(void* Iterator)
{
tree_iterator* iterator = Iterator;
return (iterator->node = tree_node_max(iterator->tree->root)) != NULL;
}
bool
tree_iterator_search(void* Iterator, const void* key)
{
tree_iterator* iterator = Iterator;
return (iterator->node = tree_search_node(iterator->tree, key)) != NULL;
}
bool
tree_iterator_search_le(void* Iterator, const void* key)
{
tree_iterator* iterator = Iterator;
return (iterator->node = tree_search_le_node(iterator->tree, key)) != NULL;
}
bool
tree_iterator_search_lt(void* Iterator, const void* key)
{
tree_iterator* iterator = Iterator;
return (iterator->node = tree_search_lt_node(iterator->tree, key)) != NULL;
}
bool
tree_iterator_search_ge(void* Iterator, const void* key)
{
tree_iterator* iterator = Iterator;
return (iterator->node = tree_search_ge_node(iterator->tree, key)) != NULL;
}
bool
tree_iterator_search_gt(void* Iterator, const void* key)
{
tree_iterator* iterator = Iterator;
return (iterator->node = tree_search_gt_node(iterator->tree, key)) != NULL;
}
int
tree_iterator_compare(const void* Iterator1, const void* Iterator2)
{
const tree_iterator* itor1 = Iterator1;
const tree_iterator* itor2 = Iterator2;
ASSERT(itor1->tree == itor2->tree);
if (!itor1->node)
return !itor2->node ? 0 : -1;
if (!itor2->node)
return 1;
return itor1->tree->cmp_func(itor1->node->key, itor2->node->key);
}
const void*
tree_iterator_key(const void* Iterator)
{
const tree_iterator* iterator = Iterator;
return iterator->node ? iterator->node->key : NULL;
}
void**
tree_iterator_datum(void* Iterator)
{
tree_iterator* iterator = Iterator;
return iterator->node ? &iterator->node->datum : NULL;
}
+135
View File
@@ -0,0 +1,135 @@
/*
* libdict - common definitions for binary search trees.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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 LIBDICT_TREE_COMMON_H__
#define LIBDICT_TREE_COMMON_H__
#include "dict.h"
#define TREE_NODE_FIELDS(node_type) \
void* key; \
void* datum; \
node_type* parent; \
node_type* llink; \
node_type* rlink
typedef struct tree_node_base {
TREE_NODE_FIELDS(struct tree_node_base);
} tree_node_base;
#define TREE_FIELDS(node_type) \
node_type* root; \
size_t count; \
dict_compare_func cmp_func; \
size_t rotation_count
typedef struct tree_base {
TREE_FIELDS(struct tree_node_base);
} tree_base;
#define TREE_ITERATOR_FIELDS(tree_type, node_type) \
tree_type* tree; \
node_type* node
/* Rotate |node| left.
* |node| and |node->rlink| must not be NULL. */
void tree_node_rot_left(void *tree, void *node);
/* Rotate |node| right.
* |node| and |node->llink| must not be NULL. */
void tree_node_rot_right(void *tree, void *node);
/* Return the predecessor of |node|, or NULL if |node| has no predecessor.
* |node| must not be NULL. */
void* tree_node_prev(void *node);
/* Return the successor of |node|, or NULL if |node| has no successor.
* |node| must not be NULL. */
void* tree_node_next(void *node);
/* Return the left child of |node|, or |node| if it has no right child.
* |node| must not be NULL. */
void* tree_node_min(void *node);
/* Return the rightmost child of |node|, or |node| if it has no right child.
* |node| must not be NULL. */
void* tree_node_max(void *node);
/* Return the address of the data for the given the key, or NULL if not found. */
void** tree_search(void *tree, const void *key);
/* Return the node has the key, or NULL if not found. */
void* tree_search_node(void *tree, const void *key);
/* Return the data/node associated with the first key less than or
* equal to the specified key, or NULL if not found. */
void** tree_search_le(void *tree, const void *key);
void* tree_search_le_node(void *tree, const void *key);
/* Return the data/node associated with the first key less than the
* specified key, or NULL if not found. */
void** tree_search_lt(void *tree, const void *key);
void* tree_search_lt_node(void *tree, const void *key);
/* Return the data/node associated with the first key greater than or
* equal to the specified key, or NULL if not found. */
void** tree_search_ge(void *tree, const void *key);
void* tree_search_ge_node(void *tree, const void *key);
/* Return the data/node associated with the first key greater than the
* specified key, or NULL if not found. */
void** tree_search_gt(void *tree, const void *key);
void* tree_search_gt_node(void *tree, const void *key);
/* Traverses the tree in order, calling |visit| with each key and value pair,
* stopping if |visit| returns false. Returns the number of times |visit| was
* called. */
size_t tree_traverse(void *tree, dict_visit_func visit, void* user_data);
/* Put the key and datum of the |n|th element of |tree| into |key| and |datum|
* and return true, or, if n is greater than or equal to the number of elements,
* return false. */
bool tree_select(void *tree, size_t n, const void **key, void **datum);
/* Return a count of the elements in |tree|. */
size_t tree_count(const void *tree);
/* Remove all elements from |tree|. */
size_t tree_clear(void *tree, dict_delete_func delete_func);
/* Remove all elements from |tree| and free its memory. */
size_t tree_free(void *tree, dict_delete_func delete_func);
/* Returns the depth of the leaf with minimal depth, or 0 for an empty tree. */
size_t tree_min_path_length(const void *tree);
/* Returns the depth of the leaf with maximal depth, or 0 for an empty tree. */
size_t tree_max_path_length(const void *tree);
/* Returns the total path length of the tree. */
size_t tree_total_path_length(const void *tree);
bool tree_iterator_valid(const void *iterator);
void tree_iterator_invalidate(void *iterator);
void tree_iterator_free(void *iterator);
bool tree_iterator_next(void *iterator);
bool tree_iterator_prev(void *iterator);
bool tree_iterator_nextn(void *iterator, size_t count);
bool tree_iterator_prevn(void *iterator, size_t count);
bool tree_iterator_first(void *iterator);
bool tree_iterator_last(void *iterator);
bool tree_iterator_search(void *iterator, const void *key);
bool tree_iterator_search_le(void *iterator, const void *key);
bool tree_iterator_search_lt(void *iterator, const void *key);
bool tree_iterator_search_ge(void *iterator, const void *key);
bool tree_iterator_search_gt(void *iterator, const void *key);
int tree_iterator_compare(const void* iterator1, const void* iterator2);
const void* tree_iterator_key(const void *iterator);
void** tree_iterator_datum(void *iterator);
#endif /* !LIBDICT_TREE_COMMON_H__ */
+475
View File
@@ -0,0 +1,475 @@
/*
* libdict -- weight-balanced tree implementation.
*
* Copyright (c) 2001-2014, Farooq Mela
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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.
*/
/*
* cf. [Gonnet 1984], [Nievergelt and Reingold 1973]
*/
#include "wb_tree.h"
#include <limits.h>
#include "dict_private.h"
#include "tree_common.h"
/* A tree BB[alpha] is said to be of weighted balance alpha if every node in
* the tree has a balance p(n) such that alpha <= p(n) <= 1 - alpha. The
* balance of a node is defined as the number of nodes in its left subtree
* divided by the number of nodes in either subtree. The weight of a node is
* defined as the number of external nodes in its subtrees.
*
* Legal values for alpha are 0 <= alpha <= 1/2. BB[0] is a normal, unbalanced
* binary tree, and BB[1/2] includes only completely balanced binary search
* trees of 2^height - 1 nodes. A higher value of alpha specifies a more
* stringent balance requirement.
*
* Values for alpha in the range 2/11 <= alpha <= 1 - sqrt(2)/2 are interesting
* because a tree can be brought back into weighted balance after an insertion or
* deletion using at most one rotation per level (thus the number of rotations
* after insertion or deletion is O(lg N)).
*
* These are the parameters for alpha = 1 - sqrt(2)/2 == .292893, as
* recommended in [Gonnet 1984]. */
/* These constants are approximated by integer fractions in the code to
* eliminate floating point arithmetic. */
#if 0
# define ALPHA_0 .292893f /* 1 - sqrt(2)/2 */
# define ALPHA_1 .707106f /* sqrt(2)/2 */
# define ALPHA_2 .414213f /* sqrt(2) - 1 */
# define ALPHA_3 .585786f /* 2 - sqrt(2) */
#endif
typedef struct wb_node wb_node;
struct wb_node {
TREE_NODE_FIELDS(wb_node);
uint32_t weight;
};
#define WEIGHT(n) ((n) ? (n)->weight : 1U)
struct wb_tree {
TREE_FIELDS(wb_node);
};
struct wb_itor {
TREE_ITERATOR_FIELDS(wb_tree, wb_node);
};
static const dict_vtable wb_tree_vtable = {
true,
(dict_inew_func) wb_dict_itor_new,
(dict_dfree_func) tree_free,
(dict_insert_func) wb_tree_insert,
(dict_search_func) tree_search,
(dict_search_func) tree_search_le,
(dict_search_func) tree_search_lt,
(dict_search_func) tree_search_ge,
(dict_search_func) tree_search_gt,
(dict_remove_func) wb_tree_remove,
(dict_clear_func) tree_clear,
(dict_traverse_func) tree_traverse,
(dict_select_func) wb_tree_select,
(dict_count_func) tree_count,
(dict_verify_func) wb_tree_verify,
};
static const itor_vtable wb_tree_itor_vtable = {
(dict_ifree_func) tree_iterator_free,
(dict_valid_func) tree_iterator_valid,
(dict_invalidate_func) tree_iterator_invalidate,
(dict_next_func) tree_iterator_next,
(dict_prev_func) tree_iterator_prev,
(dict_nextn_func) tree_iterator_nextn,
(dict_prevn_func) tree_iterator_prevn,
(dict_first_func) tree_iterator_first,
(dict_last_func) tree_iterator_last,
(dict_key_func) tree_iterator_key,
(dict_datum_func) tree_iterator_datum,
(dict_isearch_func) tree_iterator_search,
(dict_isearch_func) tree_iterator_search_le,
(dict_isearch_func) tree_iterator_search_lt,
(dict_isearch_func) tree_iterator_search_ge,
(dict_isearch_func) tree_iterator_search_gt,
(dict_iremove_func) wb_itor_remove,
(dict_icompare_func) tree_iterator_compare,
};
static wb_node* node_new(void* key);
wb_tree*
wb_tree_new(dict_compare_func cmp_func)
{
ASSERT(cmp_func != NULL);
wb_tree* tree = MALLOC(sizeof(*tree));
if (tree) {
tree->root = NULL;
tree->count = 0;
tree->cmp_func = cmp_func;
tree->rotation_count = 0;
}
return tree;
}
dict*
wb_dict_new(dict_compare_func cmp_func)
{
dict* dct = MALLOC(sizeof(*dct));
if (dct) {
if (!(dct->_object = wb_tree_new(cmp_func))) {
FREE(dct);
return NULL;
}
dct->_vtable = &wb_tree_vtable;
}
return dct;
}
void** wb_tree_search(wb_tree* tree, const void* key) { return tree_search(tree, key); }
void** wb_tree_search_le(wb_tree* tree, const void* key) { return tree_search_le(tree, key); }
void** wb_tree_search_lt(wb_tree* tree, const void* key) { return tree_search_lt(tree, key); }
void** wb_tree_search_ge(wb_tree* tree, const void* key) { return tree_search_ge(tree, key); }
void** wb_tree_search_gt(wb_tree* tree, const void* key) { return tree_search_gt(tree, key); }
static inline unsigned
fixup(wb_tree* tree, wb_node* n)
{
unsigned rotations = 0;
unsigned weight = WEIGHT(n->llink);
if (weight * 1000U < n->weight * 293U) {
wb_node* nr = n->rlink;
ASSERT(nr != NULL);
wb_node* nrl = nr->llink;
if (WEIGHT(nrl) * 1000U < nr->weight * 586U) { /* LL */
/* Rotate |n| left. */
tree_node_rot_left(tree, n);
nr->weight = (n->weight = WEIGHT(n->llink) + WEIGHT(n->rlink)) +
WEIGHT(nr->rlink);
rotations += 1;
} else { /* RL */
/* Rotate |nr| right, then |n| left. */
ASSERT(nrl != NULL);
wb_node* const p = n->parent;
nrl->parent = p;
*(p ? (p->llink == n ? &p->llink : &p->rlink) : &tree->root) = nrl;
wb_node* const a = nrl->llink;
nrl->llink = n;
n->parent = nrl;
if ((n->rlink = a) != NULL)
a->parent = n;
wb_node* const b = nrl->rlink;
nrl->rlink = nr;
nr->parent = nrl;
if ((nr->llink = b) != NULL)
b->parent = nr;
nrl->weight = (n->weight = WEIGHT(n->llink) + WEIGHT(a)) +
(nr->weight = WEIGHT(b) + WEIGHT(nr->rlink));
rotations += 2;
}
} else if (weight * 1000U > n->weight * 707U) {
wb_node* nl = n->llink;
ASSERT(nl != NULL);
weight = WEIGHT(nl->llink);
if (weight * 1000U > nl->weight * 414U) { /* RR */
tree_node_rot_right(tree, n);
n->weight = WEIGHT(n->llink) + WEIGHT(n->rlink);
nl->weight = weight + n->weight;
rotations += 1;
} else { /* LR */
/* Rotate |nl| left, then |n| right. */
wb_node* nlr = nl->rlink;
ASSERT(nlr != NULL);
wb_node* const p = n->parent;
nlr->parent = p;
*(p ? (p->llink == n ? &p->llink : &p->rlink) : &tree->root) = nlr;
wb_node* const a = nlr->llink;
nlr->llink = nl;
nl->parent = nlr;
if ((nl->rlink = a) != NULL)
a->parent = nl;
wb_node* const b = nlr->rlink;
nlr->rlink = n;
n->parent = nlr;
if ((n->llink = b) != NULL)
b->parent = n;
nlr->weight = (n->weight = WEIGHT(b) + WEIGHT(n->rlink)) +
(nl->weight = WEIGHT(nl->llink) + WEIGHT(a));
rotations += 2;
}
}
return rotations;
}
dict_insert_result
wb_tree_insert(wb_tree* tree, void* key)
{
int cmp = 0;
wb_node* node = tree->root;
wb_node* parent = NULL;
while (node) {
cmp = tree->cmp_func(key, node->key);
if (cmp < 0) {
parent = node; node = node->llink;
} else if (cmp) {
parent = node; node = node->rlink;
} else
return (dict_insert_result) { &node->datum, false };
}
wb_node* const add = node = node_new(key);
if (!add)
return (dict_insert_result) { NULL, false };
if (!(node->parent = parent)) {
ASSERT(tree->count == 0);
ASSERT(tree->root == NULL);
tree->root = node;
} else {
if (cmp < 0)
parent->llink = node;
else
parent->rlink = node;
unsigned rotations = 0;
while ((node = parent) != NULL) {
parent = node->parent;
++node->weight;
rotations += fixup(tree, node);
}
tree->rotation_count += rotations;
}
tree->count++;
return (dict_insert_result) { &add->datum, true };
}
static void
remove_node(wb_tree* tree, wb_node* node)
{
if (node->llink && node->rlink) {
wb_node* out;
if (node->llink->weight > node->rlink->weight) {
out = tree_node_max(node->llink);
} else {
out = tree_node_min(node->rlink);
}
void* tmp;
SWAP(node->key, out->key, tmp);
SWAP(node->datum, out->datum, tmp);
node = out;
}
ASSERT(!node->llink || !node->rlink);
/* Splice in the successor, if any. */
wb_node* child = node->llink ? node->llink : node->rlink;
wb_node* parent = node->parent;
if (child)
child->parent = parent;
*(parent ? (parent->llink == node ? &parent->llink : &parent->rlink) : &tree->root) = child;
FREE(node);
tree->count--;
/* Now move up the tree, decrementing weights. */
unsigned rotations = 0;
while (parent) {
--parent->weight;
wb_node* up = parent->parent;
rotations += fixup(tree, parent);
parent = up;
}
tree->rotation_count += rotations;
}
dict_remove_result
wb_tree_remove(wb_tree* tree, const void* key)
{
wb_node* node = tree_search_node(tree, key);
if (!node)
return (dict_remove_result) { NULL, NULL, false };
dict_remove_result result = { node->key, node->datum, true };
remove_node(tree, node);
return result;
}
size_t wb_tree_free(wb_tree* tree, dict_delete_func delete_func) { return tree_free(tree, delete_func); }
size_t wb_tree_clear(wb_tree* tree, dict_delete_func delete_func) { return tree_clear(tree, delete_func); }
size_t wb_tree_traverse(wb_tree* tree, dict_visit_func visit, void* user_data) { return tree_traverse(tree, visit, user_data); }
bool
wb_tree_select(wb_tree* tree, size_t n, const void** key, void** datum)
{
if (n >= tree->count) {
if (key)
*key = NULL;
if (datum)
*datum = NULL;
return false;
}
wb_node* node = tree->root;
for (;;) {
const unsigned nw = WEIGHT(node->llink);
if (n + 1 >= nw) {
if (n + 1 == nw) {
if (key)
*key = node->key;
if (datum)
*datum = node->datum;
return true;
}
n -= nw;
node = node->rlink;
} else {
node = node->llink;
}
}
}
size_t wb_tree_count(const wb_tree* tree) { return tree_count(tree); }
size_t wb_tree_min_path_length(const wb_tree* tree) { return tree_min_path_length(tree); }
size_t wb_tree_max_path_length(const wb_tree* tree) { return tree_max_path_length(tree); }
size_t wb_tree_total_path_length(const wb_tree* tree) { return tree_total_path_length(tree); }
static wb_node*
node_new(void* key)
{
wb_node* node = MALLOC(sizeof(*node));
if (node) {
node->key = key;
node->datum = NULL;
node->parent = NULL;
node->llink = NULL;
node->rlink = NULL;
node->weight = 2;
}
return node;
}
static bool
node_verify(const wb_tree* tree, const wb_node* parent, const wb_node* node,
unsigned *weight)
{
if (!parent) {
VERIFY(tree->root == node);
} else {
VERIFY(parent->llink == node || parent->rlink == node);
}
if (node) {
VERIFY(node->parent == parent);
if (parent) {
if (parent->llink == node) {
VERIFY(tree->cmp_func(parent->key, node->key) > 0);
} else {
ASSERT(parent->rlink == node);
VERIFY(tree->cmp_func(parent->key, node->key) < 0);
}
}
unsigned lweight, rweight;
if (!node_verify(tree, node, node->llink, &lweight) ||
!node_verify(tree, node, node->rlink, &rweight))
return false;
VERIFY(WEIGHT(node->llink) == lweight);
VERIFY(WEIGHT(node->rlink) == rweight);
VERIFY(node->weight == lweight + rweight);
VERIFY(lweight * 1000U >= node->weight * 292U);
VERIFY(lweight * 1000U <= node->weight * 708U);
*weight = lweight + rweight;
} else {
*weight = 1;
}
return true;
}
bool
wb_tree_verify(const wb_tree* tree)
{
if (tree->root) {
VERIFY(tree->count > 0);
VERIFY(tree->count + 1 == tree->root->weight);
} else {
VERIFY(tree->count == 0);
}
unsigned root_weight;
return node_verify(tree, NULL, tree->root, &root_weight);
}
wb_itor*
wb_itor_new(wb_tree* tree)
{
wb_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
itor->tree = tree;
itor->node = NULL;
}
return itor;
}
dict_itor*
wb_dict_itor_new(wb_tree* tree)
{
dict_itor* itor = MALLOC(sizeof(*itor));
if (itor) {
if (!(itor->_itor = wb_itor_new(tree))) {
FREE(itor);
return NULL;
}
itor->_vtable = &wb_tree_itor_vtable;
}
return itor;
}
void wb_itor_free(wb_itor* itor) { tree_iterator_free(itor); }
bool wb_itor_valid(const wb_itor* itor) { return tree_iterator_valid(itor); }
void wb_itor_invalidate(wb_itor* itor) { tree_iterator_invalidate(itor); }
bool wb_itor_next(wb_itor* itor) { return tree_iterator_next(itor); }
bool wb_itor_prev(wb_itor* itor) { return tree_iterator_prev(itor); }
bool wb_itor_nextn(wb_itor* itor, size_t count) { return tree_iterator_nextn(itor, count); } /* TODO: speed up */
bool wb_itor_prevn(wb_itor* itor, size_t count) { return tree_iterator_prevn(itor, count); } /* TODO: speed up */
bool wb_itor_first(wb_itor* itor) { return tree_iterator_first(itor); }
bool wb_itor_last(wb_itor* itor) { return tree_iterator_last(itor); }
bool wb_itor_search(wb_itor* itor, const void* key) { return tree_iterator_search(itor, key); }
bool wb_itor_search_le(wb_itor* itor, const void* key) { return tree_iterator_search_le(itor, key); }
bool wb_itor_search_lt(wb_itor* itor, const void* key) { return tree_iterator_search_lt(itor, key); }
bool wb_itor_search_ge(wb_itor* itor, const void* key) { return tree_iterator_search_ge(itor, key); }
bool wb_itor_search_gt(wb_itor* itor, const void* key) { return tree_iterator_search_gt(itor, key); }
const void* wb_itor_key(const wb_itor* itor) { return tree_iterator_key(itor); }
void** wb_itor_datum(wb_itor* itor) { return tree_iterator_datum(itor); }
int wb_itor_compare(const wb_itor* i1, const wb_itor* i2) { return tree_iterator_compare(i1, i2); }
bool
wb_itor_remove(wb_itor* itor)
{
if (!itor->node)
return false;
remove_node(itor->tree, itor->node);
itor->node = NULL;
return true;
}
+816
View File
@@ -0,0 +1,816 @@
/* unit_tests.c
* Copyright (C) 2012 Farooq Mela. All rights reserved. */
#include <assert.h>
#include <float.h>
#include <limits.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
#include "dict.h"
#include "util.h"
#include "src/hashtable_common.h" /* For dict_prime_geq() */
#define TEST_FUNC(func) { (char *)#func, func }
struct key_info {
char *key, *value, *alt;
};
struct closest_lookup_info {
const char *key;
const char *le_key, *le_val;
const char *lt_key, *lt_val;
const char *ge_key, *ge_val;
const char *gt_key, *gt_val;
};
void test_basic(dict *dct, const struct key_info *keys, const unsigned nkeys, bool keys_sorted);
void test_basic_hashtable_1bucket(void);
void test_basic_hashtable2_1bucket(void);
void test_basic_hashtable_nbuckets(void);
void test_basic_hashtable2_nbuckets(void);
void test_basic_height_balanced_tree(void);
void test_basic_path_reduction_tree(void);
void test_basic_red_black_tree(void);
void test_basic_skiplist(void);
void test_basic_splay_tree(void);
void test_basic_treap(void);
void test_basic_weight_balanced_tree(void);
void test_search(dict *dct, dict_itor *itor, const char *key, const char *value);
void test_closest_lookup(dict *dct, unsigned nkeys, bool keys_sorted);
void test_primes_geq(void);
void test_version_string(void);
static CU_TestInfo basic_tests[] = {
TEST_FUNC(test_basic_hashtable_1bucket),
TEST_FUNC(test_basic_hashtable2_1bucket),
TEST_FUNC(test_basic_hashtable_nbuckets),
TEST_FUNC(test_basic_hashtable2_nbuckets),
TEST_FUNC(test_basic_height_balanced_tree),
TEST_FUNC(test_basic_path_reduction_tree),
TEST_FUNC(test_basic_red_black_tree),
TEST_FUNC(test_basic_skiplist),
TEST_FUNC(test_basic_splay_tree),
TEST_FUNC(test_basic_treap),
TEST_FUNC(test_basic_weight_balanced_tree),
TEST_FUNC(test_primes_geq),
TEST_FUNC(test_version_string),
CU_TEST_INFO_NULL
};
#define TEST_SUITE(suite) { .pName = (char *)#suite, .pTests = suite }
static CU_SuiteInfo test_suites[] = {
TEST_SUITE(basic_tests),
CU_SUITE_INFO_NULL
};
static size_t bytes_malloced = 0;
static void*
custom_malloc(size_t n)
{
size_t* p = malloc(sizeof(size_t) + n);
assert(p);
bytes_malloced += n;
p[0] = n;
return &p[1];
}
static void
custom_free(void* p)
{
assert(p);
size_t* sp = p;
bytes_malloced -= sp[-1];
free(sp - 1);
}
int
main()
{
dict_malloc_func = custom_malloc;
dict_free_func = custom_free;
if (CU_initialize_registry() != CUE_SUCCESS ||
CU_register_suites(test_suites) != CUE_SUCCESS) {
fprintf(stderr, "Failed to initialize tests!\n");
exit(EXIT_FAILURE);
}
CU_basic_set_mode(CU_BRM_SILENT);
if (CU_basic_run_tests() != CUE_SUCCESS) {
fprintf(stderr, "Failed to run tests!\n");
exit(EXIT_FAILURE);
}
unsigned failures = 0;
for (const CU_FailureRecord *f = CU_get_failure_list(); f; f = f->pNext) {
fprintf(stderr, "%u: %s (%s:%u): %s\n",
++failures, f->pTest->pName, f->strFileName, f->uiLineNumber, f->strCondition);
}
CU_cleanup_registry();
return failures ? EXIT_FAILURE : EXIT_SUCCESS;
}
static const struct key_info unsorted_keys[] = {
{ "d", "D", "d" },
{ "b", "B", "b" },
{ "a", "A", "a" },
{ "c", "C", "c" },
{ "g", "G", "g" },
{ "f", "F", "f" },
{ "h", "H", "h" },
{ "y", "Y", "y" },
{ "z", "Z", "z" },
{ "x", "X", "x" },
{ "j", "J", "j" },
{ "r", "R", "r" },
{ "q", "Q", "q" },
{ "p", "P", "p" },
{ "l", "L", "l" },
{ "m", "M", "m" },
{ "s", "S", "s" },
{ "t", "T", "t" },
{ "u", "U", "u" },
{ "da", "DA", "da" },
{ "ba", "BA", "ba" },
{ "aa", "AA", "aa" },
{ "ca", "CA", "ca" },
{ "ga", "GA", "ga" },
{ "fa", "FA", "fa" },
{ "ha", "HA", "ha" },
{ "ya", "YA", "ya" },
{ "za", "ZA", "za" },
{ "xa", "XA", "xa" },
{ "ja", "JA", "ja" },
{ "ra", "RA", "ra" },
{ "qa", "QA", "qa" },
{ "pa", "PA", "pa" },
{ "la", "LA", "la" },
{ "ma", "MA", "ma" },
{ "sa", "SA", "sa" },
{ "ta", "TA", "ta" },
{ "ua", "UA", "ua" },
};
#define NUM_UNSORTED_KEYS (sizeof(unsorted_keys) / sizeof(unsorted_keys[0]))
static const struct key_info sorted_keys[] = {
{ "a", "A", "a" },
{ "aa", "AA", "aa" },
{ "b", "B", "b" },
{ "ba", "BA", "ba" },
{ "c", "C", "c" },
{ "ca", "CA", "ca" },
{ "d", "D", "d" },
{ "da", "DA", "da" },
{ "f", "F", "f" },
{ "fa", "FA", "fa" },
{ "g", "G", "g" },
{ "ga", "GA", "ga" },
{ "h", "H", "h" },
{ "ha", "HA", "ha" },
{ "j", "J", "j" },
{ "ja", "JA", "ja" },
{ "l", "L", "l" },
{ "la", "LA", "la" },
{ "m", "M", "m" },
{ "ma", "MA", "ma" },
{ "p", "P", "p" },
{ "pa", "PA", "pa" },
{ "q", "Q", "q" },
{ "qa", "QA", "qa" },
{ "r", "R", "r" },
{ "ra", "RA", "ra" },
{ "s", "S", "s" },
{ "sa", "SA", "sa" },
{ "t", "T", "t" },
{ "ta", "TA", "ta" },
{ "u", "U", "u" },
{ "ua", "UA", "ua" },
{ "x", "X", "x" },
{ "xa", "XA", "xa" },
{ "y", "Y", "y" },
{ "ya", "YA", "ya" },
{ "z", "Z", "z" },
{ "za", "ZA", "za" },
};
#define NUM_SORTED_KEYS (sizeof(sorted_keys) / sizeof(sorted_keys[0]))
static_assert(NUM_SORTED_KEYS == NUM_UNSORTED_KEYS, "sorted keys and unsorted keys count mismatch");
static const struct closest_lookup_info closest_lookup_infos[] = {
{.key = "_",
.ge_key = "a", .ge_val = "A",
.gt_key = "a", .gt_val = "A"},
{.key = "a",
.le_key = "a", .le_val = "A",
.ge_key = "a", .ge_val = "A",
.gt_key = "aa", .gt_val = "AA"},
{.key = "aa",
.le_key = "aa", .le_val = "AA",
.lt_key = "a", .lt_val = "A",
.ge_key = "aa", .ge_val = "AA",
.gt_key = "b", .gt_val = "B"},
{.key = "ab",
.le_key = "aa", .le_val = "AA",
.lt_key = "aa", .lt_val = "AA",
.ge_key = "b", .ge_val = "B",
.gt_key = "b", .gt_val = "B"},
{.key = "m",
.le_key = "m", .le_val = "M",
.lt_key = "la", .lt_val = "LA",
.ge_key = "m", .ge_val = "M",
.gt_key = "ma", .gt_val = "MA"},
{.key = "n",
.le_key = "ma", .le_val = "MA",
.lt_key = "ma", .lt_val = "MA",
.ge_key = "p", .ge_val = "P",
.gt_key = "p", .gt_val = "P"},
{.key = "za",
.le_key = "za", .le_val = "ZA",
.lt_key = "z", .lt_val = "Z",
.ge_key = "za", .ge_val = "ZA"},
{.key = "zb",
.le_key = "za", .le_val = "ZA",
.lt_key = "za", .lt_val = "ZA"},
};
#define NUM_CLOSEST_LOOKUP_INFOS (sizeof(closest_lookup_infos) / sizeof(closest_lookup_infos[0]))
void
test_search(dict *dct, dict_itor *itor, const char *key, const char *value)
{
void **search = dict_search(dct, key);
if (value == NULL) {
CU_ASSERT_PTR_NULL(search);
} else {
CU_ASSERT_PTR_NOT_NULL(search);
CU_ASSERT_EQUAL(*search, value);
}
if (itor != NULL) {
if (value == NULL) {
CU_ASSERT_FALSE(dict_itor_search(itor, key));
CU_ASSERT_FALSE(dict_itor_valid(itor));
} else {
CU_ASSERT_TRUE(dict_itor_search(itor, key));
CU_ASSERT_TRUE(dict_itor_valid(itor));
CU_ASSERT_EQUAL(dict_itor_key(itor), key);
CU_ASSERT_PTR_NOT_NULL(dict_itor_datum(itor));
CU_ASSERT_EQUAL(*dict_itor_datum(itor), value);
}
}
}
void
test_closest_lookup(dict *dct, unsigned nkeys, bool keys_sorted)
{
if (dict_is_sorted(dct) && keys_sorted) {
for (unsigned i = 0; i < nkeys; ++i) {
const void* key = NULL;
void* datum = NULL;
if (dct->_vtable->select) {
CU_ASSERT_TRUE(dict_select(dct, i, &key, &datum));
CU_ASSERT_EQUAL(key, sorted_keys[i].key);
CU_ASSERT_EQUAL(datum, sorted_keys[i].value);
}
}
const void* key = NULL;
void* datum = NULL;
CU_ASSERT_FALSE(dict_select(dct, nkeys, &key, &datum));
CU_ASSERT_EQUAL(key, (const void*)NULL);
CU_ASSERT_PTR_NULL(datum);
}
dict_itor* itor = dict_itor_new(dct);
for (unsigned i = 0; i < NUM_CLOSEST_LOOKUP_INFOS; i++) {
const struct closest_lookup_info* const info = &closest_lookup_infos[i];
if (!dict_is_sorted(dct)) {
CU_ASSERT_PTR_NULL(dct->_vtable->search_le);
CU_ASSERT_PTR_NULL(dct->_vtable->search_lt);
CU_ASSERT_PTR_NULL(dct->_vtable->search_ge);
CU_ASSERT_PTR_NULL(dct->_vtable->search_gt);
CU_ASSERT_PTR_NULL(itor->_vtable->search_le);
CU_ASSERT_PTR_NULL(itor->_vtable->search_lt);
CU_ASSERT_PTR_NULL(itor->_vtable->search_ge);
CU_ASSERT_PTR_NULL(itor->_vtable->search_gt);
const void* key = NULL;
void* datum = NULL;
CU_ASSERT_FALSE(dict_select(dct, i, &key, &datum));
CU_ASSERT_EQUAL(key, (const void*)NULL);
CU_ASSERT_PTR_NULL(datum);
CU_ASSERT_PTR_NULL(dict_search_le(dct, info->key));
CU_ASSERT_PTR_NULL(dict_search_lt(dct, info->key));
CU_ASSERT_PTR_NULL(dict_search_ge(dct, info->key));
CU_ASSERT_PTR_NULL(dict_search_gt(dct, info->key));
continue;
}
if (nkeys < NUM_SORTED_KEYS)
continue;
if (info->le_key) {
if (dct->_vtable->search_le) {
CU_ASSERT_PTR_NOT_NULL(dict_search_le(dct, info->key));
void **datum = dict_search_le(dct, info->key);
CU_ASSERT_TRUE(datum != NULL && *datum != NULL && strcmp(*datum, info->le_val) == 0);
}
if (itor->_vtable->search_le) {
CU_ASSERT_TRUE(dict_itor_search_le(itor, info->key));
CU_ASSERT_TRUE(dict_itor_valid(itor));
CU_ASSERT_STRING_EQUAL(dict_itor_key(itor), info->le_key);
void **datum = dict_itor_datum(itor);
CU_ASSERT_TRUE(datum != NULL && *datum != NULL && strcmp(*datum, info->le_val) == 0);
}
} else {
CU_ASSERT_PTR_NULL(dict_search_le(dct, info->key));
CU_ASSERT_FALSE(dict_itor_search_le(itor, info->key));
CU_ASSERT_FALSE(dict_itor_valid(itor));
CU_ASSERT_PTR_NULL(dict_itor_key(itor));
CU_ASSERT_PTR_NULL(dict_itor_datum(itor));
}
if (info->lt_key) {
if (dct->_vtable->search_lt) {
CU_ASSERT_PTR_NOT_NULL(dict_search_lt(dct, info->key));
void** datum = dict_search_lt(dct, info->key);
CU_ASSERT_TRUE(datum != NULL && *datum != NULL && strcmp(*datum, info->lt_val) == 0);
}
if (itor->_vtable->search_lt) {
CU_ASSERT_TRUE(dict_itor_search_lt(itor, info->key));
CU_ASSERT_TRUE(dict_itor_valid(itor));
CU_ASSERT_STRING_EQUAL(dict_itor_key(itor), info->lt_key);
void** datum = dict_itor_datum(itor);
CU_ASSERT_TRUE(datum != NULL && *datum != NULL && strcmp(*datum, info->lt_val) == 0);
}
} else {
CU_ASSERT_PTR_NULL(dict_search_lt(dct, info->key));
CU_ASSERT_FALSE(dict_itor_search_lt(itor, info->key));
CU_ASSERT_FALSE(dict_itor_valid(itor));
CU_ASSERT_PTR_NULL(dict_itor_key(itor));
CU_ASSERT_PTR_NULL(dict_itor_datum(itor));
}
if (info->ge_key) {
if (dct->_vtable->search_ge) {
CU_ASSERT_PTR_NOT_NULL(dict_search_ge(dct, info->key));
void** datum = dict_search_ge(dct, info->key);
CU_ASSERT_TRUE(datum != NULL && *datum != NULL && strcmp(*datum, info->ge_val) == 0);
}
if (itor->_vtable->search_ge) {
CU_ASSERT_TRUE(dict_itor_search_ge(itor, info->key));
CU_ASSERT_TRUE(dict_itor_valid(itor));
CU_ASSERT_STRING_EQUAL(dict_itor_key(itor), info->ge_key);
void** datum = dict_itor_datum(itor);
CU_ASSERT_TRUE(datum != NULL && *datum != NULL && strcmp(*datum, info->ge_val) == 0);
}
} else {
CU_ASSERT_PTR_NULL(dict_search_ge(dct, info->key));
CU_ASSERT_FALSE(dict_itor_search_ge(itor, info->key));
CU_ASSERT_FALSE(dict_itor_valid(itor));
CU_ASSERT_PTR_NULL(dict_itor_key(itor));
CU_ASSERT_PTR_NULL(dict_itor_datum(itor));
}
if (info->gt_key) {
if (dct->_vtable->search_gt) {
CU_ASSERT_PTR_NOT_NULL(dict_search_gt(dct, info->key));
void** datum = dict_search_gt(dct, info->key);
CU_ASSERT_TRUE(datum != NULL && *datum != NULL && strcmp(*datum, info->gt_val) == 0);
}
if (itor->_vtable->search_gt) {
CU_ASSERT_TRUE(dict_itor_search_gt(itor, info->key));
CU_ASSERT_TRUE(dict_itor_valid(itor));
CU_ASSERT_STRING_EQUAL(dict_itor_key(itor), info->gt_key);
void** datum = dict_itor_datum(itor);
CU_ASSERT_TRUE(datum != NULL && *datum != NULL && strcmp(*datum, info->gt_val) == 0);
}
} else {
CU_ASSERT_PTR_NULL(dict_search_gt(dct, info->key));
CU_ASSERT_FALSE(dict_itor_search_gt(itor, info->key));
CU_ASSERT_FALSE(dict_itor_valid(itor));
CU_ASSERT_PTR_NULL(dict_itor_key(itor));
CU_ASSERT_PTR_NULL(dict_itor_datum(itor));
}
}
dict_itor_free(itor);
}
void test_basic(dict *dct, const struct key_info *keys, const unsigned nkeys, bool keys_sorted)
{
CU_ASSERT_TRUE(dict_verify(dct));
dict_itor *itor = dict_itor_new(dct);
CU_ASSERT_PTR_NOT_NULL(itor);
CU_ASSERT_FALSE(dict_itor_valid(itor));
CU_ASSERT_FALSE(dict_itor_next(itor));
CU_ASSERT_FALSE(dict_itor_valid(itor));
CU_ASSERT_FALSE(dict_itor_prev(itor));
CU_ASSERT_FALSE(dict_itor_valid(itor));
for (unsigned i = 0; i < nkeys; ++i) {
dict_insert_result result = dict_insert(dct, keys[i].key);
CU_ASSERT_TRUE(result.inserted);
CU_ASSERT_PTR_NOT_NULL(result.datum_ptr);
CU_ASSERT_PTR_NULL(*result.datum_ptr);
*result.datum_ptr = keys[i].value;
CU_ASSERT_TRUE(dict_verify(dct));
for (unsigned j = 0; j <= i; ++j)
test_search(dct, itor, keys[j].key, keys[j].value);
for (unsigned j = i + 1; j < nkeys; ++j)
test_search(dct, itor, keys[j].key, NULL);
}
CU_ASSERT_EQUAL(dict_count(dct), nkeys);
if (dct->_vtable->insert == (dict_insert_func)hashtable_insert ||
dct->_vtable->insert == (dict_insert_func)hashtable2_insert) {
/* Verify that hashtable_resize works as expected. */
if (dct->_vtable->insert == (dict_insert_func)hashtable_insert) {
CU_ASSERT_TRUE(hashtable_resize(dict_private(dct), 3));
} else {
CU_ASSERT_TRUE(hashtable2_resize(dict_private(dct),
dict_prime_geq(nkeys * 5)));
}
CU_ASSERT_TRUE(dict_verify(dct));
CU_ASSERT_EQUAL(dict_count(dct), nkeys);
for (unsigned j = 0; j < nkeys; ++j)
test_search(dct, NULL, keys[j].key, keys[j].value);
}
for (unsigned i = 0; i < nkeys; ++i)
test_search(dct, itor, keys[i].key, keys[i].value);
for (unsigned i = 0; i < nkeys; ++i) {
dict_insert_result result = dict_insert(dct, keys[i].key);
CU_ASSERT_FALSE(result.inserted);
CU_ASSERT_PTR_NOT_NULL(result.datum_ptr);
CU_ASSERT_EQUAL(*result.datum_ptr, keys[i].value);
CU_ASSERT_TRUE(dict_verify(dct));
}
CU_ASSERT_EQUAL(dict_count(dct), nkeys);
dict_itor *first = dict_itor_new(dct);
CU_ASSERT_PTR_NOT_NULL(first);
CU_ASSERT_FALSE(dict_itor_valid(first));
if (nkeys > 0) {
CU_ASSERT_TRUE(dict_itor_first(first));
CU_ASSERT_TRUE(dict_itor_valid(first));
} else {
CU_ASSERT_FALSE(dict_itor_first(first));
CU_ASSERT_FALSE(dict_itor_valid(first));
}
dict_itor *last = dict_itor_new(dct);
CU_ASSERT_PTR_NOT_NULL(last);
CU_ASSERT_FALSE(dict_itor_valid(last));
if (nkeys > 0) {
CU_ASSERT_TRUE(dict_itor_last(last));
CU_ASSERT_TRUE(dict_itor_valid(last));
} else {
CU_ASSERT_FALSE(dict_itor_last(last));
CU_ASSERT_FALSE(dict_itor_valid(last));
}
if (dict_is_sorted(dct)) {
if (nkeys <= 1) {
CU_ASSERT_TRUE(dict_itor_compare(first, last) == 0);
CU_ASSERT_TRUE(dict_itor_compare(last, first) == 0);
} else {
CU_ASSERT_TRUE(dict_itor_compare(first, last) < 0);
CU_ASSERT_TRUE(dict_itor_compare(last, first) > 0);
}
}
char *last_key = NULL;
unsigned n = 0;
for (dict_itor_first(itor); dict_itor_valid(itor); dict_itor_next(itor)) {
CU_ASSERT_PTR_NOT_NULL(dict_itor_key(itor));
CU_ASSERT_PTR_NOT_NULL(dict_itor_datum(itor));
CU_ASSERT_PTR_NOT_NULL(*dict_itor_datum(itor));
if (dict_is_sorted(dct)) {
CU_ASSERT_TRUE(dict_itor_compare(itor, itor) == 0);
if (n == 0) {
CU_ASSERT_TRUE(dict_itor_compare(itor, first) == 0);
CU_ASSERT_TRUE(dict_itor_compare(first, itor) == 0);
} else {
CU_ASSERT_TRUE(dict_itor_compare(itor, first) > 0);
CU_ASSERT_TRUE(dict_itor_compare(first, itor) < 0);
}
if (n == nkeys - 1) {
CU_ASSERT_TRUE(dict_itor_compare(itor, last) == 0);
CU_ASSERT_TRUE(dict_itor_compare(last, itor) == 0);
} else {
CU_ASSERT_TRUE(dict_itor_compare(itor, last) < 0);
CU_ASSERT_TRUE(dict_itor_compare(last, itor) > 0);
}
if (keys_sorted) {
CU_ASSERT_EQUAL(dict_itor_key(itor), keys[n].key);
CU_ASSERT_EQUAL(*dict_itor_datum(itor), keys[n].value);
}
}
unsigned keys_matched = 0;
for (unsigned i = 0; i < nkeys; ++i) {
if (dict_itor_key(itor) == keys[i].key) {
CU_ASSERT_EQUAL(*dict_itor_datum(itor), keys[i].value);
keys_matched++;
} else {
CU_ASSERT_NOT_EQUAL(*dict_itor_datum(itor), keys[i].value);
}
}
CU_ASSERT_EQUAL(keys_matched, 1);
if (dict_is_sorted(dct)) {
if (last_key) {
CU_ASSERT_TRUE(strcmp(last_key, dict_itor_key(itor)) < 0);
}
last_key = dict_itor_key(itor);
}
++n;
}
CU_ASSERT_EQUAL(n, nkeys);
last_key = NULL;
n = 0;
for (dict_itor_last(itor); dict_itor_valid(itor); dict_itor_prev(itor)) {
CU_ASSERT_PTR_NOT_NULL(dict_itor_key(itor));
CU_ASSERT_PTR_NOT_NULL(dict_itor_datum(itor));
CU_ASSERT_PTR_NOT_NULL(*dict_itor_datum(itor));
if (dict_is_sorted(dct) && keys_sorted) {
CU_ASSERT_EQUAL(dict_itor_key(itor), keys[nkeys - 1 - n].key);
CU_ASSERT_EQUAL(*dict_itor_datum(itor), keys[nkeys - 1 - n].value);
}
unsigned keys_matched = 0;
for (unsigned i = 0; i < nkeys; ++i) {
if (dict_itor_key(itor) == keys[i].key) {
CU_ASSERT_EQUAL(*dict_itor_datum(itor), keys[i].value);
keys_matched++;
} else {
CU_ASSERT_NOT_EQUAL(*dict_itor_datum(itor), keys[i].value);
}
}
CU_ASSERT_EQUAL(keys_matched, 1);
if (dict_is_sorted(dct)) {
if (last_key) {
CU_ASSERT_TRUE(strcmp(last_key, dict_itor_key(itor)) > 0);
}
last_key = dict_itor_key(itor);
}
++n;
}
CU_ASSERT_EQUAL(n, nkeys);
for (unsigned i = 0; i < nkeys; ++i) {
dict_insert_result result = dict_insert(dct, keys[i].key);
CU_ASSERT_FALSE(result.inserted);
CU_ASSERT_PTR_NOT_NULL(result.datum_ptr);
CU_ASSERT_PTR_NOT_NULL(*result.datum_ptr);
*result.datum_ptr = keys[i].alt;
CU_ASSERT_TRUE(dict_verify(dct));
}
CU_ASSERT_EQUAL(dict_count(dct), nkeys);
for (unsigned i = 0; i < nkeys; ++i)
test_search(dct, itor, keys[i].key, keys[i].alt);
for (unsigned i = 0; i < nkeys; ++i) {
test_search(dct, itor, keys[i].key, keys[i].alt);
dict_remove_result result = dict_remove(dct, keys[i].key);
CU_ASSERT_TRUE(result.removed);
CU_ASSERT_EQUAL(result.key, keys[i].key);
CU_ASSERT_EQUAL(result.datum, keys[i].alt);
CU_ASSERT_TRUE(dict_verify(dct));
result = dict_remove(dct, keys[i].key);
CU_ASSERT_FALSE(result.removed);
CU_ASSERT_PTR_NULL(result.key);
CU_ASSERT_PTR_NULL(result.datum);
for (unsigned j = 0; j <= i; ++j) {
test_search(dct, itor, keys[j].key, NULL);
}
for (unsigned j = i + 1; j < nkeys; ++j) {
test_search(dct, itor, keys[j].key, keys[j].alt);
}
}
for (unsigned i = 0; i < nkeys; ++i) {
dict_insert_result result = dict_insert(dct, keys[i].key);
CU_ASSERT_TRUE(result.inserted);
CU_ASSERT_PTR_NOT_NULL(result.datum_ptr);
CU_ASSERT_PTR_NULL(*result.datum_ptr);
*result.datum_ptr = keys[i].value;
CU_ASSERT_TRUE(dict_verify(dct));
}
CU_ASSERT_EQUAL(dict_count(dct), nkeys);
CU_ASSERT_EQUAL(dict_clear(dct, NULL), nkeys);
CU_ASSERT_TRUE(dict_verify(dct));
CU_ASSERT_EQUAL(dict_count(dct), 0);
for (unsigned i = 0; i < nkeys; ++i) {
test_search(dct, itor, keys[i].key, NULL);
dict_insert_result result = dict_insert(dct, keys[i].key);
CU_ASSERT_TRUE(result.inserted);
CU_ASSERT_PTR_NOT_NULL(result.datum_ptr);
CU_ASSERT_PTR_NULL(*result.datum_ptr);
*result.datum_ptr = keys[i].value;
CU_ASSERT_TRUE(dict_verify(dct));
}
CU_ASSERT_EQUAL(dict_count(dct), nkeys);
test_closest_lookup(dct, nkeys, keys_sorted);
for (unsigned i = 0; i < nkeys; ++i) {
CU_ASSERT_TRUE(dict_itor_search(itor, keys[i].key));
CU_ASSERT_TRUE(dict_itor_valid(itor));
CU_ASSERT_EQUAL(dict_itor_key(itor), keys[i].key);
CU_ASSERT_PTR_NOT_NULL(dict_itor_datum(itor));
CU_ASSERT_EQUAL(*dict_itor_datum(itor), keys[i].value);
CU_ASSERT_TRUE(dict_itor_remove(itor));
CU_ASSERT_FALSE(dict_itor_valid(itor));
CU_ASSERT_FALSE(dict_itor_search(itor, keys[i].key));
CU_ASSERT_FALSE(dict_itor_valid(itor));
}
CU_ASSERT_EQUAL(dict_count(dct), 0);
for (unsigned i = 0; i < nkeys; ++i) {
test_search(dct, itor, keys[i].key, NULL);
dict_insert_result result = dict_insert(dct, keys[i].key);
CU_ASSERT_TRUE(result.inserted);
CU_ASSERT_PTR_NOT_NULL(result.datum_ptr);
CU_ASSERT_PTR_NULL(*result.datum_ptr);
*result.datum_ptr = keys[i].value;
CU_ASSERT_TRUE(dict_verify(dct));
}
CU_ASSERT_EQUAL(dict_count(dct), nkeys);
dict_itor_free(itor);
dict_itor_free(first);
dict_itor_free(last);
CU_ASSERT_EQUAL(dict_count(dct), nkeys);
CU_ASSERT_EQUAL(dict_free(dct, NULL), nkeys);
}
void test_basic_hashtable_1bucket()
{
for (unsigned n = 0; n <= NUM_SORTED_KEYS; ++n) {
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(hashtable_dict_new(dict_str_cmp, dict_str_hash, 1), unsorted_keys, n, false);
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(hashtable_dict_new(dict_str_cmp, dict_str_hash, 1), sorted_keys, n, true);
CU_ASSERT_EQUAL(bytes_malloced, 0);
}
}
void test_basic_hashtable2_1bucket()
{
for (unsigned n = 0; n <= NUM_SORTED_KEYS; ++n) {
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(hashtable2_dict_new(dict_str_cmp, dict_str_hash, 1), unsorted_keys, n, false);
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(hashtable2_dict_new(dict_str_cmp, dict_str_hash, 1), sorted_keys, n, true);
CU_ASSERT_EQUAL(bytes_malloced, 0);
}
}
void test_basic_hashtable_nbuckets()
{
for (unsigned n = 0; n <= NUM_SORTED_KEYS; ++n) {
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(hashtable_dict_new(dict_str_cmp, dict_str_hash, 7), unsorted_keys, n, false);
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(hashtable_dict_new(dict_str_cmp, dict_str_hash, 7), sorted_keys, n, true);
CU_ASSERT_EQUAL(bytes_malloced, 0);
}
}
void test_basic_hashtable2_nbuckets()
{
for (unsigned n = 0; n <= NUM_SORTED_KEYS; ++n) {
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(hashtable2_dict_new(dict_str_cmp, dict_str_hash, 7), unsorted_keys, n, false);
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(hashtable2_dict_new(dict_str_cmp, dict_str_hash, 7), sorted_keys, n, true);
CU_ASSERT_EQUAL(bytes_malloced, 0);
}
}
void test_basic_height_balanced_tree()
{
for (unsigned n = 0; n <= NUM_SORTED_KEYS; ++n) {
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(hb_dict_new(dict_str_cmp), unsorted_keys, n, false);
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(hb_dict_new(dict_str_cmp), sorted_keys, n, true);
CU_ASSERT_EQUAL(bytes_malloced, 0);
}
}
void test_basic_path_reduction_tree()
{
for (unsigned n = 0; n <= NUM_SORTED_KEYS; ++n) {
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(pr_dict_new(dict_str_cmp), unsorted_keys, n, false);
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(pr_dict_new(dict_str_cmp), sorted_keys, n, true);
CU_ASSERT_EQUAL(bytes_malloced, 0);
}
}
void test_basic_red_black_tree()
{
for (unsigned n = 0; n <= NUM_SORTED_KEYS; ++n) {
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(rb_dict_new(dict_str_cmp), unsorted_keys, n, false);
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(rb_dict_new(dict_str_cmp), sorted_keys, n, true);
CU_ASSERT_EQUAL(bytes_malloced, 0);
}
}
void test_basic_skiplist()
{
for (unsigned n = 0; n <= NUM_SORTED_KEYS; ++n) {
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(skiplist_dict_new(dict_str_cmp, 13), unsorted_keys, n, false);
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(skiplist_dict_new(dict_str_cmp, 13), sorted_keys, n, true);
CU_ASSERT_EQUAL(bytes_malloced, 0);
}
}
void test_basic_splay_tree()
{
for (unsigned n = 0; n <= NUM_SORTED_KEYS; ++n) {
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(sp_dict_new(dict_str_cmp), unsorted_keys, n, false);
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(sp_dict_new(dict_str_cmp), sorted_keys, n, true);
CU_ASSERT_EQUAL(bytes_malloced, 0);
}
}
void test_basic_treap()
{
for (unsigned n = 0; n <= NUM_SORTED_KEYS; ++n) {
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(tr_dict_new(dict_str_cmp, NULL), unsorted_keys, n, false);
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(tr_dict_new(dict_str_cmp, NULL), sorted_keys, n, true);
CU_ASSERT_EQUAL(bytes_malloced, 0);
}
}
void test_basic_weight_balanced_tree()
{
for (unsigned n = 0; n <= NUM_SORTED_KEYS; ++n) {
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(wb_dict_new(dict_str_cmp), unsorted_keys, n, false);
CU_ASSERT_EQUAL(bytes_malloced, 0);
test_basic(wb_dict_new(dict_str_cmp), sorted_keys, n, true);
CU_ASSERT_EQUAL(bytes_malloced, 0);
}
}
void test_primes_geq()
{
CU_ASSERT_TRUE(is_prime(2));
CU_ASSERT_TRUE(is_prime(3));
CU_ASSERT_FALSE(is_prime(4));
CU_ASSERT_TRUE(is_prime(5));
CU_ASSERT_FALSE(is_prime(6));
CU_ASSERT_TRUE(is_prime(7));
unsigned value = 0;
do {
const unsigned prime_geq_value = dict_prime_geq(value+1);
CU_ASSERT_TRUE(prime_geq_value >= value+1);
CU_ASSERT_TRUE(is_prime(prime_geq_value));
CU_ASSERT_TRUE(dict_prime_geq(prime_geq_value) == prime_geq_value);
value = prime_geq_value;
} while (value != 4294967291U);
}
void test_version_string()
{
char version_string[32];
snprintf(version_string, sizeof(version_string), "%d.%d.%d",
DICT_VERSION_MAJOR, DICT_VERSION_MINOR, DICT_VERSION_PATCH);
CU_ASSERT_STRING_EQUAL(kDictVersionString, version_string);
}
+63
View File
@@ -0,0 +1,63 @@
#ifndef UTIL_H__
#define UTIL_H__
#include <assert.h>
void shuffle(char **p, size_t size);
bool is_prime(unsigned n);
bool next_permutation(unsigned a[], unsigned n);
void shuffle(char **p, size_t size)
{
for (size_t i = 0; i < size - 1; i++) {
size_t n = (size_t) rand() % (size - i);
char *t = p[i+n]; p[i+n] = p[i]; p[i] = t;
}
}
bool is_prime(unsigned n)
{
if (n <= 0)
return false;
if (n <= 3)
return true;
if (!(n & 1))
return false;
for (unsigned f = 3, f2 = f * f;;) {
if (f2 >= n) {
if (f2 == n)
return false;
return true;
}
if (n % f == 0)
return false;
if (f2 + (4 * f + 4) < f2)
return true; /* Overflow */
f2 += 4 * f + 4;
f += 2;
}
}
bool next_permutation(unsigned a[], unsigned n)
{
if (n <= 1)
return false;
unsigned i = n-2;
while (!(a[i] < a[i+1]))
if (i-- == 0)
return false;
assert(a[i] < a[i+1]);
unsigned j = n-1;
while (!(a[j] > a[i]))
--j;
assert(j > i);
assert(a[j] > a[i]);
unsigned t = a[i]; a[i] = a[j]; a[j] = t;
for (++i, j=n-1; i < j; ++i, --j) {
t = a[i]; a[i] = a[j]; a[j] = t;
}
return true;
}
#endif /* !UTIL_H__ */
+12
View File
@@ -0,0 +1,12 @@
#!/bin/sh
if ! which valgrind >/dev/null; then
echo "`basename $0`: valgrind not found." >&2
exit 1
fi
valgrind --tool=memcheck --trace-children=yes \
--track-fds=yes --num-callers=32 \
--memcheck:leak-check=yes --memcheck:leak-resolution=high \
--show-reachable=yes \
"$@" 2>&1 | tee vg.log
+1 -1
View File
@@ -9,7 +9,7 @@ foreach(_file ${LSPGEN_SOURCES})
set_property(SOURCE ${_file} APPEND_STRING PROPERTY COMPILE_FLAGS "-frandom-seed=0x${checksum}")
endforeach()
add_executable(lspgen ${COMMON_SOURCES} ${LSPGEN_SOURCES})
add_executable(lspgen ${LIBDICT_SOURCES} ${COMMON_SOURCES} ${LSPGEN_SOURCES})
target_link_libraries(lspgen crypto jansson ${libdict} m)
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 8.0)
+1 -2
View File
@@ -6,8 +6,7 @@
* Copyright (C) 2015-2022, RtBrick, Inc.
*/
#include <signal.h>
#include "libdict/dict.h"
#include <dict.h>
#include "lspgen.h"
#include "lspgen_lsdb.h"
+1 -2
View File
@@ -24,8 +24,7 @@
#include <sys/un.h>
#include <arpa/inet.h>
#include "libdict/dict.h"
#include <dict.h>
#include <common_include.h>
#include "lspgen_lsdb.h"
+2 -16
View File
@@ -33,25 +33,11 @@ Build from Sources
Dependencies
^^^^^^^^^^^^
The BNG Blaster has dependencies on the RtBrick
`libdict fork <https://github.com/rtbrick/libdict>`_
and the following standard dependencies:
The BNG Blaster has the following dependcancies:
.. code-block:: none
# install libdict for Ubuntu 18.04 LTS
wget https://github.com/rtbrick/libdict/releases/download/v1.0.1/libdict-debian.zip
unzip libdict-debian.zip
sudo dpkg -i libdict_1.0.1_amd64.deb
sudo dpkg -i libdict-dev_1.0.1_amd64.deb
# install libdict for Ubuntu 22.04 LTS
wget https://github.com/rtbrick/libdict/releases/download/1.0.3/libdict-ubuntu-22.04.zip
unzip libdict-ubuntu-22.04.zip
sudo dpkg -i libdict_1.0.3_amd64.deb
sudo dpkg -i libdict-dev_1.0.3_amd64.deb
# install other dependencies
# install dependencies
sudo apt install -y cmake \
libpcap-dev \
libcunit1-dev \