├── .ignore ├── src ├── kv │ ├── tests │ │ ├── .gitattributes │ │ ├── garbage.data │ │ ├── Autark │ │ ├── iwkv_tests.h │ │ ├── iwkv_test9.c │ │ ├── iwkv_test2.c │ │ ├── iwkv_test7.c │ │ ├── iwkv_test10.c │ │ ├── iwkv_test6.c │ │ └── iwkv_test3.c │ ├── Autark │ ├── examples │ │ ├── example1.c │ │ ├── compoundkeys1.c │ │ └── cursors1.c │ ├── data-format.txt │ ├── iwal.h │ └── dbg │ │ └── iwkvdbg.c ├── json │ ├── tests │ │ ├── data │ │ │ ├── 003.json │ │ │ ├── 003.expected.json │ │ │ ├── 005.json │ │ │ ├── 005.expected.json │ │ │ ├── jbl_test2.001.expected.xml │ │ │ ├── 001.expected.json │ │ │ ├── 002.expected.json │ │ │ ├── 001.json │ │ │ ├── 002.json │ │ │ ├── 004.expected.json │ │ │ └── 004.json │ │ ├── Autark │ │ ├── jbl_test2.c │ │ └── iwjsreg_test1.c │ ├── Autark │ ├── iwjson_internal.h │ └── iwjsreg.h ├── re │ ├── README.txt │ ├── Autark │ ├── iwre.h │ ├── tests │ │ ├── Autark │ │ └── iwre_test1.c │ ├── iwre.c │ └── cregex.h ├── rdb │ ├── Autark │ └── iwrdb.h ├── .autark │ └── test_qsort_r.sh ├── log │ ├── Autark │ └── tests │ │ ├── Autark │ │ └── iwlog_test1.c ├── platform │ ├── Autark │ └── win32 │ │ └── mman │ │ ├── mman.h │ │ └── mman.c ├── fs │ ├── Autark │ ├── tests │ │ └── Autark │ ├── iwdlsnr.h │ └── iwfs.c ├── libiowow.pc.in ├── utils │ ├── iwuuid.h │ ├── tests │ │ ├── Autark │ │ ├── iwarr_test1.c │ │ ├── iwrb_test1.c │ │ ├── iwhmap_test1.c │ │ └── iwutils_test1.c │ ├── murmur3.h │ ├── Autark │ ├── iwchars.h │ ├── pthread_spin_lock_shim.h │ ├── iwuuid.c │ ├── iwrefs.h │ ├── wyhash32.h │ ├── iwrb.h │ ├── iwcsv.h │ ├── iwconv.h │ ├── iwrb.c │ ├── mt19937ar.h │ ├── iwth.c │ ├── iwth.h │ ├── iwbits.h │ ├── iwstw.h │ ├── iwxstr.h │ ├── iwhmap.h │ ├── iwtp.h │ └── iwini.h ├── iowow.h ├── iwcfg.h.in ├── iowow.c └── Autark ├── .editorconfig ├── .autark ├── test_qsort_r.sh ├── test_pthread.sh ├── test_header.sh ├── test_blocks.sh ├── test_symbol.sh ├── fetch_resource.sh └── system.sh ├── .builds ├── freebsd-latest.yml └── linux-ubuntu.yml ├── .gdbinit ├── .lvimrc ├── Makefile ├── release.sh ├── .clang-tidy ├── LICENSE ├── .gitignore └── Autark /.ignore: -------------------------------------------------------------------------------- 1 | /autark-cache* 2 | /mxe 3 | 4 | -------------------------------------------------------------------------------- /src/kv/tests/.gitattributes: -------------------------------------------------------------------------------- 1 | *w.ref eol=crlf 2 | -------------------------------------------------------------------------------- /src/json/tests/data/003.json: -------------------------------------------------------------------------------- 1 | { 2 | "empty": "" 3 | } -------------------------------------------------------------------------------- /src/json/tests/data/003.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "empty": "" 3 | } -------------------------------------------------------------------------------- /src/kv/tests/garbage.data: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Softmotions/iowow/HEAD/src/kv/tests/garbage.data -------------------------------------------------------------------------------- /src/re/README.txt: -------------------------------------------------------------------------------- 1 | Based on https://github.com/jserv/cregex library (BSD 2-Clause "Simplified" License) 2 | -------------------------------------------------------------------------------- /src/json/tests/data/005.json: -------------------------------------------------------------------------------- 1 | {"foo": "b\"ar", "num1":1223,"n\"um2":10.1226222, "list":[3,2.1,1,"one", "two", {}, {"z":false, "t":true}]} 2 | -------------------------------------------------------------------------------- /src/json/tests/data/005.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "foo": "b\"ar", 3 | "num1": 1223, 4 | "n\"um2": 10.1226222, 5 | "list": [ 6 | 3, 7 | 2.1, 8 | 1, 9 | "one", 10 | "two", 11 | {}, 12 | { 13 | "z": false, 14 | "t": true 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | charset=utf-8 3 | end_of_line=lf 4 | insert_final_newline=false 5 | trim_trailing_whitespace = true 6 | indent_style=space 7 | indent_size=2 8 | 9 | [Makefile] 10 | indent_style = tab 11 | 12 | [*.{c,cpp,cc,h,hpp,in}] 13 | insert_final_newline=true -------------------------------------------------------------------------------- /src/rdb/Autark: -------------------------------------------------------------------------------- 1 | set { 2 | parent { 3 | SOURCES 4 | } 5 | ..${SOURCES} 6 | rdb/iwrdb.c 7 | } 8 | 9 | set { 10 | root { 11 | CFLAGS 12 | } 13 | ..${CFLAGS} 14 | -I SS{} 15 | } 16 | 17 | set { 18 | parent { 19 | PUB_HDRS 20 | } 21 | ..${PUB_HDRS} 22 | rdb/iwrdb.h 23 | } -------------------------------------------------------------------------------- /.autark/test_qsort_r.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cat > './test_qsort_r.c' << 'EOF' 4 | #include 5 | int main(int argc, char **argv) { 6 | qsort_r(0, 0, 0, 0, 0); 7 | return 0; 8 | } 9 | EOF 10 | 11 | ${CC:=cc} ./test_qsort_r.c 12 | 13 | if [ "$?" -eq "0" ]; then 14 | autark set "HAVE_QSORT_R=1" 15 | else 16 | echo "No qsort_r found" 17 | fi -------------------------------------------------------------------------------- /src/.autark/test_qsort_r.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cat > './test_qsort_r.c' << 'EOF' 4 | #include 5 | int main(int argc, char **argv) { 6 | qsort_r(0, 0, 0, 0, 0); 7 | return 0; 8 | } 9 | EOF 10 | 11 | ${CC:=cc} ./test_qsort_r.c 12 | 13 | if [ "$?" -eq "0" ]; then 14 | autark set "HAVE_QSORT_R=1" 15 | else 16 | echo "No qsort_r found" 17 | fi -------------------------------------------------------------------------------- /src/log/Autark: -------------------------------------------------------------------------------- 1 | set { 2 | parent { 3 | SOURCES 4 | } 5 | ..${SOURCES} 6 | log/iwlog.c 7 | } 8 | 9 | set { 10 | parent { 11 | PUB_HDRS 12 | } 13 | ..${PUB_HDRS} 14 | log/iwlog.h 15 | } 16 | 17 | set { 18 | root { 19 | CFLAGS 20 | } 21 | ..${CFLAGS} 22 | -I SS{} 23 | } 24 | 25 | if { ${IOWOW_BUILD_TESTS} 26 | include { tests/Autark } 27 | } -------------------------------------------------------------------------------- /src/kv/Autark: -------------------------------------------------------------------------------- 1 | set { 2 | parent { 3 | SOURCES 4 | } 5 | ..${SOURCES} 6 | kv/iwkv.c 7 | kv/iwal.c 8 | } 9 | 10 | set { 11 | parent { 12 | PUB_HDRS 13 | } 14 | ..${PUB_HDRS} 15 | kv/iwkv.h 16 | } 17 | 18 | set { 19 | root { 20 | CFLAGS 21 | } 22 | ..${CFLAGS} 23 | -I SS{} 24 | } 25 | 26 | if { ${IOWOW_BUILD_TESTS} 27 | include { tests/Autark } 28 | } -------------------------------------------------------------------------------- /src/platform/Autark: -------------------------------------------------------------------------------- 1 | set { 2 | parent { 3 | SOURCES 4 | } 5 | ..${SOURCES} 6 | if { defined { SYSTEM_WINDOWS } 7 | platform/win32/mman/mman.c 8 | } 9 | platform/iwp.c 10 | } 11 | 12 | set { 13 | parent { 14 | PUB_HDRS 15 | } 16 | ..${PUB_HDRS} 17 | platform/iwp.h 18 | } 19 | 20 | set { 21 | root { 22 | CFLAGS 23 | } 24 | ..${CFLAGS} 25 | -I SS{} 26 | } -------------------------------------------------------------------------------- /.autark/test_pthread.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cat > './test_pthread.c' << 'EOF' 4 | #include 5 | int main(void) { 6 | pthread_t t; 7 | return 0; 8 | } 9 | EOF 10 | 11 | CC=${CC:-cc} 12 | 13 | if ${CC} ./test_pthread.c -pthread; then 14 | autark set "PTHREAD_LFLAG=-pthread" 15 | elif ${CC} ./test_pthread.c -lpthread; then 16 | autark set "PTHREAD_LFLAG=-lpthread" 17 | fi 18 | 19 | autark env CC -------------------------------------------------------------------------------- /src/re/Autark: -------------------------------------------------------------------------------- 1 | set { 2 | parent { 3 | SOURCES 4 | } 5 | ..${SOURCES} 6 | in-sources { 7 | ..@{autark -C .. glob 're/*.c'} 8 | } 9 | } 10 | 11 | set { 12 | parent { 13 | PUB_HDRS 14 | } 15 | ..${PUB_HDRS} 16 | re/iwre.h 17 | } 18 | 19 | set { 20 | root { 21 | CFLAGS 22 | } 23 | ..${CFLAGS} 24 | -I SS{} 25 | } 26 | 27 | if { ${IOWOW_BUILD_TESTS} 28 | include { tests/Autark } 29 | } -------------------------------------------------------------------------------- /.builds/freebsd-latest.yml: -------------------------------------------------------------------------------- 1 | image: freebsd/latest 2 | secrets: 3 | - 7179e7ce-6c56-46b5-9b85-091332eb3684 4 | sources: 5 | - git@git.sr.ht:~adamansky/iowow 6 | packages: 7 | - gcc 8 | - pkgconf 9 | - cunit 10 | tasks: 11 | - run-tests-gcc: | 12 | cd ./iowow 13 | CC=gcc BUILD_TYPE=Debug IOWOW_RUN_TESTS=1 ./build.sh -c 14 | - run-tests-clang: | 15 | cd ./iowow 16 | CC=clang BUILD_TYPE=Debug IOWOW_RUN_TESTS=1 ./build.sh -c -------------------------------------------------------------------------------- /.gdbinit: -------------------------------------------------------------------------------- 1 | #cd ./build/src/json/tests 2 | #file ./iwjsreg_test1 3 | #set args -c 4 | 5 | set confirm off 6 | set follow-fork-mode parent 7 | set detach-on-fork on 8 | set print elements 4096 9 | 10 | define lb 11 | set breakpoint pending on 12 | source .breakpoints 13 | set breakpoint pending auto 14 | echo breakpoints loaded\n 15 | end 16 | 17 | define sb 18 | save breakpoints .breakpoints 19 | echo breakpoints saved\n 20 | end -------------------------------------------------------------------------------- /.autark/test_header.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | HEADER=$1 4 | DEFINE_VAR=$2 5 | 6 | usage() { 7 | echo "Usage test_header.sh
" 8 | exit 1 9 | } 10 | 11 | [ -n "$HEADER" ] || usage 12 | [ -n "$DEFINE_VAR" ] || usage 13 | 14 | cat > "test_header.c" < 16 | int main() { 17 | return 0; 18 | } 19 | EOF 20 | 21 | if ${CC:-cc} -Werror ./test_header.c; then 22 | autark set "$DEFINE_VAR=1" 23 | fi 24 | 25 | autark env CC 26 | -------------------------------------------------------------------------------- /.builds/linux-ubuntu.yml: -------------------------------------------------------------------------------- 1 | image: ubuntu/25.04 2 | secrets: 3 | - 7179e7ce-6c56-46b5-9b85-091332eb3684 4 | sources: 5 | - git@git.sr.ht:~adamansky/iowow 6 | packages: 7 | - gcc 8 | - clang 9 | - pkgconf 10 | - libcunit1-dev 11 | tasks: 12 | - run-tests-gcc: | 13 | cd ./iowow 14 | CC=gcc BUILD_TYPE=Debug IOWOW_RUN_TESTS=1 ./build.sh -c 15 | - run-tests-clang: | 16 | cd ./iowow 17 | CC=clang BUILD_TYPE=Debug IOWOW_RUN_TESTS=1 ./build.sh -c -------------------------------------------------------------------------------- /src/fs/Autark: -------------------------------------------------------------------------------- 1 | set { 2 | parent { 3 | SOURCES 4 | } 5 | ..${SOURCES} 6 | in-sources { 7 | ..@{autark -C .. glob 'fs/*.c'} 8 | } 9 | } 10 | 11 | set { 12 | parent { 13 | PUB_HDRS 14 | } 15 | ..${PUB_HDRS} 16 | in-sources { 17 | ..@{autark -C .. glob 'fs/*.h'} 18 | } 19 | } 20 | 21 | set { 22 | root { 23 | CFLAGS 24 | } 25 | ..${CFLAGS} 26 | -I SS{} 27 | } 28 | 29 | if { ${IOWOW_BUILD_TESTS} 30 | include { tests/Autark } 31 | } -------------------------------------------------------------------------------- /src/libiowow.pc.in: -------------------------------------------------------------------------------- 1 | exec_prefix=@INSTALL_PREFIX@/@INSTALL_BIN_DIR@ 2 | libdir=@INSTALL_PREFIX@/@INSTALL_LIB_DIR@ 3 | includedir=@INSTALL_PREFIX@/@IOWOW_PUBLIC_HEADERS_DESTINATION@ 4 | artifact=@META_NAME@ 5 | 6 | Name: @META_NAME@ 7 | Description: @META_DESCRIPTION@ 8 | URL: @META_WEBSITE@ 9 | Version: @META_VERSION@ 10 | Libs: -L${libdir} -l${artifact} 11 | Libs.private: @LDFLAGS@ 12 | Cflags: -I@INSTALL_PREFIX@/@INSTALL_INCLUDE_DIR@ -I${includedir} 13 | Cflags.private: -DIW_STATIC 14 | -------------------------------------------------------------------------------- /.autark/test_blocks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | DEFINE_VAR=$1 4 | CC=${CC:-cc} 5 | 6 | usage() { 7 | echo "Usage test_blocks.sh " 8 | exit 1 9 | } 10 | 11 | [ -n "$DEFINE_VAR" ] || usage 12 | 13 | cat > "test_blocks.c" <<'EOF' 14 | int main() { 15 | void (^block)(void) = ^{ }; 16 | block(); 17 | return 0; 18 | } 19 | EOF 20 | 21 | if ${CC} -Werror -fblocks -lBlocksRuntime ./test_blocks.c -o ./test_blocks; then 22 | autark set "$DEFINE_VAR=1" 23 | fi 24 | 25 | autark env CC 26 | -------------------------------------------------------------------------------- /src/json/Autark: -------------------------------------------------------------------------------- 1 | set { 2 | parent { 3 | SOURCES 4 | } 5 | ..${SOURCES} 6 | in-sources { 7 | ..@{autark -C .. glob 'json/*.c'} 8 | } 9 | } 10 | 11 | set { 12 | parent { 13 | PUB_HDRS 14 | } 15 | ..${PUB_HDRS} 16 | json/iwbinn.h 17 | json/iwjson.h 18 | json/iwjsreg.h 19 | json/iwjson_internal.h 20 | } 21 | 22 | set { 23 | root { 24 | CFLAGS 25 | } 26 | ..${CFLAGS} 27 | -I SS{} 28 | } 29 | 30 | if { ${IOWOW_BUILD_TESTS} 31 | include { tests/Autark } 32 | } -------------------------------------------------------------------------------- /src/re/iwre.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef IW_IWRE_H 4 | #define IW_IWRE_H 5 | 6 | #include "basedefs.h" 7 | 8 | #define IWRE_MAX_MATCHES 64 9 | 10 | struct iwre; 11 | 12 | struct iwre* iwre_create(const char *pattern); 13 | 14 | const char* iwre_pattern_get(struct iwre*); 15 | 16 | /// @return Number of of matches `n`, where `2*n <= nmatches` 17 | int iwre_match(struct iwre*, const char *text, const char *mpairs[], size_t mpairs_len); 18 | 19 | void iwre_destroy(struct iwre*); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /src/log/tests/Autark: -------------------------------------------------------------------------------- 1 | cc { 2 | set { _ 3 | iwlog_test1.c 4 | } 5 | ${CFLAGS_TESTS} 6 | } 7 | 8 | foreach { 9 | OBJ 10 | ${CC_OBJS} 11 | run { 12 | exec { ${CC} ${OBJ} ${LDFLAGS_TEST} -o %{${OBJ}} } 13 | consumes { ${LIBIOWOW_A} ${OBJ} } 14 | produces { %{${OBJ}} } 15 | } 16 | } 17 | 18 | if { ${IOWOW_RUN_TESTS} 19 | foreach { 20 | OBJ 21 | ${CC_OBJS} 22 | run { 23 | always 24 | shell { %{${OBJ}} } 25 | consumes { %{${OBJ}} } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/re/tests/Autark: -------------------------------------------------------------------------------- 1 | cc { 2 | set { _ 3 | iwre_test1.c 4 | } 5 | ${CFLAGS_TESTS} 6 | } 7 | 8 | foreach { 9 | OBJ 10 | ${CC_OBJS} 11 | run { 12 | exec { ${CC} ${OBJ} ${LDFLAGS_TEST} -o %{${OBJ}} } 13 | consumes { ${LIBIOWOW_A} ${OBJ} } 14 | produces { %{${OBJ}} } 15 | } 16 | } 17 | 18 | if { ${IOWOW_RUN_TESTS} 19 | foreach { 20 | OBJ 21 | ${CC_OBJS} 22 | run { 23 | always 24 | shell { %{${OBJ}} } 25 | consumes { %{${OBJ}} } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/utils/iwuuid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWUUID_H 3 | #define IWUUID_H 4 | 5 | #include "basedefs.h" 6 | 7 | IW_EXTERN_C_START; 8 | 9 | #define IW_UUID_STR_LEN 36 10 | 11 | /** 12 | * Creates random UUID v4 string and fill a provided `buf` 13 | * with capacity of 36 bytes at least. 14 | * @note Does't write terminating `NULL` byte. 15 | */ 16 | IW_EXPORT void iwu_uuid4_fill(char dest[IW_UUID_STR_LEN]); 17 | 18 | IW_EXPORT bool iwu_uuid_valid(const char *uuid); 19 | 20 | IW_EXTERN_C_END; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/fs/tests/Autark: -------------------------------------------------------------------------------- 1 | cc { 2 | set { _ 3 | iwfs_test1.c 4 | iwfs_test2.c 5 | iwfs_test3.c 6 | } 7 | ${CFLAGS_TESTS} 8 | } 9 | 10 | foreach { 11 | OBJ 12 | ${CC_OBJS} 13 | run { 14 | exec { ${CC} ${OBJ} ${LDFLAGS_TEST} -o %{${OBJ}} } 15 | consumes { ${LIBIOWOW_A} ${OBJ} } 16 | produces { %{${OBJ}} } 17 | } 18 | } 19 | 20 | if { ${IOWOW_RUN_TESTS} 21 | foreach { 22 | OBJ 23 | ${CC_OBJS} 24 | run { 25 | always 26 | shell { %{${OBJ}} } 27 | consumes { %{${OBJ}} } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /.lvimrc: -------------------------------------------------------------------------------- 1 | " Local vim rc 2 | 3 | if exists('g:build_dir') 4 | finish 5 | endif 6 | 7 | let g:build_type = get(g:, 'build_type', 'Debug') 8 | let g:build_tests = get(g:, 'build_tests', 'ON') 9 | let g:cc = get(g:, 'cc', 'clang') 10 | let g:cxx = get(g:, 'cxx', 'clang++') 11 | 12 | let g:root_dir = g:localvimrc_script_dir_unresolved 13 | let $ROOT_DIR = g:root_dir 14 | let $UNCRUSTIFY_CONFIG = g:root_dir.'/uncrustify.cfg' 15 | let &g:makeprg = 'IOWOW_BUILD_TESTS=1 BUILD_TYPE=Debug ./build.sh' 16 | 17 | let g:termdebug_config = {'map_minus': 0, 'map_plus': 0, 'wide': 1} -------------------------------------------------------------------------------- /src/json/tests/data/jbl_test2.001.expected.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | bar 4 | bo 5 | dy 1 < 2 6 | 7 | 1 8 | 2 9 | 3 10 | 11 | 12 | k&k 13 | 14 | 15 | 16 | 17 | 18 | true 19 | 20 | 1 21 | 3.36 22 | true 23 | a 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all release release-shared-libs debug debug-shared-libs test clean 2 | 3 | all: release; 4 | 5 | release: 6 | BUILD_TYPE=Release ./build.sh 7 | 8 | release-shared-libs: 9 | BUILD_TYPE=Release IOWOW_BUILD_SHARED_LIBS=1 ./build.sh 10 | 11 | debug: 12 | BUILD_TYPE=Debug ./build.sh 13 | 14 | debug-shared-libs: 15 | BUILD_TYPE=Debug IOWOW_BUILD_SHARED_LIBS=1 ./build.sh 16 | 17 | test: 18 | BUILD_TYPE=Debug IOWOW_RUN_TESTS=1 ./build.sh 19 | 20 | test-release: 21 | BUILD_TYPE=Release IOWOW_RUN_TESTS=1 ./build.sh 22 | 23 | clean: 24 | rm -rf ./autark-cache -------------------------------------------------------------------------------- /src/utils/tests/Autark: -------------------------------------------------------------------------------- 1 | cc { 2 | set { _ 3 | iwarr_test1.c 4 | iwutils_test1.c 5 | iwhmap_test1.c 6 | iwrb_test1.c 7 | } 8 | ${CFLAGS_TESTS} 9 | } 10 | 11 | foreach { 12 | OBJ 13 | ${CC_OBJS} 14 | run { 15 | exec { ${CC} ${OBJ} ${LDFLAGS_TEST} -o %{${OBJ}} } 16 | consumes { ${LIBIOWOW_A} ${OBJ} } 17 | produces { %{${OBJ}} } 18 | } 19 | } 20 | 21 | if { ${IOWOW_RUN_TESTS} 22 | foreach { 23 | OBJ 24 | ${CC_OBJS} 25 | run { 26 | always 27 | shell { %{${OBJ}} } 28 | consumes { %{${OBJ}} } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /.autark/test_symbol.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | SYMBOL=$1 4 | HEADER=$2 5 | DEFINE_VAR=$3 6 | 7 | usage() { 8 | echo "Usage test_symbol.sh
" 9 | exit 1 10 | } 11 | 12 | [ -n "$SYMBOL" ] || usage 13 | [ -n "$HEADER" ] || usage 14 | [ -n "$DEFINE_VAR" ] || usage 15 | 16 | cat > "test_symbol.c" < 18 | int main() { 19 | #ifdef $SYMBOL 20 | return 0; // Symbol is a macro 21 | #else 22 | (void)$SYMBOL; // Symbol might be a function or variable 23 | return 0; 24 | #endif 25 | } 26 | EOF 27 | 28 | if ${CC:-cc} -Werror ./test_symbol.c; then 29 | autark set "$DEFINE_VAR=1" 30 | fi 31 | 32 | autark env CC 33 | -------------------------------------------------------------------------------- /src/utils/murmur3.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef MURMUR_H 3 | #define MURMUR_H 4 | 5 | /************************************************************************************************** 6 | * MurmurHash3 was written by Austin Appleby, and is placed in the 7 | * public domain. The author hereby disclaims copyright to this source 8 | * code. 9 | *************************************************************************************************/ 10 | 11 | #include "basedefs.h" 12 | 13 | IW_EXTERN_C_START; 14 | 15 | IW_EXPORT void murmur3_set_seed(const uint32_t seed); 16 | 17 | IW_EXPORT uint32_t murmur3(const char *key, size_t len); 18 | 19 | IW_EXTERN_C_END; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | set -x 5 | 6 | cd "$(dirname "$(readlink -f "$0")")" 7 | 8 | git pull origin master 9 | 10 | CHANGELOG=./Changelog 11 | VERSION=$(grep -m1 -o '\[v[0-9][^]]*\]' "$CHANGELOG" | sed 's/\[v//;s/\]//') 12 | TAG="v${VERSION}" 13 | 14 | CHANGESET=$(awk -v tag="$TAG" ' 15 | $0 ~ tag { skip = 1; next } 16 | skip && NF == 0 { exit } 17 | skip { print } 18 | ' "$CHANGELOG" | sed 's/^[[:space:]]*//; s/[[:space:]]*$//' ) 19 | 20 | git add "$CHANGELOG" 21 | 22 | if ! git diff-index --quiet HEAD --; then 23 | git commit -m"${TAG} landed" 24 | git push origin master 25 | fi 26 | 27 | echo "$CHANGESET" | git tag -f -a -F - "$TAG" 28 | git push origin -f --tags -------------------------------------------------------------------------------- /src/json/tests/Autark: -------------------------------------------------------------------------------- 1 | cc { 2 | set { _ 3 | jbl_test1.c 4 | jbl_test2.c 5 | jbl_test_binn1.c 6 | jbl_test_binn2.c 7 | iwjsreg_test1.c 8 | } 9 | ${CFLAGS_TESTS} 10 | } 11 | 12 | foreach { 13 | OBJ 14 | ${CC_OBJS} 15 | run { 16 | exec { ${CC} ${OBJ} ${LDFLAGS_TEST} -o %{${OBJ}} } 17 | consumes { ${LIBIOWOW_A} ${OBJ} } 18 | produces { %{${OBJ}} } 19 | } 20 | } 21 | 22 | if { ${IOWOW_RUN_TESTS} 23 | run { 24 | shell { ln -sf SS{data} ./data } 25 | produces { ./data } 26 | } 27 | foreach { 28 | OBJ 29 | ${CC_OBJS} 30 | run { 31 | always 32 | shell { %{${OBJ}} } 33 | consumes { %{${OBJ}} } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: > 3 | bugprone-*, 4 | clang-analyzer-*, 5 | google-*, 6 | misc-*, 7 | modernize-*, 8 | performance-*, 9 | portability-*, 10 | -bugprone-branch-clone, 11 | -bugprone-easily-swappable-parameters, 12 | -bugprone-macro-parentheses, 13 | -bugprone-narrowing-conversions, 14 | -bugprone-not-null-terminated-result, 15 | -bugprone-reserved-identifier, 16 | -bugprone-sizeof-expression, 17 | -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, 18 | -clang-analyzer-security.insecureAPI.strcpy, 19 | -google-readability-todo, 20 | -misc-unused-parameters, 21 | -misc-no-recursion, 22 | -performance-no-int-to-ptr, 23 | -bugprone-assignment-in-if-condition, 24 | 25 | ... 26 | -------------------------------------------------------------------------------- /src/kv/tests/Autark: -------------------------------------------------------------------------------- 1 | cc { 2 | set { _ 3 | iwkv_test1.c 4 | iwkv_test2.c 5 | iwkv_test3.c 6 | iwkv_test4.c 7 | iwkv_test5.c 8 | iwkv_test6.c 9 | iwkv_test7.c 10 | iwkv_test8.c 11 | iwkv_test9.c 12 | iwkv_test10.c 13 | } 14 | ${CFLAGS_TESTS} 15 | } 16 | 17 | foreach { 18 | OBJ 19 | ${CC_OBJS} 20 | run { 21 | exec { ${CC} ${OBJ} ${LDFLAGS_TEST} -o %{${OBJ}} } 22 | consumes { ${LIBIOWOW_A} ${OBJ} } 23 | produces { %{${OBJ}} } 24 | } 25 | } 26 | 27 | if { ${IOWOW_RUN_TESTS} 28 | run { 29 | shell { cp SS{*.ref} CC{} } 30 | shell { cp SS{*.data} CC{} } 31 | } 32 | foreach { 33 | OBJ 34 | ${CC_OBJS} 35 | run { 36 | always 37 | shell { %{${OBJ}} } 38 | consumes { %{${OBJ}} } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /src/kv/tests/iwkv_tests.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void iwkvd_trigger_xor(uint64_t val); 5 | 6 | static int cmp_files(FILE *f1, FILE *f2) { 7 | CU_ASSERT_TRUE_FATAL(f1 && f2); 8 | fseek(f1, 0, SEEK_SET); 9 | fseek(f2, 0, SEEK_SET); 10 | char c1 = getc(f1); 11 | char c2 = getc(f2); 12 | int pos = 0, line = 1; 13 | while (c1 != EOF && c2 != EOF) { 14 | pos++; 15 | if ((c1 == '\n') && (c2 == '\n')) { 16 | line++; 17 | pos = 0; 18 | } else if (c1 != c2) { 19 | fprintf(stderr, "\nDiff at: %d:%d\n", line, pos); 20 | return (c1 - c2); 21 | } 22 | c1 = getc(f1); 23 | c2 = getc(f2); 24 | } 25 | if (c1 - c2) { 26 | fprintf(stderr, "\nDiff at: %d:%d\n", line, pos); 27 | } 28 | return (c1 - c2); 29 | } 30 | -------------------------------------------------------------------------------- /src/json/tests/data/001.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "str": "𝌆", 3 | "str1": "15øC 3đ", 4 | "str2": "Mа二𐌂", 5 | "str3": "привет", 6 | "na": [ 7 | 0.1, 8 | 0.000006, 9 | 0.000001 10 | ], 11 | "glossary": { 12 | "title": "example glossary", 13 | "GlossDiv": { 14 | "title": "S", 15 | "GlossList": { 16 | "GlossEntry": { 17 | "ID": "SGML", 18 | "SortAs": "SGML", 19 | "GlossTerm": "Standard Generalized Markup Language", 20 | "Acronym": "SGML", 21 | "Abbrev": "ISO 8879:1986", 22 | "GlossDef": { 23 | "para": "A meta-markup language, used to create markup languages such as DocBook.", 24 | "GlossSeeAlso": [ 25 | "GML", 26 | "XML", 27 | true, 28 | false 29 | ] 30 | }, 31 | "GlossSee": "markup" 32 | } 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/utils/Autark: -------------------------------------------------------------------------------- 1 | set { 2 | parent { 3 | SOURCES 4 | } 5 | ..${SOURCES} 6 | in-sources { 7 | ..@{autark -C .. glob 'utils/*.c'} 8 | } 9 | } 10 | 11 | set { 12 | parent { 13 | PUB_HDRS 14 | } 15 | ..${PUB_HDRS} 16 | utils/iwarr.h 17 | utils/iwavl.h 18 | utils/iwbits.h 19 | utils/iwchars.h 20 | utils/iwconv.h 21 | utils/iwhmap.h 22 | utils/iwini.h 23 | utils/iwpool.h 24 | utils/iwrb.h 25 | utils/iwrefs.h 26 | utils/iwstw.h 27 | utils/iwth.h 28 | utils/iwtp.h 29 | utils/iwutils.h 30 | utils/iwuuid.h 31 | utils/iwxstr.h 32 | utils/murmur3.h 33 | utils/wyhash.h 34 | utils/wyhash32.h 35 | utils/utf8proc.h 36 | utils/pthread_spin_lock_shim.h 37 | utils/iwcsv.h 38 | } 39 | 40 | set { 41 | root { 42 | CFLAGS 43 | } 44 | ..${CFLAGS} 45 | -I SS{} 46 | } 47 | 48 | if { ${IOWOW_BUILD_TESTS} 49 | include { tests/Autark } 50 | } -------------------------------------------------------------------------------- /src/json/tests/data/002.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "str": "\uD834\uDF06", 3 | "str1": "15\u00F8C 3\u0111", 4 | "str2": "M\u0430\u4E8C\uD800\uDF02", 5 | "str3": "\u043F\u0440\u0438\u0432\u0435\u0442", 6 | "na": [ 7 | 0.00012, 8 | 0.000006, 9 | 0.000001 10 | ], 11 | "glossary": { 12 | "title": "example glossary", 13 | "GlossDiv": { 14 | "title": "S", 15 | "GlossList": { 16 | "GlossEntry": { 17 | "ID": "SGML", 18 | "SortAs": "SGML", 19 | "GlossTerm": "Standard Generalized Markup Language", 20 | "Acronym": "SGML", 21 | "Abbrev": "ISO 8879:1986", 22 | "GlossDef": { 23 | "para": "A meta-markup language, used to create markup languages such as DocBook.", 24 | "GlossSeeAlso": [ 25 | "GML", 26 | "XML", 27 | true, 28 | false 29 | ] 30 | }, 31 | "GlossSee": "markup" 32 | } 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/json/tests/data/001.json: -------------------------------------------------------------------------------- 1 | { 2 | "str": "\uD834\uDF06", 3 | "str1": "15\u00f8C 3\u0111", 4 | "str2": "\u004d\u0430\u4e8c\ud800\udf02", 5 | "str3": "привет", 6 | "na": [ 7 | 0.1000, 8 | 6E-06, 9 | 1E-06 10 | ], 11 | "glossary": { 12 | "title": "example glossary", 13 | "GlossDiv": { 14 | "title": "S", 15 | "GlossList": { 16 | "GlossEntry": { 17 | "ID": "SGML", 18 | "SortAs": "SGML", 19 | "GlossTerm": "Standard Generalized Markup Language", 20 | "Acronym": "SGML", 21 | "Abbrev": "ISO 8879:1986", 22 | "GlossDef": { 23 | "para": "A meta-markup language, used to create markup languages such as DocBook.", 24 | "GlossSeeAlso": [ 25 | "GML", 26 | "XML", 27 | true, 28 | false 29 | ] 30 | }, 31 | "GlossSee": "markup" 32 | } 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/json/tests/data/002.json: -------------------------------------------------------------------------------- 1 | { 2 | "str": "\uD834\uDF06", 3 | "str1": "15\u00f8C 3\u0111", 4 | "str2": "\u004d\u0430\u4e8c\ud800\udf02", 5 | "str3": "привет", 6 | "na": [ 7 | 0.00011999999999999999, 8 | 6E-06, 9 | 1E-06 10 | ], 11 | "glossary": { 12 | "title": "example glossary", 13 | "GlossDiv": { 14 | "title": "S", 15 | "GlossList": { 16 | "GlossEntry": { 17 | "ID": "SGML", 18 | "SortAs": "SGML", 19 | "GlossTerm": "Standard Generalized Markup Language", 20 | "Acronym": "SGML", 21 | "Abbrev": "ISO 8879:1986", 22 | "GlossDef": { 23 | "para": "A meta-markup language, used to create markup languages such as DocBook.", 24 | "GlossSeeAlso": [ 25 | "GML", 26 | "XML", 27 | true, 28 | false 29 | ] 30 | }, 31 | "GlossSee": "markup" 32 | } 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /src/utils/iwchars.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWCHARS_H 3 | #define IWCHARS_H 4 | 5 | #include "basedefs.h" 6 | #include 7 | 8 | IW_INLINE bool iwchars_is_blank(char c) { 9 | return c == 32 || c == 9; 10 | } 11 | 12 | IW_INLINE bool iwchars_is_space(char c) { 13 | return c == 32 || (c >= 9 && c <= 13); 14 | } 15 | 16 | IW_INLINE bool iwchars_is_digit(char c) { 17 | return c >= 48 && c <= 57; 18 | } 19 | 20 | IW_INLINE bool iwchars_is_alpha(char c) { 21 | return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); 22 | } 23 | 24 | IW_INLINE bool iwchars_is_alnum(char c) { 25 | return iwchars_is_alpha(c) || iwchars_is_digit(c); 26 | } 27 | 28 | static bool iwchars_is_digits(const char *str, int len) { 29 | for (int i = 0; i < len; ++i) { 30 | if (!iwchars_is_digit(str[i])) { 31 | return false; 32 | } 33 | } 34 | return true; 35 | } 36 | 37 | static bool iwchars_is_digits_or(const char *str, int len, char or_char) { 38 | for (int i = 0; i < len; ++i) { 39 | if (!iwchars_is_digit(str[i]) && str[i] != or_char) { 40 | return false; 41 | } 42 | } 43 | return true; 44 | } 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2012-2024 Softmotions Ltd 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/rdb/iwrdb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWRDB_H 3 | #define IWRDB_H 4 | 5 | #include "basedefs.h" 6 | 7 | IW_EXTERN_C_START; 8 | 9 | typedef uint8_t iwrdb_oflags_t; 10 | #define IWRDB_NOLOCKS ((iwrdb_oflags_t) 0x01U) 11 | 12 | typedef struct iwrdb*IWRDB; 13 | 14 | IW_EXPORT iwrc iwrdb_open(const char *path, iwrdb_oflags_t oflags, size_t bufsz, struct iwrdb **open); 15 | 16 | IW_EXPORT iwrc iwrdb_sync(struct iwrdb *db); 17 | 18 | IW_EXPORT iwrc iwrdb_append(struct iwrdb *db, const void *data, int len, uint64_t *oref); 19 | 20 | IW_EXPORT iwrc iwrdb_patch(struct iwrdb *db, uint64_t ref, off_t skip, const void *data, int len); 21 | 22 | IW_EXPORT iwrc iwrdb_close(struct iwrdb **db, bool no_sync); 23 | 24 | IW_EXPORT iwrc iwrdb_read(struct iwrdb *db, uint64_t ref, off_t skip, void *buf, int len); 25 | 26 | IW_EXPORT HANDLE iwrdb_file_handle(struct iwrdb *db); 27 | 28 | /// Returns logical data end offset including internal cache buffer. 29 | /// Returns `-1` int the case of error. 30 | IW_EXPORT off_t iwrdb_offset_end(struct iwrdb *db); 31 | 32 | IW_EXPORT uint8_t* iwrdb_mmap(struct iwrdb *db, bool readonly, int madv, size_t *msiz); 33 | 34 | IW_EXPORT void iwrdb_munmap(struct iwrdb *db); 35 | 36 | IW_EXTERN_C_END; 37 | #endif 38 | -------------------------------------------------------------------------------- /src/utils/pthread_spin_lock_shim.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | // https://idea.popcount.org/2012-09-12-reinventing-spinlocks/ 3 | // See https://stackoverflow.com/questions/8177031/does-mac-os-x-have-pthread-spinlock-t-type 4 | 5 | #ifndef PTHREAD_SPIN_LOCK_SHIM 6 | #define PTHREAD_SPIN_LOCK_SHIM 7 | 8 | #include 9 | #include 10 | 11 | typedef int pthread_spinlock_t; 12 | 13 | #ifndef PTHREAD_PROCESS_SHARED 14 | # define PTHREAD_PROCESS_SHARED 1 15 | #endif 16 | #ifndef PTHREAD_PROCESS_PRIVATE 17 | # define PTHREAD_PROCESS_PRIVATE 2 18 | #endif 19 | 20 | static inline int pthread_spin_init(pthread_spinlock_t *lock, int pshared) { 21 | __asm__ __volatile__ ("" ::: "memory"); 22 | *lock = 0; 23 | return 0; 24 | } 25 | 26 | static inline int pthread_spin_destroy(pthread_spinlock_t *lock) { 27 | return 0; 28 | } 29 | 30 | static inline int pthread_spin_lock(pthread_spinlock_t *lock) { 31 | while (1) { 32 | int i; 33 | for (i = 0; i < 10000; i++) { 34 | if (__sync_bool_compare_and_swap(lock, 0, 1)) { 35 | return 0; 36 | } 37 | } 38 | sched_yield(); 39 | } 40 | } 41 | 42 | static inline int pthread_spin_trylock(pthread_spinlock_t *lock) { 43 | if (__sync_bool_compare_and_swap(lock, 0, 1)) { 44 | return 0; 45 | } 46 | return EBUSY; 47 | } 48 | 49 | static inline int pthread_spin_unlock(pthread_spinlock_t *lock) { 50 | __asm__ __volatile__ ("" ::: "memory"); 51 | *lock = 0; 52 | return 0; 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /src/kv/examples/example1.c: -------------------------------------------------------------------------------- 1 | #include "iwkv.h" 2 | #include 3 | #include 4 | 5 | int main(void) { 6 | IWKV_OPTS opts = { 7 | .path = "example1.db", 8 | .oflags = IWKV_TRUNC // Cleanup database before open 9 | }; 10 | IWKV iwkv; 11 | IWDB mydb; 12 | iwrc rc = iwkv_open(&opts, &iwkv); 13 | if (rc) { 14 | iwlog_ecode_error3(rc); 15 | return 1; 16 | } 17 | // Now open mydb 18 | // - Database id: 1 19 | // - Using key/value as char data 20 | rc = iwkv_db(iwkv, 1, 0, &mydb); 21 | if (rc) { 22 | iwlog_ecode_error2(rc, "Failed to open mydb"); 23 | return 1; 24 | } 25 | // Work with db: put/get value 26 | IWKV_val key, val; 27 | key.data = "foo"; 28 | key.size = strlen(key.data); 29 | val.data = "bar"; 30 | val.size = strlen(val.data); 31 | 32 | fprintf(stdout, "put: %.*s => %.*s\n", 33 | (int) key.size, (char*) key.data, 34 | (int) val.size, (char*) val.data); 35 | 36 | rc = iwkv_put(mydb, &key, &val, 0); 37 | if (rc) { 38 | iwlog_ecode_error3(rc); 39 | return rc; 40 | } 41 | // Retrive value associated with `foo` key 42 | val.data = 0; 43 | val.size = 0; 44 | rc = iwkv_get(mydb, &key, &val); 45 | if (rc) { 46 | iwlog_ecode_error3(rc); 47 | return rc; 48 | } 49 | 50 | fprintf(stdout, "get: %.*s => %.*s\n", 51 | (int) key.size, (char*) key.data, 52 | (int) val.size, (char*) val.data); 53 | 54 | iwkv_val_dispose(&val); 55 | iwkv_close(&iwkv); 56 | return 0; 57 | } 58 | -------------------------------------------------------------------------------- /src/re/iwre.c: -------------------------------------------------------------------------------- 1 | #include "iwre.h" 2 | #include "cregex.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct iwre { 9 | const char *pattern; 10 | cregex_program_t *program; 11 | }; 12 | 13 | const char* iwre_pattern_get(struct iwre *re) { 14 | return re->pattern; 15 | } 16 | 17 | int iwre_match(struct iwre *re, const char *text, const char *mpairs[], size_t mpairs_len) { 18 | if (mpairs_len % 2 != 0) { 19 | errno = EINVAL; 20 | return -1; 21 | } 22 | memset(mpairs, 0, sizeof(mpairs[0]) * mpairs_len); 23 | int ret = cregex_program_run(re->program, text, mpairs, mpairs_len); 24 | if (ret < 1) { 25 | return 0; 26 | } 27 | ret = 0; 28 | for (int i = 0; i < mpairs_len && mpairs[i]; ++i) { 29 | ++ret; 30 | } 31 | return ret / 2; 32 | } 33 | 34 | void iwre_destroy(struct iwre *re) { 35 | if (re) { 36 | cregex_compile_free(re->program); 37 | free(re); 38 | } 39 | } 40 | 41 | struct iwre* iwre_create(const char *pattern) { 42 | if (!pattern || pattern[0] == '\0') { 43 | // Don't support empty regexp patterns 44 | return 0; 45 | } 46 | struct iwre *re = calloc(1, sizeof(*re)); 47 | if (!re) { 48 | return 0; 49 | } 50 | cregex_node_t *node = cregex_parse(pattern); 51 | if (!node) { 52 | goto error; 53 | } 54 | re->pattern = pattern; 55 | re->program = cregex_compile_node(node); 56 | if (!re->program) { 57 | goto error; 58 | } 59 | cregex_parse_free(node); 60 | return re; 61 | 62 | error: 63 | if (node) { 64 | cregex_parse_free(node); 65 | } 66 | iwre_destroy(re); 67 | return 0; 68 | } 69 | -------------------------------------------------------------------------------- /src/fs/iwdlsnr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWDLSNR_H 3 | #define IWDLSNR_H 4 | 5 | #include "basedefs.h" 6 | #include 7 | 8 | IW_EXTERN_C_START; 9 | 10 | /** 11 | * @brief File data events listener. 12 | */ 13 | struct iwdlsnr { 14 | /** 15 | * @brief Before file open event. 16 | * 17 | * @param path File path 18 | * @param mode File open mode same as in open(2) 19 | */ 20 | iwrc (*onopen)(struct iwdlsnr *self, const char *path, int mode); 21 | 22 | /** 23 | * @brief Before file been closed. 24 | */ 25 | iwrc (*onclosing)(struct iwdlsnr *self); 26 | 27 | /** 28 | * @brief Write @a val value starting at @a off @a len bytes 29 | */ 30 | iwrc (*onset)(struct iwdlsnr *self, off_t off, uint8_t val, off_t len, int flags); 31 | 32 | /** 33 | * @brief Copy @a len bytes from @a off offset to @a noff offset 34 | */ 35 | iwrc (*oncopy)(struct iwdlsnr *self, off_t off, off_t len, off_t noff, int flags); 36 | 37 | /** 38 | * @brief Write @buf of @a len bytes at @a off 39 | */ 40 | iwrc (*onwrite)(struct iwdlsnr *self, off_t off, const void *buf, off_t len, int flags); 41 | 42 | /** 43 | * @brief File need to be resized. 44 | * 45 | * @param osize Old file size 46 | * @param nsize New file size 47 | * @param [out] handled File resizing handled by llistener. 48 | */ 49 | iwrc (*onresize)(struct iwdlsnr *self, off_t osize, off_t nsize, int flags, bool *handled); 50 | 51 | /** 52 | * @brief File sync successful 53 | */ 54 | iwrc (*onsynced)(struct iwdlsnr *self, int flags); 55 | }; 56 | 57 | typedef struct iwdlsnr IWDLSNR; 58 | 59 | IW_EXTERN_C_END; 60 | 61 | #endif // !IWDLSNR_H 62 | -------------------------------------------------------------------------------- /src/fs/iwfs.c: -------------------------------------------------------------------------------- 1 | /************************************************************************************************** 2 | * IOWOW library 3 | * 4 | * MIT License 5 | * 6 | * Copyright (c) 2012-2024 Softmotions Ltd 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in all 16 | * copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | * SOFTWARE. 25 | *************************************************************************************************/ 26 | 27 | #include "iwfile.h" 28 | #include "iwexfile.h" 29 | #include "iwfsmfile.h" 30 | 31 | iwrc iwfs_init(void) { 32 | iwrc rc = 0; 33 | IWRC(iwfs_file_init(), rc); 34 | IWRC(iwfs_exfile_init(), rc); 35 | IWRC(iwfs_fsmfile_init(), rc); 36 | return rc; 37 | } 38 | -------------------------------------------------------------------------------- /src/platform/win32/mman/mman.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* 3 | * sys/mman.h 4 | * mman-win32 5 | * 6 | * Based on https://github.com/witwall/mman-win32 7 | */ 8 | #ifndef _SYS_MMAN_H_ 9 | #define _SYS_MMAN_H_ 10 | 11 | #include "basedefs.h" 12 | 13 | #ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. 14 | #define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. 15 | #endif 16 | 17 | /* All the headers include this file. */ 18 | #ifndef _MSC_VER 19 | #include <_mingw.h> 20 | #endif 21 | 22 | #if defined(MMAN_LIBRARY) 23 | #define MMANSHARED_EXPORT __declspec(dllexport) 24 | #else 25 | #define MMANSHARED_EXPORT __declspec(dllimport) 26 | #endif 27 | 28 | /* Determine offset type */ 29 | #include 30 | #if defined(_WIN64) 31 | typedef int64_t OffsetType; 32 | #else 33 | typedef uint32_t OffsetType; 34 | #endif 35 | 36 | #include 37 | 38 | #ifdef __cplusplus 39 | extern "C" { 40 | #endif 41 | 42 | #define PROT_NONE 0 43 | #define PROT_READ 1 44 | #define PROT_WRITE 2 45 | #define PROT_EXEC 4 46 | 47 | #define MAP_FILE 0 48 | #define MAP_SHARED 1 49 | #define MAP_PRIVATE 2 50 | #define MAP_TYPE 0xf 51 | #define MAP_FIXED 0x10 52 | #define MAP_ANONYMOUS 0x20 53 | #define MAP_ANON MAP_ANONYMOUS 54 | 55 | #define MAP_FAILED ((void*) -1) 56 | 57 | /* Flags for msync. */ 58 | #define MS_ASYNC 1 59 | #define MS_SYNC 2 60 | #define MS_INVALIDATE 4 61 | 62 | void* mmap(void *addr, size_t len, int prot, int flags, HANDLE fh, OffsetType off); 63 | int munmap(void *addr, size_t len); 64 | int msync(void *addr, size_t len, int flags); 65 | int mlock(const void *addr, size_t len); 66 | int munlock(const void *addr, size_t len); 67 | 68 | #ifdef __cplusplus 69 | } 70 | #endif 71 | 72 | #endif /* _SYS_MMAN_H_ */ 73 | -------------------------------------------------------------------------------- /src/re/tests/iwre_test1.c: -------------------------------------------------------------------------------- 1 | #include "iowow.h" 2 | #include 3 | #include "iwre.h" 4 | 5 | static int init_suite(void) { 6 | return iw_init(); 7 | } 8 | 9 | static int clean_suite(void) { 10 | return 0; 11 | } 12 | 13 | static void iwre_test1(void) { 14 | struct iwre *re = iwre_create("^(one1)(two)?"); 15 | const char *mpairs[10]; 16 | int rv = iwre_match(re, "one1two", mpairs, 10); 17 | CU_ASSERT_EQUAL(rv, 3); 18 | for (int i = 0, j = 0; i < 2 * rv; i += 2, ++j) { 19 | intptr_t l = mpairs[i + 1] - mpairs[i]; 20 | switch (j) { 21 | case 0: 22 | CU_ASSERT_EQUAL(l, 7); 23 | CU_ASSERT_EQUAL(strncmp(mpairs[i], "one1two", l), 0); 24 | break; 25 | case 1: 26 | CU_ASSERT_EQUAL(l, 4); 27 | CU_ASSERT_EQUAL(strncmp(mpairs[i], "one1", l), 0); 28 | break; 29 | case 2: 30 | CU_ASSERT_EQUAL(l, 3); 31 | CU_ASSERT_EQUAL(strncmp(mpairs[i], "two", l), 0); 32 | break; 33 | } 34 | } 35 | iwre_destroy(re); 36 | } 37 | 38 | int main(int argc, const char *argv[]) { 39 | CU_pSuite pSuite = NULL; 40 | 41 | /* Initialize the CUnit test registry */ 42 | if (CUE_SUCCESS != CU_initialize_registry()) { 43 | return CU_get_error(); 44 | } 45 | 46 | /* Add a suite to the registry */ 47 | pSuite = CU_add_suite("iwre_test1", init_suite, clean_suite); 48 | 49 | if (NULL == pSuite) { 50 | CU_cleanup_registry(); 51 | return CU_get_error(); 52 | } 53 | 54 | /* Add the tests to the suite */ 55 | if ((NULL == CU_add_test(pSuite, "iwre_test1", iwre_test1))) { 56 | CU_cleanup_registry(); 57 | return CU_get_error(); 58 | } 59 | 60 | /* Run all tests using the CUnit Basic interface */ 61 | CU_basic_set_mode(CU_BRM_VERBOSE); 62 | CU_basic_run_tests(); 63 | int ret = CU_get_error() || CU_get_number_of_failures(); 64 | CU_cleanup_registry(); 65 | return ret; 66 | } 67 | -------------------------------------------------------------------------------- /src/utils/tests/iwarr_test1.c: -------------------------------------------------------------------------------- 1 | #include "iowow.h" 2 | #include "iwcfg.h" 3 | #include 4 | #include "iwarr.h" 5 | 6 | int init_suite(void) { 7 | return iw_init(); 8 | } 9 | 10 | int clean_suite(void) { 11 | return 0; 12 | } 13 | 14 | static int icmp(const void *v1, const void *v2) { 15 | int i1, i2; 16 | memcpy(&i1, v1, sizeof(i1)); 17 | memcpy(&i2, v2, sizeof(i2)); 18 | return i1 < i2 ? -1 : i1 > i2 ? 1 : 0; 19 | } 20 | 21 | void test_iwarr1(void) { 22 | #define DSIZE 22 23 | int data[DSIZE + 1] = { 0 }; 24 | int nc = 0; 25 | off_t idx; 26 | for (int i = 0; nc < DSIZE / 2; i += 2, nc++) { 27 | idx = iwarr_sorted_insert(data, nc, sizeof(int), &i, icmp, false); 28 | } 29 | CU_ASSERT_EQUAL_FATAL(idx, 10); 30 | for (int i = 0, j = 0; i < idx; j += 2, ++i) { 31 | CU_ASSERT_EQUAL_FATAL(data[i], j); 32 | } 33 | for (int i = 1; nc < DSIZE; i += 2, nc++) { 34 | idx = iwarr_sorted_insert(data, nc, sizeof(int), &i, icmp, false); 35 | } 36 | for (int i = 0; i < nc; ++i) { 37 | CU_ASSERT_EQUAL_FATAL(data[i], i); 38 | } 39 | } 40 | 41 | int main(void) { 42 | CU_pSuite pSuite = NULL; 43 | 44 | /* Initialize the CUnit test registry */ 45 | if (CUE_SUCCESS != CU_initialize_registry()) { 46 | return CU_get_error(); 47 | } 48 | 49 | /* Add a suite to the registry */ 50 | pSuite = CU_add_suite("iwarr_test1", init_suite, clean_suite); 51 | 52 | if (NULL == pSuite) { 53 | CU_cleanup_registry(); 54 | return CU_get_error(); 55 | } 56 | 57 | /* Add the tests to the suite */ 58 | if ((NULL == CU_add_test(pSuite, "test_iwarr1", test_iwarr1))) { 59 | CU_cleanup_registry(); 60 | return CU_get_error(); 61 | } 62 | 63 | /* Run all tests using the CUnit Basic interface */ 64 | CU_basic_set_mode(CU_BRM_VERBOSE); 65 | CU_basic_run_tests(); 66 | int ret = CU_get_error() || CU_get_number_of_failures(); 67 | CU_cleanup_registry(); 68 | return ret; 69 | } 70 | -------------------------------------------------------------------------------- /src/iowow.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IOWOW_H 3 | #define IOWOW_H 4 | 5 | /************************************************************************************************** 6 | * IOWOW library 7 | * 8 | * MIT License 9 | * 10 | * Copyright (c) 2012-2024 Softmotions Ltd 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in all 20 | * copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | *************************************************************************************************/ 30 | 31 | #include "basedefs.h" 32 | IW_EXTERN_C_START; 33 | 34 | /** 35 | * @brief Init iowow subsystem. 36 | * @return `0` on success or error code. 37 | */ 38 | IW_EXPORT WUR iwrc iw_init(void); 39 | 40 | IW_EXPORT const char* iowow_version_full(void); 41 | 42 | IW_EXPORT unsigned int iowow_version_major(void); 43 | 44 | IW_EXPORT unsigned int iowow_version_minor(void); 45 | 46 | IW_EXPORT unsigned int iowow_version_patch(void); 47 | 48 | IW_EXTERN_C_END; 49 | #endif 50 | -------------------------------------------------------------------------------- /src/utils/iwuuid.c: -------------------------------------------------------------------------------- 1 | #include "iwuuid.h" 2 | #include "iwutils.h" 3 | #include 4 | 5 | union _uuid { 6 | uint8_t byte[16]; 7 | uint32_t rnd[4]; 8 | }; 9 | 10 | // [a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12} 11 | 12 | IW_INLINE bool _is_uuid_char(char ch) { 13 | return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'); 14 | } 15 | 16 | bool iwu_uuid_valid(const char *uuid) { 17 | if (!uuid || (strlen(uuid) != IW_UUID_STR_LEN)) { 18 | return false; 19 | } 20 | for (int i = 0; i < 8; ++i) { 21 | if (!_is_uuid_char(uuid[i])) { 22 | return false; 23 | } 24 | } 25 | if (uuid[8] != '-') { 26 | return false; 27 | } 28 | uuid += 9; 29 | for (int j = 0; j < 3; ++j) { 30 | for (int i = 0; i < 4; ++i) { 31 | if (!_is_uuid_char(uuid[i])) { 32 | return false; 33 | } 34 | } 35 | if (uuid[4] != '-') { 36 | return false; 37 | } 38 | uuid += 5; 39 | } 40 | for (int i = 0; i < 12; ++i) { 41 | if (!_is_uuid_char(uuid[i])) { 42 | return false; 43 | } 44 | } 45 | return true; 46 | } 47 | 48 | void iwu_uuid4_fill(char dest[static IW_UUID_STR_LEN]) { 49 | char buf[IW_UUID_STR_LEN + 1]; 50 | union _uuid uuid; 51 | for (size_t i = 0; i < 4; i++) { 52 | uuid.rnd[i] = iwu_rand_u32(); 53 | } 54 | uuid.byte[6] = (uuid.byte[6] & 0x0F) | 0x40; 55 | uuid.byte[8] = (uuid.byte[8] & 0x3F) | 0x80; 56 | snprintf(buf, IW_UUID_STR_LEN + 1, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", 57 | uuid.byte[0], 58 | uuid.byte[1], 59 | uuid.byte[2], 60 | uuid.byte[3], 61 | uuid.byte[4], 62 | uuid.byte[5], 63 | uuid.byte[6], 64 | uuid.byte[7], 65 | uuid.byte[8], 66 | uuid.byte[9], 67 | uuid.byte[10], 68 | uuid.byte[11], 69 | uuid.byte[12], 70 | uuid.byte[13], 71 | uuid.byte[14], 72 | uuid.byte[15]); 73 | memcpy(dest, buf, IW_UUID_STR_LEN); 74 | } 75 | -------------------------------------------------------------------------------- /src/json/tests/jbl_test2.c: -------------------------------------------------------------------------------- 1 | #include "iwjson.h" 2 | #include "iwjson_internal.h" 3 | #include "iwutils.h" 4 | 5 | #include 6 | #include 7 | 8 | int init_suite(void) { 9 | int rc = iw_init(); 10 | return rc; 11 | } 12 | 13 | int clean_suite(void) { 14 | return 0; 15 | } 16 | 17 | static void test_jbn_xml(void) { 18 | IWXSTR *xstr = iwxstr_create_empty(); 19 | struct iwpool *pool = iwpool_create_empty(); 20 | CU_ASSERT_PTR_NOT_NULL_FATAL(pool); 21 | 22 | JBL_NODE n; 23 | const char *val = "{" 24 | "\">attr\":\"attrvalue\"" 25 | "\">attr2\":\"ss < ' xx\"" 26 | ",\"foo\":\"bar\"" 27 | ",\"\":\"bo\\ndy 1 < 2 \"" 28 | ",\"arr\":[1,2,3,{\"obj\":\"k&k\"}]" 29 | ",\"nested\":{\"baz\":{\"zaz\":true,\"arr\":[1,3.36,true,\"a\"]}}" 30 | "}"; 31 | iwrc rc = jbn_from_json(val, &n, pool); 32 | CU_ASSERT_EQUAL_FATAL(rc, 0); 33 | 34 | rc = jbn_as_xml(n, &(struct jbn_as_xml_spec) { 35 | .flags = JBL_PRINT_PRETTY, 36 | .print_xml_header = true, 37 | .printer_fn = jbl_xstr_json_printer, 38 | .printer_fn_data = xstr, 39 | }); 40 | CU_ASSERT_EQUAL_FATAL(rc, 0); 41 | 42 | char *buf = iwu_file_read_as_buf("data/jbl_test2.001.expected.xml"); 43 | CU_ASSERT_PTR_NOT_NULL_FATAL(buf); 44 | CU_ASSERT_EQUAL(strcmp(buf, iwxstr_ptr(xstr)), 0); 45 | 46 | iwpool_destroy(pool); 47 | iwxstr_destroy(xstr); 48 | free(buf); 49 | } 50 | 51 | int main(void) { 52 | CU_pSuite pSuite = NULL; 53 | if (CUE_SUCCESS != CU_initialize_registry()) { 54 | return CU_get_error(); 55 | } 56 | pSuite = CU_add_suite("jbl_test1", init_suite, clean_suite); 57 | if (NULL == pSuite) { 58 | CU_cleanup_registry(); 59 | return CU_get_error(); 60 | } 61 | 62 | if (NULL == CU_add_test(pSuite, "test_jbn_xml", test_jbn_xml)) { 63 | CU_cleanup_registry(); 64 | return CU_get_error(); 65 | } 66 | 67 | CU_basic_set_mode(CU_BRM_VERBOSE); 68 | CU_basic_run_tests(); 69 | int ret = CU_get_error() || CU_get_number_of_failures(); 70 | CU_cleanup_registry(); 71 | return ret; 72 | } 73 | -------------------------------------------------------------------------------- /src/kv/tests/iwkv_test9.c: -------------------------------------------------------------------------------- 1 | #include "iwkv.h" 2 | #include "iwlog.h" 3 | #include "iwutils.h" 4 | #include "iwcfg.h" 5 | #include "iwkv_tests.h" 6 | 7 | int init_suite(void) { 8 | iwrc rc = iwkv_init(); 9 | return rc; 10 | } 11 | 12 | int clean_suite(void) { 13 | return 0; 14 | } 15 | 16 | static void iwkv_test9_1(void) { 17 | IWKV_OPTS opts = { 18 | .path = "iwkv_test9_1.db", 19 | .oflags = IWKV_TRUNC 20 | }; 21 | IWKV kv = NULL; 22 | iwrc rc = iwkv_open(&opts, &kv); 23 | assert(rc == 0); 24 | IWDB db = NULL; 25 | rc = iwkv_db(kv, 1, 0, &db); 26 | assert(rc == 0); 27 | 28 | { 29 | unsigned char ip1[4] = { 1, 0, 142, 235 }; 30 | IWKV_val ikey, ival; 31 | ikey.data = ip1; 32 | ikey.size = 4; 33 | ival.data = (void*) ""; 34 | ival.size = 0; 35 | iwkv_opflags opflags = IWKV_NO_OVERWRITE; 36 | iwrc rc = iwkv_put(db, &ikey, &ival, opflags); 37 | CU_ASSERT_EQUAL(rc, 0); 38 | } 39 | 40 | { 41 | unsigned char ip2[4] = { 1, 0, 145, 2 }; 42 | IWKV_val ikey, ival; 43 | ikey.data = ip2; 44 | ikey.size = 4; 45 | ival.data = (void*) ""; 46 | ival.size = 0; 47 | iwkv_opflags opflags = IWKV_NO_OVERWRITE; 48 | iwrc rc = iwkv_put(db, &ikey, &ival, opflags); 49 | CU_ASSERT_EQUAL(rc, 0); 50 | } 51 | iwkv_close(&kv); 52 | } 53 | 54 | int main(void) { 55 | CU_pSuite pSuite = NULL; 56 | 57 | /* Initialize the CUnit test registry */ 58 | if (CUE_SUCCESS != CU_initialize_registry()) { 59 | return CU_get_error(); 60 | } 61 | 62 | /* Add a suite to the registry */ 63 | pSuite = CU_add_suite("iwkv_test9", init_suite, clean_suite); 64 | 65 | if (NULL == pSuite) { 66 | CU_cleanup_registry(); 67 | return CU_get_error(); 68 | } 69 | 70 | /* Add the tests to the suite */ 71 | if ( 72 | (NULL == CU_add_test(pSuite, "iwkv_test9_1", iwkv_test9_1))) { 73 | CU_cleanup_registry(); 74 | return CU_get_error(); 75 | } 76 | 77 | /* Run all tests using the CUnit Basic interface */ 78 | CU_basic_set_mode(CU_BRM_VERBOSE); 79 | CU_basic_run_tests(); 80 | int ret = CU_get_error() || CU_get_number_of_failures(); 81 | CU_cleanup_registry(); 82 | return ret; 83 | } 84 | -------------------------------------------------------------------------------- /src/kv/tests/iwkv_test2.c: -------------------------------------------------------------------------------- 1 | #include "iwkv.h" 2 | #include "iwlog.h" 3 | #include "iwutils.h" 4 | #include "iwcfg.h" 5 | #include 6 | 7 | #define RND_DATA_SZ (10 * 1048576) 8 | char RND_DATA[RND_DATA_SZ]; 9 | 10 | int init_suite(void) { 11 | iwrc rc = iwkv_init(); 12 | return rc; 13 | } 14 | 15 | int clean_suite(void) { 16 | return 0; 17 | } 18 | 19 | static void iwkv_test2_1(void) { 20 | IWKV_OPTS opts = { 21 | .path = "iwkv_test2_1.db", 22 | .oflags = IWKV_TRUNC 23 | }; 24 | const uint64_t numrec = 1000000; // 1M 25 | // Test open/close 26 | IWKV iwkv; 27 | IWDB db1; 28 | IWKV_val key, val; 29 | iwrc rc = iwkv_open(&opts, &iwkv); 30 | CU_ASSERT_EQUAL_FATAL(rc, 0); 31 | 32 | rc = iwkv_db(iwkv, 1, IWDB_VNUM64_KEYS, &db1); 33 | CU_ASSERT_EQUAL_FATAL(rc, 0); 34 | for (uint64_t i = 0; i < numrec; ++i) { 35 | key.size = sizeof(uint64_t); 36 | key.data = &i; 37 | val.size = sizeof(uint64_t); 38 | val.data = &i; 39 | rc = iwkv_put(db1, &key, &val, 0); 40 | CU_ASSERT_EQUAL_FATAL(rc, 0); 41 | } 42 | 43 | for (uint64_t v = 0; v < numrec; ++v) { 44 | uint64_t llv; 45 | key.data = &v; 46 | key.size = sizeof(uint64_t); 47 | rc = iwkv_get(db1, &key, &val); 48 | CU_ASSERT_EQUAL_FATAL(rc, 0); 49 | memcpy(&llv, val.data, sizeof(llv)); 50 | CU_ASSERT_EQUAL_FATAL(llv, v); 51 | iwkv_val_dispose(&val); 52 | } 53 | 54 | rc = iwkv_close(&iwkv); 55 | CU_ASSERT_EQUAL_FATAL(rc, 0); 56 | } 57 | 58 | int main(void) { 59 | CU_pSuite pSuite = NULL; 60 | 61 | /* Initialize the CUnit test registry */ 62 | if (CUE_SUCCESS != CU_initialize_registry()) { 63 | return CU_get_error(); 64 | } 65 | 66 | /* Add a suite to the registry */ 67 | pSuite = CU_add_suite("iwkv_test2", init_suite, clean_suite); 68 | 69 | if (NULL == pSuite) { 70 | CU_cleanup_registry(); 71 | return CU_get_error(); 72 | } 73 | 74 | /* Add the tests to the suite */ 75 | if ((NULL == CU_add_test(pSuite, "iwkv_test2_1", iwkv_test2_1))) { 76 | CU_cleanup_registry(); 77 | return CU_get_error(); 78 | } 79 | 80 | /* Run all tests using the CUnit Basic interface */ 81 | CU_basic_set_mode(CU_BRM_VERBOSE); 82 | CU_basic_run_tests(); 83 | int ret = CU_get_error() || CU_get_number_of_failures(); 84 | CU_cleanup_registry(); 85 | return ret; 86 | } 87 | -------------------------------------------------------------------------------- /src/iwcfg.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IW_CFG_H 3 | #define IW_CFG_H 4 | 5 | // 6 | /************************************************************************************************** 7 | * IOWOW library 8 | * 9 | * MIT License 10 | * 11 | * Copyright (c) 2012-2024 Softmotions Ltd 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining a copy 14 | * of this software and associated documentation files (the "Software"), to deal 15 | * in the Software without restriction, including without limitation the rights 16 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 | * copies of the Software, and to permit persons to whom the Software is 18 | * furnished to do so, subject to the following conditions: 19 | * 20 | * The above copyright notice and this permission notice shall be included in all 21 | * copies or substantial portions of the Software. 22 | * 23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 29 | * SOFTWARE. 30 | *************************************************************************************************/ 31 | 32 | 33 | #include "basedefs.h" 34 | 35 | #define STR_HELPER(x) #x 36 | #define STR(x) STR_HELPER(x) 37 | 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | 47 | #ifndef MAXPATHLEN 48 | #ifdef PATH_MAX 49 | #define MAXPATHLEN PATH_MAX 50 | #else 51 | #define MAXPATHLEN 4096 52 | #endif 53 | #endif 54 | 55 | #if !defined(IW_32) && !defined(IW_64) 56 | #error Unknown CPU bits 57 | #endif 58 | 59 | #define IOWOW_VERSION "@META_VERSION@" 60 | #define IOWOW_VERSION_MAJOR @META_VERSION_MAJOR@ 61 | #define IOWOW_VERSION_MINOR @META_VERSION_MINOR@ 62 | #define IOWOW_VERSION_PATCH @META_VERSION_PATCH@ 63 | 64 | //autarkdef IW_HAVE_CLOCK_MONOTONIC 1 65 | 66 | #ifndef static_assert 67 | #define static_assert _Static_assert 68 | #endif 69 | 70 | #endif 71 | 72 | -------------------------------------------------------------------------------- /src/utils/iwrefs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /************************************************************************************************** 4 | * Ref counting disposable containers. 5 | * 6 | * IOWOW library 7 | * 8 | * MIT License 9 | * 10 | * Copyright (c) 2012-2024 Softmotions Ltd 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in all 20 | * copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | *************************************************************************************************/ 30 | 31 | #include "basedefs.h" 32 | #include 33 | #include 34 | 35 | IW_EXTERN_C_START; 36 | 37 | struct iwref_holder { 38 | void *data; 39 | atomic_long refs; 40 | void (*freefn)(void*); 41 | }; 42 | 43 | static void iwref_init(struct iwref_holder *h, void *data, void (*freefn)(void*)) { 44 | h->data = data; 45 | h->refs = 1; 46 | h->freefn = freefn; 47 | } 48 | 49 | static void iwref_reset(struct iwref_holder *h, void *data, void (*freefn)(void*)) { 50 | h->refs = 1; 51 | if (freefn) { 52 | h->data = data; 53 | h->freefn = freefn; 54 | } 55 | } 56 | 57 | static void iwref_ref(struct iwref_holder *h) { 58 | ++h->refs; 59 | } 60 | 61 | static bool iwref_unref(struct iwref_holder *h) { 62 | if (h) { 63 | if (--h->refs == 0) { 64 | if (h->freefn) { 65 | h->freefn(h->data); 66 | } 67 | return true; 68 | } 69 | } 70 | return false; 71 | } 72 | 73 | IW_EXTERN_C_END; 74 | -------------------------------------------------------------------------------- /src/utils/tests/iwrb_test1.c: -------------------------------------------------------------------------------- 1 | #include "iowow.h" 2 | #include "iwrb.h" 3 | #include 4 | 5 | int init_suite(void) { 6 | return iw_init(); 7 | } 8 | 9 | int clean_suite(void) { 10 | return 0; 11 | } 12 | 13 | static void test_iwrb1(void) { 14 | struct iwrb *rb = iwrb_create(1, 6); 15 | iwrb_put(rb, "A"); 16 | iwrb_put(rb, "B"); 17 | iwrb_put(rb, "C"); 18 | CU_ASSERT_EQUAL(iwrb_num_cached(rb), 3); 19 | 20 | char ch = *(char*) iwrb_peek(rb); 21 | CU_ASSERT_EQUAL(ch, 'C'); 22 | 23 | ch = *(char*) iwrb_begin(rb); 24 | CU_ASSERT_EQUAL(ch, 'A'); 25 | 26 | CU_ASSERT_EQUAL(iwrb_num_cached(rb), 3); 27 | 28 | iwrb_put(rb, "D"); 29 | iwrb_put(rb, "E"); 30 | iwrb_put(rb, "F"); 31 | 32 | ch = *(char*) iwrb_peek(rb); 33 | CU_ASSERT_EQUAL(ch, 'F'); 34 | 35 | ch = *(char*) iwrb_begin(rb); 36 | CU_ASSERT_EQUAL(ch, 'A'); 37 | 38 | iwrb_put(rb, "G"); 39 | 40 | ch = *(char*) iwrb_peek(rb); 41 | CU_ASSERT_EQUAL(ch, 'G'); 42 | 43 | ch = *(char*) iwrb_begin(rb); 44 | CU_ASSERT_EQUAL(ch, 'B'); 45 | 46 | iwrb_put(rb, "H"); 47 | ch = *(char*) iwrb_begin(rb); 48 | CU_ASSERT_EQUAL(ch, 'C'); 49 | 50 | struct iwrb_iter iter; 51 | iwrb_iter_init(rb, &iter); 52 | 53 | int i = 0; 54 | char *p = 0; 55 | for ( ; (p = iwrb_iter_prev(&iter)); ++i) { 56 | ch = *p; 57 | switch (i) { 58 | case 0: 59 | CU_ASSERT_EQUAL(ch, 'H'); 60 | break; 61 | case 1: 62 | CU_ASSERT_EQUAL(ch, 'G'); 63 | break; 64 | case 2: 65 | CU_ASSERT_EQUAL(ch, 'F'); 66 | break; 67 | case 5: 68 | CU_ASSERT_EQUAL(ch, 'C'); 69 | break; 70 | } 71 | } 72 | CU_ASSERT_EQUAL(iwrb_num_cached(rb), 6); 73 | iwrb_destroy(&rb); 74 | } 75 | 76 | int main(void) { 77 | CU_pSuite pSuite = NULL; 78 | 79 | /* Initialize the CUnit test registry */ 80 | if (CUE_SUCCESS != CU_initialize_registry()) { 81 | return CU_get_error(); 82 | } 83 | 84 | /* Add a suite to the registry */ 85 | pSuite = CU_add_suite("iwrb_test1", init_suite, clean_suite); 86 | 87 | if (NULL == pSuite) { 88 | CU_cleanup_registry(); 89 | return CU_get_error(); 90 | } 91 | 92 | /* Add the tests to the suite */ 93 | if ((NULL == CU_add_test(pSuite, "test_iwrb1", test_iwrb1))) { 94 | CU_cleanup_registry(); 95 | return CU_get_error(); 96 | } 97 | 98 | /* Run all tests using the CUnit Basic interface */ 99 | CU_basic_set_mode(CU_BRM_VERBOSE); 100 | CU_basic_run_tests(); 101 | int ret = CU_get_error() || CU_get_number_of_failures(); 102 | CU_cleanup_registry(); 103 | return ret; 104 | } 105 | -------------------------------------------------------------------------------- /src/utils/wyhash32.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Author: Wang Yi 4 | #include 5 | #include 6 | #ifndef IW_BIGENDIAN 7 | 8 | static inline unsigned _wyr32(const uint8_t *p) { 9 | unsigned v; 10 | memcpy(&v, p, 4); 11 | return v; 12 | } 13 | 14 | #elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__) 15 | 16 | static inline unsigned _wyr32(const uint8_t *p) { 17 | unsigned v; 18 | memcpy(&v, p, 4); 19 | return __builtin_bswap32(v); 20 | } 21 | 22 | #elif defined(_MSC_VER) 23 | 24 | static inline unsigned _wyr32(const uint8_t *p) { 25 | unsigned v; 26 | memcpy(&v, p, 4); 27 | return _byteswap_ulong(v); 28 | } 29 | 30 | #endif 31 | 32 | static inline unsigned _wyr24(const uint8_t *p, unsigned k) { 33 | return (((unsigned) p[0]) << 16) | (((unsigned) p[k >> 1]) << 8) | p[k - 1]; 34 | } 35 | 36 | static inline void _wymix32(unsigned *A, unsigned *B) { 37 | uint64_t c = *A ^ 0x53c5ca59u; 38 | c *= *B ^ 0x74743c1bu; 39 | *A = (unsigned) c; 40 | *B = (unsigned) (c >> 32); 41 | } 42 | 43 | // This version is vulnerable when used with a few bad seeds, which should be skipped beforehand: 44 | // 0x429dacdd, 0xd637dbf3 45 | static inline unsigned wyhash32(const void *key, uint64_t len, unsigned seed) { 46 | const uint8_t *p = (const uint8_t*) key; 47 | uint64_t i = len; 48 | unsigned see1 = (unsigned) len; 49 | seed ^= (unsigned) (len >> 32); 50 | _wymix32(&seed, &see1); 51 | for ( ; i > 8; i -= 8, p += 8) { 52 | seed ^= _wyr32(p); 53 | see1 ^= _wyr32(p + 4); 54 | _wymix32(&seed, &see1); 55 | } 56 | if (i >= 4) { 57 | seed ^= _wyr32(p); 58 | see1 ^= _wyr32(p + i - 4); 59 | } else if (i) { 60 | seed ^= _wyr24(p, (unsigned) i); 61 | } 62 | _wymix32(&seed, &see1); 63 | _wymix32(&seed, &see1); 64 | return seed ^ see1; 65 | } 66 | 67 | // duplicate definition in wyhash.h also 68 | #ifndef wyhash_final_version_3 69 | 70 | static inline uint64_t wyrand(uint64_t *seed) { 71 | *seed += 0xa0761d6478bd642full; 72 | uint64_t see1 = *seed ^ 0xe7037ed1a0b428dbull; 73 | see1 *= (see1 >> 32) | (see1 << 32); 74 | return (*seed * ((*seed >> 32) | (*seed << 32))) ^ ((see1 >> 32) | (see1 << 32)); 75 | } 76 | 77 | #endif 78 | 79 | static inline unsigned wy32x32(unsigned a, unsigned b) { 80 | _wymix32(&a, &b); 81 | _wymix32(&a, &b); 82 | return a ^ b; 83 | } 84 | 85 | static inline float wy2u01(unsigned r) { 86 | const float _wynorm = 1.0f / (1ull << 23); 87 | return (r >> 9) * _wynorm; 88 | } 89 | 90 | static inline float wy2gau(unsigned r) { 91 | const float _wynorm = 1.0f / (1ull << 9); 92 | return ((r & 0x3ff) + ((r >> 10) & 0x3ff) + ((r >> 20) & 0x3ff)) * _wynorm - 3.0f; 93 | } 94 | -------------------------------------------------------------------------------- /src/utils/iwrb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWRB_H 3 | #define IWRB_H 4 | 5 | /************************************************************************************************** 6 | * Ring buffer. 7 | * 8 | * IOWOW library 9 | * 10 | * MIT License 11 | * 12 | * Copyright (c) 2012-2024 Softmotions Ltd 13 | * 14 | * Permission is hereby granted, free of charge, to any person obtaining a copy 15 | * of this software and associated documentation files (the "Software"), to deal 16 | * in the Software without restriction, including without limitation the rights 17 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | * copies of the Software, and to permit persons to whom the Software is 19 | * furnished to do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be included in all 22 | * copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | * SOFTWARE. 31 | *************************************************************************************************/ 32 | 33 | #include "basedefs.h" 34 | 35 | IW_EXTERN_C_START; 36 | 37 | struct iwrb; 38 | struct iwrb_iter; 39 | typedef struct iwrb IWRB; 40 | typedef struct iwrb_iter IWRB_ITER; 41 | 42 | struct iwrb { 43 | ssize_t pos; 44 | size_t len; 45 | size_t usize; 46 | char *buf; 47 | }; 48 | 49 | struct iwrb_iter { 50 | const struct iwrb *rb; 51 | size_t pos; 52 | ssize_t ipos; 53 | }; 54 | 55 | IW_EXPORT IW_ALLOC struct iwrb* iwrb_create(size_t usize, size_t len); 56 | 57 | IW_EXPORT void iwrb_clear(struct iwrb *rb); 58 | 59 | IW_EXPORT void iwrb_destroy(struct iwrb **rbp); 60 | 61 | IW_EXPORT struct iwrb* iwrb_wrap(void *buf, size_t len, size_t usize); 62 | 63 | IW_EXPORT void iwrb_put(struct iwrb *rb, const void *buf); 64 | 65 | IW_EXPORT void iwrb_back(struct iwrb *rb); 66 | 67 | IW_EXPORT void* iwrb_peek(const struct iwrb *rb); 68 | 69 | IW_EXPORT void* iwrb_begin(const struct iwrb *rb); 70 | 71 | IW_EXPORT size_t iwrb_num_cached(const struct iwrb *rb); 72 | 73 | IW_EXPORT void iwrb_iter_init(const struct iwrb *rb, struct iwrb_iter *iter); 74 | 75 | IW_EXPORT void* iwrb_iter_prev(IWRB_ITER *iter); 76 | 77 | IW_EXTERN_C_END; 78 | #endif 79 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /autark-cache* 2 | /tmp 3 | .cache 4 | hints.txt 5 | tmp/ 6 | git.diff 7 | .vscode/.ropeproject 8 | .vscode/ipch 9 | .codelite/ 10 | *.tags 11 | *.dll 12 | #iowow.project 13 | #iowow.workspace 14 | build/ 15 | Testing/ 16 | compile_commands.json 17 | iowow.mk 18 | .tern* 19 | *.iml 20 | *.dat 21 | *.fsm 22 | *.db 23 | 24 | # Created by https://www.gitignore.io/api/jetbrains 25 | # Edit at https://www.gitignore.io/?templates=jetbrains 26 | 27 | ### JetBrains ### 28 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 29 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 30 | 31 | # User-specific stuff 32 | .idea/**/workspace.xml 33 | .idea/**/tasks.xml 34 | .idea/**/usage.statistics.xml 35 | .idea/**/dictionaries 36 | .idea/**/shelf 37 | 38 | # Generated files 39 | .idea/**/contentModel.xml 40 | 41 | # Sensitive or high-churn files 42 | .idea/**/dataSources/ 43 | .idea/**/dataSources.ids 44 | .idea/**/dataSources.local.xml 45 | .idea/**/sqlDataSources.xml 46 | .idea/**/dynamic.xml 47 | .idea/**/uiDesigner.xml 48 | .idea/**/dbnavigator.xml 49 | 50 | # Gradle 51 | .idea/**/gradle.xml 52 | .idea/**/libraries 53 | 54 | # Gradle and Maven with auto-import 55 | # When using Gradle or Maven with auto-import, you should exclude module files, 56 | # since they will be recreated, and may cause churn. Uncomment if using 57 | # auto-import. 58 | # .idea/modules.xml 59 | # .idea/*.iml 60 | # .idea/modules 61 | # *.iml 62 | # *.ipr 63 | 64 | # Mongo Explorer plugin 65 | .idea/**/mongoSettings.xml 66 | 67 | # File-based project format 68 | *.iws 69 | 70 | # IntelliJ 71 | out/ 72 | 73 | # mpeltonen/sbt-idea plugin 74 | .idea_modules/ 75 | 76 | # JIRA plugin 77 | atlassian-ide-plugin.xml 78 | 79 | # Cursive Clojure plugin 80 | .idea/replstate.xml 81 | 82 | # Crashlytics plugin (for Android Studio and IntelliJ) 83 | com_crashlytics_export_strings.xml 84 | crashlytics.properties 85 | crashlytics-build.properties 86 | fabric.properties 87 | 88 | # Editor-based Rest Client 89 | .idea/httpRequests 90 | 91 | # Android studio 3.1+ serialized cache file 92 | .idea/caches/build_file_checksums.ser 93 | 94 | ### JetBrains Patch ### 95 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 96 | 97 | # *.iml 98 | # modules.xml 99 | # .idea/misc.xml 100 | # *.ipr 101 | 102 | # Sonarlint plugin 103 | .idea/**/sonarlint/ 104 | 105 | # SonarQube Plugin 106 | .idea/**/sonarIssues.xml 107 | 108 | # Markdown Navigator plugin 109 | .idea/**/markdown-navigator.xml 110 | .idea/**/markdown-navigator/ 111 | 112 | # End of https://www.gitignore.io/api/jetbrains -------------------------------------------------------------------------------- /src/iowow.c: -------------------------------------------------------------------------------- 1 | // 2 | /************************************************************************************************** 3 | * IOWOW library 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2012-2024 Softmotions Ltd 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | *************************************************************************************************/ 27 | 28 | 29 | #include "iwcfg.h" 30 | #include "log/iwlog.h" 31 | #include "iwutils.h" 32 | #include "iwp.h" 33 | 34 | // Hardcoded requirements (fixme) 35 | static_assert(sizeof(off_t) == 8, "sizeof(off_t) == 8 bytes"); 36 | 37 | iwrc iwfs_init(void); 38 | iwrc iwkv_init(void); 39 | iwrc jbl_init(void); 40 | 41 | iwrc iw_init(void) { 42 | iwrc rc; 43 | static int _iw_initialized = 0; 44 | if (!__sync_bool_compare_and_swap(&_iw_initialized, 0, 1)) { 45 | return 0; // initialized already 46 | } 47 | RCC(rc, finish, iwlog_init()); 48 | RCC(rc, finish, iwu_init()); 49 | RCC(rc, finish, iwp_init()); 50 | RCC(rc, finish, jbl_init()); 51 | 52 | uint64_t ts; 53 | RCC(rc, finish, iwp_current_time_ms(&ts, false)); 54 | ts = IW_SWAB64(ts); 55 | ts >>= 32; 56 | iwu_rand_seed(ts); 57 | 58 | RCC(rc, finish, iwfs_init()); 59 | RCC(rc, finish, iwkv_init()); 60 | 61 | finish: 62 | return rc; 63 | } 64 | 65 | const char* iowow_version_full(void) { 66 | return IOWOW_VERSION; 67 | } 68 | 69 | unsigned int iowow_version_major(void) { 70 | return IOWOW_VERSION_MAJOR; 71 | } 72 | 73 | unsigned int iowow_version_minor(void) { 74 | return IOWOW_VERSION_MINOR; 75 | } 76 | 77 | unsigned int iowow_version_patch(void) { 78 | return IOWOW_VERSION_PATCH; 79 | } 80 | -------------------------------------------------------------------------------- /src/utils/iwcsv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWCSV_H 3 | #define IWCSV_H 4 | 5 | #include "iwlog.h" 6 | #include "iwconv.h" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | struct iwcsv { 14 | char *buf; 15 | char *wp; 16 | char *ep; 17 | int ncol; // Column number 18 | }; 19 | 20 | /// Wraps a given `linebuf` in order to store assembled CSV line. 21 | /// Recommended linebuf size: 4096. 22 | /// 23 | /// Returns 24 | /// - IW_ERROR_INVALID_ARGS if len of given line buf is not enouth to store a single character exclusing `\0` 25 | /// - `len` must be aligned by 8 otherwise IW_ERROR_INVALID_ARGS 26 | /// 27 | static iwrc iwcsv_wrap_line_buffer(char *linebuf, size_t len, struct iwcsv **out) { 28 | if (!out || !linebuf || len < sizeof(struct iwcsv) + 2 /* one data byte + '\0' */) { 29 | return IW_ERROR_INVALID_ARGS; 30 | } 31 | if (len & 7) { 32 | return IW_ERROR_INVALID_ARGS; 33 | } 34 | *out = 0; 35 | struct iwcsv *w = (void*) (linebuf + len - sizeof(*w)); 36 | w->wp = w->buf = linebuf; 37 | w->ep = (char*) w - 1; 38 | w->ncol = 0; 39 | *w->ep = '\0'; 40 | *out = w; 41 | return 0; 42 | } 43 | 44 | #define WW(c_) \ 45 | if (w->wp == w->ep) return 0; \ 46 | *w->wp = (c_); \ 47 | ++w->wp 48 | 49 | /// Returns non zero if CSV line built successfully 50 | static const char* iwcsv_line_flush(struct iwcsv *w, int *out_len) { 51 | WW('\r'); 52 | WW('\n'); 53 | if (out_len) { 54 | *out_len = w->wp - w->buf; 55 | } 56 | *w->wp = '\0'; 57 | w->wp = w->buf; 58 | w->ncol = 0; 59 | return w->buf; 60 | } 61 | 62 | static bool iwcsv_column_add(struct iwcsv *w, const char *s, int slen) { 63 | if (!s) { 64 | s = ""; 65 | slen = 0; 66 | } else if (slen < 0) { 67 | slen = strlen(s); 68 | } 69 | bool q = false; 70 | for (int i = 0; !q && i < slen; ++i) { 71 | switch (s[i]) { 72 | case ' ': 73 | case ',': 74 | case '\t': 75 | case '\n': 76 | case '\r': 77 | q = true; 78 | break; 79 | } 80 | } 81 | if (w->ncol++) { 82 | WW(','); 83 | } 84 | if (q) { 85 | WW('"'); 86 | } 87 | for (const char *ep = s + slen; s < ep; ++s) { 88 | if (*s == '"') { 89 | WW('"'); 90 | } 91 | WW(*s); 92 | } 93 | if (q) { 94 | WW('"'); 95 | } 96 | return true; 97 | } 98 | 99 | static bool iwcsv_column_add_i64(struct iwcsv *w, int64_t n) { 100 | char buf[IWNUMBUF_SIZE]; 101 | iwitoa(n, buf, sizeof(buf)); 102 | return iwcsv_column_add(w, buf, -1); 103 | } 104 | 105 | static bool iwcsv_column_add_f64(struct iwcsv *w, double n) { 106 | char buf[IWNUMBUF_SIZE]; 107 | iwftoa(n, buf); 108 | return iwcsv_column_add(w, buf, -1); 109 | } 110 | 111 | #undef WW 112 | #endif 113 | -------------------------------------------------------------------------------- /src/kv/data-format.txt: -------------------------------------------------------------------------------- 1 | IWKV 2 | 3 | block size: u128 4 | max key+value size: 268435455 (~255Mb) 5 | max data file size: 512G 6 | 7 | SBLK - Skip list node with pointers to next nodes and pointer to KVBLK (key/value pairs block). 8 | SBLK has fixed size (256 bytes). SBLK file position (block adress) within a file is 9 | fixed and cannot be changed. 10 | 11 | V1 SBLK layout: 12 | 13 | [u1:flags,lvl:u1,lkl:u1,pnum:u1,p0:u4,kblk:u4,pi:u1[32],n:u4[24],lk:u116]:u256 14 | 15 | V2 SBLK layout: 16 | 17 | [flags:u1,lvl:u1,lkl:u1,pnum:u1,p0:u4,kblk:u4,pi:u1[32],n:u4[24],bpos:u1,lk:u115]:u256 18 | \ 19 | KVBLK 20 | 21 | flags - Persistent block flags (1 byte) 22 | lvl - Skiplist level of this block (1 byte) 23 | lkl - Length of the lower key in this block (1 byte) 24 | pnum - Number of active kv indexes in `SBLK::pi` 25 | p0 - Address of previous sblk block at zero level 26 | kblk - Block number of associated KVBLK. (4 bytes) 27 | pi[32] - Array of key/value pair indexes in KVBLK block. 28 | Indexes are sorted by keys. (32 bytes) 29 | n[24] - Pointers to next SBLK blocks in skiplist (96 bytes) 30 | bpos - Position of SBLK in a page block starting with 1 (zero means SBLK deleted) 31 | lk - Buffer for the lowest key among all key/value pairs stored in KVBLK 32 | 33 | 34 | KVBLK - Data block stored a set of key/value pairs associated with SBLK 35 | 36 | [szpow:u1,idxsz:u2,KVI[32] ___free space___ [[KV],...]] 37 | 38 | szpow - KVBLK length as power of 2 39 | idxsz - Length of KVI array in bytes 40 | KVI[32] - [ps:vn, pl:vn] 41 | ps: key/value pair block offset on i-th place variable length encoded number. 42 | This offset is relative to end of KVBLK block 43 | pl: key/value pair block length on i-th place variable length encoded number 44 | 45 | KV - [klen:vn,key,value] 46 | Key/value pair 47 | klen: Key length as variable length encoded number 48 | key: Key data buffer 49 | value: Value data buffer 50 | 51 | DB header block: 52 | 53 | [magic:u4,dbflg:u1,dbid:u4,next_db_blk:u4,p0:u4,n[24]:u4,c[24]:u4,meta_blk:u4,meta_len:u4]:217 54 | 55 | magic - DB magic number 0x69776462 56 | dbflg - Database flags 57 | next_db_blk - Next database meta block number or zero 58 | dbid - Database ID 59 | p0 - Last database block 60 | n24 - Skiplist next pointers to `SBLK` 61 | c24 - SBLK count per level, 62 | /* since file format v1 */ 63 | meta_blk - Database metadata block number 64 | meta_blkn - Database metadata block count 65 | 66 | HEADER: 67 | 68 | [magic:u4,u8:fistdb_addr] 69 | 70 | magic - File magic number 0x69776b76 71 | fistdb_addr - Address of the first db in the DB chain 72 | 73 | ------------------------------------------------------------ 74 | 75 | WAL 76 | 77 | 1. Extra rdb methods 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /Autark: -------------------------------------------------------------------------------- 1 | meta { 2 | name { iowow } 3 | 4 | version_major { 1 } 5 | version_minor { 4 } 6 | version_patch { 20 } 7 | 8 | description { The skiplist based persistent key/value storage engine. } 9 | website { https://github.com/Softmotions/iowow } 10 | vendor { Softmotions (https://softmotions.com) } 11 | maintainer { Anton Adamansky } 12 | license { MIT } 13 | } 14 | 15 | option { IOWOW_BUILD_SHARED_LIBS Build shared library } 16 | option { IOWOW_BUILD_TESTS Build test cases } 17 | option { IOWOW_RUN_TESTS Build and run test cases } 18 | option { ENABLE_ASAN Turn on address sanitizer } 19 | option { ENABLE_UBSAN Turn on UB sanitizer } 20 | option { ENABLE_DEBINFO Generate debuginfo even in release mode } 21 | 22 | set { 23 | META_VERSION 24 | ^{ ${META_VERSION_MAJOR} . ${META_VERSION_MINOR} . ${META_VERSION_PATCH} } 25 | } 26 | 27 | set { 28 | META_REVISION 29 | @{ git rev-parse --short HEAD } 30 | } 31 | 32 | if { ${IOWOW_RUN_TESTS} 33 | set { 34 | IOWOW_BUILD_TESTS 1 35 | } 36 | } 37 | 38 | if { eq { ${BUILD_TYPE} Release } 39 | set { 40 | STRIP_CMD strip --strip-debug 41 | } 42 | } 43 | 44 | check { 45 | system.sh 46 | } 47 | 48 | set { 49 | CFLAGS 50 | -std=gnu11 51 | -pedantic 52 | -fsigned-char 53 | -Wall 54 | -Wextra 55 | -Wfatal-errors 56 | -Wno-implicit-fallthrough 57 | -Wno-missing-braces 58 | -Wno-missing-field-initializers 59 | -Wno-sign-compare 60 | -Wno-unknown-pragmas 61 | -Wno-unused-function 62 | -Wno-unused-parameter 63 | -fPIC 64 | if { ${ENABLE_ASAN} 65 | -fsanitize=address 66 | -fno-omit-frame-pointer 67 | } else { 68 | if { ${ENABLE_UBSAN} 69 | -fsanitize=undefined 70 | -fno-omit-frame-pointer 71 | } 72 | } 73 | if { prefix { ${BUILD_TYPE} Debug } 74 | -O0 75 | -ggdb 76 | -fno-omit-frame-pointer 77 | -Werror 78 | -Wno-unused-variable 79 | -DDEBUG -D_DEBUG -UNDEBUG 80 | } else { 81 | if { ${ENABLE_DEBINFO} 82 | -O2 83 | -ggdb 84 | -fno-omit-frame-pointer 85 | } else { 86 | -O3 87 | } 88 | -DNDEBUG 89 | -DIW_RELEASE 90 | } 91 | if { defined { SYSTEM_BIGENDIAN } 92 | -DIW_BIGENDIAN 93 | } 94 | if { defined { SYSTEM_BITNESS_64 } 95 | -DIW_64 96 | } 97 | if { defined { SYSTEM_BITNESS_32 } 98 | -DIW_32 99 | } 100 | if { defined { SYSTEM_LINUX } 101 | # Needed by Linux in order to use nftw() but fail to build on FreeBSD due to __BSD_VISIBLE define state. 102 | -D_XOPEN_SOURCE=700 103 | } 104 | if { defined { SYSTEM_DARWIN } 105 | -D_DARWIN_C_SOURCE 106 | } 107 | -D_DEFAULT_SOURCE 108 | -D_LARGEFILE_SOURCE 109 | -D_FILE_OFFSET_BITS=64 110 | 111 | if { !defined { IOWOW_BUILD_SHARED_LIBS } 112 | -DIW_NODLL 113 | } else { 114 | -DIW_API_EXPORTS 115 | } 116 | 117 | if { ${IOWOW_BUILD_TESTS} 118 | -DIW_TESTS=1 119 | } 120 | -I C{src} 121 | -I S{src} 122 | } 123 | 124 | include { ./src/Autark } -------------------------------------------------------------------------------- /.autark/fetch_resource.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | usage() { 5 | echo "Usage: $0 [target_dir_var] [n_strip_dirs]" >&2 6 | exit 1 7 | } 8 | 9 | [ -z "$1" ] && usage 10 | [ -z "$2" ] && usage 11 | 12 | 13 | PROJECT_URL="$1" 14 | TARGET_DIR="$2" 15 | TARGET_VAR="$3" 16 | NSTRIP="$4" 17 | 18 | rm -rf $TARGET_DIR 19 | mkdir -p "$TARGET_DIR" 20 | 21 | download_file() { 22 | URL="$1" 23 | DEST="$2" 24 | FILE=${URL#file://} 25 | 26 | if [ "$FILE" != "$URL" ]; then 27 | cp -f $FILE $DEST 28 | elif command -v curl >/dev/null 2>&1; then 29 | curl -L "$URL" -o "$DEST" 30 | elif command -v wget >/dev/null 2>&1; then 31 | wget -O "$DEST" "$URL" 32 | else 33 | echo "Error: neither wget nor curl is available on the system" >&2 34 | exit 1 35 | fi 36 | } 37 | 38 | case "$PROJECT_URL" in 39 | https://* | http://* | file://*) 40 | echo "Downloading an archive..." 41 | TMP_DIR="$(mktemp -d)" 42 | ARCHIVE="$TMP_DIR/archive" 43 | 44 | # Determine and extension 45 | case "$PROJECT_URL" in 46 | *.tar.gz|*.tgz) 47 | EXT="tar.gz" 48 | ;; 49 | *.tar.xz) 50 | EXT="tar.xz" 51 | ;; 52 | *.zip) 53 | EXT="zip" 54 | ;; 55 | *) 56 | echo "Unsupported archive format: $PROJECT_URL" >&2 57 | exit 1 58 | ;; 59 | esac 60 | 61 | download_file "$PROJECT_URL" "$ARCHIVE.$EXT" 62 | 63 | [ -z "$NSTRIP" ] && NSTRIP=1 64 | 65 | case "$EXT" in 66 | tar.gz) 67 | tar -xzf "$ARCHIVE.$EXT" -C "$TARGET_DIR" --strip-components=$NSTRIP 68 | ;; 69 | tar.xz) 70 | tar -xJf "$ARCHIVE.$EXT" -C "$TARGET_DIR" --strip-components=$NSTRIP 71 | ;; 72 | zip) 73 | unzip -q "$ARCHIVE.$EXT" -d "$TMP_DIR/unzipped" 74 | if [ "$NSTRIP" != "0" ]; then 75 | # Flatten if single top-level directory 76 | SRC_DIR="$TMP_DIR/unzipped" 77 | if [ "$(find "$SRC_DIR" -mindepth 1 -maxdepth 1 | wc -l)" -eq 1 ]; then 78 | FIRST_CHILD="$(find "$SRC_DIR" -mindepth 1 -maxdepth 1)" 79 | if [ -d "$FIRST_CHILD" ]; then 80 | SRC_DIR="$FIRST_CHILD" 81 | fi 82 | fi 83 | fi 84 | cp -a "$SRC_DIR"/. "$TARGET_DIR"/ 85 | ;; 86 | esac 87 | 88 | rm -rf "$TMP_DIR" 89 | ;; 90 | 91 | dir://*) 92 | SRC_DIR="${PROJECT_URL#dir://}" 93 | if [ ! -d "$SRC_DIR" ]; then 94 | echo "Error: Source directory does not exist: $SRC_DIR" >&2 95 | exit 1 96 | fi 97 | echo "Copying directory from file system..." 98 | mkdir -p "$TARGET_DIR" 99 | cp -a "$SRC_DIR"/. "$TARGET_DIR"/ 100 | ;; 101 | 102 | *) 103 | echo "Cloning Git repository..." 104 | git clone --depth=1 "$PROJECT_URL" "$TARGET_DIR" 105 | ;; 106 | esac 107 | 108 | if [ -n "$TARGET_VAR" ]; then 109 | autark set "$TARGET_VAR=$TARGET_DIR" 110 | fi 111 | 112 | rm -rf $TARGET_DIR/autark-cache 113 | touch $TARGET_DIR/.autark-fetch-dep 114 | autark dep $TARGET_DIR/.autark-fetch-dep -------------------------------------------------------------------------------- /.autark/system.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | SYSTEM_NAME="$(uname -s)" 6 | ARCH_RAW="$(uname -m)" 7 | 8 | case "$SYSTEM_NAME" in 9 | CYGWIN*|MINGW*|MSYS*) SYSTEM_NAME="Windows" ;; 10 | esac 11 | 12 | if uname -o 2>/dev/null | grep -qi android || uname -a | grep -qi android || [ -f /system/build.prop ]; then 13 | SYSTEM_NAME=Android 14 | fi 15 | 16 | case "$ARCH_RAW" in 17 | i386|i486|i586|i686) 18 | SYSTEM_ARCH="x86" 19 | SYSTEM_ARCH_ALT="ia32" 20 | ;; 21 | x86_64|amd64) 22 | SYSTEM_ARCH="x86_64" 23 | SYSTEM_ARCH_ALT="x64" 24 | ;; 25 | armv6l|armv7l|armv7hl|arm) 26 | SYSTEM_ARCH="armv7" 27 | SYSTEM_ARCH_ALT="arm" 28 | ;; 29 | aarch64|arm64) 30 | SYSTEM_ARCH="aarch64" 31 | SYSTEM_ARCH_ALT="arm64" 32 | ;; 33 | riscv32) 34 | SYSTEM_ARCH="riscv32" 35 | SYSTEM_ARCH_ALT="riscv32" 36 | ;; 37 | riscv64) 38 | SYSTEM_ARCH="riscv64" 39 | SYSTEM_ARCH_ALT="riscv64" 40 | ;; 41 | mips|mipsel|mips64|mips64el) 42 | SYSTEM_ARCH="mips" 43 | SYSTEM_ARCH_ALT="mips" 44 | ;; 45 | ppc) 46 | SYSTEM_ARCH="powerpc" 47 | SYSTEM_ARCH_ALT="ppc" 48 | ;; 49 | ppc64|ppc64le) 50 | SYSTEM_ARCH="powerpc" 51 | SYSTEM_ARCH_ALT="ppc64" 52 | ;; 53 | s390) 54 | SYSTEM_ARCH="s390" 55 | SYSTEM_ARCH_ALT="s390" 56 | ;; 57 | s390x) 58 | SYSTEM_ARCH="s390" 59 | SYSTEM_ARCH_ALT="s390x" 60 | ;; 61 | *) 62 | SYSTEM_ARCH="unknown" 63 | SYSTEM_ARCH_ALT="unknown" 64 | ;; 65 | esac 66 | 67 | check_tool() { 68 | command -v "$1" >/dev/null 2>&1 || { 69 | echo "error: required tool '$1' not found in PATH" >&2 70 | exit 1 71 | } 72 | } 73 | 74 | if command -v pkgconf >/dev/null 2>&1; then 75 | PKGCONF=pkgconf 76 | elif command -v pkg-config >/dev/null 2>&1; then 77 | PKGCONF=pkg-config 78 | else 79 | echo "error: neither 'pkgconf' nor 'pkg-config' found in PATH" >&2 80 | exit 1 81 | fi 82 | 83 | CC=${CC:-cc} 84 | AR=${AR:-ar} 85 | 86 | autark set "PKGCONF=$PKGCONF" 87 | check_tool $CC && autark set "CC=$CC" 88 | check_tool $AR && autark set "AR=$AR" 89 | check_tool install 90 | 91 | cat > './test_system.c' << 'EOF' 92 | #include 93 | #include 94 | int main(void) { 95 | 96 | if (sizeof(void*) == 8) { 97 | puts("SYSTEM_BITNESS_64=1"); 98 | } else if (sizeof(void*) == 4) { 99 | puts("SYSTEM_BITNESS_32=1"); 100 | } else { 101 | puts("Unknown bitness"); 102 | exit(1); 103 | } 104 | 105 | unsigned x = 1; 106 | if (*(char*)&x == 0) { 107 | puts("SYSTEM_BIGENDIAN=1"); 108 | } else { 109 | puts("SYSTEM_LITTLE_ENDIAN=1"); 110 | } 111 | 112 | return 0; 113 | } 114 | EOF 115 | 116 | ${CC} ./test_system.c -o ./test_system || exit 1 117 | 118 | ./test_system | xargs autark set 119 | eval "$(./test_system)" 120 | 121 | autark env CC 122 | autark env CFLAGS 123 | autark set "SYSTEM_NAME=$(echo -n "$SYSTEM_NAME" | tr '[:upper:]' '[:lower:]')" 124 | autark set "SYSTEM_$(echo -n "$SYSTEM_NAME" | tr '[:lower:]' '[:upper:]')=1" 125 | autark set "SYSTEM_ARCH=$SYSTEM_ARCH" 126 | autark set "SYSTEM_ARCH_ALT=$SYSTEM_ARCH_ALT" 127 | -------------------------------------------------------------------------------- /src/utils/iwconv.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWCONV_H 3 | #define IWCONV_H 4 | 5 | /************************************************************************************************** 6 | * IOWOW library 7 | * 8 | * MIT License 9 | * 10 | * Copyright (c) 2012-2024 Softmotions Ltd 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in all 20 | * copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | *************************************************************************************************/ 30 | 31 | #include "basedefs.h" 32 | #include 33 | #include 34 | #include 35 | 36 | IW_EXTERN_C_START; 37 | 38 | IW_EXPORT int64_t iwatoi(const char *str); 39 | 40 | IW_EXPORT int64_t iwatoi2(const char *str, size_t len); 41 | 42 | IW_EXPORT long double iwatof(const char *str); 43 | 44 | IW_EXPORT double iwstrtod(const char *str, char **end); 45 | 46 | IW_EXPORT int iwitoa(int64_t v, char *buf, int max); 47 | 48 | /** 49 | * Convert a given floating point number to string. 50 | * @note Exponent notation can be used during conversion 51 | */ 52 | IW_EXPORT char* iwftoa(long double v, char buf[IWNUMBUF_SIZE]); 53 | 54 | /** 55 | * Compare real(float) numbers encoded as decimal point string value. 56 | * @note Exponential notation not supported. 57 | */ 58 | IW_EXPORT int iwafcmp(const char *aptr, int asiz, const char *bptr, int bsiz); 59 | 60 | IW_EXPORT size_t iwhex2bin(const char *hex, int hexlen, char *out, int max); 61 | 62 | IW_EXPORT char* iwbin2hex( 63 | char* const hex, 64 | const size_t hex_maxlen, 65 | const unsigned char* const bin, 66 | const size_t bin_len); 67 | 68 | IW_EXPORT long double iw_strtold(const char *v, iwrc *rcp); 69 | 70 | IW_EXPORT double iw_strtod(const char *v, iwrc *rcp); 71 | 72 | IW_EXPORT long int iw_strtol(const char *v, int base, iwrc *rcp); 73 | 74 | IW_EXPORT long long iw_strtoll(const char *v, int base, iwrc *rcp); 75 | 76 | IW_EXPORT unsigned long int iw_strtoul(const char *v, int base, iwrc *rcp); 77 | 78 | IW_EXPORT unsigned long long iw_strtoull(const char *v, int base, iwrc *rcp); 79 | 80 | IW_EXTERN_C_END; 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /src/utils/iwrb.c: -------------------------------------------------------------------------------- 1 | #include "iwrb.h" 2 | 3 | #include 4 | #include 5 | 6 | struct iwrb* iwrb_create(size_t usize, size_t len) { 7 | struct iwrb *rb = malloc(sizeof(*rb) + usize * len); 8 | if (!rb) { 9 | return 0; 10 | } 11 | rb->pos = 0; 12 | rb->len = len; 13 | rb->usize = usize; 14 | rb->buf = (char*) rb + sizeof(*rb); 15 | return rb; 16 | } 17 | 18 | void iwrb_destroy(struct iwrb **rbp) { 19 | if (rbp && *rbp) { 20 | free(*rbp); 21 | *rbp = 0; 22 | } 23 | } 24 | 25 | struct iwrb* iwrb_wrap(void *buf, size_t len, size_t usize) { 26 | if (buf == 0 || len < sizeof(struct iwrb) + usize) { 27 | return 0; 28 | } 29 | struct iwrb *rb = buf; 30 | rb->pos = 0; 31 | rb->len = (len - sizeof(struct iwrb)) / usize; 32 | rb->usize = usize; 33 | rb->buf = (char*) buf + sizeof(*rb); 34 | return rb; 35 | } 36 | 37 | void iwrb_put(struct iwrb *rb, const void *buf) { 38 | if (rb->pos != 0) { 39 | size_t upos = rb->pos > 0 ? rb->pos : -rb->pos; 40 | if (upos == rb->len) { 41 | memcpy(rb->buf, buf, rb->usize); 42 | rb->pos = 1; 43 | } else { 44 | memcpy(rb->buf + upos * rb->usize, buf, rb->usize); 45 | rb->pos = rb->pos > 0 ? rb->pos + 1 : rb->pos - 1; 46 | } 47 | } else { 48 | memcpy(rb->buf, buf, rb->usize); 49 | rb->pos = -1; 50 | } 51 | } 52 | 53 | void iwrb_back(struct iwrb *rb) { 54 | if (rb->pos > 0) { 55 | --rb->pos; 56 | } else if (rb->pos < 0) { 57 | ++rb->pos; 58 | } 59 | } 60 | 61 | void* iwrb_peek(const struct iwrb *rb) { 62 | if (rb->pos == 0) { 63 | return 0; 64 | } 65 | size_t upos = rb->pos > 0 ? rb->pos : -rb->pos; 66 | return rb->buf + (upos - 1) * rb->usize; 67 | } 68 | 69 | void* iwrb_begin(const struct iwrb *rb) { 70 | if (rb->pos == 0) { 71 | return 0; 72 | } 73 | if (rb->pos < 0) { 74 | return rb->buf; 75 | } else { 76 | return rb->buf + rb->pos * rb->usize; 77 | } 78 | } 79 | 80 | void iwrb_clear(struct iwrb *rb) { 81 | rb->pos = 0; 82 | } 83 | 84 | size_t iwrb_num_cached(const struct iwrb *rb) { 85 | if (rb->pos <= 0) { 86 | return -rb->pos; 87 | } else { 88 | return rb->len; 89 | } 90 | } 91 | 92 | void iwrb_iter_init(const struct iwrb *rb, struct iwrb_iter *iter) { 93 | iter->rb = rb; 94 | iter->pos = rb->pos > 0 ? rb->pos : -rb->pos; 95 | iter->ipos = rb->pos > 0 ? -rb->pos : rb->pos; 96 | } 97 | 98 | void* iwrb_iter_prev(struct iwrb_iter *iter) { 99 | const struct iwrb *rb = iter->rb; 100 | if (iter->ipos == 0) { 101 | return 0; 102 | } 103 | if (rb->pos < 0) { 104 | if (iter->pos == 0) { 105 | return 0; 106 | } 107 | if (iter->ipos < 0) { 108 | iter->ipos = -iter->ipos; 109 | } 110 | return rb->buf + --iter->pos * rb->usize; 111 | } else { 112 | if (iter->pos == 0) { 113 | iter->pos = rb->len; 114 | } 115 | if (iter->ipos < 0) { 116 | iter->ipos = -iter->ipos; 117 | } else if (iter->ipos == iter->pos) { 118 | iter->ipos = 0; 119 | return 0; 120 | } 121 | return rb->buf + --iter->pos * rb->usize; 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/utils/mt19937ar.h: -------------------------------------------------------------------------------- 1 | #ifndef MT19937AR_H 2 | #define MT19937AR_H 3 | 4 | /* 5 | A C-program for MT19937, with initialization improved 2002/1/26. 6 | Coded by Takuji Nishimura and Makoto Matsumoto. 7 | 8 | Before using, initialize the state by using init_genrand(seed) 9 | or init_by_array(init_key, key_length). 10 | 11 | Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, 12 | All rights reserved. 13 | Copyright (C) 2005, Mutsuo Saito 14 | All rights reserved. 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions 18 | are met: 19 | 20 | 1. Redistributions of source code must retain the above copyright 21 | notice, this list of conditions and the following disclaimer. 22 | 23 | 2. Redistributions in binary form must reproduce the above copyright 24 | notice, this list of conditions and the following disclaimer in the 25 | documentation and/or other materials provided with the distribution. 26 | 27 | 3. The names of its contributors may not be used to endorse or promote 28 | products derived from this software without specific prior written 29 | permission. 30 | 31 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 32 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 33 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 34 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 35 | CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 36 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 37 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 38 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 39 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 40 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 41 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 42 | 43 | 44 | Any feedback is very welcome. 45 | http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html 46 | email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) 47 | */ 48 | 49 | void init_mt19937ar(void); 50 | 51 | /* initializes mt[N] with a seed */ 52 | void init_genrand(unsigned long s); 53 | 54 | /* initialize by an array with array-length */ 55 | /* init_key is the array for initializing keys */ 56 | /* key_length is its length */ 57 | /* slight change for C++, 2004/2/26 */ 58 | void init_by_array(unsigned long init_key[], int key_length); 59 | 60 | /* generates a random number on [0,0xffffffff]-interval */ 61 | unsigned long genrand_int32(void); 62 | 63 | /* generates a random number on [0,0x7fffffff]-interval */ 64 | long genrand_int31(void); 65 | 66 | /* These real versions are due to Isaku Wada, 2002/01/09 added */ 67 | /* generates a random number on [0,1]-real-interval */ 68 | double genrand_real1(void); 69 | 70 | /* generates a random number on [0,1)-real-interval */ 71 | double genrand_real2(void); 72 | 73 | /* generates a random number on (0,1)-real-interval */ 74 | double genrand_real3(void); 75 | 76 | /* generates a random number on [0,1) with 53-bit resolution*/ 77 | double genrand_res53(void); 78 | 79 | #endif /* MT19937AR_H */ 80 | -------------------------------------------------------------------------------- /src/kv/tests/iwkv_test7.c: -------------------------------------------------------------------------------- 1 | #include "iwkv.h" 2 | #include "iwlog.h" 3 | #include "iwutils.h" 4 | #include "iwcfg.h" 5 | #include "iwkv_tests.h" 6 | #include "iwkv_internal.h" 7 | 8 | #define KBUFSZ 1024 9 | #define VBUFSZ 1024 10 | char kbuf[KBUFSZ]; 11 | char vbuf[VBUFSZ]; 12 | 13 | uint32_t g_seed; 14 | 15 | int init_suite(void) { 16 | iwrc rc = iwkv_init(); 17 | RCRET(rc); 18 | g_seed = 2681089616; 19 | return rc; 20 | } 21 | 22 | int clean_suite(void) { 23 | return 0; 24 | } 25 | 26 | static void iwkv_test7_1_impl(int direction) { 27 | iwrc rc; 28 | IWKV iwkv; 29 | IWDB db; 30 | IWKV_val key = { 0 }; 31 | IWKV_val val = { 0 }; 32 | IWKV_OPTS opts = { 33 | .path = direction > 0 ? "iwkv_test7_2_fwd.db" : "iwkv_test7_2_back.db", 34 | .oflags = IWKV_TRUNC, 35 | .random_seed = g_seed 36 | }; 37 | rc = iwkv_open(&opts, &iwkv); 38 | CU_ASSERT_EQUAL_FATAL(rc, 0); 39 | rc = iwkv_db(iwkv, 1, IWDB_COMPOUND_KEYS, &db); 40 | CU_ASSERT_EQUAL_FATAL(rc, 0); 41 | 42 | const int nrecords = 50000; 43 | 44 | for (int i = 0; i < nrecords; ++i) { 45 | snprintf(kbuf, KBUFSZ, "5368fce5-c138-4f0d-bfee-dbce07eb28e1%d", i); 46 | snprintf(vbuf, VBUFSZ, "%04d", i); 47 | key.data = kbuf; 48 | key.size = strlen(key.data); 49 | key.compound = direction > 0 ? i + 1 : nrecords - i; 50 | 51 | val.data = vbuf; 52 | val.size = strlen(val.data); 53 | rc = iwkv_put(db, &key, &val, 0); 54 | CU_ASSERT_EQUAL_FATAL(rc, 0); 55 | } 56 | 57 | for (int i = 0; i < nrecords; ++i) { 58 | snprintf(kbuf, KBUFSZ, "5368fce5-c138-4f0d-bfee-dbce07eb28e1%d", i); 59 | snprintf(vbuf, VBUFSZ, "%04d", i); 60 | key.data = kbuf; 61 | key.size = strlen(key.data); 62 | key.compound = direction > 0 ? i + 1 : nrecords - i; 63 | rc = iwkv_get(db, &key, &val); 64 | CU_ASSERT_EQUAL_FATAL(rc, 0); 65 | iwkv_val_dispose(&val); 66 | } 67 | rc = iwkv_close(&iwkv); 68 | CU_ASSERT_EQUAL_FATAL(rc, 0); 69 | } 70 | 71 | static void iwkv_test7_1(void) { 72 | iwkv_test7_1_impl(1); 73 | iwkv_test7_1_impl(-1); 74 | IWP_FILE_STAT fwd_s = { 0 }; 75 | IWP_FILE_STAT back_s = { 0 }; 76 | iwrc rc = iwp_fstat("iwkv_test7_2_fwd.db", &fwd_s); 77 | CU_ASSERT_EQUAL_FATAL(rc, 0); 78 | rc = iwp_fstat("iwkv_test7_2_back.db", &back_s); 79 | CU_ASSERT_EQUAL_FATAL(rc, 0); 80 | CU_ASSERT_TRUE((double) fwd_s.size / back_s.size < 1.1); 81 | } 82 | 83 | int main(void) { 84 | CU_pSuite pSuite = NULL; 85 | 86 | /* Initialize the CUnit test registry */ 87 | if (CUE_SUCCESS != CU_initialize_registry()) { 88 | return CU_get_error(); 89 | } 90 | 91 | /* Add a suite to the registry */ 92 | pSuite = CU_add_suite("iwkv_test7", init_suite, clean_suite); 93 | 94 | if (NULL == pSuite) { 95 | CU_cleanup_registry(); 96 | return CU_get_error(); 97 | } 98 | 99 | /* Add the tests to the suite */ 100 | if ( 101 | (NULL == CU_add_test(pSuite, "iwkv_test7_1", iwkv_test7_1))) { 102 | CU_cleanup_registry(); 103 | return CU_get_error(); 104 | } 105 | 106 | /* Run all tests using the CUnit Basic interface */ 107 | CU_basic_set_mode(CU_BRM_VERBOSE); 108 | CU_basic_run_tests(); 109 | int ret = CU_get_error() || CU_get_number_of_failures(); 110 | CU_cleanup_registry(); 111 | return ret; 112 | } 113 | -------------------------------------------------------------------------------- /src/utils/iwth.c: -------------------------------------------------------------------------------- 1 | #include "iwth.h" 2 | #include "iwp.h" 3 | #include "iwlog.h" 4 | 5 | #include 6 | #include 7 | 8 | iwrc iw_cond_timed_wait_ms(pthread_cond_t *cond, pthread_mutex_t *mtx, long timeout_ms, bool *out_is_timeout) { 9 | iwrc rc; 10 | int rci; 11 | struct timespec tp; 12 | *out_is_timeout = false; 13 | 14 | #if defined(IW_HAVE_CLOCK_MONOTONIC) && defined(IW_HAVE_PTHREAD_CONDATTR_SETCLOCK) 15 | rc = iwp_clock_get_time(CLOCK_MONOTONIC, &tp); 16 | #else 17 | rc = iwp_clock_get_time(CLOCK_REALTIME, &tp); 18 | #endif 19 | RCRET(rc); 20 | tp.tv_sec += timeout_ms / 1000; 21 | tp.tv_nsec += (timeout_ms % 1000) * 1000000; 22 | if (tp.tv_nsec >= 1000000000) { 23 | tp.tv_sec += 1; 24 | tp.tv_nsec -= 1000000000; 25 | } 26 | do { 27 | rci = pthread_cond_timedwait(cond, mtx, &tp); 28 | } while (rci == EINTR); 29 | if (rci) { 30 | if (rci == ETIMEDOUT) { 31 | *out_is_timeout = true; 32 | } else { 33 | rc = iwrc_set_errno(IW_ERROR_THREADING_ERRNO, rci); 34 | } 35 | } 36 | 37 | return rc; 38 | } 39 | 40 | #ifdef __APPLE__ 41 | 42 | #ifndef __unused 43 | #define __unused __attribute__((unused)) 44 | #endif 45 | 46 | int pthread_barrierattr_init(pthread_barrierattr_t *attr __unused) { 47 | return 0; 48 | } 49 | 50 | int pthread_barrierattr_destroy(pthread_barrierattr_t *attr __unused) { 51 | return 0; 52 | } 53 | 54 | int pthread_barrierattr_getpshared( 55 | const pthread_barrierattr_t* restrict attr __unused, 56 | int* restrict pshared) { 57 | *pshared = PTHREAD_PROCESS_PRIVATE; 58 | return 0; 59 | } 60 | 61 | int pthread_barrierattr_setpshared( 62 | pthread_barrierattr_t *attr __unused, 63 | int pshared) { 64 | if (pshared != PTHREAD_PROCESS_PRIVATE) { 65 | errno = EINVAL; 66 | return -1; 67 | } 68 | return 0; 69 | } 70 | 71 | int pthread_barrier_init( 72 | pthread_barrier_t* restrict barrier, 73 | const pthread_barrierattr_t* restrict attr __unused, 74 | unsigned count) { 75 | if (count == 0) { 76 | errno = EINVAL; 77 | return -1; 78 | } 79 | 80 | if (pthread_mutex_init(&barrier->mutex, 0) < 0) { 81 | return -1; 82 | } 83 | if (pthread_cond_init(&barrier->cond, 0) < 0) { 84 | int errno_save = errno; 85 | pthread_mutex_destroy(&barrier->mutex); 86 | errno = errno_save; 87 | return -1; 88 | } 89 | 90 | barrier->limit = count; 91 | barrier->count = 0; 92 | barrier->phase = 0; 93 | 94 | return 0; 95 | } 96 | 97 | int pthread_barrier_destroy(pthread_barrier_t *barrier) { 98 | pthread_mutex_destroy(&barrier->mutex); 99 | pthread_cond_destroy(&barrier->cond); 100 | return 0; 101 | } 102 | 103 | int pthread_barrier_wait(pthread_barrier_t *barrier) { 104 | pthread_mutex_lock(&barrier->mutex); 105 | barrier->count++; 106 | if (barrier->count >= barrier->limit) { 107 | barrier->phase++; 108 | barrier->count = 0; 109 | pthread_cond_broadcast(&barrier->cond); 110 | pthread_mutex_unlock(&barrier->mutex); 111 | return PTHREAD_BARRIER_SERIAL_THREAD; 112 | } else { 113 | unsigned phase = barrier->phase; 114 | do { 115 | pthread_cond_wait(&barrier->cond, &barrier->mutex); 116 | } while (phase == barrier->phase); 117 | pthread_mutex_unlock(&barrier->mutex); 118 | return 0; 119 | } 120 | } 121 | 122 | #endif /* __APPLE__ */ 123 | -------------------------------------------------------------------------------- /src/kv/iwal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWAL_H 3 | #define IWAL_H 4 | 5 | /************************************************************************************************** 6 | * IOWOW library 7 | * 8 | * MIT License 9 | * 10 | * Copyright (c) 2012-2024 Softmotions Ltd 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in all 20 | * copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | *************************************************************************************************/ 30 | 31 | /** @file 32 | * @brief Write Ahead Logging (WAL) module. 33 | */ 34 | #include "iwkv.h" 35 | #include "iwfsmfile.h" 36 | 37 | IW_EXTERN_C_START; 38 | 39 | typedef enum { 40 | WOP_SET = 1, 41 | WOP_COPY, 42 | WOP_WRITE, 43 | WOP_RESIZE, 44 | WOP_SAVEPOINT, 45 | WOP_RESET, 46 | WOP_SEP = 127, /**< WAL file separator */ 47 | } wop_t; 48 | 49 | #pragma pack(push, 1) 50 | typedef struct WBSEP { 51 | uint8_t id; 52 | uint8_t pad[3]; 53 | uint32_t crc; 54 | uint32_t len; 55 | } WBSEP; 56 | 57 | typedef struct WBRESET { 58 | uint8_t id; 59 | uint8_t pad[3]; 60 | } WBRESET; 61 | 62 | typedef struct WBSET { 63 | uint8_t id; 64 | uint8_t pad[3]; 65 | uint32_t val; 66 | off_t off; 67 | off_t len; 68 | } WBSET; 69 | 70 | typedef struct WBCOPY { 71 | uint8_t id; 72 | uint8_t pad[3]; 73 | off_t off; 74 | off_t len; 75 | off_t noff; 76 | } WBCOPY; 77 | 78 | typedef struct WBWRITE { 79 | uint8_t id; 80 | uint8_t pad[3]; 81 | uint32_t crc; 82 | uint32_t len; 83 | off_t off; 84 | } WBWRITE; 85 | 86 | typedef struct WBRESIZE { 87 | uint8_t id; 88 | uint8_t pad[3]; 89 | off_t osize; 90 | off_t nsize; 91 | } WBRESIZE; 92 | 93 | typedef struct WBSAVEPOINT { 94 | uint8_t id; 95 | uint8_t pad[3]; 96 | uint64_t ts; 97 | } WBSAVEPOINT; 98 | #pragma pack(pop) 99 | 100 | iwrc iwal_create(struct iwkv *iwkv, const struct iwkv_opts *opts, IWFS_FSM_OPTS *fsmopts, bool recover_backup); 101 | 102 | iwrc iwal_sync(struct iwkv *iwkv); 103 | 104 | iwrc iwal_poke_checkpoint(struct iwkv *iwkv, bool force); 105 | 106 | iwrc iwal_poke_savepoint(struct iwkv *iwkv); 107 | 108 | iwrc iwal_savepoint_exl(struct iwkv *iwkv, bool sync); 109 | 110 | void iwal_shutdown(struct iwkv *iwkv); 111 | 112 | bool iwal_synched(struct iwkv *iwkv); 113 | 114 | iwrc iwal_online_backup(struct iwkv *iwkv, uint64_t *ts, const char *target_file); 115 | 116 | IW_EXTERN_C_END; 117 | #endif 118 | -------------------------------------------------------------------------------- /src/utils/iwth.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Aleksey Demakov 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * * Redistributions of source code must retain the above copyright notice, this 9 | * list of conditions and the following disclaimer. 10 | * 11 | * * Redistributions in binary form must reproduce the above copyright notice, 12 | * this list of conditions and the following disclaimer in the documentation 13 | * and/or other materials provided with the distribution. 14 | * 15 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 | * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #pragma once 28 | #ifndef IWTH_H 29 | #define IWTH_H 30 | 31 | #include "basedefs.h" 32 | #include 33 | 34 | /** 35 | * Timed condition wait. 36 | * NOTE: Condition `cond` must be initialized with CLOCK_MONOTONIC attribute if system supports monotonic clock. 37 | * 38 | * pthread_condattr_setclock(&cattr, CLOCK_MONOTONIC); 39 | */ 40 | IW_EXPORT iwrc iw_cond_timed_wait_ms( 41 | pthread_cond_t *cond, 42 | pthread_mutex_t *mtx, 43 | long timeout_ms, 44 | bool *out_is_timeout); 45 | 46 | #if defined(__APPLE__) || (defined(__ANDROID_API__) && __ANDROID_API__ < 24) 47 | 48 | #ifdef __cplusplus 49 | extern "C" { 50 | #endif 51 | 52 | #if !defined(PTHREAD_BARRIER_SERIAL_THREAD) 53 | # define PTHREAD_BARRIER_SERIAL_THREAD (1) 54 | #endif 55 | 56 | #if !defined(PTHREAD_PROCESS_PRIVATE) 57 | # define PTHREAD_PROCESS_PRIVATE (42) 58 | #endif 59 | #if !defined(PTHREAD_PROCESS_SHARED) 60 | # define PTHREAD_PROCESS_SHARED (43) 61 | #endif 62 | 63 | typedef struct { 64 | char noop; 65 | } pthread_barrierattr_t; 66 | 67 | typedef struct { 68 | pthread_mutex_t mutex; 69 | pthread_cond_t cond; 70 | unsigned int limit; 71 | unsigned int count; 72 | unsigned int phase; 73 | } pthread_barrier_t; 74 | 75 | IW_EXPORT int pthread_barrierattr_init(pthread_barrierattr_t *attr); 76 | IW_EXPORT int pthread_barrierattr_destroy(pthread_barrierattr_t *attr); 77 | 78 | IW_EXPORT int pthread_barrierattr_getpshared( 79 | const pthread_barrierattr_t* restrict attr, 80 | int* restrict pshared); 81 | IW_EXPORT int pthread_barrierattr_setpshared( 82 | pthread_barrierattr_t *attr, 83 | int pshared); 84 | 85 | IW_EXPORT int pthread_barrier_init( 86 | pthread_barrier_t* restrict barrier, 87 | const pthread_barrierattr_t* restrict attr, 88 | unsigned int count); 89 | IW_EXPORT int pthread_barrier_destroy(pthread_barrier_t *barrier); 90 | 91 | IW_EXPORT int pthread_barrier_wait(pthread_barrier_t *barrier); 92 | 93 | #ifdef __cplusplus 94 | } 95 | #endif 96 | 97 | #endif /* __APPLE__ */ 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /src/kv/examples/compoundkeys1.c: -------------------------------------------------------------------------------- 1 | /// 2 | /// Compound keys demo. 3 | /// 4 | /// Compound keys allows associate one `key value` with many references 5 | /// represented as VNUM64 (eg.: Non unique table indexes). 6 | /// 7 | /// Compound mainly used for non-unique indexes in ejdb2 database engine: 8 | /// 9 | /// `.` 10 | /// 11 | 12 | #include "iwkv.h" 13 | #include 14 | #include 15 | #include 16 | 17 | struct user_s { 18 | uint32_t id; 19 | char *name; 20 | }; 21 | 22 | struct chat_root_s { 23 | char *name; 24 | struct user_s users[5]; 25 | }; 26 | 27 | static struct chat_root_s rooms[] = { 28 | { 29 | .name = "Meeting room", 30 | .users = { 31 | { .id = 1, .name = "Joy Lynn" }, 32 | { .id = 2, .name = "Aubrey Sparks" }, 33 | { .id = 3, .name = "Vinnie Kaye" }, 34 | { 0 } 35 | } 36 | }, 37 | { 38 | .name = "Webinar room", 39 | .users = { 40 | { .id = 4, .name = "Arda Payne" }, 41 | { .id = 2, .name = "Joy Lynn" }, 42 | { 0 } 43 | } 44 | } 45 | }; 46 | 47 | static iwrc run(void) { 48 | IWKV_OPTS opts = { 49 | .path = "compoundkeys.db", 50 | .oflags = IWKV_TRUNC 51 | }; 52 | IWKV iwkv; 53 | IWDB db; 54 | IWKV_cursor cur = 0; 55 | iwrc rc = iwkv_open(&opts, &iwkv); 56 | RCRET(rc); 57 | 58 | rc = iwkv_db(iwkv, 1, IWDB_COMPOUND_KEYS, &db); 59 | RCGO(rc, finish); 60 | 61 | // Persist all rooms with members 62 | for (int i = 0; i < sizeof(rooms) / sizeof(rooms[0]); ++i) { 63 | int j = 0; 64 | struct chat_root_s *room = &rooms[i]; 65 | for (struct user_s *user = &room->users[0]; user->id; user = &room->users[++j]) { 66 | IWKV_val key = { .data = room->name, .size = strlen(room->name), .compound = user->id }; 67 | IWKV_val val = { .data = user->name, .size = strlen(user->name) }; 68 | RCC(rc, finish, iwkv_put(db, &key, &val, 0)); 69 | } 70 | } 71 | 72 | // Get specific user from the room 73 | { 74 | IWKV_val key = { .data = "Webinar room", .size = sizeof("Webinar room") - 1, .compound = 2 }; 75 | IWKV_val val; 76 | RCC(rc, finish, iwkv_get(db, &key, &val)); 77 | fprintf(stdout, "\n>>>> Found: '%.*s' in room '%s' by id: %d\n", 78 | (int) val.size, (char*) val.data, 79 | (char*) key.data, (int) key.compound); 80 | iwkv_val_dispose(&val); 81 | } 82 | 83 | // Iterate over all members in `Meeting room` 84 | { 85 | size_t len = strlen(rooms[0].name); 86 | fprintf(stdout, "\n>>>> Iterate over all members in %s\n", rooms[0].name); 87 | IWKV_val val, key = { .data = rooms[0].name, .size = len }; 88 | RCC(rc, finish, iwkv_cursor_open(db, &cur, IWKV_CURSOR_GE, &key)); 89 | do { 90 | RCC(rc, finish, iwkv_cursor_get(cur, &key, &val)); 91 | if (memcmp(key.data, rooms[0].name, MIN(key.size, len))) { 92 | // We rolled to end of `Meeting room` room 93 | iwkv_kv_dispose(&key, &val); 94 | break; 95 | } 96 | fprintf(stdout, "%.*s: %d %.*s\n", 97 | (int) key.size, (char*) key.data, 98 | (int) key.compound, 99 | (int) val.size, 100 | (char*) val.data); 101 | iwkv_kv_dispose(&key, &val); 102 | } while ((rc = iwkv_cursor_to(cur, IWKV_CURSOR_PREV)) == 0); 103 | rc = 0; 104 | } 105 | 106 | finish: 107 | if (cur) { 108 | iwkv_cursor_close(&cur); 109 | } 110 | iwkv_close(&iwkv); 111 | return rc; 112 | } 113 | 114 | int main(void) { 115 | iwrc rc = run(); 116 | if (rc) { 117 | iwlog_ecode_error3(rc); 118 | return 1; 119 | } 120 | return 0; 121 | } 122 | -------------------------------------------------------------------------------- /src/kv/examples/cursors1.c: -------------------------------------------------------------------------------- 1 | /// 2 | /// Fills database with a set of football table records 3 | /// then traverse records according to club name in ascending and descending orders. 4 | /// 5 | 6 | #include "iwkv.h" 7 | #include 8 | #include 9 | #include 10 | 11 | static struct data_s { 12 | const char *club; 13 | uint8_t points; 14 | } _points[] = { 15 | { "Aston Villa", 25 }, 16 | { "Manchester City", 57 }, 17 | { "Arsenal", 40 }, 18 | { "Everton", 37 }, 19 | { "West Ham United", 27 }, 20 | { "Tottenham Hotspur", 41 }, 21 | { "Wolverhampton Wanderers", 43 }, 22 | { "Norwich City", 21 }, 23 | { "Leicester City", 53 }, 24 | { "Manchester United", 45 }, 25 | { "Newcastle United", 35 }, 26 | { "Brighton & Hove Albion", 29 }, 27 | { "AFC Bournemouth", 27 }, 28 | { "Crystal Palace", 39 }, 29 | { "Sheffield United", 43 }, 30 | { "Burnley", 39 }, 31 | { "Southampton", 34 }, 32 | { "Watford", 27 }, 33 | { "Chelsea", 48 }, 34 | { "Liverpool", 82 }, 35 | }; 36 | 37 | static iwrc run(void) { 38 | IWKV_OPTS opts = { 39 | .path = "cursor1.db", 40 | .oflags = IWKV_TRUNC // Cleanup database before open 41 | }; 42 | IWKV iwkv; 43 | IWDB db; 44 | IWKV_cursor cur = 0; 45 | iwrc rc = iwkv_open(&opts, &iwkv); 46 | RCRET(rc); 47 | 48 | rc = iwkv_db(iwkv, 1, 0, &db); 49 | RCGO(rc, finish); 50 | 51 | for (int i = 0; i < sizeof(_points) / sizeof(_points[0]); ++i) { 52 | struct data_s *n = &_points[i]; 53 | IWKV_val key = { .data = (void*) n->club, .size = strlen(n->club) }; 54 | IWKV_val val = { .data = &n->points, .size = sizeof(n->points) }; 55 | RCC(rc, finish, iwkv_put(db, &key, &val, 0)); 56 | } 57 | 58 | fprintf(stdout, ">>>> Traverse in descending order\n"); 59 | RCC(rc, finish, iwkv_cursor_open(db, &cur, IWKV_CURSOR_BEFORE_FIRST, 0)); 60 | while ((rc = iwkv_cursor_to(cur, IWKV_CURSOR_NEXT)) == 0) { 61 | IWKV_val key, val; 62 | RCC(rc, finish, iwkv_cursor_get(cur, &key, &val)); 63 | fprintf(stdout, "%.*s: %u\n", 64 | (int) key.size, (char*) key.data, 65 | *(uint8_t*) val.data); 66 | iwkv_kv_dispose(&key, &val); 67 | } 68 | rc = 0; 69 | iwkv_cursor_close(&cur); 70 | 71 | fprintf(stdout, "\n>>>> Traverse in ascending order\n"); 72 | RCC(rc, finish, iwkv_cursor_open(db, &cur, IWKV_CURSOR_AFTER_LAST, 0)); 73 | while ((rc = iwkv_cursor_to(cur, IWKV_CURSOR_PREV)) == 0) { 74 | IWKV_val key, val; 75 | RCC(rc, finish, iwkv_cursor_get(cur, &key, &val)); 76 | fprintf(stdout, "%.*s: %u\n", 77 | (int) key.size, (char*) key.data, 78 | *(uint8_t*) val.data); 79 | iwkv_kv_dispose(&key, &val); 80 | } 81 | rc = 0; 82 | iwkv_cursor_close(&cur); 83 | 84 | // Select all keys greater or equal than: Manchester United 85 | { 86 | fprintf(stdout, "\n>>>> Records GE: %s\n", _points[9].club); 87 | IWKV_val key = { .data = (void*) _points[9].club, .size = strlen(_points[9].club) }, val; 88 | RCC(rc, finish, iwkv_cursor_open(db, &cur, IWKV_CURSOR_GE, &key)); 89 | do { 90 | RCC(rc, finish, iwkv_cursor_get(cur, &key, &val)); 91 | fprintf(stdout, "%.*s: %u\n", 92 | (int) key.size, (char*) key.data, 93 | *(uint8_t*) val.data); 94 | iwkv_kv_dispose(&key, &val); 95 | } while ((rc = iwkv_cursor_to(cur, IWKV_CURSOR_NEXT)) == 0); 96 | rc = 0; 97 | } 98 | iwkv_cursor_close(&cur); 99 | 100 | finish: 101 | if (cur) { 102 | iwkv_cursor_close(&cur); 103 | } 104 | iwkv_close(&iwkv); 105 | return rc; 106 | } 107 | 108 | int main(void) { 109 | iwrc rc = run(); 110 | if (rc) { 111 | iwlog_ecode_error3(rc); 112 | return 1; 113 | } 114 | return 0; 115 | } 116 | -------------------------------------------------------------------------------- /src/json/tests/data/004.expected.json: -------------------------------------------------------------------------------- 1 | { 2 | "web-app": { 3 | "servlet": [ 4 | { 5 | "servlet-name": "cofaxCDS", 6 | "servlet-class": "org.cofax.cds.CDSServlet", 7 | "init-param": { 8 | "configGlossary:installationAt": "Philadelphia, PA", 9 | "configGlossary:adminEmail": "ksm@pobox.com", 10 | "configGlossary:poweredBy": "Cofax", 11 | "configGlossary:poweredByIcon": "/images/cofax.gif", 12 | "configGlossary:staticPath": "/content/static", 13 | "templateProcessorClass": "org.cofax.WysiwygTemplate", 14 | "templateLoaderClass": "org.cofax.FilesTemplateLoader", 15 | "templatePath": "templates", 16 | "templateOverridePath": "", 17 | "defaultListTemplate": "listTemplate.htm", 18 | "defaultFileTemplate": "articleTemplate.htm", 19 | "useJSP": false, 20 | "jspListTemplate": "listTemplate.jsp", 21 | "jspFileTemplate": "articleTemplate.jsp", 22 | "cachePackageTagsTrack": 200, 23 | "cachePackageTagsStore": 200, 24 | "cachePackageTagsRefresh": 60, 25 | "cacheTemplatesTrack": 100, 26 | "cacheTemplatesStore": 50, 27 | "cacheTemplatesRefresh": 15, 28 | "cachePagesTrack": 200, 29 | "cachePagesStore": 100, 30 | "cachePagesRefresh": 10, 31 | "cachePagesDirtyRead": 10, 32 | "searchEngineListTemplate": "forSearchEnginesList.htm", 33 | "searchEngineFileTemplate": "forSearchEngines.htm", 34 | "searchEngineRobotsDb": "WEB-INF/robots.db", 35 | "useDataStore": true, 36 | "dataStoreClass": "org.cofax.SqlDataStore", 37 | "redirectionClass": "org.cofax.SqlRedirection", 38 | "dataStoreName": "cofax", 39 | "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", 40 | "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", 41 | "dataStoreUser": "sa", 42 | "dataStorePassword": "dataStoreTestQuery", 43 | "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", 44 | "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", 45 | "dataStoreInitConns": 10, 46 | "dataStoreMaxConns": 100, 47 | "dataStoreConnUsageLimit": 100, 48 | "dataStoreLogLevel": "debug", 49 | "maxUrlLength": 500 50 | } 51 | }, 52 | { 53 | "servlet-name": "cofaxEmail", 54 | "servlet-class": "org.cofax.cds.EmailServlet", 55 | "init-param": { 56 | "mailHost": "mail1", 57 | "mailHostOverride": "mail2" 58 | } 59 | }, 60 | { 61 | "servlet-name": "cofaxAdmin", 62 | "servlet-class": "org.cofax.cds.AdminServlet" 63 | }, 64 | { 65 | "servlet-name": "fileServlet", 66 | "servlet-class": "org.cofax.cds.FileServlet" 67 | }, 68 | { 69 | "servlet-name": "cofaxTools", 70 | "servlet-class": "org.cofax.cms.CofaxToolsServlet", 71 | "init-param": { 72 | "templatePath": "toolstemplates/", 73 | "log": 1, 74 | "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", 75 | "logMaxSize": "", 76 | "dataLog": 1, 77 | "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", 78 | "dataLogMaxSize": "", 79 | "removePageCache": "/content/admin/remove?cache=pages&id=", 80 | "removeTemplateCache": "/content/admin/remove?cache=templates&id=", 81 | "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", 82 | "lookInContext": 1, 83 | "adminGroupID": 4, 84 | "betaServer": true 85 | } 86 | } 87 | ], 88 | "servlet-mapping": { 89 | "cofaxCDS": "/", 90 | "cofaxEmail": "/cofaxutil/aemail/*", 91 | "cofaxAdmin": "/admin/*", 92 | "fileServlet": "/static/*", 93 | "cofaxTools": "/tools/*" 94 | }, 95 | "taglib": { 96 | "taglib-uri": "cofax.tld", 97 | "taglib-location": "/WEB-INF/tlds/cofax.tld" 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /src/json/iwjson_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef JBL_INTERNAL_H 3 | #define JBL_INTERNAL_H 4 | 5 | /************************************************************************************************** 6 | * IOWOW library 7 | * 8 | * MIT License 9 | * 10 | * Copyright (c) 2012-2024 Softmotions Ltd 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in all 20 | * copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | *************************************************************************************************/ 30 | 31 | #include "iwlog.h" 32 | #include "iwjson.h" 33 | #include "iwbinn.h" 34 | #include "iwpool.h" 35 | #include "iwconv.h" 36 | 37 | #define JBL_MAX_NESTING_LEVEL 999 38 | 39 | struct jbl { 40 | binn bn; 41 | struct jbl_node *node; 42 | }; 43 | 44 | /** 45 | * @brief struct jbl* visitor context 46 | */ 47 | typedef struct jbl_vctx { 48 | binn *bn; /**< Root node from which started visitor */ 49 | void *op; /**< Arbitrary opaque data */ 50 | void *result; 51 | struct iwpool *pool; /**< Pool placeholder, initialization is responsibility of `JBL_VCTX` creator */ 52 | int pos; /**< Aux position, not actually used by visitor core */ 53 | bool terminate; 54 | bool found; /**< Used in _jbl_at() */ 55 | } JBL_VCTX; 56 | 57 | typedef jbn_visitor_cmd_t jbl_visitor_cmd_t; 58 | 59 | typedef struct jbl_patch_ext { 60 | const struct jbl_patch *p; 61 | struct jbl_ptr *path; 62 | struct jbl_ptr *from; 63 | } JBL_PATCHEXT; 64 | 65 | typedef struct jbl_ldr_ctx { 66 | struct iwpool *pool; 67 | struct jbl_node *root; 68 | } JBLDRCTX; 69 | 70 | iwrc jbl_from_buf_keep_onstack(struct jbl *jbl, void *buf, size_t bufsz); 71 | iwrc jbl_from_buf_keep_onstack2(struct jbl *jbl, void *buf); 72 | 73 | iwrc _jbl_write_double(double num, jbl_json_printer pt, void *op); 74 | iwrc _jbl_write_int(int64_t num, jbl_json_printer pt, void *op); 75 | iwrc _jbl_write_json_string(const char *str, int len, jbl_json_printer pt, void *op, jbl_print_flags_t pf); 76 | iwrc _jbl_node_from_binn(const binn *bn, struct jbl_node **node, bool clone_strings, struct iwpool *pool); 77 | iwrc _jbl_binn_from_node(binn *res, struct jbl_node *node); 78 | iwrc _jbl_from_node(struct jbl *jbl, struct jbl_node *node); 79 | bool _jbl_at(struct jbl *jbl, JBL_PTR jp, struct jbl *res); 80 | int _jbl_compare_nodes(struct jbl_node *n1, struct jbl_node *n2, iwrc *rcp); 81 | 82 | typedef jbl_visitor_cmd_t (*jbl_visitor)(int lvl, binn *bv, const char *key, int idx, struct jbl_vctx *vctx, iwrc *rc); 83 | iwrc _jbl_visit(binn_iter *iter, int lvl, struct jbl_vctx *vctx, jbl_visitor visitor); 84 | 85 | bool _jbl_is_eq_atomic_values(struct jbl *v1, struct jbl *v2); 86 | int _jbl_cmp_atomic_values(struct jbl *v1, struct jbl *v2); 87 | 88 | BOOL binn_read_next_pair2(int expected_type, binn_iter *iter, int *klidx, char **pkey, binn *value); 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /src/Autark: -------------------------------------------------------------------------------- 1 | check { 2 | test_qsort_r.sh 3 | test_pthread.sh 4 | test_symbol.sh { CLOCK_MONOTONIC time.h IW_HAVE_CLOCK_MONOTONIC } 5 | } 6 | 7 | set { 8 | LDFLAGS 9 | -lm 10 | if { defined { PTHREAD_LFLAG } 11 | ${PTHREAD_LFLAG} 12 | } 13 | if { defined { SYSTEM_ANDROID } 14 | -llog 15 | } 16 | if { eq { ${SYSTEM_ARCH} mips } 17 | if { library { LIB_ATOMIC atomic atomic.so.1 libatomic.so.1 } 18 | -l ${LIB_ATOMIC} 19 | } 20 | } 21 | ..${LDFLAGS} 22 | } 23 | 24 | set { 25 | LDFLAGS_TEST 26 | ${LIBIOWOW_A} 27 | ..@@{ ${PKGCONF} --libs --static cunit } 28 | ..${LDFLAGS} 29 | } 30 | 31 | set { 32 | CFLAGS_TESTS 33 | -DIW_STATIC 34 | ..@@{ ${PKGCONF} --cflags --static cunit } 35 | ..${CFLAGS} 36 | } 37 | 38 | 39 | option { IOWOW_PUBLIC_HEADERS_DESTINATION Installation path relative to INSTALL_PREFIX for iowow public header files. } 40 | if { !defined { IOWOW_PUBLIC_HEADERS_DESTINATION } 41 | set { 42 | IOWOW_PUBLIC_HEADERS_DESTINATION ^{ ${INSTALL_INCLUDE_DIR} / ${META_NAME} } 43 | } 44 | } 45 | 46 | set { 47 | SOURCES 48 | iowow.c 49 | } 50 | 51 | set { 52 | PUB_HDRS 53 | basedefs.h 54 | iowow.h 55 | } 56 | 57 | configure { 58 | iwcfg.h.in 59 | } 60 | 61 | configure { 62 | libiowow.pc.in 63 | } 64 | 65 | include { fs/Autark } 66 | include { json/Autark } 67 | include { kv/Autark } 68 | include { log/Autark } 69 | include { platform/Autark } 70 | include { rdb/Autark } 71 | include { re/Autark } 72 | include { utils/Autark } 73 | 74 | cc { 75 | ${SOURCES} 76 | ${CFLAGS} 77 | ${CC} 78 | consumes { 79 | iwcfg.h 80 | } 81 | } 82 | 83 | set { 84 | LIBIOWOW_A 85 | CC { libiowow.a } 86 | } 87 | 88 | run { 89 | exec { ${AR} rcs ${LIBIOWOW_A} ${CC_OBJS} } 90 | consumes { 91 | ${CC_OBJS} 92 | } 93 | produces { 94 | ${LIBIOWOW_A} 95 | } 96 | } 97 | 98 | install { ${INSTALL_LIB_DIR} ${LIBIOWOW_A} } 99 | 100 | if { ${IOWOW_BUILD_SHARED_LIBS} 101 | if {!defined {SYSTEM_DARWIN} 102 | set { 103 | LIBIOWOW_SO_BASE 104 | libiowow.so 105 | } 106 | set { 107 | LIBIOWOW_SO_BIN 108 | ^{${LIBIOWOW_SO_BASE} . ${META_VERSION}} 109 | } 110 | set { 111 | LIBIOWOW_SO_NAME 112 | ^{${LIBIOWOW_SO_BASE} . ${META_VERSION_MAJOR}} 113 | } 114 | run { 115 | exec { ${CC} -shared -o ${LIBIOWOW_SO_BIN} ${CC_OBJS} } 116 | if { ${STRIP_CMD} 117 | exec { ${STRIP_CMD} ${LIBIOWOW_SO_BIN} } 118 | } 119 | consumes { 120 | ${CC_OBJS} 121 | } 122 | produces { 123 | ${LIBIOWOW_SO_BIN} 124 | } 125 | } 126 | } else { 127 | set { 128 | LIBIOWOW_SO_BASE 129 | libiowow.dylib 130 | } 131 | set { 132 | LIBIOWOW_SO_BIN 133 | ^{libiowow. ${META_VERSION} .dylib} 134 | } 135 | set { 136 | LIBIOWOW_SO_NAME 137 | ^{libiowow. ${META_VERSION_MAJOR} .dylib} 138 | } 139 | run { 140 | exec { ${CC} -dynamiclib 141 | -install_name ^{@rpath/ ${LIBIOWOW_SO_BIN}} 142 | -compatibility_version ${META_VERSION_MAJOR} 143 | -current_version ${META_VERSION} 144 | -o ${LIBEJDB_SO_BIN} 145 | ${CC_OBJS} 146 | } 147 | if { ${STRIP_CMD} 148 | exec { ${STRIP_CMD} ${LIBIOWOW_SO_BIN} } 149 | } 150 | consumes { 151 | ${CC_OBJS} 152 | } 153 | produces { 154 | ${LIBIOWOW_SO_BIN} 155 | } 156 | } 157 | } 158 | run { 159 | exec { ln -sf ${LIBIOWOW_SO_BIN} ${LIBIOWOW_SO_NAME} } 160 | exec { ln -sf ${LIBIOWOW_SO_BIN} ${LIBIOWOW_SO_BASE} } 161 | consumes { 162 | ${LIBIOWOW_SO_BIN} 163 | } 164 | } 165 | install { ${INSTALL_LIB_DIR} ${LIBIOWOW_SO_BIN} ${LIBIOWOW_SO_NAME} ${LIBIOWOW_SO_BASE} } 166 | } 167 | 168 | install { ${INSTALL_PKGCONFIG_DIR} libiowow.pc } 169 | install { ${IOWOW_PUBLIC_HEADERS_DESTINATION} ${PUB_HDRS} } -------------------------------------------------------------------------------- /src/re/cregex.h: -------------------------------------------------------------------------------- 1 | #ifndef CREGEX_H 2 | #define CREGEX_H 3 | 4 | typedef enum { 5 | REGEX_NODE_TYPE_EPSILON = 0, 6 | /* Characters */ 7 | REGEX_NODE_TYPE_CHARACTER, 8 | REGEX_NODE_TYPE_ANY_CHARACTER, 9 | REGEX_NODE_TYPE_CHARACTER_CLASS, 10 | REGEX_NODE_TYPE_CHARACTER_CLASS_NEGATED, 11 | /* Composites */ 12 | REGEX_NODE_TYPE_CONCATENATION, 13 | REGEX_NODE_TYPE_ALTERNATION, 14 | /* Quantifiers */ 15 | REGEX_NODE_TYPE_QUANTIFIER, 16 | /* Anchors */ 17 | REGEX_NODE_TYPE_ANCHOR_BEGIN, 18 | REGEX_NODE_TYPE_ANCHOR_END, 19 | /* Captures */ 20 | REGEX_NODE_TYPE_CAPTURE, 21 | } cregex_node_type; 22 | 23 | typedef struct cregex_node { 24 | cregex_node_type type; 25 | union { 26 | /* REGEX_NODE_TYPE_CHARACTER */ 27 | struct { 28 | int ch; 29 | }; 30 | /* REGEX_NODE_TYPE_CHARACTER_CLASS, 31 | * REGEX_NODE_TYPE_CHARACTER_CLASS_NEGATED 32 | */ 33 | struct { 34 | const char *from, *to; 35 | }; 36 | /* REGEX_NODE_TYPE_QUANTIFIER */ 37 | struct { 38 | int nmin, nmax, greedy; 39 | struct cregex_node *quantified; 40 | }; 41 | /* REGEX_NODE_TYPE_CONCATENATION, 42 | * REGEX_NODE_TYPE_ALTERNATION 43 | */ 44 | struct { 45 | struct cregex_node *left, *right; 46 | }; 47 | /* REGEX_NODE_TYPE_CAPTURE */ 48 | struct { 49 | struct cregex_node *captured; 50 | }; 51 | }; 52 | } cregex_node_t; 53 | 54 | typedef enum { 55 | REGEX_PROGRAM_OPCODE_MATCH = 0, 56 | /* Characters */ 57 | REGEX_PROGRAM_OPCODE_CHARACTER, 58 | REGEX_PROGRAM_OPCODE_ANY_CHARACTER, 59 | REGEX_PROGRAM_OPCODE_CHARACTER_CLASS, 60 | REGEX_PROGRAM_OPCODE_CHARACTER_CLASS_NEGATED, 61 | /* Control-flow */ 62 | REGEX_PROGRAM_OPCODE_SPLIT, 63 | REGEX_PROGRAM_OPCODE_JUMP, 64 | /* Assertions */ 65 | REGEX_PROGRAM_OPCODE_ASSERT_BEGIN, 66 | REGEX_PROGRAM_OPCODE_ASSERT_END, 67 | /* Saving */ 68 | REGEX_PROGRAM_OPCODE_SAVE, 69 | } cregex_program_opcode_t; 70 | 71 | #include 72 | 73 | typedef char cregex_char_class[(UCHAR_MAX + CHAR_BIT - 1) / CHAR_BIT]; 74 | 75 | static inline int cregex_char_class_contains( 76 | const cregex_char_class klass, 77 | int ch) { 78 | return klass[ch / CHAR_BIT] & (1 << ch % CHAR_BIT); 79 | } 80 | 81 | static inline int cregex_char_class_add(cregex_char_class klass, int ch) { 82 | klass[ch / CHAR_BIT] |= 1 << (ch % CHAR_BIT); 83 | return ch; 84 | } 85 | 86 | typedef struct cregex_program_instr { 87 | cregex_program_opcode_t opcode; 88 | union { 89 | /* REGEX_PROGRAM_OPCODE_CHARACTER */ 90 | struct { 91 | int ch; 92 | }; 93 | /* REGEX_PROGRAM_OPCODE_CHARACTER_CLASS, 94 | * REGEX_PROGRAM_OPCODE_CHARACTER_CLASS_NEGATED 95 | */ 96 | struct { 97 | cregex_char_class klass; 98 | }; 99 | /* REGEX_PROGRAM_OPCODE_SPLIT */ 100 | struct { 101 | struct cregex_program_instr *first, *second; 102 | }; 103 | /* REGEX_PROGRAM_OPCODE_JUMP */ 104 | struct { 105 | struct cregex_program_instr *target; 106 | }; 107 | /* REGEX_PROGRAM_OPCODE_SAVE */ 108 | struct { 109 | int save; 110 | }; 111 | }; 112 | } cregex_program_instr_t; 113 | 114 | typedef struct { 115 | int ninstructions; 116 | cregex_program_instr_t instructions[]; 117 | } cregex_program_t; 118 | 119 | /* Run program on string */ 120 | int cregex_program_run( 121 | const cregex_program_t *program, 122 | const char *string, 123 | const char **matches, 124 | int nmatches); 125 | 126 | /* Compile a parsed pattern */ 127 | cregex_program_t* cregex_compile_node(const cregex_node_t *root); 128 | 129 | /* Free a compiled program */ 130 | void cregex_compile_free(cregex_program_t *program); 131 | 132 | /* Parse a pattern */ 133 | cregex_node_t* cregex_parse(const char *pattern); 134 | 135 | /* Free a parsed pattern */ 136 | void cregex_parse_free(cregex_node_t *root); 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /src/json/tests/data/004.json: -------------------------------------------------------------------------------- 1 | { 2 | "web-app": { 3 | "servlet": [ 4 | { 5 | "servlet-name": "cofaxCDS", 6 | "servlet-class": "org.cofax.cds.CDSServlet", 7 | "init-param": { 8 | "configGlossary:installationAt": "Philadelphia, PA", 9 | "configGlossary:adminEmail": "ksm@pobox.com", 10 | "configGlossary:poweredBy": "Cofax", 11 | "configGlossary:poweredByIcon": "/images/cofax.gif", 12 | "configGlossary:staticPath": "/content/static", 13 | "templateProcessorClass": "org.cofax.WysiwygTemplate", 14 | "templateLoaderClass": "org.cofax.FilesTemplateLoader", 15 | "templatePath": "templates", 16 | "templateOverridePath": "", 17 | "defaultListTemplate": "listTemplate.htm", 18 | "defaultFileTemplate": "articleTemplate.htm", 19 | "useJSP": false, 20 | "jspListTemplate": "listTemplate.jsp", 21 | "jspFileTemplate": "articleTemplate.jsp", 22 | "cachePackageTagsTrack": 200, 23 | "cachePackageTagsStore": 200, 24 | "cachePackageTagsRefresh": 60, 25 | "cacheTemplatesTrack": 100, 26 | "cacheTemplatesStore": 50, 27 | "cacheTemplatesRefresh": 15, 28 | "cachePagesTrack": 200, 29 | "cachePagesStore": 100, 30 | "cachePagesRefresh": 10, 31 | "cachePagesDirtyRead": 10, 32 | "searchEngineListTemplate": "forSearchEnginesList.htm", 33 | "searchEngineFileTemplate": "forSearchEngines.htm", 34 | "searchEngineRobotsDb": "WEB-INF/robots.db", 35 | "useDataStore": true, 36 | "dataStoreClass": "org.cofax.SqlDataStore", 37 | "redirectionClass": "org.cofax.SqlRedirection", 38 | "dataStoreName": "cofax", 39 | "dataStoreDriver": "com.microsoft.jdbc.sqlserver.SQLServerDriver", 40 | "dataStoreUrl": "jdbc:microsoft:sqlserver://LOCALHOST:1433;DatabaseName=goon", 41 | "dataStoreUser": "sa", 42 | "dataStorePassword": "dataStoreTestQuery", 43 | "dataStoreTestQuery": "SET NOCOUNT ON;select test='test';", 44 | "dataStoreLogFile": "/usr/local/tomcat/logs/datastore.log", 45 | "dataStoreInitConns": 10, 46 | "dataStoreMaxConns": 100, 47 | "dataStoreConnUsageLimit": 100, 48 | "dataStoreLogLevel": "debug", 49 | "maxUrlLength": 500 50 | } 51 | }, 52 | { 53 | "servlet-name": "cofaxEmail", 54 | "servlet-class": "org.cofax.cds.EmailServlet", 55 | "init-param": { 56 | "mailHost": "mail1", 57 | "mailHostOverride": "mail2" 58 | } 59 | }, 60 | { 61 | "servlet-name": "cofaxAdmin", 62 | "servlet-class": "org.cofax.cds.AdminServlet" 63 | }, 64 | { 65 | "servlet-name": "fileServlet", 66 | "servlet-class": "org.cofax.cds.FileServlet" 67 | }, 68 | { 69 | "servlet-name": "cofaxTools", 70 | "servlet-class": "org.cofax.cms.CofaxToolsServlet", 71 | "init-param": { 72 | "templatePath": "toolstemplates/", 73 | "log": 1, 74 | "logLocation": "/usr/local/tomcat/logs/CofaxTools.log", 75 | "logMaxSize": "", 76 | "dataLog": 1, 77 | "dataLogLocation": "/usr/local/tomcat/logs/dataLog.log", 78 | "dataLogMaxSize": "", 79 | "removePageCache": "/content/admin/remove?cache=pages&id=", 80 | "removeTemplateCache": "/content/admin/remove?cache=templates&id=", 81 | "fileTransferFolder": "/usr/local/tomcat/webapps/content/fileTransferFolder", 82 | "lookInContext": 1, 83 | "adminGroupID": 4, 84 | "betaServer": true 85 | } 86 | } 87 | ], 88 | "servlet-mapping": { 89 | "cofaxCDS": "/", 90 | "cofaxEmail": "/cofaxutil/aemail/*", 91 | "cofaxAdmin": "/admin/*", 92 | "fileServlet": "/static/*", 93 | "cofaxTools": "/tools/*" 94 | }, 95 | "taglib": { 96 | "taglib-uri": "cofax.tld", 97 | "taglib-location": "/WEB-INF/tlds/cofax.tld" 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /src/kv/tests/iwkv_test10.c: -------------------------------------------------------------------------------- 1 | #include "iwkv.h" 2 | #include "iwlog.h" 3 | #include "iwutils.h" 4 | #include "iwkv_tests.h" 5 | #include "iwkv_internal.h" 6 | #include "iwth.h" 7 | 8 | #define KBUFSZ 1024 9 | #define VBUFSZ 1024 10 | static char kbuf[KBUFSZ]; 11 | static char vbuf[VBUFSZ]; 12 | 13 | int init_suite(void) { 14 | return iwkv_init(); 15 | } 16 | 17 | int clean_suite(void) { 18 | return 0; 19 | } 20 | 21 | static void iwkv_test10_1_impl(int fmt_version) { 22 | IWKV iwkv; 23 | IWKV_OPTS opts = { 24 | .path = fmt_version > 1 ? "iwkv_test10_1_v2.db" : "iwkv_test10_1_v1.db", 25 | .oflags = IWKV_TRUNC, 26 | .wal = { 27 | .enabled = true 28 | } 29 | }; 30 | 31 | iwrc rc = iwkv_open(&opts, &iwkv); 32 | CU_ASSERT_EQUAL_FATAL(rc, 0); 33 | 34 | for (int i = 0; i < 1024; ++i) { 35 | IWDB db; 36 | rc = iwkv_db(iwkv, i, 0, &db); 37 | CU_ASSERT_EQUAL_FATAL(rc, 0); 38 | for (int j = 0; j < 1024; ++j) { 39 | IWKV_val key, val; 40 | snprintf(kbuf, KBUFSZ, "%d", j); 41 | snprintf(vbuf, VBUFSZ, "%03dval", j); 42 | key.data = kbuf; 43 | key.size = strlen(key.data); 44 | val.data = vbuf; 45 | val.size = strlen(val.data); 46 | rc = iwkv_put(db, &key, &val, 0); 47 | CU_ASSERT_EQUAL_FATAL(rc, 0); 48 | } 49 | } 50 | 51 | for (int i = 0; i < 1024; ++i) { 52 | if ((i % 2)) { 53 | IWDB db; 54 | rc = iwkv_db(iwkv, i, 0, &db); 55 | CU_ASSERT_EQUAL_FATAL(rc, 0); 56 | rc = iwkv_db_destroy(&db); 57 | CU_ASSERT_EQUAL_FATAL(rc, 0); 58 | } 59 | } 60 | 61 | rc = iwkv_close(&iwkv); 62 | CU_ASSERT_EQUAL_FATAL(rc, 0); 63 | 64 | opts.oflags = 0; 65 | rc = iwkv_open(&opts, &iwkv); 66 | CU_ASSERT_EQUAL_FATAL(rc, 0); 67 | 68 | for (int i = 0; i < 1024; ++i) { 69 | if (!(i % 2)) { 70 | IWDB db; 71 | rc = iwkv_db(iwkv, i, 0, &db); 72 | CU_ASSERT_EQUAL_FATAL(rc, 0); 73 | for (int j = 0; j < 1024; ++j) { 74 | IWKV_val key, val; 75 | int cret = 0; 76 | snprintf(kbuf, KBUFSZ, "%d", j); 77 | snprintf(vbuf, VBUFSZ, "%03dval", j); 78 | key.data = kbuf; 79 | key.size = strlen(key.data); 80 | int vlen = strlen(vbuf); 81 | rc = iwkv_get(db, &key, &val); 82 | CU_ASSERT_EQUAL_FATAL(rc, 0); 83 | IW_CMP(cret, vbuf, vlen, val.data, val.size); 84 | CU_ASSERT_EQUAL_FATAL(cret, 0); 85 | iwkv_val_dispose(&val); 86 | } 87 | } 88 | } 89 | 90 | for (int i = 0; i < 1024; ++i) { 91 | if (!(i % 2)) { 92 | IWDB db; 93 | rc = iwkv_db(iwkv, i, 0, &db); 94 | CU_ASSERT_EQUAL_FATAL(rc, 0); 95 | rc = iwkv_db_destroy(&db); 96 | CU_ASSERT_EQUAL_FATAL(rc, 0); 97 | } 98 | } 99 | 100 | rc = iwkv_close(&iwkv); 101 | CU_ASSERT_EQUAL_FATAL(rc, 0); 102 | 103 | // IWP_FILE_STAT fs; 104 | // rc = iwp_fstat(opts.path, &fs); 105 | // CU_ASSERT_EQUAL_FATAL(rc, 0); 106 | // CU_ASSERT_TRUE(fs.size < 1024 * 1024); 107 | } 108 | 109 | static void iwkv_test10_1_v1(void) { 110 | iwkv_test10_1_impl(1); 111 | } 112 | 113 | static void iwkv_test10_1_v2(void) { 114 | iwkv_test10_1_impl(2); 115 | } 116 | 117 | int main(void) { 118 | CU_pSuite pSuite = NULL; 119 | 120 | /* Initialize the CUnit test registry */ 121 | if (CUE_SUCCESS != CU_initialize_registry()) { 122 | return CU_get_error(); 123 | } 124 | 125 | /* Add a suite to the registry */ 126 | pSuite = CU_add_suite("iwkv_test8", init_suite, clean_suite); 127 | 128 | if (NULL == pSuite) { 129 | CU_cleanup_registry(); 130 | return CU_get_error(); 131 | } 132 | 133 | /* Add the tests to the suite */ 134 | if ( (NULL == CU_add_test(pSuite, "iwkv_test10_1_v1", iwkv_test10_1_v1)) 135 | || (NULL == CU_add_test(pSuite, "iwkv_test10_1_v2", iwkv_test10_1_v2))) { 136 | CU_cleanup_registry(); 137 | return CU_get_error(); 138 | } 139 | 140 | /* Run all tests using the CUnit Basic interface */ 141 | CU_basic_set_mode(CU_BRM_VERBOSE); 142 | CU_basic_run_tests(); 143 | int ret = CU_get_error() || CU_get_number_of_failures(); 144 | CU_cleanup_registry(); 145 | return ret; 146 | } 147 | -------------------------------------------------------------------------------- /src/utils/iwbits.h: -------------------------------------------------------------------------------- 1 | #ifndef IWBITS_H 2 | #define IWBITS_H 3 | 4 | // 5 | /************************************************************************************************** 6 | * IOWOW library 7 | * 8 | * MIT License 9 | * 10 | * Copyright (c) 2012-2024 Softmotions Ltd 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in all 20 | * copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | *************************************************************************************************/ 30 | 31 | 32 | /** @file 33 | * @brief Various bit manipulation utility methods. 34 | */ 35 | 36 | #include "basedefs.h" 37 | #include 38 | 39 | IW_EXTERN_C_START; 40 | 41 | #if defined(__GNUC__) || defined(__clang__) 42 | #pragma GCC diagnostic push 43 | #pragma GCC diagnostic ignored "-Wlong-long" 44 | #endif 45 | 46 | /** 47 | * @brief Find the first set bit number. Undefined if @a x is zero. 48 | */ 49 | IW_INLINE uint8_t iwbits_find_first_sbit64(uint64_t x) { 50 | //return __builtin_ffsll(x) - 1; 51 | uint8_t ret = 0; 52 | if ((x & 0xffffffffU) == 0) { 53 | ret += 32; 54 | x >>= 32; 55 | } 56 | if ((x & 0xffffU) == 0) { 57 | ret += 16; 58 | x >>= 16; 59 | } 60 | if ((x & 0xffU) == 0) { 61 | ret += 8; 62 | x >>= 8; 63 | } 64 | if ((x & 0xfU) == 0) { 65 | ret += 4; 66 | x >>= 4; 67 | } 68 | if ((x & 0x3U) == 0) { 69 | ret += 2; 70 | x >>= 2; 71 | } 72 | if ((x & 0x1U) == 0) { 73 | ret += 1; 74 | } 75 | return ret; 76 | } 77 | 78 | /** 79 | * @brief Find the last set bit number. Undefined if @a x is zero. 80 | */ 81 | IW_INLINE uint8_t iwbits_find_last_sbit64(uint64_t x) { 82 | //return 63 - __builtin_clzll(x); 83 | uint8_t num = 63; 84 | if ((x & 0xffffffff00000000ULL) == 0) { 85 | num -= 32; 86 | x <<= 32; 87 | } 88 | if ((x & 0xffff000000000000ULL) == 0) { 89 | num -= 16; 90 | x <<= 16; 91 | } 92 | if ((x & 0xff00000000000000ULL) == 0) { 93 | num -= 8; 94 | x <<= 8; 95 | } 96 | if ((x & 0xf000000000000000ULL) == 0) { 97 | num -= 4; 98 | x <<= 4; 99 | } 100 | if ((x & 0xc000000000000000ULL) == 0) { 101 | num -= 2; 102 | x <<= 2; 103 | } 104 | if ((x & 0x8000000000000000ULL) == 0) { 105 | num -= 1; 106 | } 107 | return num; 108 | } 109 | 110 | /** 111 | * @brief Reverese bits in a given @a x 112 | * Thanks to: http://www.hackersdelight.org/hdcodetxt/reverse.c.txt 113 | */ 114 | IW_INLINE uint64_t iwbits_reverse_64(uint64_t x) { 115 | uint64_t t; 116 | x = (x << 32) | (x >> 32); /* Swap register halves. */ 117 | x = (x & 0x0001ffff0001ffffLL) << 15 /* Rotate left */ 118 | | (x & 0xfffe0000fffe0000LL) >> 17; /* 15. */ 119 | t = (x ^ (x >> 10)) & 0x003f801f003f801fLL; 120 | x = (t | (t << 10)) ^ x; 121 | t = (x ^ (x >> 4)) & 0x0e0384210e038421LL; 122 | x = (t | (t << 4)) ^ x; 123 | t = (x ^ (x >> 2)) & 0x2248884222488842LL; 124 | x = (t | (t << 2)) ^ x; 125 | return x; 126 | } 127 | 128 | #if defined(__GNUC__) || defined(__clang__) 129 | #pragma GCC diagnostic pop 130 | #endif 131 | 132 | IW_EXTERN_C_END; 133 | #endif 134 | -------------------------------------------------------------------------------- /src/platform/win32/mman/mman.c: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/witwall/mman-win32 2 | 3 | #include "mman.h" 4 | #include "log/iwlog.h" 5 | #include 6 | #include 7 | 8 | #ifndef FILE_MAP_EXECUTE 9 | #define FILE_MAP_EXECUTE 0x0020 10 | #endif /* FILE_MAP_EXECUTE */ 11 | 12 | static int __map_mman_error(const DWORD err, const int deferr) { 13 | if (!err) { 14 | return 0; 15 | } 16 | iwrc rc = IW_ERROR_FAIL; 17 | rc = iwrc_set_werror(rc, err); 18 | iwlog_ecode_error3(rc); 19 | return deferr; 20 | } 21 | 22 | static DWORD __map_mmap_prot_page(const int prot, const int flags) { 23 | DWORD protect = 0; 24 | if (prot == PROT_NONE) { 25 | return protect; 26 | } 27 | if ((prot & PROT_EXEC)) { 28 | protect = ((prot & PROT_WRITE)) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ; 29 | } else { 30 | protect = ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE)) ? PAGE_READWRITE : PAGE_READONLY; 31 | } 32 | return protect; 33 | } 34 | 35 | static DWORD __map_mmap_prot_file(const int prot, const int flags) { 36 | DWORD desiredAccess = 0; 37 | if (prot == PROT_NONE) { 38 | return desiredAccess; 39 | } 40 | if ((prot & PROT_WRITE)) { 41 | if (flags & MAP_PRIVATE) { 42 | desiredAccess |= FILE_MAP_COPY; 43 | } else { 44 | desiredAccess |= FILE_MAP_WRITE; 45 | } 46 | } else if ((prot & PROT_READ)) { 47 | desiredAccess |= FILE_MAP_READ; 48 | } 49 | if ((prot & PROT_EXEC)) { 50 | desiredAccess |= FILE_MAP_EXECUTE; 51 | } 52 | return desiredAccess; 53 | } 54 | 55 | void* mmap(void *addr, size_t len, int prot, int flags, HANDLE fh, OffsetType off) { 56 | HANDLE fm; 57 | void *map = MAP_FAILED; 58 | 59 | #ifdef _MSC_VER 60 | #pragma warning(push) 61 | #pragma warning(disable: 4293) 62 | #endif 63 | 64 | const DWORD dwFileOffsetLow = (sizeof(OffsetType) <= sizeof(DWORD)) 65 | ? (DWORD) off : (DWORD) (off & 0xFFFFFFFFL); 66 | const DWORD dwFileOffsetHigh = (sizeof(OffsetType) <= sizeof(DWORD)) 67 | ? (DWORD) 0 : (DWORD) ((off >> 32) & 0xFFFFFFFFL); 68 | const DWORD protect = __map_mmap_prot_page(prot, flags); 69 | const DWORD desiredAccess = __map_mmap_prot_file(prot, flags); 70 | const OffsetType maxSize = off + (OffsetType) len; 71 | const DWORD dwMaxSizeLow = (sizeof(OffsetType) <= sizeof(DWORD)) 72 | ? (DWORD) maxSize : (DWORD) (maxSize & 0xFFFFFFFFL); 73 | const DWORD dwMaxSizeHigh = (sizeof(OffsetType) <= sizeof(DWORD)) 74 | ? (DWORD) 0 : (DWORD) ((maxSize >> 32) & 0xFFFFFFFFL); 75 | 76 | #ifdef _MSC_VER 77 | #pragma warning(pop) 78 | #endif 79 | 80 | errno = 0; 81 | if (((flags & MAP_FIXED) != 0) || (prot == PROT_EXEC)) { 82 | errno = EINVAL; 83 | return MAP_FAILED; 84 | } 85 | if (!(flags & MAP_ANONYMOUS) && (fh == INVALID_HANDLE_VALUE)) { 86 | errno = EBADF; 87 | return MAP_FAILED; 88 | } 89 | fm = CreateFileMapping(fh, NULL, protect, dwMaxSizeHigh, dwMaxSizeLow, NULL); 90 | if (fm == NULL) { 91 | errno = __map_mman_error(GetLastError(), EPERM); 92 | return MAP_FAILED; 93 | } 94 | map = MapViewOfFile(fm, desiredAccess, dwFileOffsetHigh, dwFileOffsetLow, len); 95 | CloseHandle(fm); 96 | if (map == NULL) { 97 | errno = __map_mman_error(GetLastError(), EPERM); 98 | return MAP_FAILED; 99 | } 100 | return map; 101 | } 102 | 103 | int munmap(void *addr, size_t len) { 104 | if (UnmapViewOfFile(addr)) { 105 | return 0; 106 | } 107 | errno = __map_mman_error(GetLastError(), EPERM); 108 | return -1; 109 | } 110 | 111 | int msync(void *addr, size_t len, int flags) { 112 | if (FlushViewOfFile(addr, len)) { 113 | return 0; 114 | } 115 | errno = __map_mman_error(GetLastError(), EPERM); 116 | return -1; 117 | } 118 | 119 | int mlock(const void *addr, size_t len) { 120 | if (VirtualLock((LPVOID) addr, len)) { 121 | return 0; 122 | } 123 | errno = __map_mman_error(GetLastError(), EPERM); 124 | return -1; 125 | } 126 | 127 | int munlock(const void *addr, size_t len) { 128 | if (VirtualUnlock((LPVOID) addr, len)) { 129 | return 0; 130 | } 131 | errno = __map_mman_error(GetLastError(), EPERM); 132 | return -1; 133 | } 134 | -------------------------------------------------------------------------------- /src/utils/iwstw.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWSTW_H 3 | #define IWSTW_H 4 | 5 | /************************************************************************************************** 6 | * Single thread worker. 7 | * 8 | * IOWOW library 9 | * 10 | * MIT License 11 | * 12 | * Copyright (c) 2012-2024 Softmotions Ltd 13 | * 14 | * Permission is hereby granted, free of charge, to any person obtaining a copy 15 | * of this software and associated documentation files (the "Software"), to deal 16 | * in the Software without restriction, including without limitation the rights 17 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | * copies of the Software, and to permit persons to whom the Software is 19 | * furnished to do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be included in all 22 | * copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | * SOFTWARE. 31 | *************************************************************************************************/ 32 | 33 | #include "basedefs.h" 34 | 35 | IW_EXTERN_C_START; 36 | 37 | struct iwstw; 38 | typedef struct iwstw*IWSTW; 39 | 40 | /** 41 | * @brief Task to execute 42 | */ 43 | typedef void (*iwstw_task_f)(void *arg); 44 | 45 | typedef void (*iwstw_on_task_discard_f)(iwstw_task_f task, void *arg); 46 | 47 | /** 48 | * @brief Starts a single thread worker. 49 | * Function will block until start of worker thread. 50 | * 51 | * @param queue_limit Max length of pending tasks queue. Unlimited if zero. 52 | * @param queue_blocking If true iwstw_schedule will block when queue reached its limit. 53 | * @param[out] stwp_out Pointer to worker handler to be initialized. 54 | */ 55 | IW_EXPORT iwrc iwstw_start(const char *thread_name, int queue_limit, bool queue_blocking, IWSTW *out_stw); 56 | 57 | /** 58 | * @brief Shutdowns worker and disposes all resources. 59 | * Function will wait until current task completes or 60 | * wait for all enqueued tasks if `wait_for_all` is set to `true`. 61 | * No new tasks will be accepted during `iwstw_shutdown` call. 62 | * 63 | * @param stw Pointer to worker handler which should be destroyed. 64 | * @param wait_for_all If true worker will wait for completion of all enqueued tasks before shutdown. 65 | */ 66 | IW_EXPORT iwrc iwstw_shutdown(IWSTW *stwp, bool wait_for_all); 67 | 68 | /** 69 | * @brief Schedule task for execution. 70 | * Task will be added to pending tasks queue. 71 | * 72 | * @note If tasks queue is reached its length limit 73 | * current thread will be blocked if `queue_blocking` is true 74 | * or `IW_ERROR_OVERFLOW` will be returned. 75 | * @note If worker is in process of stopping `IW_ERROR_INVALID_STATE` will be returned. 76 | */ 77 | IW_EXPORT iwrc iwstw_schedule(IWSTW stw, iwstw_task_f task, void *task_arg); 78 | 79 | /** 80 | * @brief Schedule task for execution discading all pending tasks on queue. 81 | * @note If worker is in process of stopping `IW_ERROR_INVALID_STATE` will be returned. 82 | */ 83 | IW_EXPORT iwrc iwstw_schedule_only(IWSTW stw, iwstw_task_f task, void *task_arg); 84 | 85 | /** 86 | * @brief Schedule task only if task queue is empty. 87 | */ 88 | IW_EXPORT iwrc iwstw_schedule_empty_only(IWSTW stw, iwstw_task_f task, void *task_arg, bool *out_scheduled); 89 | 90 | /** 91 | * @brief Set on task discard callback function. 92 | * Called when pending task removed from queue and will not be executed. 93 | */ 94 | IW_EXPORT void iwstw_set_on_task_discard(IWSTW stw, iwstw_on_task_discard_f on_task_discard); 95 | 96 | /** 97 | * @brief Returns size of tasks queue. 98 | */ 99 | IW_EXPORT int iwstw_queue_size(IWSTW stw); 100 | 101 | IW_EXTERN_C_END; 102 | #endif 103 | -------------------------------------------------------------------------------- /src/utils/iwxstr.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWXSTR_H 3 | #define IWXSTR_H 4 | 5 | /************************************************************************************************** 6 | * IOWOW library 7 | * 8 | * MIT License 9 | * 10 | * Copyright (c) 2012-2024 Softmotions Ltd 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in all 20 | * copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | *************************************************************************************************/ 30 | 31 | #include "basedefs.h" 32 | 33 | #include 34 | 35 | IW_EXTERN_C_START; 36 | 37 | typedef struct iwxstr IWXSTR; 38 | 39 | IW_EXPORT IW_ALLOC struct iwxstr* iwxstr_create_empty(void); 40 | 41 | IW_EXPORT struct iwxstr* iwxstr_create(size_t siz); 42 | 43 | #define iwxstr_new iwxstr_create_empty 44 | #define iwxstr_new2 iwxstr_create 45 | 46 | IW_EXPORT struct iwxstr*iwxstr_new_printf(const char *format, ...) __attribute__((format(__printf__, 1, 2))); 47 | 48 | IW_EXPORT struct iwxstr* iwxstr_clone(const struct iwxstr *xstr); 49 | 50 | #define iwxstr_new_clone iwxstr_clone 51 | 52 | IW_EXPORT IW_ALLOC char* iwxstr_printf_alloc(const char *format, ...) __attribute__((format(__printf__, 1, 2))); 53 | 54 | IW_EXPORT struct iwxstr* iwxstr_wrap(char *buf, size_t size, size_t asize); 55 | 56 | IW_EXPORT void iwxstr_destroy(struct iwxstr *xstr); 57 | 58 | IW_EXPORT IW_ALLOC char* iwxstr_destroy_keep_ptr(struct iwxstr *xstr); 59 | 60 | IW_EXPORT iwrc iwxstr_cat(struct iwxstr *xstr, const void *buf, size_t size); 61 | 62 | IW_EXPORT iwrc iwxstr_cat2(struct iwxstr *xstr, const char *buf); 63 | 64 | IW_EXPORT iwrc iwxstr_unshift(struct iwxstr *xstr, const void *buf, size_t size); 65 | 66 | IW_EXPORT iwrc iwxstr_printf_va(struct iwxstr *xstr, const char *format, va_list va); 67 | 68 | IW_EXPORT iwrc iwxstr_printf(struct iwxstr *xstr, const char *format, ...) __attribute__((format(__printf__, 2, 3))); 69 | 70 | IW_EXPORT void iwxstr_shift(struct iwxstr *xstr, size_t shift_size); 71 | 72 | IW_EXPORT void iwxstr_pop(struct iwxstr *xstr, size_t pop_size); 73 | 74 | IW_EXPORT iwrc iwxstr_insert(struct iwxstr *xstr, size_t pos, const void *buf, size_t size); 75 | 76 | IW_EXPORT iwrc iwxstr_insert_vaprintf(struct iwxstr *xstr, size_t pos, const char *format, va_list va); 77 | 78 | IW_EXPORT iwrc iwxstr_insert_printf( 79 | struct iwxstr *xstr, size_t pos, const char *format, 80 | ...) __attribute__((format(__printf__, 3, 4))); 81 | 82 | IW_EXPORT char* iwxstr_ptr(struct iwxstr *xstr); 83 | 84 | IW_EXPORT iwrc iwxstr_set_size(struct iwxstr *xstr, size_t size); 85 | 86 | /** 87 | * Returns actual size of data stored in @a xstr buffer. 88 | */ 89 | IW_EXPORT size_t iwxstr_size(struct iwxstr *xstr); 90 | 91 | #define iwxstr_len(x) iwxstr_size(x) 92 | 93 | /** 94 | * @brief Returns allocated size of @a xstr buffer. 95 | */ 96 | IW_EXPORT size_t iwxstr_asize(struct iwxstr *xstr); 97 | 98 | IW_EXPORT void iwxstr_user_data_set(struct iwxstr *xstr, void *data, void (*free_fn)(void*)); 99 | 100 | IW_EXPORT void* iwxstr_user_data_get(struct iwxstr *xstr); 101 | 102 | IW_EXPORT void* iwxstr_user_data_detach(struct iwxstr *xstr); 103 | 104 | IW_EXPORT void iwxstr_clear(struct iwxstr *xstr); 105 | 106 | IW_EXTERN_C_END; 107 | #endif 108 | -------------------------------------------------------------------------------- /src/json/iwjsreg.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWJSREG_H 3 | #define IWJSREG_H 4 | 5 | /************************************************************************************************** 6 | * IWOWOW A simple JSON registry stored in single file supporting atomic updates. 7 | * 8 | * MIT License 9 | * 10 | * Copyright (c) 2012-2024 Softmotions Ltd 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to deal 14 | * in the Software without restriction, including without limitation the rights 15 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 | * copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in all 20 | * copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 28 | * SOFTWARE. 29 | *************************************************************************************************/ 30 | 31 | #include "basedefs.h" 32 | #include "iwjson.h" 33 | #include 34 | 35 | IW_EXTERN_C_START; 36 | 37 | #define IWJSREG_FORMAT_BINARY 0x01U 38 | #define IWJSREG_AUTOSYNC 0x02U 39 | #define IWJSREG_READONLY 0x04U 40 | 41 | struct iwjsreg; 42 | struct iwjsreg_spec { 43 | const char *path; 44 | pthread_rwlock_t *rwl; ///< Optional RWL provided for locking. 45 | iwrc (*wlock_fn)(void*); ///< Optional exclusive write lock function to set read/write registry lock. 46 | iwrc (*rlock_fn)(void*); ///< Optional shared read lock function to set the lock. 47 | iwrc (*unlock_fn)(void*); ///< Optional unlock function releasing the lock 48 | void *fn_data; ///< Arbitrary user data used in wlock_fn,rlock_fn,unlock_fn 49 | unsigned flags; 50 | }; 51 | 52 | IW_EXPORT iwrc iwjsreg_open(struct iwjsreg_spec *spec, struct iwjsreg **out); 53 | 54 | IW_EXPORT iwrc iwjsreg_close(struct iwjsreg**); 55 | 56 | IW_EXPORT iwrc iwjsreg_sync(struct iwjsreg*); 57 | 58 | IW_EXPORT iwrc iwjsreg_remove(struct iwjsreg*, const char *key); 59 | 60 | IW_EXPORT iwrc iwjsreg_set_str(struct iwjsreg*, const char *key, const char *value); 61 | 62 | IW_EXPORT iwrc iwjsreg_set_i64(struct iwjsreg*, const char *key, int64_t value); 63 | 64 | IW_EXPORT iwrc iwjsreg_inc_i64(struct iwjsreg*, const char *key, int64_t inc, int64_t *out); 65 | 66 | IW_EXPORT iwrc iwjsreg_set_bool(struct iwjsreg *reg, const char *key, bool value); 67 | 68 | IW_EXPORT iwrc iwjsreg_get_str(struct iwjsreg*, const char *key, char **out); 69 | 70 | IW_EXPORT iwrc iwjsreg_get_i64(struct iwjsreg*, const char *key, int64_t *out); 71 | 72 | IW_EXPORT iwrc iwjsreg_get_bool(struct iwjsreg*, const char *key, bool *out); 73 | 74 | IW_EXPORT iwrc iwjsreg_copy(struct iwjsreg*, const char *path, struct iwpool *pool, struct jbl_node **out); 75 | 76 | IW_EXPORT iwrc iwjsreg_merge(struct iwjsreg*, const char *path, struct jbl_node *json); 77 | 78 | IW_EXPORT iwrc iwjsreg_merge_str(struct iwjsreg*, const char *path, const char *value, int len); 79 | 80 | IW_EXPORT iwrc iwjsreg_merge_i64(struct iwjsreg *reg, const char *path, int64_t value); 81 | 82 | IW_EXPORT iwrc iwjsreg_merge_f64(struct iwjsreg *reg, const char *path, double value); 83 | 84 | IW_EXPORT iwrc iwjsreg_merge_bool(struct iwjsreg *reg, const char *path, bool value); 85 | 86 | IW_EXPORT iwrc iwjsreg_merge_remove(struct iwjsreg *reg, const char *path); 87 | 88 | IW_EXPORT iwrc iwjsreg_replace(struct iwjsreg*, const char *path, struct jbl_node *json); 89 | 90 | IW_EXPORT iwrc iwjsreg_at_i64(struct iwjsreg*, const char *path, int64_t *out); 91 | 92 | IW_EXPORT iwrc iwjsreg_at_f64(struct iwjsreg*, const char *path, double *out); 93 | 94 | IW_EXPORT iwrc iwjsreg_at_bool(struct iwjsreg*, const char *path, bool *out); 95 | 96 | IW_EXPORT iwrc iwjsreg_at_str(struct iwjsreg*, const char *path, char **out); 97 | 98 | IW_EXTERN_C_END; 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /src/utils/iwhmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWHMAP_H 3 | #define IWHMAP_H 4 | 5 | /************************************************************************************************** 6 | * Hashmap implementation. 7 | * 8 | * IOWOW library 9 | * 10 | * MIT License 11 | * 12 | * Copyright (c) 2012-2024 Softmotions Ltd 13 | * 14 | * Permission is hereby granted, free of charge, to any person obtaining a copy 15 | * of this software and associated documentation files (the "Software"), to deal 16 | * in the Software without restriction, including without limitation the rights 17 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | * copies of the Software, and to permit persons to whom the Software is 19 | * furnished to do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be included in all 22 | * copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | * SOFTWARE. 31 | *************************************************************************************************/ 32 | 33 | #include "basedefs.h" 34 | IW_EXTERN_C_START; 35 | 36 | struct iwhmap; 37 | struct iwhmap_iter; 38 | typedef struct iwhmap IWHMAP; 39 | typedef struct iwhmap_iter IWHMAP_ITER; 40 | 41 | struct iwhmap_iter { 42 | struct iwhmap *hm; 43 | const void *key; 44 | const void *val; 45 | uint32_t bucket; 46 | int32_t entry; 47 | }; 48 | 49 | /** 50 | * @brief Key/Value free callback which uses standard `free()` deallocation. 51 | * 52 | * @param key Pointer to key or zero. 53 | * @param val Pointer to value of zero. 54 | */ 55 | IW_EXPORT void iwhmap_kv_free(void *key, void *val); 56 | 57 | IW_EXPORT struct iwhmap* iwhmap_create( 58 | int (*cmp_fn)(const void*, const void*), 59 | uint32_t (*hash_key_fn)(const void*), 60 | void (*kv_free_fn)(void*, void*)); 61 | 62 | IW_EXPORT struct iwhmap* iwhmap_create_u64(void (*kv_free_fn)(void*, void*)); 63 | 64 | IW_EXPORT struct iwhmap* iwhmap_create_u32(void (*kv_free_fn)(void*, void*)); 65 | 66 | IW_EXPORT struct iwhmap* iwhmap_create_str(void (*kv_free_fn)(void*, void*)); 67 | 68 | IW_EXPORT iwrc iwhmap_put(struct iwhmap *hm, void *key, void *val); 69 | 70 | IW_EXPORT iwrc iwhmap_put_u32(struct iwhmap *hm, uint32_t key, void *val); 71 | 72 | IW_EXPORT iwrc iwhmap_put_u64(struct iwhmap *hm, uint64_t key, void *val); 73 | 74 | /// Makes copy of key (strdup) then puts key value pair into map. 75 | /// @note Key memory expected to be released by `kv_free_fn` function given to iwhmap_create_xxx. 76 | IW_EXPORT iwrc iwhmap_put_str(struct iwhmap *hm, const char *key, void *val); 77 | 78 | IW_EXPORT iwrc iwhmap_rename(struct iwhmap *hm, const void *key_old, void *key_new); 79 | 80 | IW_EXPORT bool iwhmap_remove(struct iwhmap *hm, const void *key); 81 | 82 | IW_EXPORT bool iwhmap_remove_u64(struct iwhmap *hm, uint64_t key); 83 | 84 | IW_EXPORT bool iwhmap_remove_u32(struct iwhmap *hm, uint32_t key); 85 | 86 | IW_EXPORT void* iwhmap_get(struct iwhmap *hm, const void *key); 87 | 88 | IW_EXPORT void* iwhmap_get_u64(struct iwhmap *hm, uint64_t key); 89 | 90 | IW_EXPORT void* iwhmap_get_u32(struct iwhmap *hm, uint32_t key); 91 | 92 | IW_EXPORT uint32_t iwhmap_count(const struct iwhmap *hm); 93 | 94 | IW_EXPORT void iwhmap_clear(struct iwhmap *hm); 95 | 96 | IW_EXPORT void iwhmap_iter_init(struct iwhmap *hm, struct iwhmap_iter *iter); 97 | 98 | IW_EXPORT bool iwhmap_iter_next(struct iwhmap_iter *iter); 99 | 100 | IW_EXPORT void iwhmap_destroy(struct iwhmap *hm); 101 | 102 | typedef bool (*iwhmap_lru_eviction_needed)(struct iwhmap *hm, void *user_data); 103 | 104 | IW_EXPORT bool iwhmap_lru_eviction_max_count(struct iwhmap *hm, void *max_count_val); 105 | 106 | /// Init LRU eviction mode for given `hm` map. 107 | /// @param ev Returns `true` if needed to evict the next least recently used element. 108 | /// @param ev_user_data Arbitrary user data from `ev` function. 109 | IW_EXPORT void iwhmap_lru_init(struct iwhmap *hm, iwhmap_lru_eviction_needed ev, void *ev_user_data); 110 | 111 | IW_EXTERN_C_END; 112 | #endif 113 | -------------------------------------------------------------------------------- /src/kv/tests/iwkv_test6.c: -------------------------------------------------------------------------------- 1 | #include "iwkv.h" 2 | #include "iwlog.h" 3 | #include "iwutils.h" 4 | #include "iwcfg.h" 5 | #include "iwkv_tests.h" 6 | #include "iwkv_internal.h" 7 | 8 | int init_suite(void) { 9 | iwrc rc = iwkv_init(); 10 | return rc; 11 | } 12 | 13 | int clean_suite(void) { 14 | return 0; 15 | } 16 | 17 | static void iwkv_test6_1_impl(int fmt_version) { 18 | iwrc rc; 19 | IWKV_val key = { 0 }; 20 | IWKV_val val = { 0 }; 21 | IWKV iwkv; 22 | IWDB db; 23 | IWKV_cursor cur; 24 | 25 | IWKV_OPTS opts = { 26 | .path = "iwkv_test6_1.db", 27 | .oflags = IWKV_TRUNC, 28 | .fmt_version = fmt_version 29 | }; 30 | rc = iwkv_open(&opts, &iwkv); 31 | CU_ASSERT_EQUAL_FATAL(rc, 0); 32 | rc = iwkv_db(iwkv, 1, IWDB_VNUM64_KEYS | IWDB_COMPOUND_KEYS, &db); 33 | CU_ASSERT_EQUAL_FATAL(rc, 0); 34 | 35 | for (uint32_t i = 0; i < 100; ++i) { 36 | key.data = &i; 37 | key.size = sizeof(i); 38 | for (uint32_t j = 0; j < 1000; ++j) { 39 | key.compound = j; 40 | val.data = &j; 41 | val.size = sizeof(j); 42 | rc = iwkv_put(db, &key, &val, 0); 43 | CU_ASSERT_EQUAL_FATAL(rc, 0); 44 | rc = iwkv_get(db, &key, &val); 45 | CU_ASSERT_EQUAL_FATAL(rc, 0); 46 | CU_ASSERT_EQUAL_FATAL(key.size, sizeof(i)); 47 | CU_ASSERT_TRUE_FATAL(!memcmp(key.data, &i, sizeof(i))); 48 | CU_ASSERT_TRUE_FATAL(val.size == 4 && val.compound == 0 && !memcmp(val.data, &j, sizeof(j))); 49 | iwkv_val_dispose(&val); 50 | } 51 | } 52 | for (uint32_t i = 0; i < 10; ++i) { 53 | key.data = &i; 54 | key.size = sizeof(i); 55 | for (uint32_t j = 0; j < 10; ++j) { 56 | key.compound = j; 57 | IWKV_val ckey; 58 | rc = iwkv_cursor_open(db, &cur, IWKV_CURSOR_EQ, &key); 59 | CU_ASSERT_EQUAL_FATAL(rc, 0); 60 | rc = iwkv_cursor_key(cur, &ckey); 61 | CU_ASSERT_EQUAL_FATAL(rc, 0); 62 | int64_t llv; 63 | CU_ASSERT_TRUE_FATAL(ckey.size == 8); 64 | memcpy(&llv, ckey.data, sizeof(llv)); 65 | CU_ASSERT_TRUE_FATAL(ckey.compound == j && llv == i); 66 | iwkv_val_dispose(&ckey); 67 | rc = iwkv_cursor_close(&cur); 68 | CU_ASSERT_EQUAL_FATAL(rc, 0); 69 | } 70 | } 71 | rc = iwkv_close(&iwkv); 72 | CU_ASSERT_EQUAL_FATAL(rc, 0); 73 | } 74 | 75 | void iwkv_test6_1_v1(void) { 76 | iwkv_test6_1_impl(1); 77 | } 78 | 79 | void iwkv_test6_1_v2(void) { 80 | iwkv_test6_1_impl(2); 81 | } 82 | 83 | static void iwkv_test6_2_impl(int fmt_version) { 84 | iwrc rc; 85 | IWKV iwkv; 86 | IWDB db; 87 | IWKV_val key = { 0 }; 88 | IWKV_val val = { 0 }; 89 | char kbuf[PREFIX_KEY_LEN_V2]; 90 | 91 | IWKV_OPTS opts = { 92 | .path = "iwkv_test6_2.db", 93 | .oflags = IWKV_TRUNC, 94 | .fmt_version = fmt_version 95 | }; 96 | rc = iwkv_open(&opts, &iwkv); 97 | CU_ASSERT_EQUAL_FATAL(rc, 0); 98 | 99 | for (int i = 0; i < PREFIX_KEY_LEN_V2 - 1; ++i) { 100 | kbuf[i] = (i % 94) + 33; 101 | } 102 | 103 | rc = iwkv_db(iwkv, 1, IWDB_COMPOUND_KEYS, &db); 104 | CU_ASSERT_EQUAL_FATAL(rc, 0); 105 | 106 | for (uint32_t i = 0; i < 1000; ++i) { 107 | key.data = kbuf; 108 | key.size = PREFIX_KEY_LEN_V2 - 1; 109 | key.compound = i; 110 | rc = iwkv_put(db, &key, &val, 0); 111 | CU_ASSERT_EQUAL_FATAL(rc, 0); 112 | rc = iwkv_get(db, &key, &val); 113 | CU_ASSERT_EQUAL_FATAL(rc, 0); 114 | } 115 | rc = iwkv_close(&iwkv); 116 | CU_ASSERT_EQUAL_FATAL(rc, 0); 117 | } 118 | 119 | static void iwkv_test6_2_v1(void) { 120 | iwkv_test6_2_impl(1); 121 | } 122 | 123 | static void iwkv_test6_2_v2(void) { 124 | iwkv_test6_2_impl(2); 125 | } 126 | 127 | int main(void) { 128 | CU_pSuite pSuite = NULL; 129 | 130 | /* Initialize the CUnit test registry */ 131 | if (CUE_SUCCESS != CU_initialize_registry()) { 132 | return CU_get_error(); 133 | } 134 | 135 | /* Add a suite to the registry */ 136 | pSuite = CU_add_suite("iwkv_test6", init_suite, clean_suite); 137 | 138 | if (NULL == pSuite) { 139 | CU_cleanup_registry(); 140 | return CU_get_error(); 141 | } 142 | 143 | /* Add the tests to the suite */ 144 | if ( (NULL == CU_add_test(pSuite, "iwkv_test6_1_v1", iwkv_test6_1_v1)) 145 | || (NULL == CU_add_test(pSuite, "iwkv_test6_1_v2", iwkv_test6_1_v2)) 146 | || (NULL == CU_add_test(pSuite, "iwkv_test6_2_v1", iwkv_test6_2_v1)) 147 | || (NULL == CU_add_test(pSuite, "iwkv_test6_2_v2", iwkv_test6_2_v2))) { 148 | CU_cleanup_registry(); 149 | return CU_get_error(); 150 | } 151 | 152 | /* Run all tests using the CUnit Basic interface */ 153 | CU_basic_set_mode(CU_BRM_VERBOSE); 154 | CU_basic_run_tests(); 155 | int ret = CU_get_error() || CU_get_number_of_failures(); 156 | CU_cleanup_registry(); 157 | return ret; 158 | } 159 | -------------------------------------------------------------------------------- /src/kv/dbg/iwkvdbg.c: -------------------------------------------------------------------------------- 1 | //-------------------------- DEBUG STAFF 2 | 3 | void iwkvd_trigger_xor(uint64_t val) { 4 | g_trigger ^= val; 5 | } 6 | 7 | void iwkvd_kvblk(FILE *f, KVBLK *kb, int maxvlen) { 8 | assert(f && kb && kb->addr); 9 | uint8_t *mm, *vbuf, *kbuf; 10 | uint32_t klen, vlen; 11 | IWFS_FSM *fsm = &kb->db->iwkv->fsm; 12 | blkn_t blkn = ADDR2BLK(kb->addr); 13 | fprintf(f, "\n === KVBLK[%u] maxoff=%" PRIx64 ", zidx=%d, idxsz=%d, szpow=%u, flg=%x, db=%d\n", // -V576 14 | blkn, (int64_t) kb->maxoff, kb->zidx, kb->idxsz, kb->szpow, kb->flags, kb->db->id); 15 | 16 | iwrc rc = fsm->probe_mmap(fsm, 0, &mm, 0); 17 | if (rc) { 18 | iwlog_ecode_error3(rc); 19 | return; 20 | } 21 | for (int i = 0; i < KVBLK_IDXNUM; ++i) { 22 | KVP *kvp = &kb->pidx[i]; 23 | rc = _kvblk_key_peek(kb, i, mm, &kbuf, &klen); 24 | if (rc) { 25 | iwlog_ecode_error3(rc); 26 | return; 27 | } 28 | _kvblk_value_peek(kb, i, mm, &vbuf, &vlen); 29 | fprintf(f, "\n %02d: [%04" PRIx64 ", %02u, %02d]: %.*s:%.*s", 30 | i, (int64_t) kvp->off, kvp->len, kvp->ridx, 31 | klen, kbuf, MIN(vlen, maxvlen), vbuf); 32 | } 33 | fprintf(f, "\n"); 34 | } 35 | 36 | #define IWKVD_MAX_VALSZ 96 37 | 38 | iwrc iwkvd_sblk(FILE *f, IWLCTX *lx, SBLK *sb, int flags) { 39 | assert(sb && sb->addr); 40 | uint32_t lkl = 0; 41 | char lkbuf[PREFIX_KEY_LEN_V2 + 1] = { 0 }; 42 | uint8_t *mm, *vbuf, *kbuf; 43 | uint32_t klen, vlen; 44 | IWFS_FSM *fsm = &sb->db->iwkv->fsm; 45 | blkn_t blkn = ADDR2BLK(sb->addr); 46 | iwrc rc = fsm->probe_mmap(fsm, 0, &mm, 0); 47 | if (rc) { 48 | iwlog_ecode_error3(rc); 49 | return rc; 50 | } 51 | rc = _sblk_loadkvblk_mm(lx, sb, mm); 52 | if (rc) { 53 | iwlog_ecode_error3(rc); 54 | return rc; 55 | } 56 | assert(sb->kvblk); 57 | if (sb->flags & SBLK_DB) { 58 | lkl = 0; 59 | } else { 60 | memcpy(&lkl, mm + sb->addr + SOFF_LKL_U1, 1); 61 | lkl = IW_ITOHL(lkl); 62 | if (lx->db->iwkv->fmt_version > 1) { 63 | memcpy(lkbuf, mm + sb->addr + SOFF_LK_V2, lkl); 64 | } else { 65 | memcpy(lkbuf, mm + sb->addr + SOFF_LK_V1, lkl); 66 | } 67 | } 68 | fprintf(f, "\n === SBLK[%u] lvl=%d, pnum=%d, flg=%x, kvzidx=%d, p0=%u, db=%u", 69 | blkn, 70 | ((IWKVD_PRINT_NO_LEVEVELS & flags) ? -1 : sb->lvl), 71 | sb->pnum, sb->flags, sb->kvblk->zidx, 72 | sb->p0, 73 | sb->kvblk->db->id); 74 | 75 | fprintf(f, "\n === SBLK[%u] szpow=%d, lkl=%d, lk=%s\n", blkn, sb->kvblk->szpow, lkl, lkbuf); // -V576 76 | 77 | for (int i = 0, j = 0; i < sb->pnum; ++i, ++j) { 78 | if (j == 3) { 79 | fputc('\n', f); 80 | j = 0; 81 | } 82 | if (j == 0) { 83 | fprintf(f, " === SBLK[%u]", blkn); 84 | } 85 | rc = _kvblk_key_peek(sb->kvblk, sb->pi[i], mm, &kbuf, &klen); 86 | if (rc) { 87 | iwlog_ecode_error3(rc); 88 | return rc; 89 | } 90 | if (flags & IWKVD_PRINT_VALS) { 91 | _kvblk_value_peek(sb->kvblk, sb->pi[i], mm, &vbuf, &vlen); 92 | fprintf(f, " [%03d,%03d] %.*s:%.*s", i, sb->pi[i], klen, kbuf, MIN(vlen, IWKVD_MAX_VALSZ), vbuf); 93 | } else { 94 | fprintf(f, " [%03d,%03d] %.*s", i, sb->pi[i], klen, kbuf); 95 | } 96 | } 97 | fprintf(f, "\n\n"); 98 | return rc; 99 | } 100 | 101 | IWFS_FSM* iwkvd_fsm(IWKV kv) { 102 | return &kv->fsm; 103 | } 104 | 105 | void iwkvd_db(FILE *f, IWDB db, int flags, int plvl) { 106 | assert(db); 107 | SBLK *sb, *tail; 108 | IWLCTX lx = { 109 | .db = db, 110 | .nlvl = -1 111 | }; 112 | iwrc rc = _sblk_at(&lx, db->addr, 0, &sb); 113 | if (rc) { 114 | iwlog_ecode_error3(rc); 115 | return; 116 | } 117 | rc = _sblk_at(&lx, 0, 0, &tail); 118 | if (rc) { 119 | iwlog_ecode_error3(rc); 120 | return; 121 | } 122 | fprintf(f, "\n\n== DB[%u] lvl=%d, blk=%u, dbflg=%x, p0=%u", 123 | db->id, 124 | ((IWKVD_PRINT_NO_LEVEVELS & flags) ? -1 : sb->lvl), 125 | (unsigned int) ADDR2BLK(sb->addr), 126 | db->dbflg, 127 | tail->p0); 128 | if (!(IWKVD_PRINT_NO_LEVEVELS & flags)) { 129 | fprintf(f, "\n== DB[%u]->n=[", db->id); 130 | for (int i = 0; i <= sb->lvl; ++i) { 131 | if (i > 0) { 132 | fprintf(f, ", %d:%u", i, sb->n[i]); 133 | } else { 134 | fprintf(f, "%d:%u", i, sb->n[i]); 135 | } 136 | } 137 | fprintf(f, "]"); 138 | } 139 | blkn_t blk = sb->n[plvl]; 140 | while (blk) { 141 | rc = _sblk_at(&lx, BLK2ADDR(blk), 0, &sb); 142 | if (rc) { 143 | iwlog_ecode_error3(rc); 144 | return; 145 | } 146 | iwkvd_sblk(f, &lx, sb, flags); 147 | blk = sb->n[plvl]; 148 | _sblk_release(&lx, &sb); 149 | } 150 | fflush(f); 151 | } 152 | -------------------------------------------------------------------------------- /src/log/tests/iwlog_test1.c: -------------------------------------------------------------------------------- 1 | // 2 | /************************************************************************************************** 3 | * IOWOW library 4 | * 5 | * MIT License 6 | * 7 | * Copyright (c) 2012-2024 Softmotions Ltd 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | *************************************************************************************************/ 27 | 28 | 29 | #include "iwcfg.h" 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "iwlog.h" 36 | #include "iwutils.h" 37 | 38 | int init_suite(void) { 39 | int rc = iwlog_init(); 40 | return rc; 41 | } 42 | 43 | int clean_suite(void) { 44 | return 0; 45 | } 46 | 47 | void iwlog_test1(void) { 48 | uint32_t ec = (0xfffffffdU & 0x3fffffffU); 49 | uint64_t rc = 0xfafafafaULL; 50 | rc = iwrc_set_errno(rc, ec); 51 | uint32_t ec2 = iwrc_strip_errno(&rc); 52 | CU_ASSERT_EQUAL(ec, ec2); 53 | CU_ASSERT_EQUAL(rc, 0xfafafafaULL); 54 | } 55 | 56 | void iwlog_test2(void) { 57 | IWLOG_DEFAULT_OPTS opts = { 0 }; 58 | int rv = 0; 59 | size_t sz; 60 | char fname[] = "iwlog_test1_XXXXXX"; 61 | int fd = mkstemp(fname); 62 | CU_ASSERT_TRUE(fd != 1); 63 | FILE *out = fdopen(fd, "w"); 64 | CU_ASSERT_PTR_NOT_NULL(out); 65 | 66 | fprintf(stderr, "Redirecting log to: %s" IW_LINE_SEP, fname); 67 | 68 | opts.out = out; 69 | iwlog_set_logfn(0, &opts); 70 | 71 | iwlog_info2("7fa79c75beac413d83f35ffb6bf571b9"); 72 | iwlog_error("7e94f7214af64513b30ab4df3f62714a%s", "C"); 73 | iwlog_ecode_warn(IW_ERROR_READONLY, "c94645c3b107433497ef295b1c00dcff%d", 12); 74 | 75 | errno = ENOENT; 76 | iwrc ecode = iwrc_set_errno(IW_ERROR_ERRNO, errno); 77 | rv = iwlog(IWLOG_DEBUG, ecode, NULL, 0, "ERRNO Message"); 78 | CU_ASSERT_EQUAL(rv, 0); 79 | errno = 0; 80 | fclose(out); 81 | 82 | out = fopen(fname, "r"); 83 | CU_ASSERT_PTR_NOT_NULL_FATAL(out); 84 | 85 | char buf[1024]; 86 | memset(buf, 0, 1024); 87 | sz = fread(buf, 1, 1024, out); 88 | CU_ASSERT_TRUE(sz); 89 | fprintf(stderr, "%s\n\n" IW_LINE_SEP, buf); 90 | 91 | CU_ASSERT_PTR_NOT_NULL(strstr(buf, "7fa79c75beac413d83f35ffb6bf571b9")); 92 | CU_ASSERT_PTR_NOT_NULL(strstr(buf, "7e94f7214af64513b30ab4df3f62714aC")); 93 | CU_ASSERT_PTR_NOT_NULL(strstr(buf, 94 | "DEBUG 70001|2|0|Error with expected errno " 95 | "status set. (IW_ERROR_ERRNO)|")); 96 | CU_ASSERT_PTR_NOT_NULL(strstr(buf, "ERRNO Message")); 97 | CU_ASSERT_PTR_NOT_NULL(strstr(buf, "ERROR iwlog_test1.c:")); 98 | 99 | CU_ASSERT_PTR_NOT_NULL(strstr(buf, "70005|0|0|Resource is readonly. (IW_ERROR_READONLY)|")); 100 | CU_ASSERT_PTR_NOT_NULL(strstr(buf, "c94645c3b107433497ef295b1c00dcff12")); 101 | 102 | fclose(out); 103 | unlink(fname); 104 | } 105 | 106 | int main(void) { 107 | CU_pSuite pSuite = NULL; 108 | 109 | /* Initialize the CUnit test registry */ 110 | if (CUE_SUCCESS != CU_initialize_registry()) { 111 | return CU_get_error(); 112 | } 113 | 114 | /* Add a suite to the registry */ 115 | pSuite = CU_add_suite("iwlog_test1", init_suite, clean_suite); 116 | 117 | if (NULL == pSuite) { 118 | CU_cleanup_registry(); 119 | return CU_get_error(); 120 | } 121 | 122 | /* Add the tests to the suite */ 123 | if ( (NULL == CU_add_test(pSuite, "iwlog_test1", iwlog_test1)) 124 | || (NULL == CU_add_test(pSuite, "iwlog_test2", iwlog_test2))) { 125 | CU_cleanup_registry(); 126 | return CU_get_error(); 127 | } 128 | 129 | /* Run all tests using the CUnit Basic interface */ 130 | CU_basic_set_mode(CU_BRM_VERBOSE); 131 | CU_basic_run_tests(); 132 | int ret = CU_get_error() || CU_get_number_of_failures(); 133 | CU_cleanup_registry(); 134 | return ret; 135 | } 136 | -------------------------------------------------------------------------------- /src/utils/iwtp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef IWTP_H 3 | #define IWTP_H 4 | 5 | /************************************************************************************************** 6 | * Threads pool. 7 | * 8 | * IOWOW library 9 | * 10 | * MIT License 11 | * 12 | * Copyright (c) 2012-2024 Softmotions Ltd 13 | * 14 | * Permission is hereby granted, free of charge, to any person obtaining a copy 15 | * of this software and associated documentation files (the "Software"), to deal 16 | * in the Software without restriction, including without limitation the rights 17 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | * copies of the Software, and to permit persons to whom the Software is 19 | * furnished to do so, subject to the following conditions: 20 | * 21 | * The above copyright notice and this permission notice shall be included in all 22 | * copies or substantial portions of the Software. 23 | * 24 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | * SOFTWARE. 31 | *************************************************************************************************/ 32 | 33 | #include "basedefs.h" 34 | #include 35 | 36 | IW_EXTERN_C_START; 37 | 38 | struct iwtp; 39 | typedef struct iwtp*IWTP; 40 | 41 | struct iwtp_spec { 42 | /** Optional thread name prefix in thread pool. 43 | * @note Thread name length must be not greater then 15 characters. 44 | */ 45 | const char *thread_name_prefix; 46 | 47 | /** Number of hot threads in thread pool. 48 | * Threads are allocated on when thread pool created. 49 | * @note Value must be in rage [1-1024]. 50 | * @note If zero then value will be set to number of cpu cores. 51 | */ 52 | int num_threads; 53 | 54 | /** Maximum number of tasks in queue. 55 | Zero for unlimited queue. */ 56 | int queue_limit; 57 | 58 | /** If task queue is full and the `overflow_threads_factor` is not zero 59 | * then pool is allowed to spawn extra threads to process tasks as long 60 | * as overall number of threads less of equal to `num_threads + num_threads * overflow_threads_factor` 61 | * @note Max: 2 62 | */ 63 | int overflow_threads_factor; 64 | 65 | /** 66 | * It true performs log warning in the case of spawning overflow thread. 67 | */ 68 | bool warn_on_overflow_thread_spawn; 69 | 70 | /** 71 | * Optional thread initializer function called when pool thread is created. 72 | */ 73 | void (*thread_initializer)(pthread_t); 74 | }; 75 | 76 | /** 77 | * @brief Task to execute 78 | */ 79 | typedef void (*iwtp_task_f)(void *arg); 80 | 81 | /** 82 | * @brief Creates a new thread pool instance using provided `spec` config. 83 | */ 84 | IW_EXPORT iwrc iwtp_start_by_spec(const struct iwtp_spec *spec, struct iwtp **out_tp); 85 | 86 | /** 87 | * @brief Creates a new thread pool instance. 88 | * @param num_threads Number of threads in the pool, accepted values in range `[1-1024]` 89 | * @param queue_limit Maximum number of tasks in queue. Zero for unlimited queue. 90 | * @param [out] out_tp Holder for thread pool instance. 91 | */ 92 | IW_EXPORT iwrc iwtp_start(const char *thread_name_prefix, int num_threads, int queue_limit, struct iwtp **out_tp); 93 | 94 | /** 95 | * @brief Submits new task into thread pool. 96 | * @note `IW_ERROR_INVALID_STATE` if called after `iwtp_shutdown()`. 97 | * @note `IW_ERROR_OVERFLOW` if size of tasks queue reached `queue_limit`. 98 | * @param tp Pool instance 99 | * @param task Task function 100 | * @param task_arg Argument for task function 101 | */ 102 | IW_EXPORT iwrc iwtp_schedule(struct iwtp *tp, iwtp_task_f task, void *task_arg); 103 | 104 | /** 105 | * @brief Shutdowns thread pool and disposes all nresources. 106 | * @note Function will wait until current task completes or 107 | * wait for all enqueued tasks if `wait_for_all` is set to `true`. 108 | * No new tasks will be accepted during `iwstw_shutdown` call. 109 | 110 | * @param tpp Pointer to pool which should be disposed. 111 | * @param wait_for_all If true worker will wait for completion of all enqueued tasks before shutdown. 112 | */ 113 | IW_EXPORT iwrc iwtp_shutdown(struct iwtp **tpp, bool wait_for_all); 114 | 115 | /** 116 | * @brief Returns size of tasks queue. 117 | */ 118 | IW_EXPORT int iwtp_queue_size(struct iwtp *tp); 119 | 120 | /** 121 | * @brief Returns number of threads currently executing tasks. 122 | */ 123 | IW_EXPORT int iwtp_threads_busy_num(struct iwtp *tp); 124 | 125 | IW_EXTERN_C_END; 126 | #endif 127 | -------------------------------------------------------------------------------- /src/json/tests/iwjsreg_test1.c: -------------------------------------------------------------------------------- 1 | #include "iowow.h" 2 | #include "iwjsreg.h" 3 | #include "iwlog.h" 4 | #include 5 | #include 6 | #include 7 | 8 | int init_suite(void) { 9 | int rc = iw_init(); 10 | return rc; 11 | } 12 | 13 | int clean_suite(void) { 14 | return 0; 15 | } 16 | 17 | static void _iwjsreg_basic1(void) { 18 | const char *path = "iwjsreg_basic1.dat"; 19 | unlink(path); 20 | 21 | struct iwjsreg *reg; 22 | iwrc rc = iwjsreg_open(&(struct iwjsreg_spec) { 23 | .path = path, 24 | }, ®); 25 | CU_ASSERT_EQUAL_FATAL(rc, 0); 26 | 27 | rc = iwjsreg_set_str(reg, "key1", "val1"); 28 | CU_ASSERT_EQUAL_FATAL(rc, 0); 29 | 30 | rc = iwjsreg_set_i64(reg, "key2", 8217128L); 31 | CU_ASSERT_EQUAL_FATAL(rc, 0); 32 | 33 | rc = iwjsreg_set_bool(reg, "key3", true); 34 | CU_ASSERT_EQUAL_FATAL(rc, 0); 35 | 36 | rc = iwjsreg_close(®); 37 | CU_ASSERT_EQUAL_FATAL(rc, 0); 38 | 39 | rc = iwjsreg_open(&(struct iwjsreg_spec) { 40 | .path = path, 41 | }, ®); 42 | CU_ASSERT_EQUAL_FATAL(rc, 0); 43 | 44 | int64_t val; 45 | rc = iwjsreg_get_i64(reg, "key2", &val); 46 | CU_ASSERT_EQUAL_FATAL(rc, 0); 47 | CU_ASSERT_EQUAL(val, 8217128L); 48 | 49 | rc = iwjsreg_get_i64(reg, "key22", &val); 50 | CU_ASSERT_EQUAL(rc, IW_ERROR_NOT_EXISTS); 51 | 52 | char *buf; 53 | rc = iwjsreg_get_str(reg, "key1", &buf); 54 | CU_ASSERT_EQUAL_FATAL(rc, 0); 55 | CU_ASSERT_STRING_EQUAL(buf, "val1"); 56 | free(buf); 57 | 58 | rc = iwjsreg_remove(reg, "key1"); 59 | CU_ASSERT_EQUAL_FATAL(rc, 0); 60 | 61 | rc = iwjsreg_get_i64(reg, "key1", &val); 62 | CU_ASSERT_EQUAL(rc, IW_ERROR_NOT_EXISTS); 63 | 64 | rc = iwjsreg_close(®); 65 | CU_ASSERT_EQUAL(rc, 0); 66 | } 67 | 68 | static void _iwjsreg_basic2(void) { 69 | const char *path = "iwjsreg_basic2.dat"; 70 | unlink(path); 71 | 72 | struct iwjsreg *reg; 73 | iwrc rc = iwjsreg_open(&(struct iwjsreg_spec) { 74 | .path = path, 75 | .flags = IWJSREG_FORMAT_BINARY, 76 | }, ®); 77 | CU_ASSERT_EQUAL_FATAL(rc, 0); 78 | 79 | rc = iwjsreg_set_str(reg, "key1", "val1"); 80 | CU_ASSERT_EQUAL_FATAL(rc, 0); 81 | 82 | rc = iwjsreg_set_i64(reg, "key2", 8217128L); 83 | CU_ASSERT_EQUAL_FATAL(rc, 0); 84 | 85 | rc = iwjsreg_set_bool(reg, "key3", true); 86 | CU_ASSERT_EQUAL_FATAL(rc, 0); 87 | 88 | rc = iwjsreg_close(®); 89 | CU_ASSERT_EQUAL_FATAL(rc, 0); 90 | 91 | rc = iwjsreg_open(&(struct iwjsreg_spec) { 92 | .path = path, 93 | .flags = IWJSREG_FORMAT_BINARY, 94 | }, ®); 95 | CU_ASSERT_EQUAL_FATAL(rc, 0); 96 | 97 | int64_t val; 98 | rc = iwjsreg_get_i64(reg, "key2", &val); 99 | CU_ASSERT_EQUAL_FATAL(rc, 0); 100 | CU_ASSERT_EQUAL(val, 8217128L); 101 | 102 | rc = iwjsreg_get_i64(reg, "key22", &val); 103 | CU_ASSERT_EQUAL(rc, IW_ERROR_NOT_EXISTS); 104 | 105 | char *buf; 106 | rc = iwjsreg_get_str(reg, "key1", &buf); 107 | CU_ASSERT_EQUAL_FATAL(rc, 0); 108 | CU_ASSERT_STRING_EQUAL(buf, "val1"); 109 | free(buf); 110 | 111 | rc = iwjsreg_remove(reg, "key1"); 112 | CU_ASSERT_EQUAL_FATAL(rc, 0); 113 | 114 | rc = iwjsreg_get_i64(reg, "key1", &val); 115 | CU_ASSERT_EQUAL(rc, IW_ERROR_NOT_EXISTS); 116 | 117 | rc = iwjsreg_close(®); 118 | CU_ASSERT_EQUAL(rc, 0); 119 | } 120 | 121 | static void _iwjsreg_merge(void) { 122 | const char *path = "iwjsreg_merge.dat"; 123 | unlink(path); 124 | 125 | struct iwxstr *xstr = iwxstr_create_empty(); 126 | struct iwpool *pool = iwpool_create_empty(); 127 | 128 | struct iwjsreg *reg; 129 | iwrc rc = iwjsreg_open(&(struct iwjsreg_spec) { 130 | .path = path, 131 | .flags = IWJSREG_FORMAT_BINARY, 132 | }, ®); 133 | 134 | struct jbl_node *n, *n2; 135 | rc = jbn_from_json("{\"vaz\":1, \"gaz\":\"val\"}", &n, pool); 136 | CU_ASSERT_EQUAL_FATAL(rc, 0); 137 | 138 | rc = iwjsreg_merge(reg, "/foo/bar", n); 139 | CU_ASSERT_EQUAL_FATAL(rc, 0); 140 | 141 | rc = iwjsreg_merge(reg, "/foo/bar", n); 142 | CU_ASSERT_EQUAL_FATAL(rc, 0); 143 | 144 | rc = iwjsreg_merge(reg, "/foo/bar/zaz", n); 145 | CU_ASSERT_EQUAL_FATAL(rc, 0); 146 | 147 | rc = jbn_from_json("{\"gaz\":null}", &n2, pool); 148 | CU_ASSERT_EQUAL_FATAL(rc, 0); 149 | 150 | rc = iwjsreg_merge(reg, "/foo/bar/zaz", n2); 151 | CU_ASSERT_EQUAL_FATAL(rc, 0); 152 | 153 | rc = iwjsreg_copy(reg, "", pool, &n2); 154 | CU_ASSERT_EQUAL_FATAL(rc, 0); 155 | fprintf(stderr, "\n"); 156 | jbn_as_json(n2, jbl_xstr_json_printer, xstr, 0); 157 | 158 | CU_ASSERT_STRING_EQUAL(iwxstr_ptr(xstr), "{\"foo\":{\"bar\":{\"vaz\":1,\"gaz\":\"val\",\"zaz\":{\"vaz\":1}}}}"); 159 | 160 | rc = iwjsreg_sync(reg); 161 | CU_ASSERT_EQUAL_FATAL(rc, 0); 162 | 163 | rc = iwjsreg_close(®); 164 | CU_ASSERT_EQUAL(rc, 0); 165 | iwpool_destroy(pool); 166 | iwxstr_destroy(xstr); 167 | } 168 | 169 | int main(void) { 170 | CU_pSuite pSuite = NULL; 171 | if (CUE_SUCCESS != CU_initialize_registry()) { 172 | return CU_get_error(); 173 | } 174 | pSuite = CU_add_suite("iwjsreg", init_suite, clean_suite); 175 | if (NULL == pSuite) { 176 | CU_cleanup_registry(); 177 | return CU_get_error(); 178 | } 179 | int ret = 0; 180 | if ( NULL == CU_add_test(pSuite, "iwjsreg_basic1", _iwjsreg_basic1) 181 | || NULL == CU_add_test(pSuite, "iwjsreg_basic2", _iwjsreg_basic2) 182 | || NULL == CU_add_test(pSuite, "iwjsreg_merge", _iwjsreg_merge)) { 183 | CU_cleanup_registry(); 184 | return CU_get_error(); 185 | } 186 | CU_basic_set_mode(CU_BRM_VERBOSE); 187 | CU_basic_run_tests(); 188 | ret = CU_get_error() || CU_get_number_of_failures(); 189 | CU_cleanup_registry(); 190 | return ret; 191 | } 192 | -------------------------------------------------------------------------------- /src/kv/tests/iwkv_test3.c: -------------------------------------------------------------------------------- 1 | #include "iwkv.h" 2 | #include "iwlog.h" 3 | #include "iwutils.h" 4 | #include "iwcfg.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | typedef struct VN { 12 | int kn; // key number 13 | int vs; // value seed 14 | } VN; 15 | 16 | typedef struct CTX { 17 | VN *vn; 18 | int vnsz; 19 | pthread_cond_t cond; 20 | pthread_mutex_t mtx; 21 | int readynum; 22 | const int thrnum; 23 | IWDB db; 24 | } CTX; 25 | 26 | typedef struct TASK { 27 | CTX *ctx; 28 | int start; 29 | int cnt; 30 | pthread_t thr; 31 | } TASK; 32 | 33 | int init_suite(void) { 34 | iwrc rc = iwkv_init(); 35 | return rc; 36 | } 37 | 38 | int clean_suite(void) { 39 | return 0; 40 | } 41 | 42 | static int logstage(FILE *f, const char *name, IWDB db) { 43 | int rci = fprintf(f, "\n#### Stage: %s\n", name); 44 | iwkvd_db(f, db, /*IWKVD_PRINT_NO_LEVEVELS*/ 0, 0); 45 | fflush(f); 46 | return rci < 0 ? rci : 0; 47 | } 48 | 49 | static void* iwkv_test1_worker(void *op) { 50 | TASK *t = op; 51 | CTX *ctx = t->ctx; 52 | int mynum; 53 | int rci = pthread_mutex_lock(&ctx->mtx); 54 | CU_ASSERT_EQUAL_FATAL(rci, 0); 55 | ++ctx->readynum; 56 | mynum = ctx->readynum; 57 | if (mynum == ctx->thrnum) { 58 | pthread_cond_broadcast(&ctx->cond); 59 | } else { 60 | pthread_cond_wait(&ctx->cond, &ctx->mtx); 61 | } 62 | pthread_mutex_unlock(&ctx->mtx); 63 | 64 | IWKV_val key, val; 65 | for (int i = 0; i < t->cnt; ++i) { 66 | uint64_t k = t->start + i; 67 | uint64_t v = k; 68 | key.size = sizeof(uint64_t); 69 | key.data = &k; 70 | val.size = sizeof(uint64_t); 71 | val.data = &v; 72 | iwrc rc = iwkv_put(ctx->db, &key, &val, 0); 73 | CU_ASSERT_EQUAL_FATAL(rc, 0); 74 | } 75 | return 0; 76 | } 77 | 78 | static void iwkv_test3_impl(int thrnum, int recth, bool wal) { 79 | FILE *f = fopen("iwkv_test3_1.log", "w+"); 80 | CU_ASSERT_PTR_NOT_NULL(f); 81 | const int nrecs = thrnum * recth; 82 | TASK *tasks = calloc(thrnum, sizeof(*tasks)); 83 | VN *arr = calloc(nrecs, sizeof(*arr)); 84 | CTX ctx = { 85 | .vn = arr, 86 | .vnsz = nrecs, 87 | .mtx = PTHREAD_MUTEX_INITIALIZER, 88 | .cond = PTHREAD_COND_INITIALIZER, 89 | .thrnum = thrnum 90 | }; 91 | for (int i = 0; i < nrecs; ++i) { 92 | arr[i].kn = i; 93 | arr[i].vs = iwu_rand_range(256); 94 | } 95 | // shuffle 96 | for (int i = 0; i < nrecs; ++i) { 97 | uint32_t tgt = iwu_rand_range(nrecs); 98 | int knt = arr[tgt].kn; 99 | arr[tgt].kn = arr[i].kn; 100 | arr[i].kn = knt; 101 | } 102 | for (int i = nrecs - 1; i >= 0; --i) { 103 | uint32_t tgt = iwu_rand_range(nrecs); 104 | int knt = arr[tgt].kn; 105 | arr[tgt].kn = arr[i].kn; 106 | arr[i].kn = knt; 107 | } 108 | 109 | IWKV_OPTS opts = { 110 | .path = "iwkv_test3_1.db", 111 | .oflags = IWKV_TRUNC, 112 | .wal = { 113 | .enabled = wal, 114 | .checkpoint_buffer_sz = 1024 * 1024 115 | } 116 | }; 117 | IWKV iwkv; 118 | IWKV_val key, val; 119 | iwrc rc = iwkv_open(&opts, &iwkv); 120 | CU_ASSERT_EQUAL_FATAL(rc, 0); 121 | 122 | rc = iwkv_db(iwkv, 1, IWDB_VNUM64_KEYS, &ctx.db); 123 | CU_ASSERT_EQUAL_FATAL(rc, 0); 124 | 125 | for (int i = 0; i < thrnum; ++i) { 126 | tasks[i].ctx = &ctx; 127 | tasks[i].start = i * recth; 128 | tasks[i].cnt = recth; 129 | int rci = pthread_create(&tasks[i].thr, 0, iwkv_test1_worker, &tasks[i]); 130 | CU_ASSERT_EQUAL_FATAL(rci, 0); 131 | } 132 | for (int i = 0; i < thrnum; ++i) { 133 | int rci = pthread_join(tasks[i].thr, 0); 134 | CU_ASSERT_EQUAL_FATAL(rci, 0); 135 | } 136 | fprintf(stderr, "\nChecking DB...."); 137 | for (int i = 0; i < nrecs; ++i) { 138 | uint64_t k = i, v; 139 | key.data = &k; 140 | key.size = sizeof(uint64_t); 141 | rc = iwkv_get(ctx.db, &key, &val); 142 | if (rc) { 143 | fprintf(stderr, "\nwk=%d\n", i); 144 | iwlog_ecode_error3(rc); 145 | //break; 146 | } else { 147 | CU_ASSERT_EQUAL_FATAL(val.size, sizeof(uint64_t)); 148 | memcpy(&v, val.data, sizeof(uint64_t)); 149 | CU_ASSERT_EQUAL_FATAL(v, i); 150 | } 151 | iwkv_val_dispose(&val); 152 | } 153 | pthread_cond_destroy(&ctx.cond); 154 | pthread_mutex_destroy(&ctx.mtx); 155 | free(arr); 156 | free(tasks); 157 | rc = iwkv_close(&iwkv); 158 | CU_ASSERT_EQUAL_FATAL(rc, 0); 159 | fclose(f); 160 | } 161 | 162 | static void iwkv_test3_1(void) { 163 | iwkv_test3_impl(4, 30000, false); 164 | } 165 | 166 | static void iwkv_test3_2(void) { 167 | iwkv_test3_impl(4, 30000, true); 168 | } 169 | 170 | int main(void) { 171 | CU_pSuite pSuite = NULL; 172 | 173 | /* Initialize the CUnit test registry */ 174 | if (CUE_SUCCESS != CU_initialize_registry()) { 175 | return CU_get_error(); 176 | } 177 | 178 | /* Add a suite to the registry */ 179 | pSuite = CU_add_suite("iwkv_test3", init_suite, clean_suite); 180 | 181 | if (NULL == pSuite) { 182 | CU_cleanup_registry(); 183 | return CU_get_error(); 184 | } 185 | 186 | /* Add the tests to the suite */ 187 | if ( (NULL == CU_add_test(pSuite, "iwkv_test3_1", iwkv_test3_1)) 188 | || (NULL == CU_add_test(pSuite, "iwkv_test3_2", iwkv_test3_2))) { 189 | CU_cleanup_registry(); 190 | return CU_get_error(); 191 | } 192 | 193 | /* Run all tests using the CUnit Basic interface */ 194 | CU_basic_set_mode(CU_BRM_VERBOSE); 195 | CU_basic_run_tests(); 196 | int ret = CU_get_error() || CU_get_number_of_failures(); 197 | CU_cleanup_registry(); 198 | return ret; 199 | } 200 | -------------------------------------------------------------------------------- /src/utils/tests/iwhmap_test1.c: -------------------------------------------------------------------------------- 1 | #include "iowow.h" 2 | #include "iwcfg.h" 3 | #include "iwhmap.h" 4 | #include 5 | 6 | static int init_suite(void) { 7 | return iw_init(); 8 | } 9 | 10 | static int clean_suite(void) { 11 | return 0; 12 | } 13 | 14 | static void hex32(uint32_t *hash, char *buf) { 15 | sprintf(buf, "%08x", *hash); 16 | } 17 | 18 | static void hex128(uint32_t hash[4], char *buf) { 19 | sprintf(buf, "%08x%08x%08x%08x", hash[0], hash[1], hash[2], hash[3]); 20 | } 21 | 22 | void murmur3_x86_32(const void *key, size_t len, uint32_t seed, void *out); 23 | void murmur3_x86_128(const void *key, const size_t len, uint32_t seed, void *out); 24 | void murmur3_x64_128(const void *key, const size_t len, const uint32_t seed, void *out); 25 | 26 | static void test_murmur_hash(void) { 27 | #define TESTHASH(arch, nbytes, seed, str, expected) { \ 28 | char *input = str; \ 29 | uint32_t hash[4]; \ 30 | char buf[33]; \ 31 | murmur3_ ## arch ## _ ## nbytes(input, strlen(input), (seed), hash); \ 32 | hex ## nbytes(hash, buf); \ 33 | CU_ASSERT_STRING_EQUAL(buf, expected) \ 34 | } 35 | 36 | TESTHASH(x86, 32, 1234, "Hello, world!", "faf6cdb3"); 37 | TESTHASH(x86, 32, 4321, "Hello, world!", "bf505788"); 38 | TESTHASH(x86, 32, 1234, "xxxxxxxxxxxxxxxxxxxxxxxxxxxx", "8905ac28"); 39 | TESTHASH(x86, 32, 1234, "", "0f2cc00b"); 40 | 41 | TESTHASH(x86, 128, 123, "Hello, world!", "61c9129e5a1aacd7a41621629e37c886"); 42 | TESTHASH(x86, 128, 321, "Hello, world!", "d5fbdcb3c26c4193045880c5a7170f0f"); 43 | TESTHASH(x86, 128, 123, "xxxxxxxxxxxxxxxxxxxxxxxxxxxx", "5e40bab278825a164cf929d31fec6047"); 44 | TESTHASH(x86, 128, 123, "", "fedc524526f3e79926f3e79926f3e799"); 45 | 46 | TESTHASH(x64, 128, 123, "Hello, world!", "8743acad421c8c73d373c3f5f19732fd"); 47 | TESTHASH(x64, 128, 321, "Hello, world!", "f86d4004ca47f42bb9546c7979200aee"); 48 | TESTHASH(x64, 128, 123, "xxxxxxxxxxxxxxxxxxxxxxxxxxxx", "becf7e04dbcf74637751664ef66e73e0"); 49 | TESTHASH(x64, 128, 123, "", "4cd9597081679d1abd92f8784bace33d"); 50 | } 51 | 52 | static void test_basic_crud_str(void) { 53 | char kbuf[64]; 54 | char vbuf[64]; 55 | IWHMAP *hm = iwhmap_create_str(iwhmap_kv_free); 56 | CU_ASSERT_PTR_NOT_NULL_FATAL(hm); 57 | for (int i = 0; i < 10000; ++i) { 58 | snprintf(kbuf, sizeof(kbuf), "key%d", i); 59 | snprintf(vbuf, sizeof(vbuf), "value%d", i); 60 | iwrc rc = iwhmap_put(hm, strdup(kbuf), strdup(vbuf)); 61 | CU_ASSERT_EQUAL_FATAL(rc, 0); 62 | } 63 | for (int i = 0; i < 10000; ++i) { 64 | snprintf(kbuf, sizeof(kbuf), "key%d", i); 65 | snprintf(vbuf, sizeof(vbuf), "value%d", i); 66 | const char *vp = iwhmap_get(hm, kbuf); 67 | CU_ASSERT_PTR_NOT_NULL_FATAL(vp); 68 | CU_ASSERT_STRING_EQUAL(vbuf, vp); 69 | if (i % 2 == 0) { 70 | iwhmap_remove(hm, kbuf); 71 | } 72 | } 73 | CU_ASSERT_EQUAL(iwhmap_count(hm), 5000); 74 | for (int i = 0; i < 10000; ++i) { 75 | if ((i % 2) == 0) { 76 | continue; 77 | } 78 | snprintf(kbuf, sizeof(kbuf), "key%d", i); 79 | snprintf(vbuf, sizeof(vbuf), "value%d", i); 80 | const char *vp = iwhmap_get(hm, kbuf); 81 | CU_ASSERT_PTR_NOT_NULL_FATAL(vp); 82 | CU_ASSERT_STRING_EQUAL(vbuf, vp); 83 | if (i % 3 == 0) { 84 | iwhmap_remove(hm, kbuf); 85 | } 86 | } 87 | CU_ASSERT_EQUAL(iwhmap_count(hm), 3333); 88 | 89 | // TODO: finish tests 90 | iwhmap_destroy(hm); 91 | } 92 | 93 | static void test_lru1(void) { 94 | IWHMAP *hm = iwhmap_create_u32(0); 95 | CU_ASSERT_PTR_NOT_NULL_FATAL(hm); 96 | 97 | // Init LRU mode max 2 records in map 98 | iwhmap_lru_init(hm, iwhmap_lru_eviction_max_count, (void*) (uintptr_t) 2UL); 99 | 100 | iwrc rc = iwhmap_put_u32(hm, 1, (void*) 1L); 101 | CU_ASSERT_EQUAL_FATAL(rc, 0); 102 | long val = (intptr_t) iwhmap_get_u64(hm, 1); 103 | CU_ASSERT_EQUAL(val, 1L); 104 | 105 | rc -= iwhmap_put_u32(hm, 2, (void*) 2L); 106 | CU_ASSERT_EQUAL_FATAL(rc, 0); 107 | val = (intptr_t) iwhmap_get_u64(hm, 1); 108 | CU_ASSERT_EQUAL(val, 1L); 109 | val = (intptr_t) iwhmap_get_u64(hm, 2); 110 | CU_ASSERT_EQUAL(val, 2L); 111 | 112 | rc = iwhmap_put_u32(hm, 3, (void*) 3L); 113 | CU_ASSERT_EQUAL_FATAL(rc, 0); 114 | val = (intptr_t) iwhmap_get_u64(hm, 1); 115 | CU_ASSERT_EQUAL(val, 0L); 116 | val = (intptr_t) iwhmap_get_u64(hm, 3); 117 | CU_ASSERT_EQUAL(val, 3L); 118 | val = (intptr_t) iwhmap_get_u64(hm, 2); 119 | CU_ASSERT_EQUAL(val, 2L); 120 | 121 | rc = iwhmap_put_u32(hm, 4, (void*) 4L); 122 | val = (intptr_t) iwhmap_get_u64(hm, 3); 123 | CU_ASSERT_EQUAL(val, 0); 124 | CU_ASSERT_EQUAL(iwhmap_count(hm), 2); 125 | 126 | iwhmap_destroy(hm); 127 | } 128 | 129 | static void test_lru2(void) { 130 | iwrc rc = 0; 131 | IWHMAP *hm = iwhmap_create_u32(0); 132 | CU_ASSERT_PTR_NOT_NULL_FATAL(hm); 133 | iwhmap_lru_init(hm, iwhmap_lru_eviction_max_count, (void*) (uintptr_t) 1024UL); 134 | for (int i = 0; i < 2048; ++i) { 135 | rc = iwhmap_put_u32(hm, i + 1, (void*) (intptr_t) i); 136 | CU_ASSERT_EQUAL_FATAL(rc, 0); 137 | } 138 | uint32_t val = iwhmap_count(hm); 139 | CU_ASSERT_EQUAL(val, 1024); 140 | 141 | iwhmap_destroy(hm); 142 | } 143 | 144 | int main(void) { 145 | CU_pSuite pSuite = NULL; 146 | 147 | /* Initialize the CUnit test registry */ 148 | if (CUE_SUCCESS != CU_initialize_registry()) { 149 | return CU_get_error(); 150 | } 151 | 152 | /* Add a suite to the registry */ 153 | pSuite = CU_add_suite("iwhmap_test1", init_suite, clean_suite); 154 | 155 | if (NULL == pSuite) { 156 | CU_cleanup_registry(); 157 | return CU_get_error(); 158 | } 159 | 160 | /* Add the tests to the suite */ 161 | if ( (NULL == CU_add_test(pSuite, "test_murmur_hash", test_murmur_hash)) 162 | || (NULL == CU_add_test(pSuite, "test_basic_crud_str", test_basic_crud_str)) 163 | || (NULL == CU_add_test(pSuite, "test_lru1", test_lru1)) 164 | || (NULL == CU_add_test(pSuite, "test_lru2", test_lru2))) { 165 | CU_cleanup_registry(); 166 | return CU_get_error(); 167 | } 168 | 169 | /* Run all tests using the CUnit Basic interface */ 170 | CU_basic_set_mode(CU_BRM_VERBOSE); 171 | CU_basic_run_tests(); 172 | int ret = CU_get_error() || CU_get_number_of_failures(); 173 | CU_cleanup_registry(); 174 | return ret; 175 | } 176 | -------------------------------------------------------------------------------- /src/utils/iwini.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* inih -- simple .INI file parser 4 | 5 | SPDX-License-Identifier: BSD-3-Clause 6 | 7 | Copyright (C) 2009-2020, Ben Hoyt 8 | 9 | ini is released under the New BSD license. 10 | 11 | https://github.com/benhoyt/inih 12 | 13 | */ 14 | 15 | #ifndef IWINI_H 16 | #define IWINI_H 17 | 18 | #include "basedefs.h" 19 | #include "iwlog.h" 20 | 21 | #include 22 | #include 23 | 24 | IW_EXTERN_C_START; 25 | 26 | #define IWINI_PARSE_BOOL(var__) { \ 27 | if (!strcasecmp(value, "true") || !strcasecmp(value, "on") || !strcasecmp(value, "yes")) { \ 28 | var__ = true; \ 29 | } else if (!strcasecmp(value, "false") || !strcasecmp(value, "off") || !strcasecmp(value, "no")) { \ 30 | var__ = false; \ 31 | } else { \ 32 | iwlog_error("Config: Wrong [%s] section property %s value", section, name); \ 33 | } \ 34 | } 35 | 36 | /* Nonzero if ini_handler callback should accept lineno parameter. */ 37 | #ifndef IWINI_HANDLER_LINENO 38 | #define IWINI_HANDLER_LINENO 0 39 | #endif 40 | 41 | /* Typedef for prototype of handler function. */ 42 | #if IWINI_HANDLER_LINENO 43 | typedef int (*iwini_handler)( 44 | void *user, const char *section, 45 | const char *name, const char *value, 46 | int lineno); 47 | #else 48 | typedef int (*iwini_handler)( 49 | void *user, const char *section, 50 | const char *name, const char *value); 51 | #endif 52 | 53 | /* Typedef for prototype of fgets-style reader function. */ 54 | typedef char* (*iwini_reader)(char *str, int num, void *stream); 55 | 56 | /* Parse given INI-style file. May have [section]s, name=value pairs 57 | (whitespace stripped), and comments starting with ';' (semicolon). Section 58 | is "" if name=value pair parsed before any section heading. name:value 59 | pairs are also supported as a concession to Python's configparser. 60 | 61 | For each name=value pair parsed, call handler function with given user 62 | pointer as well as section, name, and value (data only valid for duration 63 | of handler call). Handler should return nonzero on success, zero on error. 64 | 65 | Returns 0 on success, line number of first error on parse error (doesn't 66 | stop on first error), -1 on file open error, or -2 on memory allocation 67 | error (only when INI_USE_STACK is zero). 68 | */ 69 | IW_EXPORT int iwini_parse(const char *filename, iwini_handler handler, void *user); 70 | 71 | /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't 72 | close the file when it's finished -- the caller must do that. */ 73 | IW_EXPORT int iwini_parse_file(FILE *file, iwini_handler handler, void *user); 74 | 75 | /* Same as ini_parse(), but takes an ini_reader function pointer instead of 76 | filename. Used for implementing custom or string-based I/O (see also 77 | iwini_parse_string). */ 78 | IW_EXPORT int iwini_parse_stream( 79 | iwini_reader reader, void *stream, iwini_handler handler, 80 | void *user); 81 | 82 | /* Same as ini_parse(), but takes a zero-terminated string with the INI data 83 | instead of a file. Useful for parsing INI data from a network socket or 84 | already in memory. */ 85 | IW_EXPORT int iwini_parse_string(const char *string, iwini_handler handler, void *user); 86 | 87 | /* Nonzero to allow multi-line value parsing, in the style of Python's 88 | configparser. If allowed, ini_parse() will call the handler with the same 89 | name for each subsequent line parsed. */ 90 | #ifndef IWINI_ALLOW_MULTILINE 91 | #define IWINI_ALLOW_MULTILINE 1 92 | #endif 93 | 94 | /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of 95 | the file. See https://github.com/benhoyt/inih/issues/21 */ 96 | #ifndef IWINI_ALLOW_BOM 97 | #define IWINI_ALLOW_BOM 1 98 | #endif 99 | 100 | /* Chars that begin a start-of-line comment. Per Python configparser, allow 101 | both ; and # comments at the start of a line by default. */ 102 | #ifndef IWINI_START_COMMENT_PREFIXES 103 | #define IWINI_START_COMMENT_PREFIXES ";#" 104 | #endif 105 | 106 | /* Nonzero to allow inline comments (with valid inline comment characters 107 | specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match 108 | Python 3.2+ configparser behaviour. */ 109 | #ifndef IWINI_ALLOW_INLINE_COMMENTS 110 | #define IWINI_ALLOW_INLINE_COMMENTS 1 111 | #endif 112 | #ifndef IWINI_INLINE_COMMENT_PREFIXES 113 | #define IWINI_INLINE_COMMENT_PREFIXES ";" 114 | #endif 115 | 116 | /* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ 117 | #ifndef IWINI_USE_STACK 118 | #define IWINI_USE_STACK 1 119 | #endif 120 | 121 | /* Maximum line length for any line in INI file (stack or heap). Note that 122 | this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ 123 | #ifndef IWINI_MAX_LINE 124 | #define IWINI_MAX_LINE 200 125 | #endif 126 | 127 | /* Nonzero to allow heap line buffer to grow via realloc(), zero for a 128 | fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is 129 | zero. */ 130 | #ifndef IWINI_ALLOW_REALLOC 131 | #define IWINI_ALLOW_REALLOC 0 132 | #endif 133 | 134 | /* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK 135 | is zero. */ 136 | #ifndef IWINI_INITIAL_ALLOC 137 | #define IWINI_INITIAL_ALLOC 200 138 | #endif 139 | 140 | /* Stop parsing on first error (default is to keep parsing). */ 141 | #ifndef IWINI_STOP_ON_FIRST_ERROR 142 | #define IWINI_STOP_ON_FIRST_ERROR 0 143 | #endif 144 | 145 | /* Nonzero to call the handler at the start of each new section (with 146 | name and value NULL). Default is to only call the handler on 147 | each name=value pair. */ 148 | #ifndef IWINI_CALL_HANDLER_ON_NEW_SECTION 149 | #define IWINI_CALL_HANDLER_ON_NEW_SECTION 0 150 | #endif 151 | 152 | /* Nonzero to allow a name without a value (no '=' or ':' on the line) and 153 | call the handler with value NULL in this case. Default is to treat 154 | no-value lines as an error. */ 155 | #ifndef IWINI_ALLOW_NO_VALUE 156 | #define IWINI_ALLOW_NO_VALUE 0 157 | #endif 158 | 159 | /* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory 160 | allocation functions (INI_USE_STACK must also be 0). These functions must 161 | have the same signatures as malloc/free/realloc and behave in a similar 162 | way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */ 163 | #ifndef IWINI_CUSTOM_ALLOCATOR 164 | #define IWINI_CUSTOM_ALLOCATOR 0 165 | #endif 166 | 167 | 168 | #ifdef __cplusplus 169 | } 170 | #endif 171 | 172 | IW_EXTERN_C_END; 173 | #endif /* IWINI_H */ 174 | -------------------------------------------------------------------------------- /src/utils/tests/iwutils_test1.c: -------------------------------------------------------------------------------- 1 | #include "iowow.h" 2 | #include "iwcfg.h" 3 | #include 4 | #include "iwutils.h" 5 | #include "iwpool.h" 6 | #include "iwrb.h" 7 | #include "iwconv.h" 8 | 9 | static int init_suite(void) { 10 | return iw_init(); 11 | } 12 | 13 | static int clean_suite(void) { 14 | return 0; 15 | } 16 | 17 | static const char* _replace_mapper1(const char *key, void *op) { 18 | if (!strcmp(key, "{}")) { 19 | return "Mother"; 20 | } else if (!strcmp(key, "you")) { 21 | return "I"; 22 | } else if (!strcmp(key, "?")) { 23 | return "?!!"; 24 | } else { 25 | return 0; 26 | } 27 | } 28 | 29 | static void test_iwu_replace_into(void) { 30 | IWXSTR *res = 0; 31 | const char *data = "What you said about my {}?"; 32 | const char *keys[] = { "{}", "$", "?", "you", "my" }; 33 | iwrc rc = iwu_replace(&res, data, strlen(data), keys, 5, _replace_mapper1, 0); 34 | CU_ASSERT_EQUAL_FATAL(rc, 0); 35 | CU_ASSERT_PTR_NOT_NULL_FATAL(res); 36 | fprintf(stderr, "\n%s", iwxstr_ptr(res)); 37 | CU_ASSERT_STRING_EQUAL(iwxstr_ptr(res), "What I said about my Mother?!!"); 38 | iwxstr_destroy(res); 39 | } 40 | 41 | static void test_iwpool_split_string(void) { 42 | struct iwpool *pool = iwpool_create(128); 43 | CU_ASSERT_PTR_NOT_NULL_FATAL(pool); 44 | const char **res = iwpool_split_string(pool, " foo , bar:baz,,z,", ",:", true); 45 | CU_ASSERT_PTR_NOT_NULL_FATAL(res); 46 | int i = 0; 47 | for ( ; res[i]; ++i) { 48 | switch (i) { 49 | case 0: 50 | CU_ASSERT_STRING_EQUAL(res[i], "foo"); 51 | break; 52 | case 1: 53 | CU_ASSERT_STRING_EQUAL(res[i], "bar"); 54 | break; 55 | case 2: 56 | CU_ASSERT_STRING_EQUAL(res[i], "baz"); 57 | break; 58 | case 3: 59 | CU_ASSERT_STRING_EQUAL(res[i], ""); 60 | break; 61 | case 4: 62 | CU_ASSERT_STRING_EQUAL(res[i], "z"); 63 | break; 64 | } 65 | } 66 | CU_ASSERT_EQUAL(i, 5); 67 | 68 | res = iwpool_split_string(pool, " foo , bar:baz,,z,", ",:", false); 69 | CU_ASSERT_PTR_NOT_NULL_FATAL(res); 70 | i = 0; 71 | for ( ; res[i]; ++i) { 72 | switch (i) { 73 | case 0: 74 | CU_ASSERT_STRING_EQUAL(res[i], " foo "); 75 | break; 76 | case 1: 77 | CU_ASSERT_STRING_EQUAL(res[i], " bar"); 78 | break; 79 | case 2: 80 | CU_ASSERT_STRING_EQUAL(res[i], "baz"); 81 | break; 82 | case 3: 83 | CU_ASSERT_STRING_EQUAL(res[i], ""); 84 | break; 85 | case 4: 86 | CU_ASSERT_STRING_EQUAL(res[i], "z"); 87 | break; 88 | } 89 | } 90 | CU_ASSERT_EQUAL(i, 5); 91 | 92 | res = iwpool_split_string(pool, " foo ", ",", false); 93 | CU_ASSERT_PTR_NOT_NULL_FATAL(res); 94 | i = 0; 95 | for ( ; res[i]; ++i) { 96 | switch (i) { 97 | case 0: 98 | CU_ASSERT_STRING_EQUAL(res[i], " foo "); 99 | break; 100 | } 101 | } 102 | CU_ASSERT_EQUAL(i, 1); 103 | 104 | 105 | res = iwpool_printf_split(pool, ",", true, "%s,%s", "foo", "bar"); 106 | CU_ASSERT_PTR_NOT_NULL_FATAL(res); 107 | i = 0; 108 | for ( ; res[i]; ++i) { 109 | switch (i) { 110 | case 0: 111 | CU_ASSERT_STRING_EQUAL(res[i], "foo"); 112 | break; 113 | case 1: 114 | CU_ASSERT_STRING_EQUAL(res[i], "bar"); 115 | break; 116 | } 117 | } 118 | CU_ASSERT_EQUAL(i, 2); 119 | 120 | 121 | iwpool_destroy(pool); 122 | } 123 | 124 | static void test_iwpool_printf(void) { 125 | struct iwpool *pool = iwpool_create(128); 126 | CU_ASSERT_PTR_NOT_NULL_FATAL(pool); 127 | const char *res = iwpool_printf(pool, "%s=%s", "foo", "bar"); 128 | CU_ASSERT_PTR_NOT_NULL_FATAL(pool); 129 | CU_ASSERT_STRING_EQUAL(res, "foo=bar"); 130 | iwpool_destroy(pool); 131 | } 132 | 133 | static void test_iwrb1(void) { 134 | int *p; 135 | IWRB_ITER iter; 136 | IWRB *rb = iwrb_create(sizeof(int), 7); 137 | CU_ASSERT_PTR_NOT_NULL_FATAL(rb); 138 | CU_ASSERT_EQUAL(iwrb_num_cached(rb), 0); 139 | int idx = 0; 140 | int data[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 }; 141 | 142 | iwrb_put(rb, &data[idx++]); 143 | CU_ASSERT_EQUAL(iwrb_num_cached(rb), 1); 144 | 145 | iwrb_iter_init(rb, &iter); 146 | p = iwrb_iter_prev(&iter); 147 | CU_ASSERT_PTR_NOT_NULL_FATAL(p); 148 | CU_ASSERT_EQUAL(*p, 1); 149 | p = iwrb_iter_prev(&iter); 150 | CU_ASSERT_PTR_NULL(p); 151 | p = iwrb_peek(rb); 152 | CU_ASSERT_PTR_NOT_NULL_FATAL(p); 153 | CU_ASSERT_EQUAL(*p, 1); 154 | 155 | for (int i = 0; i < 6; ++i) { 156 | iwrb_put(rb, &data[i + 1]); 157 | } 158 | p = iwrb_peek(rb); 159 | CU_ASSERT_PTR_NOT_NULL_FATAL(p); 160 | CU_ASSERT_EQUAL(*p, 7); 161 | 162 | iwrb_iter_init(rb, &iter); 163 | for (int i = 7; i > 0; --i) { 164 | p = iwrb_iter_prev(&iter); 165 | CU_ASSERT_PTR_NOT_NULL_FATAL(p); 166 | CU_ASSERT_EQUAL(*p, i); 167 | } 168 | CU_ASSERT_PTR_NULL(iwrb_iter_prev(&iter)); 169 | iwrb_put(rb, &data[7]); 170 | p = iwrb_peek(rb); 171 | CU_ASSERT_PTR_NOT_NULL_FATAL(p); 172 | CU_ASSERT_EQUAL(*p, 8); 173 | 174 | iwrb_iter_init(rb, &iter); 175 | for (int i = 8; i > 1; --i) { 176 | p = iwrb_iter_prev(&iter); 177 | CU_ASSERT_PTR_NOT_NULL_FATAL(p); 178 | CU_ASSERT_EQUAL(*p, i); 179 | } 180 | CU_ASSERT_PTR_NULL(iwrb_iter_prev(&iter)); 181 | 182 | for (int i = 8; i < 14; ++i) { 183 | iwrb_put(rb, &data[i]); 184 | } 185 | 186 | iwrb_iter_init(rb, &iter); 187 | for (int i = 0; i < 7; ++i) { 188 | p = iwrb_iter_prev(&iter); 189 | CU_ASSERT_PTR_NOT_NULL_FATAL(p); 190 | CU_ASSERT_EQUAL(*p, 14 - i); 191 | } 192 | CU_ASSERT_PTR_NULL(iwrb_iter_prev(&iter)); 193 | 194 | iwrb_destroy(&rb); 195 | CU_ASSERT_PTR_NULL(rb); 196 | } 197 | 198 | static void iwitoa_issue48(void) { 199 | char buf[IWNUMBUF_SIZE]; 200 | int len = iwitoa(INT64_MIN, buf, sizeof(buf)); 201 | CU_ASSERT_EQUAL(len, 20); 202 | CU_ASSERT_STRING_EQUAL("-9223372036854775808", buf); 203 | } 204 | 205 | int main(void) { 206 | CU_pSuite pSuite = NULL; 207 | 208 | /* Initialize the CUnit test registry */ 209 | if (CUE_SUCCESS != CU_initialize_registry()) { 210 | return CU_get_error(); 211 | } 212 | 213 | /* Add a suite to the registry */ 214 | pSuite = CU_add_suite("iwutils_test1", init_suite, clean_suite); 215 | 216 | if (NULL == pSuite) { 217 | CU_cleanup_registry(); 218 | return CU_get_error(); 219 | } 220 | 221 | /* Add the tests to the suite */ 222 | if ( (NULL == CU_add_test(pSuite, "test_iwu_replace_into", test_iwu_replace_into)) 223 | || (NULL == CU_add_test(pSuite, "test_iwpool_split_string", test_iwpool_split_string)) 224 | || (NULL == CU_add_test(pSuite, "test_iwpool_printf", test_iwpool_printf)) 225 | || (NULL == CU_add_test(pSuite, "test_iwrb1", test_iwrb1)) 226 | || (NULL == CU_add_test(pSuite, "iwitoa_issue48", iwitoa_issue48))) { 227 | CU_cleanup_registry(); 228 | return CU_get_error(); 229 | } 230 | 231 | /* Run all tests using the CUnit Basic interface */ 232 | CU_basic_set_mode(CU_BRM_VERBOSE); 233 | CU_basic_run_tests(); 234 | int ret = CU_get_error() || CU_get_number_of_failures(); 235 | CU_cleanup_registry(); 236 | return ret; 237 | } 238 | --------------------------------------------------------------------------------