Compare commits
41 commits
main
...
gburd/add-
Author | SHA1 | Date | |
---|---|---|---|
bbdbc7eb12 | |||
1798225efb | |||
c8f3e9c9d6 | |||
3fc9bcc24b | |||
e3dc5cb9d1 | |||
061f566264 | |||
b98111b7df | |||
7b71e55f76 | |||
9817b78013 | |||
ca107d889a | |||
4af769e4ef | |||
8dcf0ab311 | |||
919f595d29 | |||
eb6367f950 | |||
98c802a169 | |||
3b1f4babd3 | |||
76048df2f6 | |||
339c9b7351 | |||
e5906249b4 | |||
b032d61b63 | |||
e4b21f3f0f | |||
d5c0f9abbc | |||
c19fecde2e | |||
40951e37e6 | |||
bf3840e183 | |||
abf73dd7b9 | |||
e36cf1c2b2 | |||
1825f0d43f | |||
a0613a1ffb | |||
e85b39ad63 | |||
b78e706496 | |||
30421d8bfe | |||
020df515b6 | |||
90c70298d2 | |||
b498607e84 | |||
50b88a7e7c | |||
b0280271df | |||
dfda0017d5 | |||
99035d8a0f | |||
d99b1ac98d | |||
5ab1579123 |
31 changed files with 5053 additions and 2246 deletions
32
.editorconfig
Normal file
32
.editorconfig
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
# Editor configuration, see http://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
|
||||||
|
# Ignore diffs/patches
|
||||||
|
[*.{diff,patch}]
|
||||||
|
end_of_line = unset
|
||||||
|
insert_final_newline = unset
|
||||||
|
trim_trailing_whitespace = unset
|
||||||
|
indent_size = unset
|
||||||
|
|
||||||
|
[{.*,secrets}/**]
|
||||||
|
end_of_line = unset
|
||||||
|
insert_final_newline = unset
|
||||||
|
trim_trailing_whitespace = unset
|
||||||
|
charset = unset
|
||||||
|
indent_style = unset
|
||||||
|
indent_size = unset
|
||||||
|
|
||||||
|
[*.py]
|
||||||
|
indent_size = 4
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
max_line_length = off
|
||||||
|
trim_trailing_whitespace = false
|
|
@ -3,5 +3,8 @@
|
||||||
<clangFormatSettings>
|
<clangFormatSettings>
|
||||||
<option name="ENABLED" value="true" />
|
<option name="ENABLED" value="true" />
|
||||||
</clangFormatSettings>
|
</clangFormatSettings>
|
||||||
|
<editorconfig>
|
||||||
|
<option name="ENABLED" value="false" />
|
||||||
|
</editorconfig>
|
||||||
</code_scheme>
|
</code_scheme>
|
||||||
</component>
|
</component>
|
|
@ -1,15 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CLionExternalBuildManager">
|
|
||||||
<target id="db0ccaeb-4851-470b-83d0-afa663f6ceb9" name="tests/soak" defaultType="MAKE">
|
|
||||||
<configuration id="98973a90-a9d0-431b-9071-9ce6960b0b01" name="tests/soak">
|
|
||||||
<build type="MAKE">
|
|
||||||
<make targetName="tests/soak" />
|
|
||||||
</build>
|
|
||||||
<clean type="MAKE">
|
|
||||||
<make targetName="clean" />
|
|
||||||
</clean>
|
|
||||||
</configuration>
|
|
||||||
</target>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -1,25 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="MakefileBuildTargetsManager">
|
|
||||||
<user-build-targets>
|
|
||||||
<build-target name="tests/soak">
|
|
||||||
<build-configurations>
|
|
||||||
<build-configuration>
|
|
||||||
<make-targets>
|
|
||||||
<make-target>tests/soak</make-target>
|
|
||||||
</make-targets>
|
|
||||||
</build-configuration>
|
|
||||||
</build-configurations>
|
|
||||||
</build-target>
|
|
||||||
<build-target name="clean">
|
|
||||||
<build-configurations>
|
|
||||||
<build-configuration>
|
|
||||||
<make-targets>
|
|
||||||
<make-target>clean</make-target>
|
|
||||||
</make-targets>
|
|
||||||
</build-configuration>
|
|
||||||
</build-configurations>
|
|
||||||
</build-target>
|
|
||||||
</user-build-targets>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
|
@ -4,28 +4,8 @@
|
||||||
<option name="executionMode" value="BINARY" />
|
<option name="executionMode" value="BINARY" />
|
||||||
<option name="pathToExecutable" value="$USER_HOME$/.nix-profile/bin/black" />
|
<option name="pathToExecutable" value="$USER_HOME$/.nix-profile/bin/black" />
|
||||||
</component>
|
</component>
|
||||||
<component name="CidrRootsConfiguration">
|
<component name="CMakePythonSetting">
|
||||||
<sourceRoots>
|
<option name="pythonIntegrationState" value="YES" />
|
||||||
<file path="$PROJECT_DIR$/examples" />
|
|
||||||
<file path="$PROJECT_DIR$/include" />
|
|
||||||
<file path="$PROJECT_DIR$/lib" />
|
|
||||||
<file path="$PROJECT_DIR$/src" />
|
|
||||||
<file path="$PROJECT_DIR$/tests" />
|
|
||||||
</sourceRoots>
|
|
||||||
</component>
|
</component>
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="CMakeWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
||||||
<component name="MakefileSettings">
|
|
||||||
<option name="linkedExternalProjectsSettings">
|
|
||||||
<MakefileProjectSettings>
|
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
|
||||||
<option name="modules">
|
|
||||||
<set>
|
|
||||||
<option value="$PROJECT_DIR$" />
|
|
||||||
</set>
|
|
||||||
</option>
|
|
||||||
<option name="version" value="2" />
|
|
||||||
</MakefileProjectSettings>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
<component name="MakefileWorkspace" PROJECT_DIR="$PROJECT_DIR$" />
|
|
||||||
</project>
|
</project>
|
|
@ -11,13 +11,12 @@ set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
set(CMAKE_C_OUTPUT_EXTENSION .o)
|
set(CMAKE_C_OUTPUT_EXTENSION .o)
|
||||||
|
|
||||||
# Set source and header file locations
|
set(SOURCE_DIR .)
|
||||||
set(SOURCE_DIR src)
|
set(HEADER_DIR . test)
|
||||||
set(HEADER_DIR include)
|
|
||||||
|
|
||||||
set(COMMON_CMAKE_C_FLAGS "-Wall -Wextra -Wpedantic")
|
set(COMMON_CMAKE_C_FLAGS "-std=c11 -Wall -Wextra -Wpedantic")
|
||||||
set(CMAKE_C_FLAGS_DEBUG "-DSPARSEMAP_DIAGNOSTIC -DDEBUG -g -O0")
|
set(CMAKE_C_FLAGS_DEBUG "-DSPARSEMAP_DIAGNOSTIC -DSPARSEMAP_TESTING -DDEBUG -g -O0")
|
||||||
set(CMAKE_C_FLAGS_PROFILE "-DSPARSEMAP_DIAGNOSTIC -DDEBUG -g -Og -fsanitize=address,leak,object-size,pointer-compare,pointer-subtract,null,return,bounds,pointer-overflow,undefined -fsanitize-address-use-after-scope")
|
set(CMAKE_C_FLAGS_PROFILE "-DSPARSEMAP_DIAGNOSTIC -DSPARSEMAP_TESTING -DDEBUG -g -Og -fsanitize=address,leak,object-size,pointer-compare,pointer-subtract,null,return,bounds,pointer-overflow,undefined -fsanitize-address-use-after-scope")
|
||||||
set(CMAKE_C_FLAGS_RELEASE "-Ofast")
|
set(CMAKE_C_FLAGS_RELEASE "-Ofast")
|
||||||
|
|
||||||
# Include all header files from the header directory
|
# Include all header files from the header directory
|
||||||
|
@ -50,44 +49,45 @@ set_target_properties(sparsemap PROPERTIES
|
||||||
target_include_directories(sparsemap PRIVATE ${HEADER_DIR})
|
target_include_directories(sparsemap PRIVATE ${HEADER_DIR})
|
||||||
|
|
||||||
# Add ex_1 program
|
# Add ex_1 program
|
||||||
add_executable(ex_1 examples/ex_1.c tests/munit.c lib/common.c)
|
add_executable(ex_1 test/ex_1.c test/munit.c test/qc.c test/common.c)
|
||||||
target_link_libraries(ex_1 PRIVATE sparsemap)
|
target_link_libraries(ex_1 PRIVATE sparsemap)
|
||||||
target_include_directories(ex_1 PRIVATE ${HEADER_DIR})
|
target_include_directories(ex_1 PRIVATE ${HEADER_DIR})
|
||||||
add_custom_target(run_ex_1 COMMAND ex_1 WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
add_custom_target(run_ex_1 COMMAND ex_1 WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
# Add ex_2 program
|
# Add ex_2 program
|
||||||
add_executable(ex_2 examples/ex_2.c tests/munit.c lib/common.c)
|
add_executable(ex_2 test/ex_2.c test/munit.c test/qc.c test/common.c)
|
||||||
target_link_libraries(ex_2 PRIVATE sparsemap)
|
target_link_libraries(ex_2 PRIVATE sparsemap)
|
||||||
target_include_directories(ex_2 PRIVATE ${HEADER_DIR})
|
target_include_directories(ex_2 PRIVATE ${HEADER_DIR})
|
||||||
add_custom_target(run_ex_2 COMMAND ex_2 WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
add_custom_target(run_ex_2 COMMAND ex_2 WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
# Add ex_3 program
|
# Add ex_3 program
|
||||||
add_executable(ex_3 examples/ex_3.c tests/munit.c lib/common.c)
|
add_executable(ex_3 test/ex_3.c test/munit.c test/qc.c test/common.c)
|
||||||
target_link_libraries(ex_3 PRIVATE sparsemap)
|
target_link_libraries(ex_3 PRIVATE sparsemap)
|
||||||
target_include_directories(ex_3 PRIVATE ${HEADER_DIR})
|
target_include_directories(ex_3 PRIVATE ${HEADER_DIR})
|
||||||
add_custom_target(run_ex_3 COMMAND ex_3 WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
add_custom_target(run_ex_3 COMMAND ex_3 WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
# Add ex_4 program
|
# Add ex_4 program
|
||||||
add_executable(ex_4 examples/ex_4.c tests/munit.c lib/common.c)
|
add_executable(ex_4 test/ex_4.c test/munit.c test/qc.c test/common.c)
|
||||||
target_link_libraries(ex_4 PRIVATE sparsemap)
|
target_link_libraries(ex_4 PRIVATE sparsemap)
|
||||||
target_include_directories(ex_4 PRIVATE ${HEADER_DIR})
|
target_include_directories(ex_4 PRIVATE ${HEADER_DIR})
|
||||||
add_custom_target(run_ex_4 COMMAND ex_4 WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
add_custom_target(run_ex_4 COMMAND ex_4 WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
# Add test program
|
# Add test program
|
||||||
add_executable(test tests/test.c tests/munit.c lib/common.c)
|
add_executable(test test/test.c test/munit.c test/qc.c test/common.c)
|
||||||
target_link_libraries(test PRIVATE sparsemap)
|
target_link_libraries(test PRIVATE sparsemap)
|
||||||
target_include_directories(test PRIVATE ${HEADER_DIR})
|
target_include_directories(test PRIVATE ${HEADER_DIR})
|
||||||
|
set_source_files_properties(test/test.c PROPERTIES COMPILE_FLAGS "-DDEBUG -DSPARSEMAP_DIAGNOSTIC -DSPARSEMAP_TESTING" )
|
||||||
add_custom_target(run_test COMMAND test WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
add_custom_target(run_test COMMAND test WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
# Add soak program
|
# Add soak program
|
||||||
add_executable(soak tests/soak.c lib/common.c lib/tdigest.c lib/roaring.c)
|
add_executable(soak test/soak.c test/common.c test/tdigest.c test/qc.c test/roaring.c)
|
||||||
target_link_libraries(soak PRIVATE sparsemap)
|
target_link_libraries(soak PRIVATE sparsemap)
|
||||||
target_include_directories(soak PRIVATE ${HEADER_DIR} lib)
|
target_include_directories(soak PRIVATE ${HEADER_DIR} lib)
|
||||||
target_link_libraries(soak PUBLIC m)
|
target_link_libraries(soak PUBLIC m)
|
||||||
add_custom_target(run_soak COMMAND soak WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
add_custom_target(run_soak COMMAND soak WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||||
|
|
||||||
# Add fuzzer program
|
# Add fuzzer program
|
||||||
# add_executable(fuzzer tests/fuzzer.c)
|
# add_executable(fuzzer EXCLUDE_FROM_ALL tests/fuzzer.c)
|
||||||
# target_link_libraries(fuzzer PRIVATE sparsemap)
|
# target_link_libraries(fuzzer PRIVATE sparsemap)
|
||||||
# target_include_directories(fuzzer PRIVATE ${HEADER_DIR} lib)
|
# target_include_directories(fuzzer PRIVATE ${HEADER_DIR} lib)
|
||||||
# target_link_libraries(fuzzer PUBLIC m)
|
# target_link_libraries(fuzzer PUBLIC m)
|
||||||
|
|
106
Makefile
106
Makefile
|
@ -1,106 +0,0 @@
|
||||||
|
|
||||||
OBJS = sparsemap.o
|
|
||||||
STATIC_LIB = libsparsemap.a
|
|
||||||
SHARED_LIB = libsparsemap.so
|
|
||||||
|
|
||||||
LIBS = -lm
|
|
||||||
#CFLAGS = -Wall -Wextra -Wpedantic -Of -std=c11 -Iinclude/ -fPIC
|
|
||||||
#CFLAGS = -Wall -Wextra -Wpedantic -Og -g -std=c11 -Iinclude/ -fPIC
|
|
||||||
#CFLAGS = -DSPARSEMAP_DIAGNOSTIC -DDEBUG -Wall -Wextra -Wpedantic -O0 -g -std=c11 -Iinclude/ -fPIC
|
|
||||||
CFLAGS = -DSPARSEMAP_DIAGNOSTIC -DDEBUG -Wall -Wextra -Ofast -g -std=c11 -Iinclude/ -fPIC
|
|
||||||
#CFLAGS = -Wall -Wextra -Wpedantic -Og -g -std=c11 -Iinclude/ -fPIC
|
|
||||||
#CFLAGS = -Wall -Wextra -Wpedantic -Ofast -g -std=c11 -Iinclude/ -fPIC
|
|
||||||
#CFLAGS = -DSPARSEMAP_DIAGNOSTIC -DDEBUG -Wall -Wextra -Wpedantic -Og -g -fsanitize=address,leak,object-size,pointer-compare,pointer-subtract,null,return,bounds,pointer-overflow,undefined -fsanitize-address-use-after-scope -std=c11 -Iinclude/ -fPIC
|
|
||||||
#CFLAGS = -Wall -Wextra -Wpedantic -Og -g -fsanitize=all -fhardened -std=c11 -Iinclude/ -fPIC
|
|
||||||
|
|
||||||
#TEST_FLAGS = -DDEBUG -Wall -Wextra -Wpedantic -O0 -g -std=c11 -Iinclude/ -Itests/ -fPIC
|
|
||||||
TEST_FLAGS = -Wall -Wextra -Wpedantic -Ofast -g -std=c11 -Iinclude/ -Itests/ -fPIC
|
|
||||||
#TEST_FLAGS = -Wall -Wextra -Wpedantic -Og -g -std=c11 -Iinclude/ -Itests/ -fPIC
|
|
||||||
#TEST_FLAGS = -DDEBUG -Wall -Wextra -Wpedantic -Og -g -fsanitize=address,leak,object-size,pointer-compare,pointer-subtract,null,return,bounds,pointer-overflow,undefined -fsanitize-address-use-after-scope -std=c11 -Iinclude/ -fPIC
|
|
||||||
|
|
||||||
TESTS = tests/test tests/soak
|
|
||||||
TEST_OBJS = tests/test.o lib/munit.o lib/tdigest.o lib/common.o
|
|
||||||
LIB_OBJS = lib/munit.o lib/tdigest.o lib/common.o lib/roaring.o
|
|
||||||
EXAMPLES = examples/ex_1 examples/ex_2 examples/ex_3 examples/ex_4
|
|
||||||
|
|
||||||
.PHONY: all shared static clean test examples mls
|
|
||||||
|
|
||||||
all: static shared
|
|
||||||
|
|
||||||
static: $(STATIC_LIB)
|
|
||||||
|
|
||||||
shared: $(SHARED_LIB)
|
|
||||||
|
|
||||||
$(STATIC_LIB): $(OBJS)
|
|
||||||
ar rcs $(STATIC_LIB) $?
|
|
||||||
|
|
||||||
$(SHARED_LIB): $(OBJS)
|
|
||||||
$(CC) $(CFLAGS) -o $@ $? -shared
|
|
||||||
|
|
||||||
examples: $(STATIC_LIB) $(EXAMPLES) $(TEST_OBJS)
|
|
||||||
|
|
||||||
mls: examples/mls
|
|
||||||
|
|
||||||
tests: $(TESTS)
|
|
||||||
|
|
||||||
test: tests
|
|
||||||
env ASAN_OPTIONS=detect_leaks=1 LSAN_OPTIONS=verbosity=1:log_threads=1 ./tests/test
|
|
||||||
|
|
||||||
soak: tests
|
|
||||||
env ASAN_OPTIONS=detect_leaks=1 LSAN_OPTIONS=verbosity=1:log_threads=1 ./tests/soak
|
|
||||||
|
|
||||||
fuzzer: tests
|
|
||||||
env ASAN_OPTIONS=detect_leaks=1 LSAN_OPTIONS=verbosity=1:log_threads=1 ./tests/fuzzer ./crash.case
|
|
||||||
|
|
||||||
tests/test: $(TEST_OBJS) $(LIB_OBJS) $(STATIC_LIB)
|
|
||||||
$(CC) $^ $(LIBS) -o $@ $(TEST_FLAGS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f $(OBJS)
|
|
||||||
rm -f examples/main.c
|
|
||||||
rm -f $(STATIC_LIB) $(SHARED_LIB)
|
|
||||||
rm -f $(TESTS) tests/*.o
|
|
||||||
rm -f $(EXAMPLES) examples/*.o
|
|
||||||
|
|
||||||
format:
|
|
||||||
clang-format -i src/sparsemap.c include/sparsemap.h examples/ex_*.c tests/soak.c tests/test.c tests/midl.c lib/common.c include/common.h
|
|
||||||
# clang-format -i include/*.h src/*.c tests/*.c tests/*.h examples/*.c
|
|
||||||
|
|
||||||
%.o: src/%.c
|
|
||||||
$(CC) $(CFLAGS) -c -o $@ $^
|
|
||||||
|
|
||||||
lib/%.o: tests/%.c
|
|
||||||
$(CC) $(CFLAGS) -c -o $@ $^
|
|
||||||
|
|
||||||
tests/%.o: tests/%.c
|
|
||||||
$(CC) $(CFLAGS) -c -o $@ $^
|
|
||||||
|
|
||||||
examples/%.o: examples/%.c
|
|
||||||
$(CC) $(CFLAGS) -c -o $@ $^
|
|
||||||
|
|
||||||
examples/ex_1: $(LIB_OBJS) examples/ex_1.o $(STATIC_LIB)
|
|
||||||
$(CC) $^ $(LIBS) -o $@ $(TEST_FLAGS)
|
|
||||||
|
|
||||||
examples/ex_2: $(LIB_OBJS) examples/ex_2.o $(STATIC_LIB)
|
|
||||||
$(CC) $^ $(LIBS) -o $@ $(TEST_FLAGS)
|
|
||||||
|
|
||||||
examples/ex_3: $(LIB_OBJS) examples/ex_3.o $(STATIC_LIB)
|
|
||||||
$(CC) $^ $(LIBS) -o $@ $(TEST_FLAGS)
|
|
||||||
|
|
||||||
examples/ex_4: $(LIB_OBJS) examples/ex_4.o $(STATIC_LIB)
|
|
||||||
$(CC) $^ $(LIBS) -o $@ $(TEST_FLAGS)
|
|
||||||
|
|
||||||
tests/soak: $(LIB_OBJS) tests/soak.o $(STATIC_LIB)
|
|
||||||
$(CC) $^ $(LIBS) -o $@ $(TEST_FLAGS)
|
|
||||||
|
|
||||||
tests/fuzzer: $(LIB_OBJS) tests/fuzzer.o $(STATIC_LIB)
|
|
||||||
$(CC) $^ $(LIBS) -o $@ $(TEST_FLAGS) -DFUZZ_DEBUG
|
|
||||||
|
|
||||||
todo:
|
|
||||||
rg -i 'todo|gsb|abort'
|
|
||||||
|
|
||||||
# cp src/sparsemap.c /tmp && clang-tidy src/sparsemap.c -fix -fix-errors -checks="readability-braces-around-statements" -- -DDEBUG -DSPARSEMAP_DIAGNOSTIC -DSPARSEMAP_ASSERT -Wall -Wextra -Wpedantic -Og -g -std=c11 -Iinclude/ -fPIC
|
|
||||||
|
|
||||||
# clear; make clean examples test && env ASAN_OPTIONS=detect_leaks=1 LSAN_OPTIONS=verbosity=1:log_threads=1 ./tests/test
|
|
||||||
|
|
||||||
# clear; make clean examples test && env ASAN_OPTIONS=detect_leaks=1 LSAN_OPTIONS=verbosity=1:log_threads=1 ./examples/soak
|
|
113
README.md
113
README.md
|
@ -5,47 +5,106 @@ Unfortunately, they can use too much memory. To compensate, we often use
|
||||||
compressed bitmaps.
|
compressed bitmaps.
|
||||||
|
|
||||||
`sparsemap` is a sparse, compressed bitmap. In the best case, it can store 2048
|
`sparsemap` is a sparse, compressed bitmap. In the best case, it can store 2048
|
||||||
bits in just 8 bytes. In the worst case, it stores the 2048 bits uncompressed and
|
bits in just 8 bytes. In the worst case, it stores the 2048 bits uncompressed
|
||||||
requires an additional 8 bytes of overhead.
|
and requires an additional 8 bytes of overhead.
|
||||||
|
|
||||||
The "best" case happens when large consecutive sequences of the bits are
|
The "best" case happens when large consecutive sequences of the bits are
|
||||||
either set ("1") or not set ("0"). If your numbers are consecutive 64bit
|
either set ("1") or not set ("0"). If your numbers are consecutive 64 bit
|
||||||
integers then sparsemap can compress up to 16kb in just 8 bytes.
|
integers then sparsemap can compress up to 16kb in just 8 bytes.
|
||||||
|
|
||||||
## How does it work?
|
## How does it work? TODO... refine...
|
||||||
|
|
||||||
On the lowest level stores bits in sm_bitvec_t's (a uint32_t or uint64_t).
|
On the lowest level a bitmap contains a number of chunks. Each chunk has a
|
||||||
|
starting offset (`uint32_t`), a descriptor (the first `sm_bitvec_t`), and may
|
||||||
|
require a variable amount of additional space for encoding some bit patterns.
|
||||||
|
|
||||||
Each sm_bitvec_t has an additional descriptor (2 bits). A single word prepended
|
So, if the user sets bit 0 and bit 10000, and the chunk capacity is 2048,
|
||||||
to each sm_bitvec_t describes its condition. The descriptor word and the
|
the sparsemap creates two vectors; the first starts at offset 0, the second
|
||||||
sm_bitvec_t's have the same size. The descriptor of a sm_bitvec_t
|
starts at offset 8192. Offsets must align with the capacity of a vector.
|
||||||
specifies whether the sm_bitvec_t consists only of set bits ("1"), unset
|
|
||||||
bits ("0") or has a mixed payload. In the first and second cases, the
|
|
||||||
sm_bitvec_t is not stored.
|
|
||||||
|
|
||||||
An example shows a sequence of 4 x 16 bits (here, each sm_bitvec_t and the
|
Every 2 bit pair within the descriptor (the first vector size portion of the
|
||||||
Descriptor word has 16 bits):
|
chunk after the 4 bytes for the offset) indicates the encoded bit pattern at
|
||||||
|
that location's relative offset. This can be only set bits ("1"), only unset
|
||||||
|
bits ("0"), a mixed payload, or a run-length encoded extent of set bits
|
||||||
|
("1s"). A mixed vector consumes an additional `sm_bitvec_t`'s worth of space in
|
||||||
|
the buffer used to encode the bit pattern within that range.
|
||||||
|
|
||||||
|
Our examples below ignore the 4 byte overhead for the starting offset of these
|
||||||
|
chunks because they focus on the compressed encoding. Also, for brevity, we use
|
||||||
|
16 bit wide vectors (`sm_bitvec_t`), rather than 64 bits.
|
||||||
|
|
||||||
|
The first example, shows a sequence of 4 x 16 bits:
|
||||||
|
|
||||||
Descriptor:
|
Descriptor:
|
||||||
00 00 00 00 11 00 11 10
|
00 00 00 00 11 00 11 10
|
||||||
^^ ^^ ^^ ^^-- sm_bitvec_t #0 - #3 are "0000000000000000"
|
^^ ^^ ^^ ^^-- sm_bitvec_t [0..3] are "0000000000000000"
|
||||||
^^-- sm_bitvec_t #4 is "1111111111111111"
|
^^-- sm_bitvec_t 4 is "1111111111111111"
|
||||||
^^-- sm_bitvec_t #5 is "0000000000000000"
|
^^-- sm_bitvec_t 5 is "0000000000000000"
|
||||||
^^-- sm_bitvec_t #6 is "1111111111111111"
|
^^-- sm_bitvec_t 6 is "1111111111111111"
|
||||||
^^-- sm_bitvec_t #7 is "0110010101111001"
|
^^-- sm_bitvec_t 7 is "0110010101111001"
|
||||||
|
|
||||||
Since the first 7 sm_bitvec_t's are either all "1" or "0" they are not stored.
|
Since the first 7 (0 through 6) `sm_bitvec_t`'s are either all "1" or "0" and
|
||||||
The actual memory sequence looks like this:
|
their encoding reqiures no additional storage in the buffer, so the actual
|
||||||
|
memory sequence for this chunk within the buffer looks like this:
|
||||||
|
|
||||||
0000000011001110 0110010101111001
|
0000000011001110 0110010101111001
|
||||||
|
|
||||||
Instead of storing 8 Words (16 bytes), we only store 2 Words (2 bytes): one
|
Instead of storing 16 bytes, we only store 2 bytes: one for the descriptor, and
|
||||||
for the descriptor, and one for the last sm_bitvec_t #7.
|
one for the last `sm_bitvec_t` #7.
|
||||||
|
|
||||||
The sparsemap stores a list of chunk maps, and for each chunk map, it stores the
|
A 2nd example shows a chunk with reduced capacity.
|
||||||
absolute address (i.e. if the user sets bit 0 and bit 10000, and the chunk map
|
|
||||||
capacity is 2048, the sparsemap creates two chunk maps; the first starts at
|
Descriptor:
|
||||||
offset 0, the second starts at offset 8192).
|
00 00 00 00 11 01 01 01
|
||||||
|
^^ ^^ ^^ ^^-- sm_bitvec_t [0..3] are "0000000000000000"
|
||||||
|
^^-- sm_bitvec_t 4 is "1111111111111111"
|
||||||
|
^^ ^^ ^^-- sm_bitvec_t [5..8] represent nothing
|
||||||
|
|
||||||
|
The memory sequence for this second, truncated chunk, looks like this:
|
||||||
|
|
||||||
|
0000000011010101
|
||||||
|
|
||||||
|
The bit pattern "01" can exist at the end of a chunk to indicate a reduced chunk
|
||||||
|
capacity. In this case the chunk's last 3 descriptors indicate that it can
|
||||||
|
encode up to 5 * 16 or 80 bit positions rather than the normal 128 (when using
|
||||||
|
16 bit wide vectors, `sm_bitvec_t`). When a chunk's capacity is entirely
|
||||||
|
truncated, it is empty and removed from the sparsemap entirely.
|
||||||
|
|
||||||
|
A 3rd example shows a single vector representing a long run of adjacent 1s
|
||||||
|
greater than the vector width (16 bits). Let's examine the representation:
|
||||||
|
|
||||||
|
Descriptor:
|
||||||
|
01 00 00 00 10 01 00 00
|
||||||
|
^^-- sm_bitvec_t #0 is '01' indicating a run-length encoding of 1s
|
||||||
|
^^ ^^ ^^ ^^ ^^ ^^ ^^-- the lenght of the run, 144
|
||||||
|
|
||||||
|
When (if, and only if) the first 2 bits of the descriptor are '01' they indicate
|
||||||
|
that this is an run-length encoded (RLE) vector. The number of 1s is the
|
||||||
|
remaining portion of the descriptor -- in this case 14 of the 16 bits -- encode
|
||||||
|
the run length. Simply mask the first two bits and interpret the remaining as an
|
||||||
|
`size_t`.
|
||||||
|
|
||||||
|
With that in mind, the memory sequence for this third example looks like this:
|
||||||
|
|
||||||
|
01 00 00 00 10 00 01 00
|
||||||
|
|
||||||
|
Which decodes to a run of 144 adjacent 1s:
|
||||||
|
|
||||||
|
1111 ... <then another 139 1s followed by the final> ... 1
|
||||||
|
|
||||||
|
The run must always be modulo the width of the descriptor (144 % 16 = 0). The
|
||||||
|
next chunk would encode any additional 1s adjacent to this set of 144 unless
|
||||||
|
there were 16 more, then this chunk would change to:
|
||||||
|
|
||||||
|
Descriptor:
|
||||||
|
01 00 00 00 10 10 00 00
|
||||||
|
^^-- sm_bitvec_t #0 is '01' meaning RLE a set of adjacent 1s
|
||||||
|
^^ ^^ ^^ ^^ ^^ ^^ ^^-- the new length of the run is 160
|
||||||
|
|
||||||
|
Using this method of RLE for adjacent 1s we can compress (again, in this case
|
||||||
|
where bitvec_t is 16 bits wide) 2^14 or 16348 adjacent 1s to the width of a
|
||||||
|
single descriptor, 2 bytes in this case, rather than the approximately 4096
|
||||||
|
bytes without RLE.
|
||||||
|
|
||||||
## Usage instructions
|
## Usage instructions
|
||||||
|
|
||||||
|
|
65
bin/gen_chunk_vector_size_table.py
Normal file
65
bin/gen_chunk_vector_size_table.py
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# Gererate a C function that contains a pre-calculated static table where each
|
||||||
|
# 8bit offset into that table encodes the required additional space for what is
|
||||||
|
# described.
|
||||||
|
|
||||||
|
# The 2 bit patters are:
|
||||||
|
# 00 -> 0 additional sm_bitvec_t (ZEROS)
|
||||||
|
# 11 -> 0 additional sm_bitvec_t (ONES)
|
||||||
|
# 10 -> 1 additional sm_bitvec_t (MIXED)
|
||||||
|
# 01 -> 0 additional sm_bitvec_t (NONE)
|
||||||
|
|
||||||
|
# The goal is to output this:
|
||||||
|
|
||||||
|
# /**
|
||||||
|
# * Calculates the number of sm_bitvec_ts required by a single byte with flags
|
||||||
|
# * (in m_data[0]).
|
||||||
|
# */
|
||||||
|
# static size_t
|
||||||
|
# __sm_chunk_calc_vector_size(uint8_t b)
|
||||||
|
# {
|
||||||
|
# // clang-format off
|
||||||
|
# static int lookup[] = {
|
||||||
|
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||||
|
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||||
|
# 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1,
|
||||||
|
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||||
|
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||||
|
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||||
|
# 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1,
|
||||||
|
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||||
|
# 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1,
|
||||||
|
# 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1,
|
||||||
|
# 2, 2, 3, 2, 2, 2, 3, 2, 3, 3, 4, 3, 2, 2, 3, 2,
|
||||||
|
# 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1,
|
||||||
|
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||||
|
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0,
|
||||||
|
# 1, 1, 2, 1, 1, 1, 2, 1, 2, 2, 3, 2, 1, 1, 2, 1,
|
||||||
|
# 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 2, 1, 0, 0, 1, 0
|
||||||
|
# };
|
||||||
|
# // clang-format on
|
||||||
|
# return (size_t)lookup[b];
|
||||||
|
# }
|
||||||
|
|
||||||
|
def create_lookup_table_c_format():
|
||||||
|
"""Creates a lookup table in C-style format."""
|
||||||
|
lookup_table = []
|
||||||
|
for byte in range(256):
|
||||||
|
count = 0
|
||||||
|
for i in range(3):
|
||||||
|
if (byte >> (i * 2)) & 3 == 2:
|
||||||
|
count += 1
|
||||||
|
lookup_table.append(count)
|
||||||
|
|
||||||
|
# Format the output as a C array
|
||||||
|
output = "static int lookup[] = {\n"
|
||||||
|
for i in range(0, 256, 16):
|
||||||
|
line = " " + ", ".join(str(x) for x in lookup_table[i:i+16]) + ",\n"
|
||||||
|
output += line
|
||||||
|
output += "};"
|
||||||
|
|
||||||
|
print(output)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
create_lookup_table_c_format()
|
3389
sparsemap.c
Normal file
3389
sparsemap.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -29,31 +29,31 @@
|
||||||
*
|
*
|
||||||
* The implementation is separated into tiers.
|
* The implementation is separated into tiers.
|
||||||
*
|
*
|
||||||
* Tier 0 (lowest): bits are stored in a sm_bitvec_t (uint64_t).
|
* Tier 0 (lowest): bits are stored in a __sm_bitvec_t (uint64_t).
|
||||||
*
|
*
|
||||||
* Tier 1 (middle): multiple sm_bitvec_t are managed in a chunk map. The chunk
|
* Tier 1 (middle): multiple __sm_bitvec_t are managed in a chunk map. The chunk
|
||||||
* map only stores those sm_bitvec_t that have a mixed payload of bits (i.e.
|
* map only stores those __sm_bitvec_t that have a mixed payload of bits (i.e.
|
||||||
* some bits are 1, some are 0). As soon as ALL bits in a sm_bitvec_t are
|
* some bits are 1, some are 0). As soon as ALL bits in a __sm_bitvec_t are
|
||||||
* identical, this sm_bitvec_t is no longer stored, it is compressed.
|
* identical, this __sm_bitvec_t is no longer stored, it is compressed.
|
||||||
*
|
*
|
||||||
* The chunk maps store additional flags (2 bit) for each sm_bitvec_t in an
|
* The chunk maps store additional flags (2 bit) for each __sm_bitvec_t in an
|
||||||
* additional word (same size as the sm_bitvec_t itself).
|
* additional word (same size as the __sm_bitvec_t itself).
|
||||||
*
|
*
|
||||||
* 00 11 22 33
|
* 00 11 22 33
|
||||||
* ^-- descriptor for sm_bitvec_t 1
|
* ^-- descriptor for __sm_bitvec_t 1
|
||||||
* ^-- descriptor for sm_bitvec_t 2
|
* ^-- descriptor for __sm_bitvec_t 2
|
||||||
* ^-- descriptor for sm_bitvec_t 3
|
* ^-- descriptor for __sm_bitvec_t 3
|
||||||
* ^-- descriptor for sm_bitvec_t 4
|
* ^-- descriptor for __sm_bitvec_t 4
|
||||||
*
|
*
|
||||||
* Those flags (*) can have one of the following values:
|
* Those flags (*) can have one of the following values:
|
||||||
*
|
*
|
||||||
* 00 The sm_bitvec_t is all zero -> sm_bitvec_t is not stored
|
* 00 The __sm_bitvec_t is all zero -> __sm_bitvec_t is not stored
|
||||||
* 11 The sm_bitvec_t is all one -> sm_bitvec_t is not stored
|
* 11 The __sm_bitvec_t is all one -> __sm_bitvec_t is not stored
|
||||||
* 10 The sm_bitvec_t contains a bitmap -> sm_bitvec_t is stored
|
* 10 The __sm_bitvec_t contains a bitmap -> __sm_bitvec_t is stored
|
||||||
* 01 The sm_bitvec_t is not used (**)
|
* 01 The __sm_bitvec_t is not used (**)
|
||||||
*
|
*
|
||||||
* The serialized size of a chunk map in memory therefore is at least
|
* The serialized size of a chunk map in memory therefore is at least
|
||||||
* one sm_bitvec_t for the flags, and (optionally) additional sm_bitvec_ts
|
* one __sm_bitvec_t for the flags, and (optionally) additional __sm_bitvec_ts
|
||||||
* if they are required.
|
* if they are required.
|
||||||
*
|
*
|
||||||
* (*) The code comments often use the Erlang format for binary
|
* (*) The code comments often use the Erlang format for binary
|
||||||
|
@ -82,7 +82,7 @@ extern "C" {
|
||||||
* The public interface for a sparse bit-mapped index, a "sparse map".
|
* The public interface for a sparse bit-mapped index, a "sparse map".
|
||||||
*
|
*
|
||||||
* |sm_idx_t| is the user's numerical data type which is mapped to a single bit
|
* |sm_idx_t| is the user's numerical data type which is mapped to a single bit
|
||||||
* in the bitmap. Usually this is uint32_t or uint64_t. |sm_bitvec_t| is the
|
* in the bitmap. Usually this is uint32_t or uint64_t. |__sm_bitvec_t| is the
|
||||||
* storage type for a bit vector used by the __sm_chunk_t internal maps.
|
* storage type for a bit vector used by the __sm_chunk_t internal maps.
|
||||||
* Usually this is an uint64_t.
|
* Usually this is an uint64_t.
|
||||||
*/
|
*/
|
||||||
|
@ -92,8 +92,6 @@ typedef size_t sparsemap_idx_t;
|
||||||
#define SPARSEMAP_IDX_MAX SIZE_MAX
|
#define SPARSEMAP_IDX_MAX SIZE_MAX
|
||||||
#define SPARSEMAP_FOUND(x) ((x) != SPARSEMAP_IDX_MAX)
|
#define SPARSEMAP_FOUND(x) ((x) != SPARSEMAP_IDX_MAX)
|
||||||
#define SPARSEMAP_NOT_FOUND(x) ((x) == SPARSEMAP_IDX_MAX)
|
#define SPARSEMAP_NOT_FOUND(x) ((x) == SPARSEMAP_IDX_MAX)
|
||||||
typedef uint32_t sm_idx_t;
|
|
||||||
typedef uint64_t sm_bitvec_t;
|
|
||||||
|
|
||||||
/** @brief Allocate a new, empty sparsemap_t with a buffer of \b size on the
|
/** @brief Allocate a new, empty sparsemap_t with a buffer of \b size on the
|
||||||
* heap to use for storage of bitmap data.
|
* heap to use for storage of bitmap data.
|
||||||
|
@ -229,7 +227,7 @@ size_t sparsemap_get_capacity(sparsemap_t *map);
|
||||||
*/
|
*/
|
||||||
bool sparsemap_is_set(sparsemap_t *map, sparsemap_idx_t idx);
|
bool sparsemap_is_set(sparsemap_t *map, sparsemap_idx_t idx);
|
||||||
|
|
||||||
/** @brief Sets the bit at index \b idx to \b value.
|
/** @brief Assigns the bit at 0-based index \b idx to \b value.
|
||||||
*
|
*
|
||||||
* A sparsemap has a fixed size buffer with a capacity that can be exhausted by
|
* A sparsemap has a fixed size buffer with a capacity that can be exhausted by
|
||||||
* when calling this function. In such cases the return value is not equal to
|
* when calling this function. In such cases the return value is not equal to
|
||||||
|
@ -239,10 +237,41 @@ bool sparsemap_is_set(sparsemap_t *map, sparsemap_idx_t idx);
|
||||||
*
|
*
|
||||||
* @param[in] map The sparsemap reference.
|
* @param[in] map The sparsemap reference.
|
||||||
* @param[in] idx The 0-based offset into the bitmap index to modify.
|
* @param[in] idx The 0-based offset into the bitmap index to modify.
|
||||||
|
* @param[in] value When true idx set to 1, otherwise idx set to 0.
|
||||||
* @returns the \b idx supplied on success or SPARSEMAP_IDX_MAX on error
|
* @returns the \b idx supplied on success or SPARSEMAP_IDX_MAX on error
|
||||||
* with \b errno set to ENOSPC when the map is full.
|
* with \b errno set to ENOSPC when the map is full.
|
||||||
*/
|
*/
|
||||||
sparsemap_idx_t sparsemap_set(sparsemap_t *map, sparsemap_idx_t idx, bool value);
|
sparsemap_idx_t sparsemap_assign(sparsemap_t *map, sparsemap_idx_t idx, bool value);
|
||||||
|
|
||||||
|
/** @brief Sets the bit at 0-based index \b idx to 1.
|
||||||
|
*
|
||||||
|
* A sparsemap has a fixed size buffer with a capacity that can be exhausted by
|
||||||
|
* when calling this function. In such cases the return value is not equal to
|
||||||
|
* the provided \b idx and errno is set to ENOSPC. In such situations it is
|
||||||
|
* possible to grow the data size and retry the set() operation under certain
|
||||||
|
* circumstances (see #sparsemap() and #sparsemap_set_data_size()).
|
||||||
|
*
|
||||||
|
* @param[in] map The sparsemap reference.
|
||||||
|
* @param[in] idx The 0-based offset into the bitmap index to set to 1;
|
||||||
|
* @returns the \b idx supplied on success or SPARSEMAP_IDX_MAX on error
|
||||||
|
* with \b errno set to ENOSPC when the map is full.
|
||||||
|
*/
|
||||||
|
sparsemap_idx_t sparsemap_set(sparsemap_t *map, sparsemap_idx_t idx);
|
||||||
|
|
||||||
|
/** @brief Unsets the bit at 0-based index \b idx (sets it to 0).
|
||||||
|
*
|
||||||
|
* A sparsemap has a fixed size buffer with a capacity that can be exhausted by
|
||||||
|
* when calling this function. In such cases the return value is not equal to
|
||||||
|
* the provided \b idx and errno is set to ENOSPC. In such situations it is
|
||||||
|
* possible to grow the data size and retry the set() operation under certain
|
||||||
|
* circumstances (see #sparsemap() and #sparsemap_set_data_size()).
|
||||||
|
*
|
||||||
|
* @param[in] map The sparsemap reference.
|
||||||
|
* @param[in] idx The 0-based offset into the bitmap index to unset.
|
||||||
|
* @returns the \b idx supplied on success or SPARSEMAP_IDX_MAX on error
|
||||||
|
* with \b errno set to ENOSPC when the map is full.
|
||||||
|
*/
|
||||||
|
sparsemap_idx_t sparsemap_unset(sparsemap_t *map, sparsemap_idx_t idx);
|
||||||
|
|
||||||
/** @brief Returns the byte size of the data buffer that has been used thus far.
|
/** @brief Returns the byte size of the data buffer that has been used thus far.
|
||||||
*
|
*
|
||||||
|
@ -265,9 +294,9 @@ void *sparsemap_get_data(sparsemap_t *map);
|
||||||
*/
|
*/
|
||||||
size_t sparsemap_count(sparsemap_t *map);
|
size_t sparsemap_count(sparsemap_t *map);
|
||||||
|
|
||||||
/** @brief Returns the offset of the first bit set in the map.
|
/** @brief Returns the position of the first bit set in the map.
|
||||||
*
|
*
|
||||||
* This is the same as the value of the first set bit in the
|
* This is the same as the offset of the first set bit in the
|
||||||
* map.
|
* map.
|
||||||
*
|
*
|
||||||
* @param[in] map The sparsemap reference.
|
* @param[in] map The sparsemap reference.
|
||||||
|
@ -302,7 +331,7 @@ double sparsemap_fill_factor(sparsemap_t *map);
|
||||||
* @param[in] skip Start the scan after \b skip position in the map.
|
* @param[in] skip Start the scan after \b skip position in the map.
|
||||||
* @param[in] aux Auxiliary information passed to the scanner.
|
* @param[in] aux Auxiliary information passed to the scanner.
|
||||||
*/
|
*/
|
||||||
void sparsemap_scan(sparsemap_t *map, void (*scanner)(sm_idx_t vec[], size_t n, void *aux), size_t skip, void *aux);
|
void sparsemap_scan(sparsemap_t *map, void (*scanner)(uint32_t vec[], size_t n, void *aux), size_t skip, void *aux);
|
||||||
|
|
||||||
/** @brief Merges the values from \b source into \b destination, \b source is unchanged.
|
/** @brief Merges the values from \b source into \b destination, \b source is unchanged.
|
||||||
*
|
*
|
||||||
|
@ -312,21 +341,24 @@ void sparsemap_scan(sparsemap_t *map, void (*scanner)(sm_idx_t vec[], size_t n,
|
||||||
* @param[in] source The bitmap to merge into \b destination.
|
* @param[in] source The bitmap to merge into \b destination.
|
||||||
* @returns 0 on success, or sets errno to ENOSPC and returns the amount of
|
* @returns 0 on success, or sets errno to ENOSPC and returns the amount of
|
||||||
* additional space required to successfully merge the maps.
|
* additional space required to successfully merge the maps.
|
||||||
|
* @todo change return to SPARSEMAP_IDX_MAX on ENOSPC to match other functions
|
||||||
*/
|
*/
|
||||||
int sparsemap_merge(sparsemap_t *destination, sparsemap_t *source);
|
int sparsemap_merge(sparsemap_t *destination, sparsemap_t *source);
|
||||||
|
|
||||||
/** @brief Splits the bitmap by assigning all bits starting at \b offset to the
|
/** @brief Splits the bitmap by assigning all bits starting at \b idx to the
|
||||||
* \b other bitmap while removing them from \b map.
|
* \b other bitmap while removing them from \b map.
|
||||||
*
|
*
|
||||||
* The \b other bitmap is expected to be empty.
|
* Splits into [start, idx), and [idx, end]. The \b other bitmap is
|
||||||
|
* expected to be empty.
|
||||||
*
|
*
|
||||||
* @param[in] map The sparsemap reference.
|
* @param[in] map The sparsemap reference.
|
||||||
* @param[in] offset The 0-based offset into the bitmap at which to split, if
|
* @param[in] idx The 0-based idx into the bitmap at which to split, if
|
||||||
* set to SPARSEMAP_IDX_MAX then the bits will be evenly split.
|
* set to SPARSEMAP_IDX_MAX then the bits will be evenly split.
|
||||||
* @param[in] other The bitmap into which we place the split.
|
* @param[in] other The bitmap into which we place the split.
|
||||||
* @returns the offset at which the map was split
|
* @returns the idx at which the map was split, or sets errno to ENOSPC and
|
||||||
|
* returns SPARSEMAP_IDX_MAX.
|
||||||
*/
|
*/
|
||||||
sparsemap_idx_t sparsemap_split(sparsemap_t *map, sparsemap_idx_t offset, sparsemap_t *other);
|
sparsemap_idx_t sparsemap_split(sparsemap_t *map, sparsemap_idx_t idx, sparsemap_t *other);
|
||||||
|
|
||||||
/** @brief Finds the index of the \b n'th bit set to \b value.
|
/** @brief Finds the index of the \b n'th bit set to \b value.
|
||||||
*
|
*
|
1882
src/sparsemap.c
1882
src/sparsemap.c
File diff suppressed because it is too large
Load diff
|
@ -18,8 +18,8 @@
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "../include/common.h"
|
#include <common.h>
|
||||||
#include "../include/sparsemap.h"
|
#include <sparsemap.h>
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wvariadic-macros"
|
#pragma GCC diagnostic ignored "-Wvariadic-macros"
|
||||||
|
@ -51,6 +51,12 @@ tsc(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO remove me, this is only used for debugging.
|
||||||
|
#ifdef SPARSEMAP_TESTING
|
||||||
|
char *QCC_showSparsemap(void *value, int len);
|
||||||
|
char *QCC_showChunk(void *value, int len);
|
||||||
|
#endif
|
||||||
|
|
||||||
// get microsecond timestamp
|
// get microsecond timestamp
|
||||||
uint64_t
|
uint64_t
|
||||||
msts()
|
msts()
|
||||||
|
@ -122,7 +128,7 @@ shuffle(int *array, size_t n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
static int
|
||||||
compare_ints(const void *a, const void *b)
|
compare_ints(const void *a, const void *b)
|
||||||
{
|
{
|
||||||
return *(const int *)a - *(const int *)b;
|
return *(const int *)a - *(const int *)b;
|
||||||
|
@ -347,7 +353,7 @@ bitmap_from_uint32(sparsemap_t *map, uint32_t number)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 32; i++) {
|
for (int i = 0; i < 32; i++) {
|
||||||
bool bit = number & (1 << i);
|
bool bit = number & (1 << i);
|
||||||
sparsemap_set(map, i, bit);
|
sparsemap_assign(map, i, bit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,7 +404,7 @@ sm_bitmap_from_uint64(sparsemap_t *map, int offset, uint64_t number)
|
||||||
{
|
{
|
||||||
for (int i = offset; i < 64; i++) {
|
for (int i = offset; i < 64; i++) {
|
||||||
bool bit = number & ((uint64_t)1 << i);
|
bool bit = number & ((uint64_t)1 << i);
|
||||||
sparsemap_set(map, i, bit);
|
sparsemap_assign(map, i, bit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,7 +422,7 @@ sm_add_span(sparsemap_t *map, int map_size, int span_length)
|
||||||
}
|
}
|
||||||
} while (attempts);
|
} while (attempts);
|
||||||
for (sparsemap_idx_t i = placed_at; i < placed_at + span_length; i++) {
|
for (sparsemap_idx_t i = placed_at; i < placed_at + span_length; i++) {
|
||||||
if (sparsemap_set(map, i, true) != i) {
|
if (sparsemap_set(map, i) != i) {
|
||||||
return placed_at; // TODO error?
|
return placed_at; // TODO error?
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
#include "../include/sparsemap.h"
|
#include <sparsemap.h>
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wvariadic-macros"
|
#pragma GCC diagnostic ignored "-Wvariadic-macros"
|
|
@ -4,7 +4,7 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "../include/sparsemap.h"
|
#include <sparsemap.h>
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wvariadic-macros"
|
#pragma GCC diagnostic ignored "-Wvariadic-macros"
|
||||||
|
@ -35,16 +35,16 @@ main()
|
||||||
uint8_t buffer2[1024];
|
uint8_t buffer2[1024];
|
||||||
sparsemap_init(map, buffer, sizeof(buffer));
|
sparsemap_init(map, buffer, sizeof(buffer));
|
||||||
assert(sparsemap_get_size(map) == size);
|
assert(sparsemap_get_size(map) == size);
|
||||||
sparsemap_set(map, 0, true);
|
sparsemap_set(map, 0);
|
||||||
assert(sparsemap_get_size(map) == size + 4 + 8 + 8);
|
assert(sparsemap_get_size(map) == size + 4 + 8 + 8);
|
||||||
assert(sparsemap_is_set(map, 0) == true);
|
assert(sparsemap_is_set(map, 0) == true);
|
||||||
assert(sparsemap_get_size(map) == size + 4 + 8 + 8);
|
assert(sparsemap_get_size(map) == size + 4 + 8 + 8);
|
||||||
assert(sparsemap_is_set(map, 1) == false);
|
assert(sparsemap_is_set(map, 1) == false);
|
||||||
sparsemap_set(map, 0, false);
|
sparsemap_unset(map, 0);
|
||||||
assert(sparsemap_get_size(map) == size);
|
assert(sparsemap_get_size(map) == size);
|
||||||
|
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
sparsemap_set(map, 64, true);
|
sparsemap_set(map, 64);
|
||||||
assert(sparsemap_is_set(map, 64) == true);
|
assert(sparsemap_is_set(map, 64) == true);
|
||||||
assert(sparsemap_get_size(map) == size + 4 + 8 + 8);
|
assert(sparsemap_get_size(map) == size + 4 + 8 + 8);
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@ main()
|
||||||
// set [0..100000]
|
// set [0..100000]
|
||||||
for (int i = 0; i < 100000; i++) {
|
for (int i = 0; i < 100000; i++) {
|
||||||
assert(sparsemap_is_set(map, i) == false);
|
assert(sparsemap_is_set(map, i) == false);
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
if (i > 5) {
|
if (i > 5) {
|
||||||
for (int j = i - 5; j <= i; j++) {
|
for (int j = i - 5; j <= i; j++) {
|
||||||
assert(sparsemap_is_set(map, j) == true);
|
assert(sparsemap_is_set(map, j) == true);
|
||||||
|
@ -73,7 +73,7 @@ main()
|
||||||
// unset [0..10000]
|
// unset [0..10000]
|
||||||
for (int i = 0; i < 10000; i++) {
|
for (int i = 0; i < 10000; i++) {
|
||||||
assert(sparsemap_is_set(map, i) == true);
|
assert(sparsemap_is_set(map, i) == true);
|
||||||
sparsemap_set(map, i, false);
|
sparsemap_unset(map, i);
|
||||||
assert(sparsemap_is_set(map, i) == false);
|
assert(sparsemap_is_set(map, i) == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +87,7 @@ main()
|
||||||
// set [10000..0]
|
// set [10000..0]
|
||||||
for (int i = 10000; i >= 0; i--) {
|
for (int i = 10000; i >= 0; i--) {
|
||||||
assert(sparsemap_is_set(map, i) == false);
|
assert(sparsemap_is_set(map, i) == false);
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
assert(sparsemap_is_set(map, i) == true);
|
assert(sparsemap_is_set(map, i) == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ main()
|
||||||
// unset [10000..0]
|
// unset [10000..0]
|
||||||
for (int i = 10000; i >= 0; i--) {
|
for (int i = 10000; i >= 0; i--) {
|
||||||
assert(sparsemap_is_set(map, i) == true);
|
assert(sparsemap_is_set(map, i) == true);
|
||||||
sparsemap_set(map, i, false);
|
sparsemap_unset(map, i);
|
||||||
assert(sparsemap_is_set(map, i) == false);
|
assert(sparsemap_is_set(map, i) == false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,13 +117,13 @@ main()
|
||||||
fprintf(stderr, ".");
|
fprintf(stderr, ".");
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
|
|
||||||
sparsemap_set(map, 0, true);
|
sparsemap_set(map, 0);
|
||||||
sparsemap_set(map, 2048 * 2 + 1, true);
|
sparsemap_set(map, 2048 * 2 + 1);
|
||||||
assert(sparsemap_is_set(map, 0) == true);
|
assert(sparsemap_is_set(map, 0) == true);
|
||||||
assert(sparsemap_is_set(map, 2048 * 2 + 0) == false);
|
assert(sparsemap_is_set(map, 2048 * 2 + 0) == false);
|
||||||
assert(sparsemap_is_set(map, 2048 * 2 + 1) == true);
|
assert(sparsemap_is_set(map, 2048 * 2 + 1) == true);
|
||||||
assert(sparsemap_is_set(map, 2048 * 2 + 2) == false);
|
assert(sparsemap_is_set(map, 2048 * 2 + 2) == false);
|
||||||
sparsemap_set(map, 2048, true);
|
sparsemap_set(map, 2048);
|
||||||
assert(sparsemap_is_set(map, 0) == true);
|
assert(sparsemap_is_set(map, 0) == true);
|
||||||
assert(sparsemap_is_set(map, 2047) == false);
|
assert(sparsemap_is_set(map, 2047) == false);
|
||||||
assert(sparsemap_is_set(map, 2048) == true);
|
assert(sparsemap_is_set(map, 2048) == true);
|
||||||
|
@ -137,7 +137,7 @@ main()
|
||||||
fprintf(stderr, ".");
|
fprintf(stderr, ".");
|
||||||
|
|
||||||
for (int i = 0; i < 100000; i++) {
|
for (int i = 0; i < 100000; i++) {
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 100000; i++) {
|
for (int i = 0; i < 100000; i++) {
|
||||||
assert(sparsemap_select(map, i, true) == (unsigned)i);
|
assert(sparsemap_select(map, i, true) == (unsigned)i);
|
||||||
|
@ -147,7 +147,7 @@ main()
|
||||||
fprintf(stderr, ".");
|
fprintf(stderr, ".");
|
||||||
|
|
||||||
for (int i = 1; i < 513; i++) {
|
for (int i = 1; i < 513; i++) {
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
}
|
}
|
||||||
for (int i = 1; i < 513; i++) {
|
for (int i = 1; i < 513; i++) {
|
||||||
assert(sparsemap_select(map, i - 1, true) == (unsigned)i);
|
assert(sparsemap_select(map, i - 1, true) == (unsigned)i);
|
||||||
|
@ -157,7 +157,7 @@ main()
|
||||||
fprintf(stderr, ".");
|
fprintf(stderr, ".");
|
||||||
|
|
||||||
for (size_t i = 0; i < 8; i++) {
|
for (size_t i = 0; i < 8; i++) {
|
||||||
sparsemap_set(map, i * 10, true);
|
sparsemap_set(map, i * 10);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < 8; i++) {
|
for (size_t i = 0; i < 8; i++) {
|
||||||
assert(sparsemap_select(map, i, true) == (sparsemap_idx_t)i * 10);
|
assert(sparsemap_select(map, i, true) == (sparsemap_idx_t)i * 10);
|
||||||
|
@ -168,7 +168,7 @@ main()
|
||||||
sparsemap_init(sm2, buffer2, sizeof(buffer2));
|
sparsemap_init(sm2, buffer2, sizeof(buffer2));
|
||||||
sparsemap_clear(sm2);
|
sparsemap_clear(sm2);
|
||||||
for (int i = 0; i < 2048 * 2; i++) {
|
for (int i = 0; i < 2048 * 2; i++) {
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
}
|
}
|
||||||
sparsemap_split(map, 2048, sm2);
|
sparsemap_split(map, 2048, sm2);
|
||||||
for (int i = 0; i < 2048; i++) {
|
for (int i = 0; i < 2048; i++) {
|
||||||
|
@ -185,7 +185,7 @@ main()
|
||||||
sparsemap_init(sm2, buffer2, sizeof(buffer2));
|
sparsemap_init(sm2, buffer2, sizeof(buffer2));
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
for (int i = 0; i < 2048 * 3; i++) {
|
for (int i = 0; i < 2048 * 3; i++) {
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
assert(sparsemap_is_set(map, i) == true);
|
assert(sparsemap_is_set(map, i) == true);
|
||||||
}
|
}
|
||||||
sparsemap_split(map, 64, sm2);
|
sparsemap_split(map, 64, sm2);
|
|
@ -3,7 +3,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "../include/sparsemap.h"
|
#include <sparsemap.h>
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wvariadic-macros"
|
#pragma GCC diagnostic ignored "-Wvariadic-macros"
|
||||||
|
@ -33,13 +33,13 @@ main(void)
|
||||||
// when the map is full.
|
// when the map is full.
|
||||||
for (i = 0; i < 7744; i++) {
|
for (i = 0; i < 7744; i++) {
|
||||||
if (!i % 2) {
|
if (!i % 2) {
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
assert(sparsemap_is_set(map, i) == true);
|
assert(sparsemap_is_set(map, i) == true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// On 1024 KiB of buffer with every other bit set the map holds 7744 bits
|
// On 1024 KiB of buffer with every other bit set the map holds 7744 bits
|
||||||
// and then runs out of space. This next _set() call will fail.
|
// and then runs out of space. This next _set() call will fail.
|
||||||
sparsemap_set(map, ++i, true);
|
sparsemap_set(map, ++i);
|
||||||
assert(sparsemap_is_set(map, i) == true);
|
assert(sparsemap_is_set(map, i) == true);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
|
@ -62,11 +62,11 @@ main(void)
|
||||||
// set all the bits on in a random order
|
// set all the bits on in a random order
|
||||||
for (i = 0; i < 1024; i++) {
|
for (i = 0; i < 1024; i++) {
|
||||||
__diag("set %d\n", array[i]);
|
__diag("set %d\n", array[i]);
|
||||||
sparsemap_set(map, array[i], true);
|
sparsemap_set(map, array[i]);
|
||||||
assert(sparsemap_is_set(map, array[i]) == true);
|
assert(sparsemap_is_set(map, array[i]) == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
sparsemap_set(map, 1025, true);
|
sparsemap_set(map, 1025);
|
||||||
assert(sparsemap_is_set(map, 1025) == true);
|
assert(sparsemap_is_set(map, 1025) == true);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
|
@ -35,7 +35,7 @@ main(void)
|
||||||
|
|
||||||
// set all the bits on in a random order
|
// set all the bits on in a random order
|
||||||
for (i = 0; i < TEST_ARRAY_SIZE; i++) {
|
for (i = 0; i < TEST_ARRAY_SIZE; i++) {
|
||||||
sparsemap_set(map, array[i], true);
|
sparsemap_set(map, array[i]);
|
||||||
assert(sparsemap_is_set(map, array[i]) == true);
|
assert(sparsemap_is_set(map, array[i]) == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ main(void)
|
||||||
shuffle(array, TEST_ARRAY_SIZE);
|
shuffle(array, TEST_ARRAY_SIZE);
|
||||||
print_spans(array, TEST_ARRAY_SIZE);
|
print_spans(array, TEST_ARRAY_SIZE);
|
||||||
for (i = 0; i < TEST_ARRAY_SIZE; i++) {
|
for (i = 0; i < TEST_ARRAY_SIZE; i++) {
|
||||||
sparsemap_set(map, array[i], true);
|
sparsemap_set(map, array[i]);
|
||||||
assert(sparsemap_is_set(map, array[i]) == true);
|
assert(sparsemap_is_set(map, array[i]) == true);
|
||||||
}
|
}
|
||||||
has_span(map, array, TEST_ARRAY_SIZE, (int)len);
|
has_span(map, array, TEST_ARRAY_SIZE, (int)len);
|
831
test/qc.c
Normal file
831
test/qc.c
Normal file
|
@ -0,0 +1,831 @@
|
||||||
|
/********************************************************************
|
||||||
|
* Copyright (c) 2014, Andrea Zito
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* License: BSD3
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
#include "qc.h"
|
||||||
|
|
||||||
|
// TODO: fix these...
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wimplicit-function-declaration"
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||||
|
#pragma GCC diagnostic ignored "-Wint-conversion"
|
||||||
|
#pragma GCC diagnostic ignored "-Wpedantic"
|
||||||
|
|
||||||
|
typedef void (*QCC_genRaw)(void *ptr);
|
||||||
|
typedef void (*QCC_genRawR)(void *ptr, void *from, void *to);
|
||||||
|
|
||||||
|
struct QCC_Stamp {
|
||||||
|
char *label;
|
||||||
|
int n;
|
||||||
|
struct QCC_Stamp *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef struct QCC_Result {
|
||||||
|
QCC_TestStatus status;
|
||||||
|
QCC_Stamp *stamps;
|
||||||
|
QCC_GenValue **arguments;
|
||||||
|
int argumentsN;
|
||||||
|
} QCC_Result;
|
||||||
|
|
||||||
|
enum QCC_deref_type { NONE, LONG, INT, FLOAT, DOUBLE, BYTE, CHAR };
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_init(int seed)
|
||||||
|
{
|
||||||
|
if (seed) {
|
||||||
|
srandom(seed);
|
||||||
|
} else {
|
||||||
|
srandom(time(NULL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* Generators helper functions
|
||||||
|
***********************************************************************/
|
||||||
|
static char *
|
||||||
|
QCC_showSimpleValue(void *value, enum QCC_deref_type dt, int maxsize, const char *format)
|
||||||
|
{
|
||||||
|
char *vc = malloc(sizeof(char) * (maxsize + 1));
|
||||||
|
|
||||||
|
switch (dt) {
|
||||||
|
case LONG:
|
||||||
|
snprintf(vc, maxsize + 1, format, *(long *)value);
|
||||||
|
break;
|
||||||
|
case INT:
|
||||||
|
snprintf(vc, maxsize + 1, format, *(int *)value);
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
snprintf(vc, maxsize + 1, format, *(float *)value);
|
||||||
|
break;
|
||||||
|
case DOUBLE:
|
||||||
|
snprintf(vc, maxsize + 1, format, *(double *)value);
|
||||||
|
break;
|
||||||
|
case CHAR:
|
||||||
|
snprintf(vc, maxsize + 1, format, *(char *)value);
|
||||||
|
break;
|
||||||
|
case BYTE:
|
||||||
|
snprintf(vc, maxsize + 1, format, *(uint8_t *)value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
snprintf(vc, maxsize + 1, format, *(int *)value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
vc[maxsize] = 0;
|
||||||
|
return vc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
QCC_freeSimpleValue(void *value)
|
||||||
|
{
|
||||||
|
free(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_initGenValue(void *value, int n, QCC_showValue show, QCC_freeValue free)
|
||||||
|
{
|
||||||
|
QCC_GenValue *gv = malloc(sizeof(QCC_GenValue));
|
||||||
|
*gv = (QCC_GenValue) { .value = value, .n = n, .show = show, .free = free };
|
||||||
|
|
||||||
|
return gv;
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* Generators implementations
|
||||||
|
***********************************************************************/
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showLong(void *value, int len)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleValue(value, LONG, 21, "%ld");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_genLongAtR(long *l, long *from, long *to)
|
||||||
|
{
|
||||||
|
long _from = *from;
|
||||||
|
long _to = *to;
|
||||||
|
unsigned long n = _to - _from;
|
||||||
|
|
||||||
|
if (n > RAND_MAX) {
|
||||||
|
_from = _from + n / 2 - RAND_MAX / 2;
|
||||||
|
_to = _from + n / 2 + RAND_MAX / 2;
|
||||||
|
n = _to - _from;
|
||||||
|
}
|
||||||
|
*l = (random() % n) + _from;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_genLongAt(long *l)
|
||||||
|
{
|
||||||
|
long from = QCC_LONG_FROM;
|
||||||
|
long to = QCC_LONG_TO;
|
||||||
|
QCC_genLongAtR(l, &from, &to);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genLongR(long from, long to)
|
||||||
|
{
|
||||||
|
long *v = malloc(sizeof(long));
|
||||||
|
QCC_genLongAtR(v, &from, &to);
|
||||||
|
|
||||||
|
return QCC_initGenValue(v, 1, QCC_showLong, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genLong()
|
||||||
|
{
|
||||||
|
return QCC_genLongR(QCC_LONG_FROM, QCC_LONG_TO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showInt(void *value, int len)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleValue(value, INT, 11, "%d");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_genIntAtR(int *i, int *from, int *to)
|
||||||
|
{
|
||||||
|
int n = *to - *from;
|
||||||
|
*i = (random() % n) + *from;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_genIntAt(int *i)
|
||||||
|
{
|
||||||
|
int from = QCC_INT_FROM;
|
||||||
|
int to = QCC_INT_TO;
|
||||||
|
QCC_genIntAtR(i, &from, &to);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genIntR(int from, int to)
|
||||||
|
{
|
||||||
|
int *v = malloc(sizeof(int));
|
||||||
|
QCC_genIntAtR(v, &from, &to);
|
||||||
|
|
||||||
|
return QCC_initGenValue(v, 1, QCC_showInt, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genInt()
|
||||||
|
{
|
||||||
|
return QCC_genIntR(QCC_INT_FROM, QCC_INT_TO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showDouble(void *value, int len)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleValue(value, DOUBLE, 50, "%.12e");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_genDoubleAtR(double *d, double *from, double *to)
|
||||||
|
{
|
||||||
|
double r = (double)random() / (double)RAND_MAX;
|
||||||
|
*d = *from + (*to - *from) * r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_genDoubleAt(double *d)
|
||||||
|
{
|
||||||
|
double from = QCC_DOUBLE_FROM;
|
||||||
|
double to = QCC_DOUBLE_TO;
|
||||||
|
QCC_genDoubleAtR(d, &from, &to);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genDoubleR(double from, double to)
|
||||||
|
{
|
||||||
|
double *v = malloc(sizeof(double));
|
||||||
|
QCC_genDoubleAtR(v, &from, &to);
|
||||||
|
return QCC_initGenValue(v, 1, QCC_showDouble, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genDouble()
|
||||||
|
{
|
||||||
|
return QCC_genDoubleR(QCC_DOUBLE_FROM, QCC_DOUBLE_TO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showFloat(void *value, int len)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleValue(value, FLOAT, 50, "%.12e");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_genFloatAtR(float *f, float *from, float *to)
|
||||||
|
{
|
||||||
|
float r = (float)random() / (float)RAND_MAX;
|
||||||
|
*f = *from + (*to - *from) * r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_genFloatAt(float *f)
|
||||||
|
{
|
||||||
|
float from = QCC_FLOAT_FROM;
|
||||||
|
float to = QCC_FLOAT_TO;
|
||||||
|
QCC_genFloatAtR(f, &from, &to);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genFloatR(float from, float to)
|
||||||
|
{
|
||||||
|
float *v = malloc(sizeof(float));
|
||||||
|
QCC_genFloatAtR(v, &from, &to);
|
||||||
|
|
||||||
|
return QCC_initGenValue(v, 1, QCC_showFloat, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genFloat()
|
||||||
|
{
|
||||||
|
return QCC_genFloatR(QCC_FLOAT_FROM, QCC_FLOAT_TO);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showBoolean(void *value, int len)
|
||||||
|
{
|
||||||
|
QCC_Boolean *b = (QCC_Boolean *)value;
|
||||||
|
|
||||||
|
if (*b) {
|
||||||
|
return strdup("TRUE");
|
||||||
|
} else {
|
||||||
|
return strdup("FALSE");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_genBooleanAt(QCC_Boolean *b)
|
||||||
|
{
|
||||||
|
double r = (double)random() / (double)RAND_MAX;
|
||||||
|
*b = r > 0.5 ? QCC_TRUE : QCC_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genBoolean()
|
||||||
|
{
|
||||||
|
QCC_Boolean *v = malloc(sizeof(QCC_Boolean));
|
||||||
|
QCC_genBooleanAt(v);
|
||||||
|
|
||||||
|
return QCC_initGenValue(v, 1, QCC_showBoolean, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showChar(void *value, int len)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleValue(value, CHAR, 3, "'%c'");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_genCharAt(char *c)
|
||||||
|
{
|
||||||
|
*c = (char)(random() % 93) + 33;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genChar()
|
||||||
|
{
|
||||||
|
char *v = malloc(sizeof(char));
|
||||||
|
QCC_genCharAt(v);
|
||||||
|
|
||||||
|
return QCC_initGenValue(v, 1, QCC_showChar, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* Array generators implementations
|
||||||
|
***********************************************************************/
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayOf(int len, QCC_genRaw elemGen, size_t elemSize, QCC_showValue show, QCC_freeValue free)
|
||||||
|
{
|
||||||
|
int n = random() % len;
|
||||||
|
uint8_t *arr = malloc(n * elemSize);
|
||||||
|
|
||||||
|
int p, i;
|
||||||
|
for (i = 0, p = 0; i < n; i++, p += elemSize)
|
||||||
|
elemGen(arr + p);
|
||||||
|
|
||||||
|
return QCC_initGenValue(arr, n, show, free);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayOfR(int len, QCC_genRawR elemGen, void *from, void *to, size_t elemSize, QCC_showValue show, QCC_freeValue free)
|
||||||
|
{
|
||||||
|
int n = random() % len;
|
||||||
|
uint8_t *arr = malloc(n * elemSize);
|
||||||
|
|
||||||
|
int p, i;
|
||||||
|
for (i = 0, p = 0; i < n; i++, p += elemSize)
|
||||||
|
elemGen(arr + p, from, to);
|
||||||
|
|
||||||
|
return QCC_initGenValue(arr, n, show, free);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showString(void *value, int len)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleValue(value, NONE, len, "%s");
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genStringL(int len)
|
||||||
|
{
|
||||||
|
QCC_GenValue *s = QCC_genArrayOf(len, (QCC_genRaw)QCC_genCharAt, sizeof(char), QCC_showString, QCC_freeSimpleValue);
|
||||||
|
((char *)s->value)[s->n - 1] = '\0';
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genString()
|
||||||
|
{
|
||||||
|
return QCC_genStringL(50);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showSimpleArray(void *value, size_t elemSize, QCC_showValue showValue, int len)
|
||||||
|
{
|
||||||
|
char **valStr = malloc(sizeof(char *) * len);
|
||||||
|
int valStrLen = 0;
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
valStr[i] = showValue(((uint8_t *)value) + (i * elemSize), 1);
|
||||||
|
valStrLen += strlen(valStr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
int totStrLen = valStrLen + 2 + 2 * len + 1;
|
||||||
|
char *str = malloc(sizeof(char) * totStrLen);
|
||||||
|
sprintf(str, "[");
|
||||||
|
int currLen = 1;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
if (i == 0) {
|
||||||
|
sprintf(str + currLen, "%s", valStr[i]);
|
||||||
|
} else {
|
||||||
|
sprintf(str + currLen, ", %s", valStr[i]);
|
||||||
|
currLen += 2;
|
||||||
|
}
|
||||||
|
currLen += strlen(valStr[i]);
|
||||||
|
free(valStr[i]);
|
||||||
|
}
|
||||||
|
sprintf(str + currLen, "]");
|
||||||
|
free(valStr);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showByte(void *value, int len)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleValue(value, BYTE, 3, "'%x'");
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showArrayByte(void *value, int n)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleArray(value, sizeof(long), QCC_showByte, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_genByteAtR(uint8_t *b, uint8_t *from, uint8_t *to)
|
||||||
|
{
|
||||||
|
uint8_t r = (uint8_t)random() / (uint8_t)RAND_MAX;
|
||||||
|
*b = *from + (*to - *from) * r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_genByteAt(uint8_t *b)
|
||||||
|
{
|
||||||
|
*b = ((uint8_t)random() / (uint8_t)RAND_MAX) % UINT8_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayByteLR(int len, long from, long to)
|
||||||
|
{
|
||||||
|
return QCC_genArrayOfR(len, (QCC_genRawR)QCC_genByteAtR, &from, &to, sizeof(long), QCC_showArrayByte, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayByteL(int len)
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genByteAt, sizeof(long), QCC_showArrayByte, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayByte()
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genByteAt, sizeof(long), QCC_showArrayByte, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showArrayLong(void *value, int n)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleArray(value, sizeof(long), QCC_showLong, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayLongLR(int len, long from, long to)
|
||||||
|
{
|
||||||
|
return QCC_genArrayOfR(len, (QCC_genRawR)QCC_genLongAtR, &from, &to, sizeof(long), QCC_showArrayLong, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayLongL(int len)
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genLongAt, sizeof(long), QCC_showArrayLong, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayLong()
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genLongAt, sizeof(long), QCC_showArrayLong, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showArrayInt(void *value, int n)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleArray(value, sizeof(int), QCC_showInt, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayIntLR(int len, int from, int to)
|
||||||
|
{
|
||||||
|
return QCC_genArrayOfR(len, (QCC_genRawR)QCC_genIntAtR, &from, &to, sizeof(int), QCC_showArrayInt, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayIntL(int len)
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genIntAt, sizeof(int), QCC_showArrayInt, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayInt()
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genIntAt, sizeof(int), QCC_showArrayInt, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showArrayDouble(void *value, int n)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleArray(value, sizeof(double), QCC_showDouble, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayDoubleLR(int len, double from, double to)
|
||||||
|
{
|
||||||
|
return QCC_genArrayOfR(len, (QCC_genRawR)QCC_genDoubleAtR, &from, &to, sizeof(double), QCC_showArrayDouble, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayDoubleL(int len)
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genDoubleAt, sizeof(double), QCC_showArrayDouble, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayDouble()
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genDoubleAt, sizeof(double), QCC_showArrayDouble, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showArrayFloat(void *value, int n)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleArray(value, sizeof(float), QCC_showFloat, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayFloatLR(int len, float from, float to)
|
||||||
|
{
|
||||||
|
return QCC_genArrayOfR(len, (QCC_genRawR)QCC_genFloatAtR, &from, &to, sizeof(float), QCC_showArrayFloat, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayFloatL(int len)
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genFloatAt, sizeof(float), QCC_showArrayFloat, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayFloat()
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genFloatAt, sizeof(float), QCC_showArrayFloat, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showArrayBoolean(void *value, int n)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleArray(value, sizeof(QCC_Boolean), QCC_showBoolean, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayBooleanL(int len)
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genBooleanAt, sizeof(QCC_Boolean), QCC_showArrayBoolean, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayBoolean()
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genBooleanAt, sizeof(QCC_Boolean), QCC_showArrayBoolean, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *
|
||||||
|
QCC_showArrayChar(void *value, int n)
|
||||||
|
{
|
||||||
|
return QCC_showSimpleArray(value, sizeof(char), QCC_showChar, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayCharL(int len)
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(len, (QCC_genRaw)QCC_genCharAt, sizeof(char), QCC_showArrayChar, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_GenValue *
|
||||||
|
QCC_genArrayChar()
|
||||||
|
{
|
||||||
|
return QCC_genArrayOf(50, (QCC_genRaw)QCC_genCharAt, sizeof(char), QCC_showArrayChar, QCC_freeSimpleValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* Convenience functions
|
||||||
|
***********************************************************************/
|
||||||
|
QCC_TestStatus
|
||||||
|
QCC_not(QCC_TestStatus propStatus)
|
||||||
|
{
|
||||||
|
switch (propStatus) {
|
||||||
|
case QCC_OK:
|
||||||
|
return QCC_FAIL;
|
||||||
|
case QCC_FAIL:
|
||||||
|
return QCC_OK;
|
||||||
|
case QCC_NOTHING:
|
||||||
|
return QCC_NOTHING;
|
||||||
|
default:
|
||||||
|
return QCC_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_TestStatus
|
||||||
|
QCC_and(QCC_TestStatus prop1Status, QCC_TestStatus prop2Status)
|
||||||
|
{
|
||||||
|
switch (prop1Status) {
|
||||||
|
case QCC_OK:
|
||||||
|
return prop2Status;
|
||||||
|
case QCC_FAIL:
|
||||||
|
return QCC_FAIL;
|
||||||
|
case QCC_NOTHING:
|
||||||
|
return QCC_NOTHING;
|
||||||
|
default:
|
||||||
|
return QCC_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_TestStatus
|
||||||
|
QCC_or(QCC_TestStatus prop1Status, QCC_TestStatus prop2Status)
|
||||||
|
{
|
||||||
|
switch (prop1Status) {
|
||||||
|
case QCC_OK:
|
||||||
|
return QCC_OK;
|
||||||
|
case QCC_FAIL:
|
||||||
|
case QCC_NOTHING:
|
||||||
|
return prop2Status;
|
||||||
|
default:
|
||||||
|
return QCC_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_TestStatus
|
||||||
|
QCC_xor(QCC_TestStatus prop1Status, QCC_TestStatus prop2Status)
|
||||||
|
{
|
||||||
|
switch (prop1Status) {
|
||||||
|
case QCC_OK:
|
||||||
|
return QCC_not(prop2Status);
|
||||||
|
case QCC_FAIL:
|
||||||
|
return prop2Status;
|
||||||
|
case QCC_NOTHING:
|
||||||
|
return QCC_NOTHING;
|
||||||
|
default:
|
||||||
|
return QCC_FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* Categorization function
|
||||||
|
***********************************************************************/
|
||||||
|
void
|
||||||
|
QCC_freeStamp(QCC_Stamp *stamps)
|
||||||
|
{
|
||||||
|
QCC_Stamp *tstamps;
|
||||||
|
QCC_Stamp *curr;
|
||||||
|
for (tstamps = stamps; tstamps != NULL;) {
|
||||||
|
curr = tstamps;
|
||||||
|
tstamps = tstamps->next;
|
||||||
|
free(curr->label);
|
||||||
|
free(curr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
QCC_insertOrdStamp(QCC_Stamp **stamps, QCC_Stamp *s)
|
||||||
|
{
|
||||||
|
QCC_Stamp *new = malloc(sizeof(QCC_Stamp));
|
||||||
|
*new = (QCC_Stamp) { .label = strdup(s->label), .n = s->n, .next = NULL };
|
||||||
|
|
||||||
|
QCC_Stamp *tstamp = *stamps;
|
||||||
|
QCC_Stamp **pre = stamps;
|
||||||
|
while (tstamp) {
|
||||||
|
if (tstamp->n < new->n) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
pre = &tstamp->next;
|
||||||
|
tstamp = tstamp->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*pre = new;
|
||||||
|
new->next = tstamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QCC_Stamp *
|
||||||
|
QCC_sortStamp(QCC_Stamp *stamps)
|
||||||
|
{
|
||||||
|
QCC_Stamp *sortedStamps = NULL;
|
||||||
|
QCC_Stamp *tstamp;
|
||||||
|
for (tstamp = stamps; tstamp != NULL; tstamp = tstamp->next) {
|
||||||
|
QCC_insertOrdStamp(&sortedStamps, tstamp);
|
||||||
|
}
|
||||||
|
return sortedStamps;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
QCC_labelN(QCC_Stamp **stamps, char *label, int n)
|
||||||
|
{
|
||||||
|
QCC_Stamp *ptr = *stamps;
|
||||||
|
QCC_Stamp *pre = NULL;
|
||||||
|
QCC_Stamp *new;
|
||||||
|
|
||||||
|
while (ptr) {
|
||||||
|
if (strcmp(ptr->label, label) == 0) {
|
||||||
|
(ptr->n)++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pre = ptr;
|
||||||
|
ptr = ptr->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
new = malloc(sizeof(QCC_Stamp));
|
||||||
|
*new = (QCC_Stamp) { .label = strdup(label), .n = 1, .next = NULL };
|
||||||
|
|
||||||
|
if (pre) {
|
||||||
|
pre->next = new;
|
||||||
|
} else {
|
||||||
|
*stamps = new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_label(QCC_Stamp **stamps, char *label)
|
||||||
|
{
|
||||||
|
return QCC_labelN(stamps, label, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
QCC_mergeLabels(QCC_Stamp **dst, QCC_Stamp *src)
|
||||||
|
{
|
||||||
|
for (; src != NULL; src = src->next)
|
||||||
|
QCC_labelN(dst, src->label, src->n);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***********************************************************************
|
||||||
|
* Testing functions
|
||||||
|
***********************************************************************/
|
||||||
|
void
|
||||||
|
QCC_freeGenValues(QCC_GenValue **arguments, int argumentsN)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < argumentsN; i++) {
|
||||||
|
arguments[i]->free(arguments[i]->value);
|
||||||
|
free(arguments[i]);
|
||||||
|
}
|
||||||
|
if (arguments)
|
||||||
|
free(arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
QCC_freeResult(QCC_Result *res)
|
||||||
|
{
|
||||||
|
QCC_freeStamp(res->stamps);
|
||||||
|
QCC_freeGenValues(res->arguments, res->argumentsN);
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_Result
|
||||||
|
QCC_vforAll(QCC_property prop, int genNum, va_list genP)
|
||||||
|
{ // QCC_gen *genLst,
|
||||||
|
QCC_GenValue **vals = NULL;
|
||||||
|
if (genNum) {
|
||||||
|
int i;
|
||||||
|
vals = malloc(sizeof(QCC_GenValue *) * genNum);
|
||||||
|
for (i = 0; i < genNum; i++) {
|
||||||
|
QCC_gen gen = va_arg(genP, QCC_gen);
|
||||||
|
vals[i] = gen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_Stamp *stamps = NULL;
|
||||||
|
QCC_TestStatus status = prop(vals, genNum, &stamps);
|
||||||
|
return (QCC_Result) { .status = status, .stamps = stamps, .arguments = vals, .argumentsN = genNum };
|
||||||
|
}
|
||||||
|
|
||||||
|
QCC_Result
|
||||||
|
QCC_forAll(QCC_property prop, int genNum, ...)
|
||||||
|
{ // QCC_gen *genLst,
|
||||||
|
va_list genP;
|
||||||
|
QCC_Result res;
|
||||||
|
va_start(genP, genNum);
|
||||||
|
res = QCC_vforAll(prop, genNum, genP);
|
||||||
|
va_end(genP);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
QCC_printStamps(QCC_Stamp *stamps, int n)
|
||||||
|
{
|
||||||
|
QCC_Stamp *sortedStamps = QCC_sortStamp(stamps);
|
||||||
|
QCC_Stamp *tstamps;
|
||||||
|
for (tstamps = sortedStamps; tstamps != NULL; tstamps = tstamps->next)
|
||||||
|
printf("%.2f%%\t%s\n", (tstamps->n / (float)n) * 100, tstamps->label);
|
||||||
|
if (sortedStamps)
|
||||||
|
QCC_freeStamp(sortedStamps);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
QCC_printArguments(QCC_GenValue **arguments, int argumentsN)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < argumentsN; i++) {
|
||||||
|
char *s = arguments[i]->show(arguments[i]->value, arguments[i]->n);
|
||||||
|
printf("%s\n", s);
|
||||||
|
free(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
QCC_testForAll(int num, int maxFail, QCC_property prop, int genNum, ...)
|
||||||
|
{
|
||||||
|
va_list genP;
|
||||||
|
int succ = 0;
|
||||||
|
int fail = 0;
|
||||||
|
|
||||||
|
QCC_Result res = { .status = QCC_OK };
|
||||||
|
QCC_Stamp *stamps = NULL;
|
||||||
|
while (succ < num && fail < maxFail) {
|
||||||
|
va_start(genP, genNum);
|
||||||
|
res = QCC_vforAll(prop, genNum, genP);
|
||||||
|
va_end(genP);
|
||||||
|
|
||||||
|
if (res.status == QCC_FAIL) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (res.status == QCC_OK) {
|
||||||
|
succ++;
|
||||||
|
QCC_mergeLabels(&stamps, res.stamps);
|
||||||
|
} else
|
||||||
|
fail++;
|
||||||
|
QCC_freeResult(&res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (succ == num) {
|
||||||
|
//printf("%d test passed (%d)!\n", succ, fail);
|
||||||
|
QCC_printStamps(stamps, succ);
|
||||||
|
if (stamps)
|
||||||
|
QCC_freeStamp(stamps);
|
||||||
|
return 0;
|
||||||
|
} else if (res.status == QCC_FAIL) {
|
||||||
|
printf("\nFalsifiable after %d test\n", succ + 1);
|
||||||
|
QCC_printArguments(res.arguments, res.argumentsN);
|
||||||
|
QCC_freeResult(&res);
|
||||||
|
if (stamps)
|
||||||
|
QCC_freeStamp(stamps);
|
||||||
|
return 1;
|
||||||
|
} else if (fail >= maxFail) {
|
||||||
|
printf("\nGave up after %d tests!\n", succ);
|
||||||
|
QCC_printStamps(stamps, succ);
|
||||||
|
if (stamps)
|
||||||
|
QCC_freeStamp(stamps);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#pragma GCC diagnostic pop
|
284
test/qc.h
Normal file
284
test/qc.h
Normal file
|
@ -0,0 +1,284 @@
|
||||||
|
/********************************************************************
|
||||||
|
* Copyright (c) 2014, Andrea Zito
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* License: BSD3
|
||||||
|
********************************************************************/
|
||||||
|
|
||||||
|
#ifndef QUICKCHECK4C_C
|
||||||
|
#define QUICKCHECK4C_C
|
||||||
|
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/* Default ranges used for type generation */
|
||||||
|
#define QCC_LONG_FROM -RAND_MAX / 2
|
||||||
|
#define QCC_LONG_TO RAND_MAX / 2
|
||||||
|
#define QCC_INT_FROM -RAND_MAX / 2
|
||||||
|
#define QCC_INT_TO RAND_MAX / 2
|
||||||
|
#define QCC_DOUBLE_FROM ((double)-(RAND_MAX / 2))
|
||||||
|
#define QCC_DOUBLE_TO ((double)(RAND_MAX / 2))
|
||||||
|
#define QCC_FLOAT_FROM ((float)-(RAND_MAX / 2))
|
||||||
|
#define QCC_FLOAT_TO ((float)(RAND_MAX / 2))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Explicit definition of a boolean type compatible with
|
||||||
|
* normal C boolean interpretation.
|
||||||
|
*/
|
||||||
|
typedef enum { QCC_TRUE = 1, QCC_FALSE = 0 } QCC_Boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enumerator defining the evaluation of a property.
|
||||||
|
* QCC_OK represent a satisfied property.
|
||||||
|
* QCC_FAIL represent a falsified property.
|
||||||
|
* QCC_NOTHING indicates a property that didn't fail
|
||||||
|
* but should NOT be considered in the successes count or
|
||||||
|
* categorization (used by QCC_IMPLY)
|
||||||
|
*/
|
||||||
|
typedef enum { QCC_OK = 1, QCC_FAIL = 0, QCC_NOTHING = -1 } QCC_TestStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenince macro to extract generated values inside a property.
|
||||||
|
* Es: int a = *QCC_getValue(vals, 0, int*)
|
||||||
|
*
|
||||||
|
* @param vals A QCC_GenValue array
|
||||||
|
* @param idx Index of the value to extract
|
||||||
|
* @param type Type of the generated value
|
||||||
|
*/
|
||||||
|
#define QCC_getValue(vals, idx, type) ((type)vals[idx]->value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signature of function used to represent values in a human readable
|
||||||
|
* fashion.
|
||||||
|
* Calling this function allocates memory that must be freed by the caller.
|
||||||
|
*
|
||||||
|
* @param value Pointer to the value to show
|
||||||
|
* @param len Lenght of the data (used to keep track of array lengths)
|
||||||
|
* @return String representation of the value.
|
||||||
|
*/
|
||||||
|
typedef char *(*QCC_showValue)(void *value, int len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signature of function used to free generated values.
|
||||||
|
*
|
||||||
|
* @param value Pointer to the value to free
|
||||||
|
*/
|
||||||
|
typedef void (*QCC_freeValue)(void *value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure defining a generated value.
|
||||||
|
* It specifies the value itself (alongside its length in case
|
||||||
|
* of arrays), a function pointer for getting a human readable
|
||||||
|
* string representation of the value and a function pointer to
|
||||||
|
* free the memory allocated for the value.
|
||||||
|
*
|
||||||
|
* @param value Pointer to the memory allocated for the value
|
||||||
|
* @param n Length of the value (1 for simple types, length for arrays)
|
||||||
|
* @param show Function to get string representation of the value
|
||||||
|
* @param free Function to free the memory of the value
|
||||||
|
*/
|
||||||
|
typedef struct QCC_GenValue {
|
||||||
|
void *value;
|
||||||
|
int n;
|
||||||
|
QCC_showValue show;
|
||||||
|
QCC_freeValue free;
|
||||||
|
} QCC_GenValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opaque structure used to categorize test instances.
|
||||||
|
* See QCC_property and QCC_label.
|
||||||
|
*/
|
||||||
|
typedef struct QCC_Stamp QCC_Stamp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signature of functions used to generate random values
|
||||||
|
*/
|
||||||
|
typedef QCC_GenValue *(*QCC_gen)();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signature of property functions.
|
||||||
|
*
|
||||||
|
* @param vals Array of generated random values
|
||||||
|
* @param len Number of generated values
|
||||||
|
* @param stamp Used to categorize test instances via QCC_label
|
||||||
|
*/
|
||||||
|
typedef QCC_TestStatus (*QCC_property)(QCC_GenValue **vals, int len, QCC_Stamp **stamps);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements logic implication.
|
||||||
|
* The implication is satisfied if both the precondition and the property are
|
||||||
|
* satisfied.
|
||||||
|
* In case the precondition is not satisfied the special QCC_NOTHING value is
|
||||||
|
* returned, indicating that the current test should not be considered as a
|
||||||
|
* success.
|
||||||
|
*
|
||||||
|
* @param precondition A boolean condition
|
||||||
|
* @param property A QCC_property to evaluate iff the precondition is satisfied
|
||||||
|
* @return QCC_TestStatus
|
||||||
|
*/
|
||||||
|
#define QCC_imply(precondition, property) precondition ? property : QCC_NOTHING
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to express property conjunction.
|
||||||
|
* The function returns:
|
||||||
|
* - QCC_OK iff prop1Status == prop2Status == QCC_OK
|
||||||
|
* - QCC_FAIL iff prop1Status == QCC_FAIL || prop2Status == QCC_FAIL
|
||||||
|
* - QCC_NOTHING iff prop1Status != QCC_FAIL && prop2Status != QCC_FAIL &&
|
||||||
|
* (prop1Status == QCC_NOTHING || prop2Status == QCC_NOTHING)
|
||||||
|
*
|
||||||
|
* @param prop1Status The evaluation status of property 1
|
||||||
|
* @param prop2Status The evaluation status of property 2
|
||||||
|
* @return The evaluation status of the conjunction of property 1 and property 2
|
||||||
|
*/
|
||||||
|
QCC_TestStatus QCC_and(QCC_TestStatus prop1Status, QCC_TestStatus prop2Status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to express property disjunction.
|
||||||
|
* The function returns:
|
||||||
|
* - QCC_OK iff prop1Status == QCC_OK || prop2Status == QCC_OK
|
||||||
|
* - QCC_FAIL iff prop1Status == QCC_FAIL && prop2Status == QCC_FAIL
|
||||||
|
* - QCC_NOTHING iff prop1Status != QCC_OK && prop2Status != QCC_OK &&
|
||||||
|
* (prop1Status == QCC_NOTHING || prop2Status == QCC_NOTHING)
|
||||||
|
*
|
||||||
|
* @param prop1Status The evaluation status of property 1
|
||||||
|
* @param prop2Status The evaluation status of property 2
|
||||||
|
* @return The evaluation status of the disjunction of property 1 and property 2
|
||||||
|
*/
|
||||||
|
QCC_TestStatus QCC_or(QCC_TestStatus prop1Status, QCC_TestStatus prop2Status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to express property exclusive disjunction.
|
||||||
|
* The function returns:
|
||||||
|
* - QCC_OK iff (prop1Status == QCC_OK && prop2Status == QCC_FAIL) ||
|
||||||
|
* (prop1Status == QCC_FAIL && prop2Status == QCC_OK)
|
||||||
|
* - QCC_FAIL iff prop1Status == prop2Status != QCC_NOTHING
|
||||||
|
* - QCC_NOTHING iff prop1Status == QCC_NOTHING || prop2Status = QCC_NOTHING
|
||||||
|
*
|
||||||
|
* @param prop1Status The evaluation status of property 1
|
||||||
|
* @param prop2Status The evaluation status of property 2
|
||||||
|
* @return The evaluation status of the exclusive disjunction of property 1 and
|
||||||
|
* property 2
|
||||||
|
*/
|
||||||
|
QCC_TestStatus QCC_xor(QCC_TestStatus prop1Status, QCC_TestStatus prop2Status);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to express property negation.
|
||||||
|
* The function returns:
|
||||||
|
* - QCC_OK iff propStatus == QCC_FAIL
|
||||||
|
* - QCC_FAIL iff propStatus == QCC_OK
|
||||||
|
* - QCC_NOTHING iff propStatus == QCC_NOTHING
|
||||||
|
*/
|
||||||
|
QCC_TestStatus QCC_not(QCC_TestStatus propStatus);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the random generator using a specific seed.
|
||||||
|
*
|
||||||
|
* @param seed The seed to use or 0 to automatically select seed
|
||||||
|
*/
|
||||||
|
void QCC_init(int seed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a label to the test stamps.
|
||||||
|
*
|
||||||
|
* @param stamps Stamps associated to the current test
|
||||||
|
* @param label Label to add to the test stamps
|
||||||
|
*/
|
||||||
|
void QCC_label(QCC_Stamp **stamps, char *label);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test a property for num times allowing at most maxFail unsuccesful
|
||||||
|
* argument generation.
|
||||||
|
*
|
||||||
|
* If all num test succede, the function outputs a success string and
|
||||||
|
* return 0. If argument generation fails maxFail time before a success
|
||||||
|
* string is printed reporting the number of success test cases and return -1.
|
||||||
|
*
|
||||||
|
* In both cases any label label gathered during test evaluation is printed
|
||||||
|
* alongside its distribution.
|
||||||
|
*
|
||||||
|
* If a set of arguments falsifying the property is found, the testing is
|
||||||
|
* interrupted immediately and a failure string is printed alongside the
|
||||||
|
* set of arguments which caused the failure.
|
||||||
|
*
|
||||||
|
* @parm num Number of successful test to perform
|
||||||
|
* @parm maxFail Maximum number of unsuccessful argument generation
|
||||||
|
* (i.e. unsatisfied implication precondition)
|
||||||
|
* @parm prop Property to test
|
||||||
|
* @parm genNum Number of generators specified as vararg
|
||||||
|
* @param ... genNum QCC_gen function to use as generators
|
||||||
|
* @return 0 (all num test passed),
|
||||||
|
* -1 (gave up after maxFail unsuccessful arguments generation),
|
||||||
|
* 1 (property falsified)
|
||||||
|
*/
|
||||||
|
int QCC_testForAll(int num, int maxFail, QCC_property prop, int genNum, ...);
|
||||||
|
|
||||||
|
/*************************************************************
|
||||||
|
* Helper function for generator definitions
|
||||||
|
*************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize a QCC_GenValue using the specified parameters
|
||||||
|
*
|
||||||
|
* @param value Raw generated balue
|
||||||
|
* @param n Length of the value in case of an array (1 should be used otherwise)
|
||||||
|
* @param show Pointer to a QCC_ShowValue function to use for displaying the raw
|
||||||
|
* value
|
||||||
|
* @param free Pointer to a QCC_FreeValue function to use for free the raw value
|
||||||
|
* memory
|
||||||
|
* @return Initialized QCC_GenValue
|
||||||
|
*/
|
||||||
|
QCC_GenValue *QCC_initGenValue(void *value, int n, QCC_showValue show, QCC_freeValue free);
|
||||||
|
|
||||||
|
/*************************************************************
|
||||||
|
* Simple types generators
|
||||||
|
*************************************************************/
|
||||||
|
QCC_GenValue *QCC_genLong();
|
||||||
|
QCC_GenValue *QCC_genLongR(long from, long to);
|
||||||
|
|
||||||
|
QCC_GenValue *QCC_genInt();
|
||||||
|
QCC_GenValue *QCC_genIntR(int from, int to);
|
||||||
|
|
||||||
|
QCC_GenValue *QCC_genDouble();
|
||||||
|
QCC_GenValue *QCC_genDoubleR(double from, double to);
|
||||||
|
|
||||||
|
QCC_GenValue *QCC_genFloat();
|
||||||
|
QCC_GenValue *QCC_genFloatR(float from, float to);
|
||||||
|
|
||||||
|
QCC_GenValue *QCC_genBoolean();
|
||||||
|
QCC_GenValue *QCC_genChar();
|
||||||
|
|
||||||
|
/*************************************************************
|
||||||
|
* Array types generators
|
||||||
|
*************************************************************/
|
||||||
|
QCC_GenValue *QCC_genString();
|
||||||
|
QCC_GenValue *QCC_genStringL(int len);
|
||||||
|
|
||||||
|
QCC_GenValue *QCC_genArrayByte();
|
||||||
|
QCC_GenValue *QCC_genArrayByteL(int len);
|
||||||
|
QCC_GenValue *QCC_genArrayByteLR(int len, long from, long to);
|
||||||
|
|
||||||
|
QCC_GenValue *QCC_genArrayLong();
|
||||||
|
QCC_GenValue *QCC_genArrayLongL(int len);
|
||||||
|
QCC_GenValue *QCC_genArrayLongLR(int len, long from, long to);
|
||||||
|
|
||||||
|
QCC_GenValue *QCC_genArrayInt();
|
||||||
|
QCC_GenValue *QCC_genArrayIntL(int len);
|
||||||
|
QCC_GenValue *QCC_genArrayIntLR(int len, int from, int to);
|
||||||
|
|
||||||
|
QCC_GenValue *QCC_genArrayDouble();
|
||||||
|
QCC_GenValue *QCC_genArrayDoubleL(int len);
|
||||||
|
QCC_GenValue *QCC_genArrayDoubleLR(int len, double from, double to);
|
||||||
|
|
||||||
|
QCC_GenValue *QCC_genArrayFloat();
|
||||||
|
QCC_GenValue *QCC_genArrayFloatL(int len);
|
||||||
|
QCC_GenValue *QCC_genArrayFloatLR(int len, float from, float to);
|
||||||
|
|
||||||
|
QCC_GenValue *QCC_genArrayBoolean();
|
||||||
|
QCC_GenValue *QCC_genArrayBooleanL(int len);
|
||||||
|
|
||||||
|
QCC_GenValue *QCC_genArrayChar();
|
||||||
|
QCC_GenValue *QCC_genArrayCharL(int len);
|
||||||
|
|
||||||
|
QCC_GenValue *QCC_genArrayString();
|
||||||
|
QCC_GenValue *QCC_genArrayStringL(int len, int strLen);
|
||||||
|
#endif
|
|
@ -10,10 +10,10 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "../include/common.h"
|
#include <common.h>
|
||||||
#include "../include/roaring.h"
|
#include <roaring.h>
|
||||||
#include "../include/sparsemap.h"
|
#include <sparsemap.h>
|
||||||
#include "../include/tdigest.h"
|
#include <tdigest.h>
|
||||||
|
|
||||||
#include "midl.c"
|
#include "midl.c"
|
||||||
|
|
||||||
|
@ -336,7 +336,7 @@ record_release_span_mutation(FILE *out, pgno_t pg, unsigned len)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
__scan_record_offsets(sm_idx_t v[], size_t n, void *aux)
|
__scan_record_offsets(uint32_t v[], size_t n, void *aux)
|
||||||
{
|
{
|
||||||
FILE *out = (FILE *)aux;
|
FILE *out = (FILE *)aux;
|
||||||
for (size_t i = 0; i < n; i++) {
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
@ -377,7 +377,7 @@ _sparsemap_set(sparsemap_t **_map, sparsemap_idx_t idx, bool value)
|
||||||
{
|
{
|
||||||
sparsemap_t *map = *_map, *new_map = NULL;
|
sparsemap_t *map = *_map, *new_map = NULL;
|
||||||
do {
|
do {
|
||||||
sparsemap_idx_t l = sparsemap_set(map, idx, value);
|
sparsemap_idx_t l = sparsemap_assign(map, idx, value);
|
||||||
if (l != idx) {
|
if (l != idx) {
|
||||||
if (errno == ENOSPC) {
|
if (errno == ENOSPC) {
|
||||||
size_t capacity = sparsemap_get_capacity(map) + 64;
|
size_t capacity = sparsemap_get_capacity(map) + 64;
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* smartmap is MIT-licensed, but for this file:
|
* sparsemap is MIT-licensed, but for this file:
|
||||||
*
|
*
|
||||||
* To the extent possible under law, the author(s) of this file have
|
* To the extent possible under law, the author(s) of this file have
|
||||||
* waived all copyright and related or neighboring rights to this
|
* waived all copyright and related or neighboring rights to this
|
||||||
|
@ -10,15 +10,15 @@
|
||||||
#define MUNIT_NO_FORK (1)
|
#define MUNIT_NO_FORK (1)
|
||||||
#define MUNIT_ENABLE_ASSERT_ALIASES (1)
|
#define MUNIT_ENABLE_ASSERT_ALIASES (1)
|
||||||
|
|
||||||
|
#include <common.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
#include <munit.h>
|
||||||
|
#include <qc.h>
|
||||||
|
#include <sparsemap.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "../include/sparsemap.h"
|
|
||||||
#include "common.h"
|
|
||||||
#include "munit.h"
|
|
||||||
|
|
||||||
#define munit_free free
|
#define munit_free free
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
|
@ -27,6 +27,9 @@
|
||||||
|
|
||||||
#define SELECT_FALSE
|
#define SELECT_FALSE
|
||||||
|
|
||||||
|
char *QCC_showSparsemap(void *value, int len);
|
||||||
|
char *QCC_showChunk(void *value, int len);
|
||||||
|
|
||||||
/* !!! Duplicated here for testing purposes. Keep in sync, or suffer. !!! */
|
/* !!! Duplicated here for testing purposes. Keep in sync, or suffer. !!! */
|
||||||
struct sparsemap {
|
struct sparsemap {
|
||||||
size_t m_capacity;
|
size_t m_capacity;
|
||||||
|
@ -40,21 +43,33 @@ struct user_data {
|
||||||
|
|
||||||
/* -------------------------- Supporting Functions for Testing */
|
/* -------------------------- Supporting Functions for Testing */
|
||||||
|
|
||||||
void
|
size_t
|
||||||
|
populate_map_rle(sparsemap_t *map, size_t loc, size_t num, size_t amount)
|
||||||
|
{
|
||||||
|
size_t i, len = munit_rand_int_range(1, num) * amount;
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
sparsemap_set(map, loc + i);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
populate_map(sparsemap_t *map, int size, int max_value)
|
populate_map(sparsemap_t *map, int size, int max_value)
|
||||||
{
|
{
|
||||||
int array[size];
|
int array[size];
|
||||||
size_t before;
|
size_t i, before;
|
||||||
|
|
||||||
setup_test_array(array, size, max_value);
|
setup_test_array(array, size, max_value);
|
||||||
shuffle(array, size);
|
shuffle(array, size);
|
||||||
before = sparsemap_count(map);
|
before = sparsemap_count(map);
|
||||||
for (int i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
||||||
sparsemap_set(map, array[i], true);
|
sparsemap_set(map, array[i]);
|
||||||
bool set = sparsemap_is_set(map, array[i]);
|
bool set = sparsemap_is_set(map, array[i]);
|
||||||
assert_true(set);
|
assert_true(set);
|
||||||
}
|
}
|
||||||
assert_true(sparsemap_count(map) == before + size);
|
assert_true(sparsemap_count(map) == before + size);
|
||||||
|
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
|
@ -88,6 +103,7 @@ test_api_new(const MunitParameter params[], void *data)
|
||||||
assert_ptr_not_null(map);
|
assert_ptr_not_null(map);
|
||||||
assert_true(map->m_capacity == 1024);
|
assert_true(map->m_capacity == 1024);
|
||||||
assert_true(map->m_data_used == sizeof(uint32_t));
|
assert_true(map->m_data_used == sizeof(uint32_t));
|
||||||
|
assert_true((((uint8_t)map->m_data[0]) & 0x03) == 0x00);
|
||||||
|
|
||||||
munit_free(map);
|
munit_free(map);
|
||||||
|
|
||||||
|
@ -205,7 +221,7 @@ test_api_clear(const MunitParameter params[], void *data)
|
||||||
|
|
||||||
assert_ptr_not_null(map);
|
assert_ptr_not_null(map);
|
||||||
|
|
||||||
sparsemap_set(map, 42, true);
|
sparsemap_set(map, 42);
|
||||||
assert_true(sparsemap_is_set(map, 42));
|
assert_true(sparsemap_is_set(map, 42));
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
assert_false(sparsemap_is_set(map, 42));
|
assert_false(sparsemap_is_set(map, 42));
|
||||||
|
@ -314,7 +330,7 @@ test_api_remaining_capacity(const MunitParameter params[], void *data)
|
||||||
int i = 0;
|
int i = 0;
|
||||||
double cap;
|
double cap;
|
||||||
do {
|
do {
|
||||||
sparsemap_set(map, i++, true);
|
sparsemap_set(map, i++ * 2);
|
||||||
cap = sparsemap_capacity_remaining(map);
|
cap = sparsemap_capacity_remaining(map);
|
||||||
} while (cap > 1.0 && errno != ENOSPC);
|
} while (cap > 1.0 && errno != ENOSPC);
|
||||||
errno = 0;
|
errno = 0;
|
||||||
|
@ -327,7 +343,7 @@ test_api_remaining_capacity(const MunitParameter params[], void *data)
|
||||||
i = 0;
|
i = 0;
|
||||||
do {
|
do {
|
||||||
int p = munit_rand_int_range(0, 150000);
|
int p = munit_rand_int_range(0, 150000);
|
||||||
sparsemap_set(map, p, true);
|
sparsemap_set(map, p);
|
||||||
i++;
|
i++;
|
||||||
cap = sparsemap_capacity_remaining(map);
|
cap = sparsemap_capacity_remaining(map);
|
||||||
} while (cap > 1.0 && errno != ENOSPC);
|
} while (cap > 1.0 && errno != ENOSPC);
|
||||||
|
@ -346,6 +362,7 @@ test_api_get_capacity_setup(const MunitParameter params[], void *user_data)
|
||||||
|
|
||||||
sparsemap_init(map, buf, 1024);
|
sparsemap_init(map, buf, 1024);
|
||||||
populate_map(map, 1024, 3 * 1024);
|
populate_map(map, 1024, 3 * 1024);
|
||||||
|
populate_map_rle(map, 3 * 1024, 5, 4096);
|
||||||
|
|
||||||
return (void *)map;
|
return (void *)map;
|
||||||
}
|
}
|
||||||
|
@ -365,7 +382,7 @@ test_api_get_capacity(const MunitParameter params[], void *data)
|
||||||
|
|
||||||
assert_ptr_not_null(map);
|
assert_ptr_not_null(map);
|
||||||
|
|
||||||
sparsemap_set(map, 42, true);
|
sparsemap_set(map, 42);
|
||||||
assert_true(sparsemap_is_set(map, 42));
|
assert_true(sparsemap_is_set(map, 42));
|
||||||
assert_true(sparsemap_get_capacity(map) == 1024);
|
assert_true(sparsemap_get_capacity(map) == 1024);
|
||||||
|
|
||||||
|
@ -400,9 +417,16 @@ test_api_is_set(const MunitParameter params[], void *data)
|
||||||
|
|
||||||
assert_ptr_not_null(map);
|
assert_ptr_not_null(map);
|
||||||
|
|
||||||
sparsemap_set(map, 42, true);
|
sparsemap_set(map, 42);
|
||||||
assert_true(sparsemap_is_set(map, 42));
|
assert_true(sparsemap_is_set(map, 42));
|
||||||
|
|
||||||
|
sparsemap_clear(map);
|
||||||
|
size_t n = populate_map_rle(map, 0, 10, 2718);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < n; i++) {
|
||||||
|
assert_true(sparsemap_is_set(map, i));
|
||||||
|
}
|
||||||
|
|
||||||
return MUNIT_OK;
|
return MUNIT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,12 +459,12 @@ test_api_set(const MunitParameter params[], void *data)
|
||||||
|
|
||||||
assert_false(sparsemap_is_set(map, 1));
|
assert_false(sparsemap_is_set(map, 1));
|
||||||
assert_false(sparsemap_is_set(map, 8192));
|
assert_false(sparsemap_is_set(map, 8192));
|
||||||
sparsemap_set(map, 1, true);
|
sparsemap_set(map, 1);
|
||||||
sparsemap_set(map, 8192, true);
|
sparsemap_set(map, 8192);
|
||||||
assert_true(sparsemap_is_set(map, 1));
|
assert_true(sparsemap_is_set(map, 1));
|
||||||
assert_true(sparsemap_is_set(map, 8192));
|
assert_true(sparsemap_is_set(map, 8192));
|
||||||
sparsemap_set(map, 1, false);
|
sparsemap_unset(map, 1);
|
||||||
sparsemap_set(map, 8192, false);
|
sparsemap_unset(map, 8192);
|
||||||
assert_false(sparsemap_is_set(map, 1));
|
assert_false(sparsemap_is_set(map, 1));
|
||||||
assert_false(sparsemap_is_set(map, 8192));
|
assert_false(sparsemap_is_set(map, 8192));
|
||||||
|
|
||||||
|
@ -456,6 +480,7 @@ test_api_get_size_setup(const MunitParameter params[], void *user_data)
|
||||||
|
|
||||||
sparsemap_init(map, buf, 1024);
|
sparsemap_init(map, buf, 1024);
|
||||||
populate_map(map, 1024, 3 * 1024);
|
populate_map(map, 1024, 3 * 1024);
|
||||||
|
populate_map_rle(map, 3 * 1024, 5, 4096);
|
||||||
|
|
||||||
return (void *)map;
|
return (void *)map;
|
||||||
}
|
}
|
||||||
|
@ -512,18 +537,22 @@ test_api_count(const MunitParameter params[], void *data)
|
||||||
assert_true(sparsemap_count(map) == 1024);
|
assert_true(sparsemap_count(map) == 1024);
|
||||||
|
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
sparsemap_set(map, 0, true);
|
sparsemap_set(map, 0);
|
||||||
assert_true(sparsemap_count(map) == 1);
|
assert_true(sparsemap_count(map) == 1);
|
||||||
|
|
||||||
sparsemap_set(map, 8675309, true);
|
sparsemap_set(map, 8675309);
|
||||||
assert_true(sparsemap_count(map) == 2);
|
assert_true(sparsemap_count(map) == 2);
|
||||||
|
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
for (int i = 0; i < 512; i++) {
|
for (int i = 0; i < 512; i++) {
|
||||||
sparsemap_set(map, i + 13, true);
|
sparsemap_set(map, i + 13);
|
||||||
}
|
}
|
||||||
assert_true(sparsemap_count(map) == 512);
|
assert_true(sparsemap_count(map) == 512);
|
||||||
|
|
||||||
|
sparsemap_clear(map);
|
||||||
|
size_t n = populate_map_rle(map, 3 * 1024, 7, 4001);
|
||||||
|
assert_true(sparsemap_count(map) == n);
|
||||||
|
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
assert_true(sparsemap_count(map) == 0);
|
assert_true(sparsemap_count(map) == 0);
|
||||||
|
|
||||||
|
@ -550,7 +579,7 @@ test_api_get_data(const MunitParameter params[], void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
test_api_get_starting_offset_setup(const MunitParameter params[], void *user_data)
|
test_api_get_start_offset_setup(const MunitParameter params[], void *user_data)
|
||||||
{
|
{
|
||||||
uint8_t *buf = munit_calloc(1024, sizeof(uint8_t));
|
uint8_t *buf = munit_calloc(1024, sizeof(uint8_t));
|
||||||
assert_ptr_not_null(buf);
|
assert_ptr_not_null(buf);
|
||||||
|
@ -561,7 +590,7 @@ test_api_get_starting_offset_setup(const MunitParameter params[], void *user_dat
|
||||||
return (void *)map;
|
return (void *)map;
|
||||||
}
|
}
|
||||||
static void
|
static void
|
||||||
test_api_get_starting_offset_tear_down(void *fixture)
|
test_api_get_start_offset_tear_down(void *fixture)
|
||||||
{
|
{
|
||||||
sparsemap_t *map = (sparsemap_t *)fixture;
|
sparsemap_t *map = (sparsemap_t *)fixture;
|
||||||
assert_ptr_not_null(map->m_data);
|
assert_ptr_not_null(map->m_data);
|
||||||
|
@ -569,45 +598,45 @@ test_api_get_starting_offset_tear_down(void *fixture)
|
||||||
test_api_tear_down(fixture);
|
test_api_tear_down(fixture);
|
||||||
}
|
}
|
||||||
static MunitResult
|
static MunitResult
|
||||||
test_api_get_starting_offset(const MunitParameter params[], void *data)
|
test_api_get_start_offset(const MunitParameter params[], void *data)
|
||||||
{
|
{
|
||||||
sparsemap_t *map = (sparsemap_t *)data;
|
sparsemap_t *map = (sparsemap_t *)data;
|
||||||
(void)params;
|
(void)params;
|
||||||
|
|
||||||
assert_ptr_not_null(map);
|
assert_ptr_not_null(map);
|
||||||
|
|
||||||
sparsemap_set(map, 0, true);
|
sparsemap_set(map, 0);
|
||||||
assert_true(sparsemap_get_starting_offset(map) == 0);
|
assert_true(sparsemap_get_starting_offset(map) == 0);
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
|
|
||||||
sparsemap_set(map, 1, true);
|
sparsemap_set(map, 1);
|
||||||
assert_true(sparsemap_get_starting_offset(map) == 1);
|
assert_true(sparsemap_get_starting_offset(map) == 1);
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
|
|
||||||
sparsemap_set(map, 1025, true);
|
sparsemap_set(map, 1025);
|
||||||
assert_true(sparsemap_get_starting_offset(map) == 1025);
|
assert_true(sparsemap_get_starting_offset(map) == 1025);
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
}
|
}
|
||||||
assert_true(sparsemap_get_starting_offset(map) == 0);
|
assert_true(sparsemap_get_starting_offset(map) == 0);
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
|
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
sparsemap_set(map, i + 1024, true);
|
sparsemap_set(map, i + 1024);
|
||||||
}
|
}
|
||||||
assert_true(sparsemap_get_starting_offset(map) == 1024);
|
assert_true(sparsemap_get_starting_offset(map) == 1024);
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
|
|
||||||
sparsemap_set(map, 13012, true);
|
sparsemap_set(map, 13012);
|
||||||
assert_true(sparsemap_get_starting_offset(map) == 13012);
|
assert_true(sparsemap_get_starting_offset(map) == 13012);
|
||||||
|
|
||||||
return MUNIT_OK;
|
return MUNIT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
test_api_get_ending_offset_setup(const MunitParameter params[], void *user_data)
|
test_api_get_end_offset_setup(const MunitParameter params[], void *user_data)
|
||||||
{
|
{
|
||||||
uint8_t *buf = munit_calloc(1024, sizeof(uint8_t));
|
uint8_t *buf = munit_calloc(1024, sizeof(uint8_t));
|
||||||
assert_ptr_not_null(buf);
|
assert_ptr_not_null(buf);
|
||||||
|
@ -618,7 +647,7 @@ test_api_get_ending_offset_setup(const MunitParameter params[], void *user_data)
|
||||||
return (void *)map;
|
return (void *)map;
|
||||||
}
|
}
|
||||||
static void
|
static void
|
||||||
test_api_get_ending_offset_tear_down(void *fixture)
|
test_api_get_end_offset_tear_down(void *fixture)
|
||||||
{
|
{
|
||||||
sparsemap_t *map = (sparsemap_t *)fixture;
|
sparsemap_t *map = (sparsemap_t *)fixture;
|
||||||
assert_ptr_not_null(map->m_data);
|
assert_ptr_not_null(map->m_data);
|
||||||
|
@ -626,34 +655,44 @@ test_api_get_ending_offset_tear_down(void *fixture)
|
||||||
test_api_tear_down(fixture);
|
test_api_tear_down(fixture);
|
||||||
}
|
}
|
||||||
static MunitResult
|
static MunitResult
|
||||||
test_api_get_ending_offset(const MunitParameter params[], void *data)
|
test_api_get_end_offset(const MunitParameter params[], void *data)
|
||||||
{
|
{
|
||||||
sparsemap_t *map = (sparsemap_t *)data;
|
sparsemap_t *map = (sparsemap_t *)data;
|
||||||
(void)params;
|
(void)params;
|
||||||
|
|
||||||
assert_ptr_not_null(map);
|
assert_ptr_not_null(map);
|
||||||
|
|
||||||
sparsemap_set(map, 0, true);
|
sparsemap_set(map, 0);
|
||||||
assert_true(sparsemap_get_ending_offset(map) == 0);
|
assert_true(sparsemap_get_ending_offset(map) == 0);
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
|
|
||||||
sparsemap_set(map, 0, true);
|
sparsemap_set(map, 0);
|
||||||
sparsemap_set(map, 1, true);
|
sparsemap_set(map, 1);
|
||||||
assert_true(sparsemap_get_ending_offset(map) == 1);
|
assert_true(sparsemap_get_ending_offset(map) == 1);
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
|
|
||||||
sparsemap_set(map, 0, true);
|
sparsemap_set(map, 0);
|
||||||
sparsemap_set(map, 67, true);
|
sparsemap_set(map, 67);
|
||||||
sparsemap_set(map, 1002, true);
|
sparsemap_set(map, 1002);
|
||||||
sparsemap_set(map, 3087, true);
|
sparsemap_set(map, 3087);
|
||||||
sparsemap_set(map, 13012, true);
|
sparsemap_set(map, 13012);
|
||||||
assert_true(sparsemap_get_ending_offset(map) == 13012);
|
assert_true(sparsemap_get_ending_offset(map) == 13012);
|
||||||
|
|
||||||
|
sparsemap_clear(map);
|
||||||
|
size_t n = populate_map_rle(map, 13012, 10, 2718);
|
||||||
|
size_t exp = n + 13012 - 1;
|
||||||
|
size_t eoff = sparsemap_get_ending_offset(map);
|
||||||
|
assert_true(sparsemap_get_ending_offset(map) == 13012 + n - 1);
|
||||||
|
// fprintf(stdout, "\n%s\n", QCC_showSparsemap(map, 0));
|
||||||
|
sparsemap_set(map, 13012 + n + 100);
|
||||||
|
// fprintf(stdout, "\n%s\n", QCC_showSparsemap(map, 0));
|
||||||
|
assert_true(sparsemap_get_ending_offset(map) == 13112 + n);
|
||||||
|
|
||||||
return MUNIT_OK;
|
return MUNIT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
test_api_get_starting_offset_rolling_setup(const MunitParameter params[], void *user_data)
|
test_api_get_start_offset_roll_setup(const MunitParameter params[], void *user_data)
|
||||||
{
|
{
|
||||||
(void)params;
|
(void)params;
|
||||||
(void)user_data;
|
(void)user_data;
|
||||||
|
@ -662,24 +701,27 @@ test_api_get_starting_offset_rolling_setup(const MunitParameter params[], void *
|
||||||
return (void *)map;
|
return (void *)map;
|
||||||
}
|
}
|
||||||
static void
|
static void
|
||||||
test_api_get_starting_offset_rolling_tear_down(void *fixture)
|
test_api_get_start_offset_roll_tear_down(void *fixture)
|
||||||
{
|
{
|
||||||
sparsemap_t *map = (sparsemap_t *)fixture;
|
sparsemap_t *map = (sparsemap_t *)fixture;
|
||||||
assert_ptr_not_null(map);
|
assert_ptr_not_null(map);
|
||||||
munit_free(map);
|
munit_free(map);
|
||||||
}
|
}
|
||||||
static MunitResult
|
static MunitResult
|
||||||
test_api_get_starting_offset_rolling(const MunitParameter params[], void *data)
|
test_api_get_start_offset_roll(const MunitParameter params[], void *data)
|
||||||
{
|
{
|
||||||
sparsemap_t *map = (sparsemap_t *)data;
|
sparsemap_t *map = (sparsemap_t *)data;
|
||||||
(void)params;
|
(void)params;
|
||||||
|
|
||||||
for (sparsemap_idx_t i = 0; i < 10 * 2048; i++) {
|
for (sparsemap_idx_t i = 0; i < 10 * 2048; i++) {
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
if (i > 2047) {
|
if (i > 2047) {
|
||||||
sparsemap_set(map, i - 2048, false);
|
sparsemap_unset(map, i - 2048);
|
||||||
|
// if (sparsemap_get_starting_offset(map) != i - 2047) {
|
||||||
|
// fprintf(stdout, "\n%s\n", QCC_showSparsemap(map, 0));
|
||||||
|
// fprintf(stdout, "%ld\t%ld\t%zu\n", i, i - 2047, sparsemap_get_starting_offset(map));
|
||||||
|
// }
|
||||||
assert_true(sparsemap_get_starting_offset(map) == i - 2047);
|
assert_true(sparsemap_get_starting_offset(map) == i - 2047);
|
||||||
// printf("%d\t%d\t%zu\n", i, i - 2047, sparsemap_get_starting_offset(map));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return MUNIT_OK;
|
return MUNIT_OK;
|
||||||
|
@ -703,7 +745,7 @@ test_api_scan_tear_down(void *fixture)
|
||||||
test_api_tear_down(fixture);
|
test_api_tear_down(fixture);
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
scan_for_0xfeedfacebadcoffee(sm_idx_t v[], size_t n, void *aux)
|
scan_for_0xfeedfacebadcoffee(uint32_t v[], size_t n, void *aux)
|
||||||
{
|
{
|
||||||
size_t bit_pos[] = { 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 22, 23, 24, 26, 27, 29, 31, 32, 33, 34, 35, 38, 39, 41, 43, 44, 45, 46, 47, 48, 50, 51,
|
size_t bit_pos[] = { 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 22, 23, 24, 26, 27, 29, 31, 32, 33, 34, 35, 38, 39, 41, 43, 44, 45, 46, 47, 48, 50, 51,
|
||||||
53, 54, 55, 57, 58, 59, 60, 61, 62, 63 };
|
53, 54, 55, 57, 58, 59, 60, 61, 62, 63 };
|
||||||
|
@ -747,6 +789,7 @@ test_api_split_tear_down(void *fixture)
|
||||||
static MunitResult
|
static MunitResult
|
||||||
test_api_split(const MunitParameter params[], void *data)
|
test_api_split(const MunitParameter params[], void *data)
|
||||||
{
|
{
|
||||||
|
sparsemap_idx_t offset;
|
||||||
sparsemap_t *map = (sparsemap_t *)data;
|
sparsemap_t *map = (sparsemap_t *)data;
|
||||||
uint8_t buf[1024] = { 0 };
|
uint8_t buf[1024] = { 0 };
|
||||||
sparsemap_t portion;
|
sparsemap_t portion;
|
||||||
|
@ -756,11 +799,33 @@ test_api_split(const MunitParameter params[], void *data)
|
||||||
|
|
||||||
sparsemap_init(&portion, buf, 1024);
|
sparsemap_init(&portion, buf, 1024);
|
||||||
|
|
||||||
|
size_t amt = populate_map_rle(map, 0, 7, 8178);
|
||||||
|
for (sparsemap_idx_t i = 0; i < amt + 2049; i++) {
|
||||||
|
sparsemap_clear(&portion);
|
||||||
|
size_t rank = sparsemap_rank(map, 0, i, true);
|
||||||
|
offset = sparsemap_split(map, i + 1, &portion);
|
||||||
|
if (sparsemap_count(map) != rank) {
|
||||||
|
fprintf(stdout, "exp: %lu\tgot: %lu", rank, sparsemap_count(map));
|
||||||
|
}
|
||||||
|
assert_true(sparsemap_count(map) == rank);
|
||||||
|
if (sparsemap_count(&portion) != amt - rank) {
|
||||||
|
fprintf(stdout, "exp: %lu\tgot: %lu", amt - rank, sparsemap_count(&portion));
|
||||||
|
}
|
||||||
|
assert_true(sparsemap_count(&portion) == amt - rank);
|
||||||
|
sparsemap_merge(map, &portion);
|
||||||
|
if (*(uint32_t *)map->m_data != 1) {
|
||||||
|
fprintf(stdout, "yikes");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sparsemap_clear(map);
|
||||||
|
sparsemap_clear(&portion);
|
||||||
|
|
||||||
for (sparsemap_idx_t off = 0; off < 1024; off++) {
|
for (sparsemap_idx_t off = 0; off < 1024; off++) {
|
||||||
for (sparsemap_idx_t seg = 0; seg < 10 * 1024; seg += 1024) {
|
for (sparsemap_idx_t seg = 0; seg < 10 * 1024; seg += 1024) {
|
||||||
|
|
||||||
for (sparsemap_idx_t i = 0; i < 1024; i++) {
|
for (sparsemap_idx_t i = 0; i < 1024; i++) {
|
||||||
assert_true(sparsemap_set(map, i + seg, true) == i + seg);
|
assert_true(sparsemap_set(map, i + seg) == i + seg);
|
||||||
}
|
}
|
||||||
for (sparsemap_idx_t i = 0; i < 1024; i++) {
|
for (sparsemap_idx_t i = 0; i < 1024; i++) {
|
||||||
assert_true(sparsemap_is_set(map, i + seg));
|
assert_true(sparsemap_is_set(map, i + seg));
|
||||||
|
@ -784,20 +849,20 @@ test_api_split(const MunitParameter params[], void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (sparsemap_idx_t i = 0; i < 100; i++) {
|
for (sparsemap_idx_t i = 0; i < 100; i++) {
|
||||||
assert_true(sparsemap_set(map, i, true) == i);
|
assert_true(sparsemap_set(map, i) == i);
|
||||||
}
|
}
|
||||||
for (sparsemap_idx_t i = 0; i < 100; i++) {
|
for (sparsemap_idx_t i = 0; i < 100; i++) {
|
||||||
assert_true(sparsemap_is_set(map, i));
|
assert_true(sparsemap_is_set(map, i));
|
||||||
assert_false(sparsemap_is_set(&portion, i));
|
assert_false(sparsemap_is_set(&portion, i));
|
||||||
}
|
}
|
||||||
|
|
||||||
sparsemap_idx_t offset = sparsemap_split(map, SPARSEMAP_IDX_MAX, &portion);
|
offset = sparsemap_split(map, SPARSEMAP_IDX_MAX, &portion);
|
||||||
|
|
||||||
for (size_t i = 0; i < offset; i++) {
|
for (sparsemap_idx_t i = 0; i < offset; i++) {
|
||||||
assert_true(sparsemap_is_set(map, i));
|
assert_true(sparsemap_is_set(map, i));
|
||||||
assert_false(sparsemap_is_set(&portion, i));
|
assert_false(sparsemap_is_set(&portion, i));
|
||||||
}
|
}
|
||||||
for (int i = offset; i < 100; i++) {
|
for (sparsemap_idx_t i = offset + 1; i < sparsemap_get_ending_offset(&portion); i++) {
|
||||||
assert_false(sparsemap_is_set(map, i));
|
assert_false(sparsemap_is_set(map, i));
|
||||||
assert_true(sparsemap_is_set(&portion, i));
|
assert_true(sparsemap_is_set(&portion, i));
|
||||||
}
|
}
|
||||||
|
@ -807,10 +872,14 @@ test_api_split(const MunitParameter params[], void *data)
|
||||||
|
|
||||||
sparsemap_init(&portion, buf, 1024);
|
sparsemap_init(&portion, buf, 1024);
|
||||||
for (sparsemap_idx_t i = 0; i < 13; i++) {
|
for (sparsemap_idx_t i = 0; i < 13; i++) {
|
||||||
assert_true(sparsemap_set(map, i + 24, true) == i + 24);
|
assert_true(sparsemap_set(map, i + 24) == i + 24);
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = sparsemap_split(map, SPARSEMAP_IDX_MAX, &portion);
|
offset = sparsemap_split(map, SPARSEMAP_IDX_MAX, &portion);
|
||||||
|
assert_true(sparsemap_get_ending_offset(map) < offset);
|
||||||
|
assert_true(sparsemap_get_starting_offset(&portion) >= offset);
|
||||||
|
assert_true(sparsemap_count(map) == 6);
|
||||||
|
assert_true(sparsemap_count(&portion) == 7);
|
||||||
|
|
||||||
for (sparsemap_idx_t i = 0; i < offset - 24; i++) {
|
for (sparsemap_idx_t i = 0; i < offset - 24; i++) {
|
||||||
assert_true(sparsemap_is_set(map, i + 24));
|
assert_true(sparsemap_is_set(map, i + 24));
|
||||||
|
@ -854,7 +923,7 @@ test_api_merge(const MunitParameter params[], void *data)
|
||||||
sparsemap_merge(map, other);
|
sparsemap_merge(map, other);
|
||||||
|
|
||||||
// Merge a single set bit in the first chunk into the empty map.
|
// Merge a single set bit in the first chunk into the empty map.
|
||||||
sparsemap_set(other, 0, true);
|
sparsemap_set(other, 0);
|
||||||
sparsemap_merge(map, other);
|
sparsemap_merge(map, other);
|
||||||
assert_true(sparsemap_is_set(other, 0));
|
assert_true(sparsemap_is_set(other, 0));
|
||||||
assert_true(sparsemap_is_set(map, 0));
|
assert_true(sparsemap_is_set(map, 0));
|
||||||
|
@ -862,32 +931,32 @@ test_api_merge(const MunitParameter params[], void *data)
|
||||||
sparsemap_clear(other);
|
sparsemap_clear(other);
|
||||||
|
|
||||||
// Merge two maps with the same single bit set.
|
// Merge two maps with the same single bit set.
|
||||||
sparsemap_set(map, 0, true);
|
sparsemap_set(map, 0);
|
||||||
sparsemap_set(other, 0, true);
|
sparsemap_set(other, 0);
|
||||||
sparsemap_merge(map, other);
|
sparsemap_merge(map, other);
|
||||||
assert_true(sparsemap_is_set(map, 0));
|
assert_true(sparsemap_is_set(map, 0));
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
sparsemap_clear(other);
|
sparsemap_clear(other);
|
||||||
|
|
||||||
// Merge an empty map with one that has the first bit set.
|
// Merge an empty map with one that has the first bit set.
|
||||||
sparsemap_set(map, 0, true);
|
sparsemap_set(map, 0);
|
||||||
sparsemap_merge(map, other);
|
sparsemap_merge(map, other);
|
||||||
assert_true(sparsemap_is_set(map, 0));
|
assert_true(sparsemap_is_set(map, 0));
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
sparsemap_clear(other);
|
sparsemap_clear(other);
|
||||||
|
|
||||||
sparsemap_set(other, 2049, true);
|
sparsemap_set(other, 2049);
|
||||||
sparsemap_merge(map, other);
|
sparsemap_merge(map, other);
|
||||||
assert_true(sparsemap_is_set(map, 2049));
|
assert_true(sparsemap_is_set(map, 2049));
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
sparsemap_clear(other);
|
sparsemap_clear(other);
|
||||||
|
|
||||||
sparsemap_set(other, 1, true);
|
sparsemap_set(other, 1);
|
||||||
sparsemap_set(other, 2049, true);
|
sparsemap_set(other, 2049);
|
||||||
sparsemap_set(map, 2050, true);
|
sparsemap_set(map, 2050);
|
||||||
sparsemap_set(other, 4097, true);
|
sparsemap_set(other, 4097);
|
||||||
sparsemap_set(map, 6113, true);
|
sparsemap_set(map, 6113);
|
||||||
sparsemap_set(other, 8193, true);
|
sparsemap_set(other, 8193);
|
||||||
sparsemap_merge(map, other);
|
sparsemap_merge(map, other);
|
||||||
assert_true(sparsemap_is_set(map, 1));
|
assert_true(sparsemap_is_set(map, 1));
|
||||||
assert_true(sparsemap_is_set(map, 2049));
|
assert_true(sparsemap_is_set(map, 2049));
|
||||||
|
@ -904,11 +973,11 @@ test_api_merge(const MunitParameter params[], void *data)
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
sparsemap_clear(other);
|
sparsemap_clear(other);
|
||||||
|
|
||||||
sparsemap_set(map, 0, true);
|
sparsemap_set(map, 0);
|
||||||
sparsemap_set(map, 2048, true);
|
sparsemap_set(map, 2048);
|
||||||
sparsemap_set(map, 8193, true);
|
sparsemap_set(map, 8193);
|
||||||
for (int i = 2049; i < 4096; i++) {
|
for (int i = 2049; i < 4096; i++) {
|
||||||
sparsemap_set(other, i, true);
|
sparsemap_set(other, i);
|
||||||
}
|
}
|
||||||
sparsemap_merge(map, other);
|
sparsemap_merge(map, other);
|
||||||
assert(sparsemap_is_set(map, 0));
|
assert(sparsemap_is_set(map, 0));
|
||||||
|
@ -921,7 +990,7 @@ test_api_merge(const MunitParameter params[], void *data)
|
||||||
sparsemap_clear(other);
|
sparsemap_clear(other);
|
||||||
|
|
||||||
for (int i = 2049; i < 4096; i++) {
|
for (int i = 2049; i < 4096; i++) {
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
}
|
}
|
||||||
sparsemap_split(map, 2051, other);
|
sparsemap_split(map, 2051, other);
|
||||||
for (int i = 2049; i < 4096; i++) {
|
for (int i = 2049; i < 4096; i++) {
|
||||||
|
@ -1048,9 +1117,9 @@ test_api_select_neg(const MunitParameter params[], void *data)
|
||||||
|
|
||||||
assert_ptr_not_null(map);
|
assert_ptr_not_null(map);
|
||||||
|
|
||||||
sparsemap_set(map, 42, true);
|
sparsemap_set(map, 42);
|
||||||
sparsemap_set(map, 420, true);
|
sparsemap_set(map, 420);
|
||||||
sparsemap_set(map, 4200, true);
|
sparsemap_set(map, 4200);
|
||||||
|
|
||||||
f = sparsemap_select(map, 0, false);
|
f = sparsemap_select(map, 0, false);
|
||||||
assert_true(f == 0);
|
assert_true(f == 0);
|
||||||
|
@ -1095,7 +1164,7 @@ test_api_rank_true(const MunitParameter params[], void *data)
|
||||||
assert_ptr_not_null(map);
|
assert_ptr_not_null(map);
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++) {
|
for (int i = 0; i < 10; i++) {
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
}
|
}
|
||||||
/* rank() is also 0-based, for consistency (and confusion sake); consider the
|
/* rank() is also 0-based, for consistency (and confusion sake); consider the
|
||||||
range as [start, end] of [0, 9] counts the bits set in the first 10
|
range as [start, end] of [0, 9] counts the bits set in the first 10
|
||||||
|
@ -1109,14 +1178,23 @@ test_api_rank_true(const MunitParameter params[], void *data)
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
|
|
||||||
for (int i = 0; i < 10000; i++) {
|
for (int i = 0; i < 10000; i++) {
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: separate test for slicing a run within the chunk size of the end of the run
|
||||||
|
// sparsemap_unset(map, 9990);
|
||||||
|
// sparsemap_set(map, 9990);
|
||||||
|
|
||||||
sparsemap_idx_t hole = 4999;
|
sparsemap_idx_t hole = 4999;
|
||||||
sparsemap_set(map, hole, false);
|
sparsemap_unset(map, hole);
|
||||||
for (size_t i = 0; i < 10000; i++) {
|
for (size_t i = 0; i < 10000; i++) {
|
||||||
for (size_t j = i; j < 10000; j++) {
|
for (size_t j = i; j < 10000; j++) {
|
||||||
size_t amt = (i > j) ? 0 : j - i + 1 - ((hole >= i && j >= hole) ? 1 : 0);
|
size_t amt = (i > j) ? 0 : j - i + 1 - ((hole >= i && j >= hole) ? 1 : 0);
|
||||||
size_t r = sparsemap_rank(map, i, j, true);
|
size_t r = sparsemap_rank(map, i, j, true);
|
||||||
|
// if (r != amt) {
|
||||||
|
// fprintf(stdout, "\n%s\n", QCC_showSparsemap(map, 0));
|
||||||
|
// sparsemap_rank(map, i, j, true);
|
||||||
|
// }
|
||||||
assert_true(r == amt);
|
assert_true(r == amt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1146,7 +1224,7 @@ test_api_rank_false_tear_down(void *fixture)
|
||||||
static MunitResult
|
static MunitResult
|
||||||
test_api_rank_false(const MunitParameter params[], void *data)
|
test_api_rank_false(const MunitParameter params[], void *data)
|
||||||
{
|
{
|
||||||
int r;
|
size_t r;
|
||||||
sparsemap_t *map = (sparsemap_t *)data;
|
sparsemap_t *map = (sparsemap_t *)data;
|
||||||
(void)params;
|
(void)params;
|
||||||
|
|
||||||
|
@ -1162,18 +1240,36 @@ test_api_rank_false(const MunitParameter params[], void *data)
|
||||||
|
|
||||||
// One chunk means not so empty now!
|
// One chunk means not so empty now!
|
||||||
sparsemap_idx_t hole = 4999;
|
sparsemap_idx_t hole = 4999;
|
||||||
sparsemap_set(map, hole, true);
|
sparsemap_set(map, hole);
|
||||||
for (size_t i = 0; i < 10000; i++) {
|
for (size_t i = 0; i < 10000; i++) {
|
||||||
for (size_t j = i; j < 10000; j++) {
|
for (size_t j = i; j < 10000; j++) {
|
||||||
int amt = (i > j) ? 0 : j - i + 1 - ((hole >= i && j >= hole) ? 1 : 0);
|
size_t amt = (i > j) ? 0 : j - i + 1 - ((hole >= i && j >= hole) ? 1 : 0);
|
||||||
r = sparsemap_rank(map, i, j, false);
|
r = sparsemap_rank(map, i, j, false);
|
||||||
assert_true(r == amt);
|
assert_true(r == amt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RLE
|
||||||
|
for (size_t i = 0; i < 10000; i++) {
|
||||||
|
sparsemap_set(map, i);
|
||||||
|
}
|
||||||
|
r = sparsemap_rank(map, 9990, 10010, false);
|
||||||
|
// if (r != 10) {
|
||||||
|
// fprintf(stdout, "\n%s\n", QCC_showSparsemap(map, 0));
|
||||||
|
// sparsemap_rank(map, 9990, 10010, true);
|
||||||
|
// }
|
||||||
|
assert_true(r == 10);
|
||||||
|
|
||||||
|
r = sparsemap_rank(map, 9990, 4294967295, false);
|
||||||
|
// if (r != 4294957295) {
|
||||||
|
// fprintf(stdout, "\n%s\n", QCC_showSparsemap(map, 0));
|
||||||
|
// sparsemap_rank(map, 9990, 4294967295, true);
|
||||||
|
// }
|
||||||
|
assert_true(r == 4294957295);
|
||||||
|
|
||||||
sparsemap_clear(map);
|
sparsemap_clear(map);
|
||||||
sparsemap_set(map, 1, true);
|
sparsemap_set(map, 1);
|
||||||
sparsemap_set(map, 11, true);
|
sparsemap_set(map, 11);
|
||||||
r = sparsemap_rank(map, 0, 11, false);
|
r = sparsemap_rank(map, 0, 11, false);
|
||||||
assert_true(r == 10);
|
assert_true(r == 10);
|
||||||
|
|
||||||
|
@ -1246,9 +1342,9 @@ static MunitTest api_test_suite[] = {
|
||||||
{ (char *)"/get_size", test_api_get_size, test_api_get_size_setup, test_api_get_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
{ (char *)"/get_size", test_api_get_size, test_api_get_size_setup, test_api_get_size_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
{ (char *)"/count", test_api_count, test_api_count_setup, test_api_count_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
{ (char *)"/count", test_api_count, test_api_count_setup, test_api_count_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
{ (char *)"/get_data", test_api_get_data, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
{ (char *)"/get_data", test_api_get_data, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
{ (char *)"/get_starting_offset", test_api_get_starting_offset, test_api_get_starting_offset_setup, test_api_get_starting_offset_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
{ (char *)"/get_start_offset", test_api_get_start_offset, test_api_get_start_offset_setup, test_api_get_start_offset_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
{ (char *)"/get_starting_offset/rolling", test_api_get_starting_offset_rolling, test_api_get_starting_offset_rolling_setup, test_api_get_starting_offset_rolling_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
{ (char *)"/get_start_offset/roll", test_api_get_start_offset_roll, test_api_get_start_offset_roll_setup, test_api_get_start_offset_roll_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
{ (char *)"/get_ending_offset", test_api_get_ending_offset, test_api_get_ending_offset_setup, test_api_get_ending_offset_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
{ (char *)"/get_end_offset", test_api_get_end_offset, test_api_get_end_offset_setup, test_api_get_end_offset_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
{ (char *)"/scan", test_api_scan, test_api_scan_setup, test_api_scan_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
{ (char *)"/scan", test_api_scan, test_api_scan_setup, test_api_scan_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
{ (char *)"/split", test_api_split, test_api_split_setup, test_api_split_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
{ (char *)"/split", test_api_split, test_api_split_setup, test_api_split_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
{ (char *)"/merge", test_api_merge, test_api_merge_setup, test_api_merge_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
{ (char *)"/merge", test_api_merge, test_api_merge_setup, test_api_merge_tear_down, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
@ -1266,6 +1362,61 @@ static MunitTest api_test_suite[] = {
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
|
/* -------------------------- Quickcheck, Property Based Tests */
|
||||||
|
|
||||||
|
extern QCC_GenValue *QCC_genChunk();
|
||||||
|
extern QCC_GenValue *QCC_genSparsemap();
|
||||||
|
extern QCC_TestStatus _tst_chunk_calc_vector_size_equality(QCC_GenValue **vals, int len, QCC_Stamp **stamp);
|
||||||
|
extern QCC_TestStatus _tst_chunk_get_position(QCC_GenValue **vals, int len, QCC_Stamp **stamp);
|
||||||
|
extern QCC_TestStatus _tst_chunk_get_capacity(QCC_GenValue **vals, int len, QCC_Stamp **stamp);
|
||||||
|
extern QCC_TestStatus _tst_get_chunk_offset(QCC_GenValue **vals, int len, QCC_Stamp **stamp);
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
qc__sm_chunk_calc_vector_size(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
return QCC_testForAll(1000, 1000, _tst_chunk_calc_vector_size_equality, 1, QCC_genInt);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
qc__sm_chunk_get_position(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
return QCC_testForAll(100, 1000, _tst_chunk_get_position, 1, QCC_genChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
qc__sm_chunk_get_capacity(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
return QCC_testForAll(100, 1000, _tst_chunk_get_capacity, 1, QCC_genChunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
qc__sm_get_chunk_offset(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
return QCC_testForAll(100, 1000, _tst_get_chunk_offset, 2, QCC_genInt, QCC_genSparsemap);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
static MunitTest qc_test_suite[] = {
|
||||||
|
{ (char *)"/__sm_chunk_calc_vector_size", qc__sm_chunk_calc_vector_size, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ (char *)"/__sm_chunk_get_position", qc__sm_chunk_get_position, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ (char *)"/__sm_chunk_get_capacity", qc__sm_chunk_get_capacity, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ (char *)"/__sm_get_chunk_offset", qc__sm_get_chunk_offset, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL }
|
||||||
|
};
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
/* -------------------------- Scale Tests */
|
/* -------------------------- Scale Tests */
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
|
@ -1407,7 +1558,7 @@ test_scale_alternating(const MunitParameter params[], void *data)
|
||||||
|
|
||||||
for (sparsemap_idx_t i = 0; i < (1000 * 8192); i++) {
|
for (sparsemap_idx_t i = 0; i < (1000 * 8192); i++) {
|
||||||
if (i % 2) {
|
if (i % 2) {
|
||||||
if (sparsemap_set(map, i, true) != i) {
|
if (sparsemap_set(map, i) != i) {
|
||||||
// printf("%zu\n", i);
|
// printf("%zu\n", i);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1463,7 +1614,7 @@ test_scale_spans_come_spans_go(const MunitParameter params[], void *data)
|
||||||
assert_true(sparsemap_is_set(map, j) == true);
|
assert_true(sparsemap_is_set(map, j) == true);
|
||||||
}
|
}
|
||||||
for (int j = b; j < s; j++) {
|
for (int j = b; j < s; j++) {
|
||||||
sparsemap_set(map, j, false);
|
sparsemap_unset(map, j);
|
||||||
}
|
}
|
||||||
for (int j = b; j < s; j++) {
|
for (int j = b; j < s; j++) {
|
||||||
assert_true(sparsemap_is_set(map, j) == false);
|
assert_true(sparsemap_is_set(map, j) == false);
|
||||||
|
@ -1515,7 +1666,7 @@ test_scale_best_case(const MunitParameter params[], void *data)
|
||||||
for (int i = 0; i < 172032; i++) {
|
for (int i = 0; i < 172032; i++) {
|
||||||
/* ANSI esc code to clear line, carrage return, then print on the same line */
|
/* ANSI esc code to clear line, carrage return, then print on the same line */
|
||||||
// printf("\033[2K\r%d", i);
|
// printf("\033[2K\r%d", i);
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MUNIT_OK;
|
return MUNIT_OK;
|
||||||
|
@ -1559,7 +1710,7 @@ test_scale_worst_case(const MunitParameter params[], void *data)
|
||||||
for (int i = 0; i < 7744; i += 2) {
|
for (int i = 0; i < 7744; i += 2) {
|
||||||
/* ANSI esc code to clear line, carrage return, then print on the same line */
|
/* ANSI esc code to clear line, carrage return, then print on the same line */
|
||||||
// printf("\033[2K\r%d", i);
|
// printf("\033[2K\r%d", i);
|
||||||
sparsemap_set(map, i, true);
|
sparsemap_set(map, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MUNIT_OK;
|
return MUNIT_OK;
|
||||||
|
@ -1680,6 +1831,7 @@ static MunitTest perf_test_suite[] = {
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static MunitSuite other_test_suite[] = {
|
static MunitSuite other_test_suite[] = {
|
||||||
{ "/api", api_test_suite, NULL, 1, MUNIT_SUITE_OPTION_NONE },
|
{ "/api", api_test_suite, NULL, 1, MUNIT_SUITE_OPTION_NONE },
|
||||||
|
{ "/qc", qc_test_suite, NULL, 1, MUNIT_SUITE_OPTION_NONE },
|
||||||
{ "/perf", perf_test_suite, NULL, 1, MUNIT_SUITE_OPTION_NONE },
|
{ "/perf", perf_test_suite, NULL, 1, MUNIT_SUITE_OPTION_NONE },
|
||||||
{ "/scale", scale_test_suite, NULL, 1, MUNIT_SUITE_OPTION_NONE },
|
{ "/scale", scale_test_suite, NULL, 1, MUNIT_SUITE_OPTION_NONE },
|
||||||
{ NULL, NULL, NULL, 0, MUNIT_SUITE_OPTION_NONE } };
|
{ NULL, NULL, NULL, 0, MUNIT_SUITE_OPTION_NONE } };
|
||||||
|
@ -1691,7 +1843,7 @@ static MunitTest sparsemap_test_suite[] = {
|
||||||
};
|
};
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
static const MunitSuite main_test_suite = { (char *)"/sparsemap", sparsemap_test_suite, other_test_suite, 1, MUNIT_SUITE_OPTION_NONE };
|
static const MunitSuite main_test_suite = { (char *)"", sparsemap_test_suite, other_test_suite, 1, MUNIT_SUITE_OPTION_NONE };
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)])
|
main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)])
|
||||||
|
@ -1702,6 +1854,8 @@ main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)])
|
||||||
setvbuf(stdout, NULL, _IONBF, 0);
|
setvbuf(stdout, NULL, _IONBF, 0);
|
||||||
setvbuf(stderr, NULL, _IONBF, 0);
|
setvbuf(stderr, NULL, _IONBF, 0);
|
||||||
|
|
||||||
|
QCC_init(0);
|
||||||
|
|
||||||
return munit_suite_main(&main_test_suite, (void *)&info, argc, argv);
|
return munit_suite_main(&main_test_suite, (void *)&info, argc, argv);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue