mirror of
https://github.com/rtbrick/bngblaster.git
synced 2024-05-06 15:54:57 +00:00
add libdict to repo
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
@@ -34,8 +34,7 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <common.h>
|
||||
|
||||
#include "libdict/dict.h"
|
||||
#include <dict.h>
|
||||
|
||||
/* LwIP */
|
||||
#ifdef BNGBLASTER_LWIP
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
BasedOnStyle: None
|
||||
@@ -0,0 +1,9 @@
|
||||
words-*
|
||||
shuffle.pl
|
||||
bin/*
|
||||
build/
|
||||
*.log
|
||||
*.pdf
|
||||
*.aux
|
||||
.*.swp
|
||||
*.pdf
|
||||
@@ -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
@@ -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()
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
@@ -0,0 +1,62 @@
|
||||
# libdict
|
||||
|
||||
[](https://github.com/rtbrick/libdict/actions/workflows/test.yml)
|
||||
[](https://github.com/rtbrick/libdict/releases)
|
||||
[](https://rtbrick.github.io/libdict/html)
|
||||
[](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).
|
||||
@@ -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
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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__ */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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__ */
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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__ */
|
||||
Executable
+12
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 \
|
||||
|
||||
Reference in New Issue
Block a user