├── CMake ├── FindMath.cmake ├── FindValgrind.cmake ├── GCCCompatibleCompilerOptions.cmake └── test.cmake ├── CMakeLists.txt ├── LICENSE ├── README.md ├── chck.pc.in ├── chck ├── CMakeLists.txt ├── atlas │ ├── CMakeLists.txt │ ├── atlas.c │ ├── atlas.h │ └── test.c ├── bams │ ├── CMakeLists.txt │ ├── README.md │ ├── bams.h │ └── test.c ├── buffer │ ├── CMakeLists.txt │ ├── README.md │ ├── buffer.c │ ├── buffer.h │ ├── endianess.h │ └── test.c ├── dl │ ├── CMakeLists.txt │ ├── dl.c │ ├── dl.h │ └── test.c ├── fs │ ├── CMakeLists.txt │ ├── README.md │ ├── fs.c │ ├── fs.h │ └── test.c ├── lut │ ├── CMakeLists.txt │ ├── README.md │ ├── lut.c │ ├── lut.h │ └── test.c ├── macros.h ├── math │ ├── CMakeLists.txt │ ├── README.md │ ├── math.h │ └── test.c ├── overflow │ ├── CMakeLists.txt │ ├── overflow.h │ └── test.c ├── pool │ ├── CMakeLists.txt │ ├── README.md │ ├── pool.c │ ├── pool.h │ └── test.c ├── sjis │ ├── CMakeLists.txt │ ├── README.md │ ├── sjis.c │ ├── sjis.h │ ├── test.c │ └── utf8sjis.h ├── string │ ├── CMakeLists.txt │ ├── string.c │ ├── string.h │ └── test.c ├── thread │ ├── CMakeLists.txt │ ├── README.md │ └── queue │ │ ├── CMakeLists.txt │ │ ├── README.md │ │ ├── queue.c │ │ ├── queue.h │ │ └── test.c ├── unicode │ ├── CMakeLists.txt │ ├── README.md │ ├── test.c │ ├── unicode.c │ └── unicode.h └── xdg │ ├── CMakeLists.txt │ ├── test.c │ ├── xdg.c │ └── xdg.h └── doxygen ├── CMakeLists.txt └── Doxyfile.in /CMake/FindMath.cmake: -------------------------------------------------------------------------------- 1 | #.rst: 2 | # FindMath 3 | # ------- 4 | # 5 | # Find standard C math library 6 | # 7 | # Try to find standard C math library. The following values are defined 8 | # 9 | # :: 10 | # 11 | # MATH_FOUND - True if math is available 12 | # MATH_LIBRARY - Library for math 13 | # 14 | #============================================================================= 15 | # Copyright (c) 2015 Jari Vetoniemi 16 | # 17 | # Distributed under the OSI-approved BSD License (the "License"); 18 | # 19 | # This software is distributed WITHOUT ANY WARRANTY; without even the 20 | # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 21 | # See the License for more information. 22 | #============================================================================= 23 | 24 | set_package_properties(Math PROPERTIES 25 | DESCRIPTION "Standard C math library") 26 | 27 | find_library(MATH_LIBRARY m) 28 | include(FindPackageHandleStandardArgs) 29 | find_package_handle_standard_args(MATH DEFAULT_MSG MATH_LIBRARY) 30 | mark_as_advanced(MATH_LIBRARY) 31 | -------------------------------------------------------------------------------- /CMake/FindValgrind.cmake: -------------------------------------------------------------------------------- 1 | # - Find Valgrind 2 | # 3 | # VALGRIND_FOUND 4 | # VALGRIND_INCLUDE_DIRS 5 | 6 | find_path(VALGRIND_INCLUDE_DIRS NAMES valgrind/valgrind.h valgrind/helgrind.h valgrind/memcheck.h) 7 | 8 | include(FindPackageHandleStandardArgs) 9 | find_package_handle_standard_args(valgrind DEFAULT_MSG VALGRIND_INCLUDE_DIRS) 10 | mark_as_advanced(VALGRIND_INCLUDE_DIRS) 11 | -------------------------------------------------------------------------------- /CMake/GCCCompatibleCompilerOptions.cmake: -------------------------------------------------------------------------------- 1 | include(CheckCCompilerFlag) 2 | 3 | # Add list of compiler warnings 4 | # Every warning gets checked with check_c_compiler_flags 5 | function(add_compiler_warnings) 6 | foreach (flag ${ARGN}) 7 | check_c_compiler_flag(${flag} ok) 8 | if (ok) 9 | add_compile_options(${flag}) 10 | endif () 11 | endforeach () 12 | endfunction () 13 | 14 | # Create new ${EXE,MODULE,SHARED}_LINKER_FLAGS build type for list of linker flags 15 | # Every linker flag gets checked with check_c_compiler_flag 16 | function(create_custom_linker_flags name) 17 | set(ldflags) 18 | foreach (flag ${ARGN}) 19 | check_c_compiler_flag(-Wl,${flag} ok) 20 | if (ok) 21 | if (ldflags) 22 | set(ldflags "${ldflags},${flag}") 23 | else () 24 | set(ldflags "-Wl,${flag}") 25 | endif () 26 | endif () 27 | endforeach () 28 | 29 | string(TOUPPER ${name} upper) 30 | set(CMAKE_EXE_LINKER_FLAGS_${upper} "${ldflags}" CACHE STRING "${name} exe linker flags" FORCE) 31 | set(CMAKE_MODULE_LINKER_FLAGS_${upper} "${ldflags}" CACHE STRING "${name} module linker flags" FORCE) 32 | set(CMAKE_SHARED_LINKER_FLAGS_${upper} "${ldflags}" CACHE STRING "${name} shared linker flags" FORCE) 33 | mark_as_advanced(CMAKE_EXE_LINKER_FLAGS_${upper} CMAKE_SHARED_LINKER_FLAGS_${upper} CMAKE_MODULE_LINKER_FLAGS_${upper}) 34 | endfunction () 35 | 36 | # Create new {C,CXX}_FLAGS build type for list of compiler flags 37 | # Every compiler flag gets checked with check_c_compiler_flag 38 | function(create_custom_compiler_flags name) 39 | set(cflags) 40 | foreach (flag ${ARGN}) 41 | check_c_compiler_flag(${flag} ok) 42 | if (ok) 43 | if (cflags) 44 | set(cflags "${cflags} ${flag}") 45 | else () 46 | set(cflags "${flag}") 47 | endif () 48 | endif () 49 | endforeach () 50 | 51 | string(TOUPPER ${name} upper) 52 | set(CMAKE_C_FLAGS_${upper} "${cflags}" CACHE STRING "${name} C flags" FORCE) 53 | set(CMAKE_CXX_FLAGS_${upper} "${cflags}" CACHE STRING "${name} CXX flags" FORCE) 54 | mark_as_advanced(CMAKE_CXX_FLAGS_${upper} CMAKE_C_FLAGS_${upper}) 55 | endfunction () 56 | -------------------------------------------------------------------------------- /CMake/test.cmake: -------------------------------------------------------------------------------- 1 | # Wraps add_test 2 | # Uses CTEST_EXEC_WITH variable which you can set to for example valgrind to run tests with valgrind. 3 | # Also uses CTEST_OUTPUT_DIRECTORY to set global output directory 4 | 5 | function(add_test_ex target) 6 | add_test(NAME ${target} COMMAND ${CMAKE_CROSSCOMPILING_EMULATOR} ${CTEST_EXEC_WITH} $) 7 | set_target_properties(${target} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CTEST_OUTPUT_DIRECTORY}") 8 | endfunction() 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.1) 2 | PROJECT(chck VERSION 0.0.1 LANGUAGES C) 3 | list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMake") 4 | 5 | # CPack 6 | set(CPACK_SYSTEM_NAME "${CMAKE_SYSTEM_NAME}-${CMAKE_SYSTEM_PROCESSOR}") 7 | set(CPACK_GENERATOR "7Z") 8 | set(CPACK_PACKAGE_VERSION "${PROJECT_VERSION}") 9 | set(CPACK_PACKAGE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/pkg") 10 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Collection of C utilites") 11 | set(CPACK_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION ON) 12 | 13 | # Includes 14 | include(GNUInstallDirs) 15 | include(FeatureSummary) 16 | include(CPack) 17 | include(CTest) 18 | include(test) 19 | 20 | OPTION(CHCK_BUILD_STATIC "Build chck as static library" OFF) 21 | OPTION(CHCK_BUILD_TESTS "Build chck tests" ON) 22 | 23 | add_feature_info(Static CHCK_BUILD_STATIC "Compile as static library") 24 | add_feature_info(Tests CHCK_BUILD_TESTS "Compile tests") 25 | 26 | if (NOT CHCK_BUILD_STATIC) 27 | set(BUILD_SHARED_LIBS ON) 28 | endif () 29 | 30 | if (MINGW) 31 | set(BUILD_SHARED_LIBS OFF) 32 | endif () 33 | 34 | set(CTEST_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/test") 35 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CTEST_OUTPUT_DIRECTORY}") 36 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") 37 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") 38 | 39 | # Compiler options 40 | include(GCCCompatibleCompilerOptions) 41 | 42 | if (MINGW) 43 | set(ldflags -O1 --sort-common --as-needed -static) 44 | add_definitions(-D__USE_MINGW_ANSI_STDIO=1) 45 | elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) 46 | set(ldflags -O1 --sort-common --as-needed -z,relro -z,now) 47 | endif () 48 | 49 | check_c_compiler_flag(-fstack-protector-strong has_fstack_protector_strong) 50 | if (has_fstack_protector_strong) 51 | list(APPEND cflags -fstack-protector-strong -fstack-check --param ssp-buffer-size=4) 52 | else () 53 | list(APPEND cflags -fstack-protector-all -fstack-check --param ssp-buffer-size=4) 54 | endif () 55 | 56 | create_custom_linker_flags(Upstream ${ldflags}) 57 | create_custom_compiler_flags(Upstream -g -O2 ${cflags}) 58 | 59 | add_compiler_warnings(-Wall -Wextra -Wno-variadic-macros -Wno-long-long -Wformat=2 -Winit-self -Wfloat-equal -Wcast-align -Wpointer-arith -Wmissing-prototypes) 60 | 61 | if (CMAKE_C_COMPILER_ID MATCHES "Clang") 62 | add_compiler_warnings(-Wno-pointer-bool-conversion -Wno-missing-field-initializers -Wno-missing-braces) 63 | endif () 64 | 65 | # - Each utility should be independant. 66 | # This makes sure everything that isn't visible is marked as static in source files. 67 | # Thus we can simply set -fvisibilty=default and have only relevant symbols in shared libraries. 68 | add_compile_options(-fvisibility=default) 69 | 70 | # -std=c99 -fpic -fpie -D_DEFAULT_SOURCE 71 | set(CMAKE_C_STANDARD 99) 72 | set(CMAKE_C_STANDARD_REQUIRED ON) 73 | set(CMAKE_C_EXTENSIONS OFF) 74 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 75 | add_definitions(-D_DEFAULT_SOURCE) 76 | 77 | # include prefix is allowed 78 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}) 79 | 80 | # Code 81 | add_subdirectory(chck) 82 | 83 | # Documentation 84 | add_subdirectory(doxygen) 85 | 86 | # Phony for add_subproject 87 | add_custom_target(chck) 88 | 89 | set(CHCK_LINKABLE) 90 | foreach (c ${CHCK_COMPONENTS}) 91 | list(APPEND CHCK_LINKABLE -l${c}) 92 | endforeach () 93 | string(REPLACE ";" " " CHCK_LINKABLE "${CHCK_LINKABLE}") 94 | string(REPLACE "_" "-" CHCK_LINKABLE "${CHCK_LINKABLE}") 95 | 96 | # Add pkgconfig 97 | configure_file(chck.pc.in chck.pc @ONLY) 98 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/chck.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 99 | 100 | set(CHCK_INCLUDE_DIRS "${CMAKE_CURRENT_SOURCE_DIR}" CACHE STRING "Include directories of chck" FORCE) 101 | set(CHCK_LIBRARIES ${CHCK_COMPONENTS} CACHE STRING "Libraries needed for chck" FORCE) 102 | mark_as_advanced(CHCK_LIBRARIES CHCK_INCLUDE_DIRS) 103 | 104 | if ("${CMAKE_PROJECT_NAME}" STREQUAL "${PROJECT_NAME}") 105 | feature_summary(WHAT ALL) 106 | endif () 107 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) <2013> 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chck 2 | [![buildhck status](http://build.cloudef.pw/build/chck/master/linux x86_64/current/status.svg)](http://build.cloudef.pw/build/chck/master/linux%20x86_64) 3 | 4 | Collection of C utilities taken and cleaned up from my other projects 5 | 6 | ## Building 7 | 8 | mkdir target && cd target # - create build target directory 9 | cmake -DCMAKE_BUILD_TYPE=Upstream .. # - run CMake 10 | make # - compile 11 | 12 | ## Running tests 13 | 14 | cd target # - cd to your target directory 15 | make test # - run tests 16 | 17 | ## Installing 18 | 19 | The utilites are small and mainly meant to be copied to your project. 20 | However libraries for each component is built with the cmake. 21 | 22 | ## Some rules what goes here 23 | 24 | * Each utility should be independant. 25 | * Though, it's ok to include macros.h and overflow.h 26 | * Each utility should contain tests. 27 | * Each utility should have use more than once. 28 | 29 | ## License 30 | 31 | zlib, see LICENSE file for more information 32 | -------------------------------------------------------------------------------- /chck.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ 4 | includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ 5 | 6 | Name: @PROJECT_NAME@ 7 | Description: Collection of C utilities (Shared linking not recommended) 8 | Version: @PROJECT_VERSION@ 9 | Libs: -L${libdir} @CHCK_LINKABLE@ 10 | Libs.private: -lm -ldl 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /chck/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(modules buffer pool lut atlas math bams dl fs sjis xdg string thread overflow unicode) 2 | 3 | macro (install_headers) 4 | file(RELATIVE_PATH rel "${CMAKE_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}") 5 | install(FILES ${ARGV} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${rel}") 6 | endmacro () 7 | 8 | macro (install_libraries) 9 | string(REGEX MATCHALL "[0-9]+" VERSION_COMPONENTS ${PROJECT_VERSION}) 10 | list(GET VERSION_COMPONENTS 0 SOVERSION) 11 | foreach (target ${ARGN}) 12 | string(REGEX REPLACE "_" "-" name ${target}) 13 | set_target_properties(${target} PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION ${SOVERSION} OUTPUT_NAME ${name}) 14 | endforeach () 15 | install(TARGETS ${ARGV} DESTINATION "${CMAKE_INSTALL_LIBDIR}") 16 | set(CHCK_COMPONENTS ${CHCK_COMPONENTS} ${ARGV} PARENT_SCOPE) 17 | endmacro () 18 | 19 | foreach (module ${modules}) 20 | add_subdirectory(${module}) 21 | endforeach (module) 22 | 23 | set(CHCK_COMPONENTS ${CHCK_COMPONENTS} PARENT_SCOPE) 24 | 25 | install_headers(macros.h) 26 | -------------------------------------------------------------------------------- /chck/atlas/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(chck_atlas atlas.c) 2 | install_libraries(chck_atlas) 3 | install_headers(atlas.h) 4 | 5 | if (CHCK_BUILD_TESTS) 6 | add_executable(atlas_test test.c) 7 | target_link_libraries(atlas_test PRIVATE chck_atlas) 8 | add_test_ex(atlas_test) 9 | endif () 10 | -------------------------------------------------------------------------------- /chck/atlas/atlas.c: -------------------------------------------------------------------------------- 1 | #include "atlas.h" 2 | #include 3 | #include 4 | #include /* for malloc */ 5 | #include /* for UINT_MAX */ 6 | #include /* for assert */ 7 | 8 | static void 9 | texture(struct chck_atlas_texture *texture, uint32_t width, uint32_t height) 10 | { 11 | assert(texture); 12 | *texture = (struct chck_atlas_texture){ .rect = { .w = width, .h = height }, .area = width * height, .longest_edge = (width >= height ? width : height) }; 13 | } 14 | 15 | static void 16 | texture_place(struct chck_atlas_texture *texture, const uint32_t x, const uint32_t y, const bool flipped) 17 | { 18 | assert(texture); 19 | texture->rect.x = x; 20 | texture->rect.y = y; 21 | texture->flipped = flipped; 22 | texture->placed = true; 23 | } 24 | 25 | static bool 26 | node_fits(struct chck_atlas_node *node, uint32_t width, uint32_t height, uint32_t *out_edge_count) 27 | { 28 | assert(node && out_edge_count); 29 | 30 | uint32_t ec = 0; 31 | if (width == node->rect.w) ec += (height == node->rect.w ? 2 : 1); 32 | else if (width == node->rect.h) ec += (height == node->rect.w ? 2 : 1); 33 | else if (height == node->rect.w) ec++; 34 | else if (height == node->rect.h) ec++; 35 | 36 | *out_edge_count = ec; 37 | return ((width <= node->rect.w && height <= node->rect.w) || (height <= node->rect.h && width <= node->rect.h)); 38 | } 39 | 40 | static bool 41 | node_merge(struct chck_atlas_node *n1, struct chck_atlas_node *n2) 42 | { 43 | assert(n1 && n2); 44 | bool ret = false; 45 | 46 | /* if we share the top edge then.. */ 47 | if (n1->rect.x == n2->rect.x && n1->rect.w == n2->rect.w && n1->rect.y == n2->rect.y + n2->rect.h) { 48 | n1->rect.y = n2->rect.y; 49 | n1->rect.h += n2->rect.h; 50 | ret = true; 51 | } 52 | /* if we share the bottom edge */ 53 | else if (n1->rect.x == n2->rect.x && n1->rect.w == n2->rect.w && n1->rect.y + n1->rect.h == n2->rect.y) { 54 | n1->rect.h += n2->rect.h; 55 | ret = true; 56 | } 57 | /* if we share the left edge */ 58 | else if (n1->rect.y == n2->rect.y && n1->rect.y + n1->rect.h == n2->rect.y && n1->rect.x == n2->rect.x + n2->rect.w) { 59 | n1->rect.x = n2->rect.x; 60 | n1->rect.w += n2->rect.w; 61 | ret = true; 62 | } 63 | /* if we share the left edge */ 64 | else if (n1->rect.y == n2->rect.y && n1->rect.y + n1->rect.h == n2->rect.y && n1->rect.x + n1->rect.w == n2->rect.x) { 65 | n1->rect.w += n2->rect.w; 66 | ret = true; 67 | } 68 | 69 | return ret; 70 | } 71 | 72 | static bool 73 | atlas_node_add(struct chck_atlas *atlas, uint32_t x, uint32_t y, uint32_t width, uint32_t height) 74 | { 75 | assert(atlas); 76 | 77 | struct chck_atlas_node *node; 78 | if (!(node = calloc(1, sizeof(*node)))) 79 | return false; 80 | 81 | node->rect.x = x; 82 | node->rect.y = y; 83 | node->rect.w = width; 84 | node->rect.h = height; 85 | node->next = atlas->free_list; 86 | atlas->free_list = node; 87 | return true; 88 | } 89 | 90 | static bool 91 | merge_nodes(struct chck_atlas *atlas) 92 | { 93 | assert(atlas); 94 | 95 | struct chck_atlas_node *f = atlas->free_list; 96 | while (f) { 97 | struct chck_atlas_node *c = atlas->free_list, *prev = NULL; 98 | while (c) { 99 | if (f != c) { 100 | if (node_merge(f, c) && prev) { 101 | prev->next = c->next; 102 | free(c); 103 | return true; 104 | } 105 | } 106 | prev = c; 107 | c = c->next; 108 | } 109 | f = f->next; 110 | } 111 | 112 | return false; 113 | } 114 | 115 | static void 116 | check_dimensions(struct chck_atlas *atlas, uint32_t *in_out_w, uint32_t *in_out_h) 117 | { 118 | assert(atlas && in_out_w && in_out_h); 119 | for (uint32_t i = 0; i != atlas->count; ++i) { 120 | const struct chck_atlas_texture *t = &atlas->textures[i]; 121 | if (t->rect.w > *in_out_w) *in_out_w = t->rect.w; 122 | if (t->rect.h > *in_out_h) *in_out_h = t->rect.h; 123 | } 124 | } 125 | 126 | bool 127 | chck_atlas(struct chck_atlas *atlas) 128 | { 129 | assert(atlas); 130 | *atlas = (struct chck_atlas){0}; 131 | return true; 132 | } 133 | 134 | void 135 | chck_atlas_release(struct chck_atlas *atlas) 136 | { 137 | if (!atlas) 138 | return; 139 | 140 | free(atlas->textures); 141 | 142 | if (atlas->free_list) { 143 | struct chck_atlas_node *next = atlas->free_list, *kill; 144 | while (next) { 145 | kill = next; 146 | next = next->next; 147 | free(kill); 148 | } 149 | } 150 | 151 | *atlas = (struct chck_atlas){0}; 152 | } 153 | 154 | uint32_t 155 | chck_atlas_push(struct chck_atlas *atlas, uint32_t width, uint32_t height) 156 | { 157 | assert(atlas); 158 | 159 | size_t ncount; 160 | if (unlikely(chck_add_ofsz(atlas->count, 1, &ncount)) || !ncount) 161 | return atlas->count; 162 | 163 | void *tmp; 164 | if (!(tmp = chck_realloc_mul_of(atlas->textures, ncount, sizeof(*atlas->textures)))) 165 | return atlas->count; 166 | 167 | atlas->textures = tmp; 168 | atlas->count = ncount; 169 | 170 | texture(&atlas->textures[atlas->count - 1], width, height); 171 | 172 | if (width > atlas->longest_edge) 173 | atlas->longest_edge = width; 174 | 175 | if (height > atlas->longest_edge) 176 | atlas->longest_edge = height; 177 | 178 | atlas->total_area += width * height; 179 | return atlas->count; 180 | } 181 | 182 | uint32_t 183 | chck_atlas_pop(struct chck_atlas *atlas) 184 | { 185 | assert(atlas); 186 | 187 | if (atlas->count <= 0) 188 | return atlas->count; 189 | 190 | struct chck_atlas_texture *t = &atlas->textures[atlas->count - 1]; 191 | atlas->total_area -= t->rect.w * t->rect.h; 192 | 193 | void *tmp = NULL; 194 | if (atlas->count > 1) { 195 | if (!(tmp = chck_realloc_mul_of(atlas->textures, (atlas->count - 1), sizeof(*atlas->textures)))) 196 | return atlas->count; 197 | } else { 198 | free(atlas->textures); 199 | } 200 | 201 | atlas->textures = tmp; 202 | atlas->count -= 1; 203 | return atlas->count; 204 | } 205 | 206 | const struct chck_atlas_texture* 207 | chck_atlas_get(const struct chck_atlas *atlas, uint32_t index, struct chck_atlas_rect *out_transformed) 208 | { 209 | assert(atlas && index > 0); 210 | 211 | if (out_transformed) 212 | *out_transformed = (struct chck_atlas_rect){0}; 213 | 214 | if (index - 1 >= atlas->count) 215 | return NULL; 216 | 217 | const struct chck_atlas_texture *t = &atlas->textures[index - 1]; 218 | if (out_transformed) { 219 | out_transformed->x = t->rect.x; 220 | out_transformed->y = t->rect.y; 221 | out_transformed->w = (t->flipped ? t->rect.h : t->rect.w); 222 | out_transformed->h = (t->flipped ? t->rect.w : t->rect.h); 223 | } 224 | return t; 225 | } 226 | 227 | uint32_t 228 | chck_atlas_pack(struct chck_atlas *atlas, bool force_pot, bool one_px_border, uint32_t *out_w, uint32_t *out_h) 229 | { 230 | assert(atlas); 231 | 232 | if (one_px_border) { 233 | for (uint32_t i = 0; i != atlas->count; ++i) { 234 | struct chck_atlas_texture *t = &atlas->textures[i]; 235 | t->rect.w += 2; 236 | t->rect.h += 2; 237 | } 238 | atlas->longest_edge += 2; 239 | } 240 | 241 | if (force_pot) 242 | atlas->longest_edge = chck_npotu32(atlas->longest_edge); 243 | 244 | uint32_t width = atlas->longest_edge; 245 | uint32_t count = (uint32_t)((float)atlas->total_area/(atlas->longest_edge * atlas->longest_edge) + 0.5f); 246 | uint32_t height = (count + 2) * atlas->longest_edge; 247 | 248 | if (force_pot) 249 | height = chck_npotu32(height); 250 | 251 | if (width > height && !chck_equalf(height, width * 0.5f, 10.0f)) { 252 | height = width * 0.5f; 253 | width *= 0.5f; 254 | check_dimensions(atlas, &width, &height); 255 | } else if (height > width && !chck_equalf(width, height * 0.5f, 10.0f)) { 256 | width = height * 0.5f; 257 | height *= 0.5f; 258 | check_dimensions(atlas, &width, &height); 259 | } 260 | 261 | atlas_node_add(atlas, 0, 0, width, height); 262 | 263 | for (uint32_t i = 0; i != atlas->count; ++i) { 264 | uint32_t longest_edge = 0, most_area = 0, index = 0; 265 | for(uint32_t i2 = 0; i2 != atlas->count; ++i2) { 266 | struct chck_atlas_texture *t = &atlas->textures[i2]; 267 | if (t->placed) 268 | continue; 269 | 270 | if (t->longest_edge > longest_edge) { 271 | most_area = t->area; 272 | longest_edge = t->longest_edge; 273 | index = i2; 274 | } else if (t->longest_edge == longest_edge && t->area > most_area) { 275 | most_area = t->area; 276 | index = i2; 277 | } 278 | } 279 | 280 | uint32_t edge_count = 0; 281 | uint32_t least_y = UINT_MAX; 282 | uint32_t least_x = UINT_MAX; 283 | struct chck_atlas_texture *t = &atlas->textures[index]; 284 | struct chck_atlas_node *previous_best_fit = NULL; 285 | struct chck_atlas_node *best_fit = NULL; 286 | struct chck_atlas_node *previous = NULL; 287 | struct chck_atlas_node *search = atlas->free_list; 288 | 289 | while (search) { 290 | uint32_t ec; 291 | if (node_fits(search, t->rect.w, t->rect.h, &ec)) { 292 | if (ec == 2) { 293 | previous_best_fit = previous; 294 | best_fit = search; 295 | edge_count = ec; 296 | break; 297 | } else if (search->rect.y < least_y) { 298 | least_y = search->rect.y; 299 | least_x = search->rect.x; 300 | previous_best_fit = previous; 301 | best_fit = search; 302 | edge_count = ec; 303 | } else if (search->rect.y == least_y && search->rect.x < least_x) { 304 | least_x = search->rect.x; 305 | previous_best_fit = previous; 306 | best_fit = search; 307 | edge_count = ec; 308 | } 309 | } 310 | previous = search; 311 | search = search->next; 312 | } 313 | 314 | /* should always find a fit */ 315 | assert(best_fit); 316 | 317 | /* bail out on NDEBUG */ 318 | if (!best_fit) 319 | goto out; 320 | 321 | switch (edge_count) { 322 | case 0: 323 | if (t->longest_edge <= best_fit->rect.w) { 324 | bool flipped = false; 325 | uint32_t width = t->rect.w; 326 | uint32_t height = t->rect.h; 327 | 328 | if (width > height) { 329 | width = t->rect.h; 330 | height = t->rect.w; 331 | flipped = 1; 332 | } 333 | 334 | texture_place(t, best_fit->rect.x, best_fit->rect.y, flipped); 335 | atlas_node_add(atlas, best_fit->rect.x, best_fit->rect.y + height, best_fit->rect.w, best_fit->rect.h - height); 336 | 337 | best_fit->rect.x += width; 338 | best_fit->rect.w -= width; 339 | best_fit->rect.h = height; 340 | } else { 341 | assert(t->longest_edge <= best_fit->rect.h); 342 | bool flipped = false; 343 | uint32_t width = t->rect.w; 344 | uint32_t height = t->rect.h; 345 | 346 | if (width > height) { 347 | width = t->rect.h; 348 | height = t->rect.w; 349 | flipped = true; 350 | } 351 | 352 | texture_place(t, best_fit->rect.x, best_fit->rect.y, flipped); 353 | atlas_node_add(atlas, best_fit->rect.x + width, best_fit->rect.y, best_fit->rect.w - width, best_fit->rect.h); 354 | 355 | best_fit->rect.y += height; 356 | best_fit->rect.h -= height; 357 | best_fit->rect.w = width; 358 | } 359 | break; 360 | case 1: 361 | if (t->rect.w == best_fit->rect.w) { 362 | texture_place(t, best_fit->rect.x, best_fit->rect.y, 0); 363 | best_fit->rect.y += t->rect.h; 364 | best_fit->rect.h -= t->rect.h; 365 | } else if (t->rect.h == best_fit->rect.h) { 366 | texture_place(t, best_fit->rect.x, best_fit->rect.y, 0); 367 | best_fit->rect.x += t->rect.w; 368 | best_fit->rect.w -= t->rect.w; 369 | } else if (t->rect.w == best_fit->rect.h) { 370 | texture_place(t, best_fit->rect.x, best_fit->rect.y, 1); 371 | best_fit->rect.x += t->rect.h; 372 | best_fit->rect.w -= t->rect.h; 373 | } else if (t->rect.h == best_fit->rect.w) { 374 | texture_place(t, best_fit->rect.x, best_fit->rect.y, 1); 375 | best_fit->rect.y += t->rect.w; 376 | best_fit->rect.h -= t->rect.w; 377 | } 378 | break; 379 | case 2: 380 | { 381 | const bool flipped = (t->rect.w != best_fit->rect.w || t->rect.h != best_fit->rect.h); 382 | texture_place(t, best_fit->rect.x, best_fit->rect.y, flipped); 383 | 384 | if (previous_best_fit) { 385 | previous_best_fit->next = best_fit->next; 386 | } else { 387 | atlas->free_list = best_fit->next; 388 | } 389 | 390 | if (best_fit) 391 | free(best_fit); 392 | } 393 | break; 394 | } 395 | 396 | /* merge as much as we can */ 397 | while (merge_nodes(atlas)); 398 | } 399 | 400 | width = 0, height = 0; 401 | for(uint32_t i = 0; i < atlas->count; ++i) { 402 | struct chck_atlas_texture *t = &atlas->textures[i]; 403 | if (one_px_border) { 404 | t->rect.w -= 2; 405 | t->rect.h -= 2; 406 | t->rect.x += 1; 407 | t->rect.y += 1; 408 | } 409 | 410 | const uint32_t x = (t->flipped ? t->rect.x + t->rect.h : t->rect.x + t->rect.w); 411 | const uint32_t y = (t->flipped ? t->rect.y + t->rect.w : t->rect.y + t->rect.h); 412 | width = (x > width ? x : width); 413 | height = (y > height ? y : height); 414 | } 415 | 416 | if (force_pot) { 417 | width = chck_npotu32(width); 418 | height = chck_npotu32(height); 419 | } 420 | 421 | out: 422 | if (out_w) *out_w = width; 423 | if (out_h) *out_h = height; 424 | return (width * height) - atlas->total_area; 425 | } 426 | -------------------------------------------------------------------------------- /chck/atlas/atlas.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_atlas_h__ 2 | #define __chck_atlas_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct chck_atlas_rect { 9 | uint32_t x, y, w, h; 10 | }; 11 | 12 | struct chck_atlas_texture { 13 | struct chck_atlas_rect rect; 14 | 15 | // area (w * h) 16 | uint32_t area; 17 | 18 | // longest edge (w > h : h > w) 19 | uint32_t longest_edge; 20 | 21 | // is texture flipped (90 degrees) and/or placed 22 | bool flipped, placed; 23 | }; 24 | 25 | struct chck_atlas_node { 26 | struct chck_atlas_node *next; 27 | struct chck_atlas_rect rect; 28 | }; 29 | 30 | struct chck_atlas { 31 | // which nodes are not yet placed 32 | struct chck_atlas_node *free_list; 33 | 34 | // textures in this atlas 35 | struct chck_atlas_texture *textures; 36 | uint32_t count; 37 | 38 | // longest edge (w > h : h > w) 39 | uint32_t longest_edge; 40 | 41 | // total area (w * h * textures) 42 | uint32_t total_area; 43 | }; 44 | 45 | bool chck_atlas(struct chck_atlas *atlas); 46 | void chck_atlas_release(struct chck_atlas *atlas); 47 | uint32_t chck_atlas_push(struct chck_atlas *atlas, uint32_t width, uint32_t height); 48 | uint32_t chck_atlas_pop(struct chck_atlas *atlas); 49 | const struct chck_atlas_texture* chck_atlas_get(const struct chck_atlas *atlas, uint32_t index, struct chck_atlas_rect *out_transformed); 50 | 51 | // returns total unused area 52 | uint32_t chck_atlas_pack(struct chck_atlas *atlas, bool force_pot, bool one_px_border, uint32_t *out_w, uint32_t *out_h); 53 | 54 | #endif /* __chck_atlas_h__ */ 55 | -------------------------------------------------------------------------------- /chck/atlas/test.c: -------------------------------------------------------------------------------- 1 | #include "atlas.h" 2 | #include 3 | #include 4 | 5 | #undef NDEBUG 6 | #include 7 | 8 | int main(void) 9 | { 10 | /* TEST: atlas packing */ 11 | { 12 | struct chck_atlas atlas; 13 | assert(chck_atlas(&atlas)); 14 | assert(atlas.count == 0); 15 | 16 | assert(chck_atlas_push(&atlas, 320, 320)); 17 | assert(atlas.count == 1); 18 | 19 | assert(chck_atlas_pop(&atlas) == 0); 20 | assert(atlas.count == 0); 21 | 22 | assert(chck_atlas_push(&atlas, 320, 320) == 1); 23 | assert(chck_atlas_push(&atlas, 32, 32) == 2); 24 | assert(chck_atlas_push(&atlas, 32, 288) == 3); 25 | assert(atlas.count == 3); 26 | assert(atlas.longest_edge == 320 && atlas.total_area == 320 * 320 + 32 * 32 + 32 * 288); 27 | 28 | uint32_t w, h; 29 | uint32_t free = chck_atlas_pack(&atlas, false, false, &w, &h); 30 | 31 | printf("%ux%u (%u)\n", w, h, free); 32 | assert(w == 352 && h == 320 && free == w * h - atlas.total_area); 33 | 34 | struct chck_atlas_rect tr; 35 | const struct chck_atlas_texture *t; 36 | assert((t = chck_atlas_get(&atlas, 1, &tr))); 37 | assert(tr.x == 0 && tr.y == 0 && tr.w == 320 && tr.h == 320); 38 | assert(!t->flipped && t->placed && t->longest_edge == 320 && t->area == 320 * 320); 39 | 40 | assert((t = chck_atlas_get(&atlas, 2, &tr))); 41 | assert(tr.x == 320 && tr.y == 288 && tr.w == 32 && tr.h == 32); 42 | assert(!t->flipped && t->placed && t->longest_edge == 32 && t->area == 32 * 32); 43 | 44 | assert((t = chck_atlas_get(&atlas, 3, &tr))); 45 | assert(tr.x == 320 && tr.y == 0 && tr.w == 32 && tr.h == 288); 46 | assert(!t->flipped && t->placed && t->longest_edge == 288 && t->area == 32 * 288); 47 | 48 | chck_atlas_release(&atlas); 49 | } 50 | 51 | /* TEST: atlas packing (forced pot) */ 52 | { 53 | struct chck_atlas atlas; 54 | assert(chck_atlas(&atlas)); 55 | assert(chck_atlas_push(&atlas, 320, 320) == 1); 56 | assert(chck_atlas_push(&atlas, 32, 32) == 2); 57 | assert(chck_atlas_push(&atlas, 32, 288) == 3); 58 | assert(atlas.count == 3 && atlas.total_area == 320 * 320 + 32 * 32 + 32 * 288); 59 | 60 | uint32_t w, h; 61 | uint32_t free = chck_atlas_pack(&atlas, true, false, &w, &h); 62 | printf("%ux%u (%u)\n", w, h, free); 63 | assert(w == 512 && h == 512 && free == w * h - atlas.total_area); 64 | 65 | struct chck_atlas_rect tr; 66 | const struct chck_atlas_texture *t; 67 | assert((t = chck_atlas_get(&atlas, 1, &tr))); 68 | assert(tr.x == 0 && tr.y == 0 && tr.w == 320 && tr.h == 320); 69 | assert(!t->flipped && t->placed && t->longest_edge == 320 && t->area == 320 * 320); 70 | 71 | assert((t = chck_atlas_get(&atlas, 2, &tr))); 72 | assert(tr.x == 320 && tr.y == 288 && tr.w == 32 && tr.h == 32); 73 | assert(!t->flipped && t->placed && t->longest_edge == 32 && t->area == 32 * 32); 74 | 75 | assert((t = chck_atlas_get(&atlas, 3, &tr))); 76 | assert(tr.x == 320 && tr.y == 0 && tr.w == 32 && tr.h == 288); 77 | assert(!t->flipped && t->placed && t->longest_edge == 288 && t->area == 32 * 288); 78 | 79 | chck_atlas_release(&atlas); 80 | } 81 | 82 | return EXIT_SUCCESS; 83 | } 84 | -------------------------------------------------------------------------------- /chck/bams/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | install_headers(bams.h) 2 | 3 | if (CHCK_BUILD_TESTS) 4 | find_package(Math REQUIRED) 5 | add_executable(bams_test test.c) 6 | target_link_libraries(bams_test PRIVATE ${MATH_LIBRARY}) 7 | add_test_ex(bams_test) 8 | endif () 9 | -------------------------------------------------------------------------------- /chck/bams/README.md: -------------------------------------------------------------------------------- 1 | # Binary scaling (Binary angular measurement) 2 | 3 | Small helpers for mapping ranges into bams and back. 4 | Useful for fixed point math, serialization, packing, etc... 5 | -------------------------------------------------------------------------------- /chck/bams/bams.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_bams_h__ 2 | #define __chck_bams_h__ 3 | 4 | #include 5 | #include 6 | 7 | typedef int64_t bams64; 8 | typedef int32_t bams32; 9 | typedef int16_t bams16; 10 | typedef int8_t bams8; 11 | 12 | #define decl_bams_func(B, R, T) \ 13 | static inline R B##_from_##T(T x, T range) { return (x / range) * (B##_most + B##_erro); } \ 14 | static inline T T##_from_##B(R x, T range) { return (x / (T)(B##_most + B##_erro)) * range; } 15 | 16 | #define decl_bams(B, R) \ 17 | static const R B##_erro = (sizeof(B) < sizeof(uint32_t)); \ 18 | static const R B##_most = (B)~0; \ 19 | static const R B##_half = ((B)~0 >> 1) + 1; \ 20 | decl_bams_func(B, R, double) \ 21 | decl_bams_func(B, R, float) 22 | 23 | decl_bams(bams64, uint64_t) 24 | decl_bams(bams32, uint32_t) 25 | decl_bams(bams16, uint16_t) 26 | decl_bams(bams8, uint8_t) 27 | 28 | #undef decl_bams_func 29 | #undef decl_bams 30 | 31 | #endif /* __chck_bams_h__ */ 32 | -------------------------------------------------------------------------------- /chck/bams/test.c: -------------------------------------------------------------------------------- 1 | #include "bams.h" 2 | #include 3 | #include 4 | 5 | #undef NDEBUG 6 | #include 7 | 8 | #pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" 9 | 10 | int main(void) 11 | { 12 | for (uint32_t x = 0; x < 360; x += 10) { 13 | assert(bams64_from_double(x, 360.0) == (uint64_t)((x / 360.0) * (uint64_t)~0)); 14 | assert(bams64_from_float(x, 360.0f) == (uint64_t)((x / 360.0f) * (uint64_t)~0)); 15 | assert(chck_equal(double_from_bams64(bams64_from_double(x, 360.0), 360.0), x, 1.0)); 16 | assert(chck_equalf(float_from_bams64(bams64_from_float(x, 360.0f), 360.0f), x, 1.0)); 17 | assert(bams32_from_double(x, 360.0) == (uint32_t)((x / 360.0) * (uint32_t)~0)); 18 | assert(bams32_from_float(x, 360.0f) == (uint32_t)((x / 360.0f) * (uint32_t)~0)); 19 | assert(chck_equal(double_from_bams32(bams32_from_double(x, 360.0), 360.0), x, pow(1000.0, 3))); 20 | assert(chck_equalf(float_from_bams32(bams32_from_float(x, 360.0f), 360.0f), x, 1.0)); 21 | assert(bams16_from_double(x, 360.0) == (uint16_t)((x / 360.0) * ((uint16_t)~0 + 1))); 22 | assert(bams16_from_float(x, 360.0f) == (uint16_t)((x / 360.0f) * ((uint16_t)~0 + 1))); 23 | assert(chck_equal(double_from_bams16(bams16_from_double(x, 360.0), 360.0), x, pow(1000.0, 4))); 24 | assert(chck_equalf(float_from_bams16(bams16_from_float(x, 360.0f), 360.0f), x, pow(1000.0, 2))); 25 | assert(bams8_from_double(x, 360.0) == (uint8_t)((x / 360.0) * ((uint8_t)~0 + 1))); 26 | assert(bams8_from_float(x, 360.0f) == (uint8_t)((x / 360.0f) * ((uint8_t)~0 + 1))); 27 | assert(chck_equal(double_from_bams8(bams8_from_double(x, 360.0), 360.0), x, pow(1000.0, 5))); 28 | assert(chck_equalf(float_from_bams8(bams8_from_float(x, 360.0f), 360.0f), x, pow(1000.0, 3))); 29 | } 30 | return EXIT_SUCCESS; 31 | } 32 | -------------------------------------------------------------------------------- /chck/buffer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(ZLIB) 2 | set_package_properties(ZLIB PROPERTIES TYPE PURPOSE "Enables compression support") 3 | 4 | if (ZLIB_FOUND) 5 | list(APPEND incs ${ZLIB_INCLUDE_DIRS}) 6 | list(APPEND libs ${ZLIB_LIBRARIES}) 7 | add_definitions(-DHAS_ZLIB=1) 8 | endif (ZLIB_FOUND) 9 | 10 | include_directories(${incs}) 11 | add_library(chck_buffer buffer.c) 12 | target_link_libraries(chck_buffer PRIVATE ${libs}) 13 | install_libraries(chck_buffer) 14 | install_headers(endianess.h buffer.h) 15 | 16 | if (CHCK_BUILD_TESTS) 17 | add_executable(buffer_test test.c) 18 | target_link_libraries(buffer_test PRIVATE chck_buffer) 19 | add_test_ex(buffer_test) 20 | endif () 21 | -------------------------------------------------------------------------------- /chck/buffer/README.md: -------------------------------------------------------------------------------- 1 | # Endian aware buffers 2 | 3 | Useful for reading your usual buffers and files. 4 | 5 | ## TODO 6 | * Make it possible to map buffer to a FILE* allowing easy abstraction of FILE and memory location 7 | -------------------------------------------------------------------------------- /chck/buffer/buffer.c: -------------------------------------------------------------------------------- 1 | #include "buffer.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #if HAS_ZLIB 8 | # include 9 | #elif !defined(HAS_ZLIB) 10 | # define HAS_ZLIB 0 11 | #endif 12 | 13 | struct chck_variant { 14 | union { 15 | uint64_t u64; 16 | uint32_t u32; 17 | uint16_t u16; 18 | uint8_t u8; 19 | uint8_t b[sizeof(uint64_t)]; 20 | }; 21 | enum chck_bits bits; 22 | }; 23 | 24 | static inline bool 25 | valid_bits(enum chck_bits bits) 26 | { 27 | return (bits == CHCK_BUFFER_B8 || 28 | bits == CHCK_BUFFER_B16 || 29 | bits == CHCK_BUFFER_B32 || 30 | bits == CHCK_BUFFER_B64); 31 | } 32 | 33 | static inline enum chck_bits 34 | smallest_bits_for_value(uintmax_t v) 35 | { 36 | static const struct { 37 | uintmax_t off; 38 | enum chck_bits bits; 39 | } map[3] = { 40 | { ~(uint32_t)0, CHCK_BUFFER_B64 }, 41 | { ~(uint16_t)0, CHCK_BUFFER_B32 }, 42 | { ~(uint8_t)0, CHCK_BUFFER_B16 }, 43 | }; 44 | 45 | for (size_t i = 0; i < sizeof(map) / sizeof(map[0]); ++i) { 46 | if (v <= map[i].off) 47 | continue; 48 | 49 | return map[i].bits; 50 | } 51 | 52 | return CHCK_BUFFER_B8; 53 | } 54 | 55 | static inline uintmax_t 56 | variant_get_value(struct chck_variant v) 57 | { 58 | switch (v.bits) { 59 | case CHCK_BUFFER_B8: 60 | return v.u8; 61 | case CHCK_BUFFER_B16: 62 | return v.u16; 63 | case CHCK_BUFFER_B32: 64 | return v.u32; 65 | case CHCK_BUFFER_B64: 66 | return v.u64; 67 | } 68 | 69 | assert(0 && "should not happen"); 70 | return 0; 71 | } 72 | 73 | void 74 | chck_buffer_flush(struct chck_buffer *buf) 75 | { 76 | assert(buf); 77 | 78 | if (buf->copied) 79 | free(buf->buffer); 80 | 81 | buf->copied = false; 82 | buf->curpos = buf->buffer = NULL; 83 | } 84 | 85 | void 86 | chck_buffer_release(struct chck_buffer *buf) 87 | { 88 | if (!buf) 89 | return; 90 | 91 | chck_buffer_flush(buf); 92 | *buf = (struct chck_buffer){0}; 93 | } 94 | 95 | bool 96 | chck_buffer_from_pointer(struct chck_buffer *buf, void *ptr, size_t size, enum chck_endianess endianess) 97 | { 98 | assert(buf); 99 | *buf = (struct chck_buffer){ .step = 32 }; 100 | chck_buffer_set_pointer(buf, ptr, size, endianess); 101 | return true; 102 | } 103 | 104 | bool 105 | chck_buffer(struct chck_buffer *buf, size_t size, enum chck_endianess endianess) 106 | { 107 | assert(buf); 108 | 109 | void *data = NULL; 110 | if (size > 0 && !(data = malloc(size))) 111 | return false; 112 | 113 | if (unlikely(!chck_buffer_from_pointer(buf, data, size, endianess))) 114 | goto fail; 115 | 116 | buf->copied = true; 117 | return true; 118 | 119 | fail: 120 | free(data); 121 | return false; 122 | } 123 | 124 | void 125 | chck_buffer_set_pointer(struct chck_buffer *buf, void *ptr, size_t size, enum chck_endianess endianess) 126 | { 127 | assert(buf); 128 | 129 | if (buf->copied) { 130 | free(buf->buffer); 131 | buf->buffer = NULL; 132 | } 133 | 134 | if (endianess == CHCK_ENDIANESS_NATIVE) { 135 | buf->endianess = chck_endianess(); 136 | } else { 137 | buf->endianess = endianess; 138 | } 139 | 140 | buf->size = size; 141 | buf->buffer = buf->curpos = ptr; 142 | buf->copied = false; 143 | } 144 | 145 | bool 146 | chck_buffer_resize(struct chck_buffer *buf, size_t size) 147 | { 148 | assert(buf); 149 | 150 | if (unlikely(size == buf->size)) 151 | return true; 152 | 153 | if (unlikely(size == 0)) { 154 | chck_buffer_flush(buf); 155 | return true; 156 | } 157 | 158 | uint8_t *tmp; 159 | if (!(tmp = realloc((buf->copied ? buf->buffer : NULL), size))) 160 | return false; 161 | 162 | /* set new buffer position */ 163 | if (buf->curpos - buf->buffer > (ptrdiff_t)size) { 164 | buf->curpos = tmp + (buf->size - size); 165 | } else { 166 | buf->curpos = tmp + (buf->curpos - buf->buffer); 167 | } 168 | 169 | buf->size = size; 170 | buf->buffer = tmp; 171 | buf->copied = true; 172 | return true; 173 | } 174 | 175 | ptrdiff_t 176 | chck_buffer_seek(struct chck_buffer *buf, long offset, int whence) 177 | { 178 | assert(buf); 179 | assert(whence == SEEK_SET || whence == SEEK_END || whence == SEEK_CUR); 180 | 181 | switch (whence) { 182 | case SEEK_SET: 183 | if (buf->buffer + offset > buf->buffer + buf->size) { 184 | buf->curpos = buf->buffer + buf->size; 185 | } else if (offset >= 0) { 186 | buf->curpos = buf->buffer + offset; 187 | } 188 | break; 189 | case SEEK_CUR: 190 | if (buf->curpos + offset > buf->buffer + buf->size) { 191 | buf->curpos = buf->curpos + buf->size; 192 | } else if (buf->curpos + offset < buf->buffer) { 193 | buf->curpos = buf->buffer; 194 | } else { 195 | buf->curpos = buf->curpos + offset; 196 | } 197 | break; 198 | case SEEK_END: 199 | buf->curpos = buf->buffer + buf->size; 200 | break; 201 | default:break; 202 | } 203 | 204 | return buf->curpos - buf->buffer; 205 | } 206 | 207 | static bool 208 | bounds_check(struct chck_buffer *buf, size_t size, size_t memb) 209 | { 210 | size_t nsz; 211 | if (unlikely(chck_mul_ofsz(size, memb, &nsz))) 212 | return false; 213 | 214 | const size_t sz = buf->size - (buf->curpos - buf->buffer); 215 | if (nsz > sz) { 216 | /* buf->size + size * memb + buf->step */ 217 | if (unlikely(chck_add_ofsz(buf->size, nsz, &nsz)) || unlikely(chck_add_ofsz(buf->step, nsz, &nsz))) 218 | return false; 219 | 220 | if (!chck_buffer_resize(buf, nsz)) 221 | return false; 222 | } 223 | 224 | return true; 225 | } 226 | 227 | size_t 228 | chck_buffer_fill(const void *src, size_t size, size_t memb, struct chck_buffer *buf) 229 | { 230 | assert(src && buf); 231 | 232 | if (!bounds_check(buf, size, memb) || !buf->curpos || !src) 233 | return 0; 234 | 235 | memcpy(buf->curpos, src, size * memb); 236 | return memb; 237 | } 238 | 239 | size_t 240 | chck_buffer_fill_from_file(FILE *src, size_t size, size_t memb, struct chck_buffer *buf) 241 | { 242 | assert(src && buf); 243 | 244 | if (!bounds_check(buf, size, memb) || !buf->curpos || !src) 245 | return 0; 246 | 247 | return fread(buf->curpos, size, memb, src); 248 | } 249 | 250 | size_t 251 | chck_buffer_fill_from_fd(int fd, size_t size, size_t memb, struct chck_buffer *buf) 252 | { 253 | assert(fd && buf); 254 | 255 | if (!bounds_check(buf, size, memb) || !buf->curpos) 256 | return 0; 257 | 258 | const ssize_t ret = read(fd, buf->curpos, size * memb); 259 | if (unlikely(ret == -1 || ret == 0)) 260 | return 0; 261 | 262 | return (size_t)ret / size; 263 | } 264 | 265 | size_t 266 | chck_buffer_read(void *dst, size_t size, size_t memb, struct chck_buffer *buf) 267 | { 268 | assert(dst && buf); 269 | 270 | size_t nsz; 271 | if (unlikely(chck_mul_ofsz(size, memb, &nsz))) 272 | return 0; 273 | 274 | if (unlikely(nsz > buf->size - (buf->curpos - buf->buffer))) { 275 | assert(size != 0); // should never happen 276 | // read as much as we can 277 | memb = (buf->size - (buf->curpos - buf->buffer)) / size; 278 | } 279 | 280 | memcpy(dst, buf->curpos, size * memb); 281 | buf->curpos += size * memb; 282 | return memb; 283 | } 284 | 285 | bool 286 | chck_buffer_read_int(void *i, enum chck_bits bits, struct chck_buffer *buf) 287 | { 288 | assert(i && buf); 289 | 290 | if (!valid_bits(bits)) 291 | return false; 292 | 293 | if (unlikely(chck_buffer_read(i, bits, 1, buf) != 1)) 294 | return false; 295 | 296 | if (!chck_buffer_native_endianess(buf)) 297 | chck_bswap_single(i, bits); 298 | 299 | return true; 300 | } 301 | 302 | bool 303 | chck_buffer_read_string_of_type(char **str, size_t *out_len, enum chck_bits bits, struct chck_buffer *buf) 304 | { 305 | assert(buf && str); 306 | *str = NULL; 307 | 308 | if (out_len) 309 | *out_len = 0; 310 | 311 | struct chck_variant v = { .bits = bits }; 312 | if (unlikely(!chck_buffer_read_int(v.b, bits, buf))) 313 | return false; 314 | 315 | const size_t len = variant_get_value(v); 316 | 317 | if (out_len) 318 | *out_len = len; 319 | 320 | if (len <= 0) 321 | return true; 322 | 323 | if (!(*str = chck_calloc_add_of(len, 1))) 324 | return false; 325 | 326 | if (unlikely(chck_buffer_read(*str, 1, len, buf) != len)) { 327 | free(*str); 328 | return false; 329 | } 330 | 331 | return true; 332 | } 333 | 334 | bool 335 | chck_buffer_read_string(char **str, size_t *len, struct chck_buffer *buf) 336 | { 337 | assert(str && buf); 338 | *str = NULL; 339 | 340 | if (len) 341 | *len = 0; 342 | 343 | uint8_t bits; 344 | if (unlikely(!chck_buffer_read_int(&bits, sizeof(bits), buf))) 345 | return false; 346 | 347 | return likely(chck_buffer_read_string_of_type(str, len, bits, buf)); 348 | } 349 | 350 | size_t 351 | chck_buffer_write(const void *src, size_t size, size_t memb, struct chck_buffer *buf) 352 | { 353 | memb = chck_buffer_fill(src, size, memb, buf); 354 | buf->curpos += size * memb; 355 | return memb; 356 | } 357 | 358 | size_t 359 | chck_buffer_write_from_file(FILE *src, size_t size, size_t memb, struct chck_buffer *buf) 360 | { 361 | memb = chck_buffer_fill_from_file(src, size, memb, buf); 362 | buf->curpos += size * memb; 363 | return memb; 364 | } 365 | 366 | size_t 367 | chck_buffer_write_from_fd(int fd, size_t size, size_t memb, struct chck_buffer *buf) 368 | { 369 | memb = chck_buffer_fill_from_fd(fd, size, memb, buf); 370 | buf->curpos += size * memb; 371 | return memb; 372 | } 373 | 374 | bool 375 | chck_buffer_write_int(const void *i, enum chck_bits bits, struct chck_buffer *buf) 376 | { 377 | assert(buf); 378 | 379 | if (!valid_bits(bits)) 380 | return false; 381 | 382 | bool ret; 383 | if (!chck_buffer_native_endianess(buf)) { 384 | uint8_t b[sizeof(uint64_t)]; 385 | memcpy(b, i, bits); 386 | chck_bswap_single(b, bits); 387 | ret = (chck_buffer_write(b, bits, 1, buf) == 1); 388 | } else { 389 | ret = (chck_buffer_write(i, bits, 1, buf) == 1); 390 | } 391 | 392 | return ret; 393 | } 394 | 395 | bool 396 | chck_buffer_write_string_of_type(const char *str, size_t len, enum chck_bits bits, struct chck_buffer *buf) 397 | { 398 | assert(buf); 399 | 400 | bool ret = false; 401 | switch (bits) { 402 | case CHCK_BUFFER_B8: 403 | ret = chck_buffer_write_int((uint8_t[]){len}, bits, buf); 404 | break; 405 | case CHCK_BUFFER_B16: 406 | ret = chck_buffer_write_int((uint16_t[]){len}, bits, buf); 407 | break; 408 | case CHCK_BUFFER_B32: 409 | ret = chck_buffer_write_int((uint32_t[]){len}, bits, buf); 410 | break; 411 | case CHCK_BUFFER_B64: 412 | ret = chck_buffer_write_int((uint64_t[]){len}, bits, buf); 413 | break; 414 | } 415 | 416 | if (unlikely(!ret)) 417 | return false; 418 | 419 | return likely(chck_buffer_write(str, 1, len, buf) == len); 420 | } 421 | 422 | bool 423 | chck_buffer_write_string(const char *str, size_t len, struct chck_buffer *buf) 424 | { 425 | assert(buf); 426 | 427 | const uint8_t bits = smallest_bits_for_value(len); 428 | if (unlikely(!chck_buffer_write_int(&bits, sizeof(bits), buf))) 429 | return false; 430 | 431 | return likely(chck_buffer_write_string_of_type(str, len, bits, buf)); 432 | } 433 | 434 | #pragma GCC diagnostic push 435 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" 436 | 437 | size_t 438 | chck_buffer_write_format(struct chck_buffer *buf, const char *fmt, ...) 439 | { 440 | va_list argp; 441 | va_start(argp, fmt); 442 | const size_t wrote = chck_buffer_write_varg(buf, fmt, argp); 443 | va_end(argp); 444 | return wrote; 445 | } 446 | 447 | size_t 448 | chck_buffer_write_varg(struct chck_buffer *buf, const char *fmt, va_list args) 449 | { 450 | va_list cpy; 451 | va_copy(cpy, args); 452 | 453 | char *str = NULL; 454 | const size_t len = vsnprintf(NULL, 0, fmt, args); 455 | if (len > 0 && !(str = chck_malloc_add_of(len, 1))) { 456 | va_end(cpy); 457 | return false; 458 | } 459 | 460 | vsnprintf(str, len + 1, fmt, cpy); 461 | va_end(cpy); 462 | 463 | const size_t wrote = chck_buffer_write(str, 1, len, buf); 464 | free(str); 465 | return wrote; 466 | } 467 | 468 | #pragma GCC diagnostic pop 469 | 470 | bool 471 | chck_buffer_has_zlib(void) 472 | { 473 | return HAS_ZLIB; 474 | } 475 | 476 | #pragma GCC diagnostic ignored "-Wsuggest-attribute=const" 477 | 478 | bool 479 | chck_buffer_compress_zlib(struct chck_buffer *buf) 480 | { 481 | #if HAS_ZLIB 482 | uLongf dsize, bsize; 483 | dsize = bsize = compressBound(buf->size); 484 | 485 | void *compressed; 486 | if (!(compressed = malloc(dsize))) 487 | return false; 488 | 489 | int ret; 490 | while ((ret = compress(compressed, &dsize, buf->buffer, buf->size)) == Z_BUF_ERROR) { 491 | void *tmp; 492 | if (!(tmp = chck_realloc_mul_of(compressed, bsize, 2))) 493 | goto fail; 494 | 495 | compressed = tmp; 496 | dsize = (bsize *= 2); 497 | } 498 | 499 | if (unlikely(ret != Z_OK)) 500 | goto fail; 501 | 502 | chck_buffer_set_pointer(buf, compressed, bsize, buf->endianess); 503 | buf->copied = true; 504 | 505 | if (buf->size > dsize) 506 | chck_buffer_resize(buf, dsize); 507 | 508 | return true; 509 | 510 | fail: 511 | free(compressed); 512 | return false; 513 | #else 514 | (void)buf; 515 | return false; 516 | #endif 517 | } 518 | 519 | bool 520 | chck_buffer_decompress_zlib(struct chck_buffer *buf) 521 | { 522 | #if HAS_ZLIB 523 | uLongf dsize, bsize; 524 | 525 | { 526 | size_t sz; 527 | if (unlikely(chck_mul_ofsz(buf->size, 2, &sz)) || (uLongf)sz < sz) 528 | return false; 529 | 530 | dsize = bsize = sz; 531 | } 532 | 533 | if (!dsize) 534 | return false; 535 | 536 | void *decompressed; 537 | if (!(decompressed = malloc(dsize))) 538 | return false; 539 | 540 | int ret; 541 | while ((ret = uncompress(decompressed, &dsize, buf->buffer, buf->size)) == Z_BUF_ERROR) { 542 | void *tmp; 543 | if (!(tmp = chck_realloc_mul_of(decompressed, bsize, 2))) 544 | goto fail; 545 | 546 | decompressed = tmp; 547 | dsize = (bsize *= 2); 548 | } 549 | 550 | if (unlikely(ret != Z_OK)) 551 | goto fail; 552 | 553 | chck_buffer_set_pointer(buf, decompressed, bsize, buf->endianess); 554 | buf->copied = true; 555 | 556 | if (bsize > dsize) 557 | chck_buffer_resize(buf, dsize); 558 | 559 | return true; 560 | 561 | fail: 562 | free(decompressed); 563 | return false; 564 | #else 565 | (void)buf; 566 | return false; 567 | #endif 568 | } 569 | -------------------------------------------------------------------------------- /chck/buffer/buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_buffer__ 2 | #define __chck_buffer__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "endianess.h" 10 | 11 | enum chck_bits { 12 | CHCK_BUFFER_B8 = sizeof(int8_t), 13 | CHCK_BUFFER_B16 = sizeof(int16_t), 14 | CHCK_BUFFER_B32 = sizeof(int32_t), 15 | CHCK_BUFFER_B64 = sizeof(int64_t), 16 | }; 17 | 18 | struct chck_buffer { 19 | // pointer to current buffer and the current position 20 | uint8_t *buffer, *curpos; 21 | 22 | // size of the buffer 23 | size_t size; 24 | 25 | // growth step for the buffer incase writing to full buffer 26 | size_t step; 27 | 28 | // endianess true == big, false == little 29 | bool endianess; 30 | 31 | // copied == true, means that buffer is owned by this struct and will be freed on chck_buffer_release 32 | bool copied; 33 | }; 34 | 35 | static inline bool 36 | chck_buffer_native_endianess(const struct chck_buffer *buf) 37 | { 38 | return (chck_endianess() == buf->endianess); 39 | } 40 | 41 | void chck_buffer_release(struct chck_buffer *buf); 42 | void chck_buffer_flush(struct chck_buffer *buf); 43 | bool chck_buffer_from_pointer(struct chck_buffer *buf, void *ptr, size_t size, enum chck_endianess endianess); 44 | bool chck_buffer(struct chck_buffer *buf, size_t size, enum chck_endianess endianess); 45 | void chck_buffer_set_pointer(struct chck_buffer *buf, void *ptr, size_t size, enum chck_endianess endianess); 46 | 47 | size_t chck_buffer_fill(const void *src, size_t size, size_t memb, struct chck_buffer *buf); 48 | size_t chck_buffer_fill_from_file(FILE *src, size_t size, size_t memb, struct chck_buffer *buf); 49 | size_t chck_buffer_fill_from_fd(int fd, size_t size, size_t memb, struct chck_buffer *buf); 50 | 51 | size_t chck_buffer_write(const void *src, size_t size, size_t nmemb, struct chck_buffer *buf); 52 | size_t chck_buffer_write_from_file(FILE *src, size_t size, size_t nmemb, struct chck_buffer *buf); 53 | size_t chck_buffer_write_from_fd(int fd, size_t size, size_t nmemb, struct chck_buffer *buf); 54 | 55 | size_t chck_buffer_read(void *dst, size_t size, size_t memb, struct chck_buffer *buf); 56 | bool chck_buffer_read_int(void *i, enum chck_bits bits, struct chck_buffer *buf); 57 | bool chck_buffer_read_string(char **str, size_t *len, struct chck_buffer *buf); 58 | bool chck_buffer_read_string_of_type(char **str, size_t *len, enum chck_bits bits, struct chck_buffer *buf); 59 | 60 | bool chck_buffer_write_int(const void *i, enum chck_bits bits, struct chck_buffer *buf); 61 | bool chck_buffer_write_string(const char *str, size_t len, struct chck_buffer *buf); 62 | bool chck_buffer_write_string_of_type(const char *str, size_t len, enum chck_bits bits, struct chck_buffer *buf); 63 | 64 | CHCK_FORMAT(printf, 2, 3) size_t chck_buffer_write_format(struct chck_buffer *buf, const char *fmt, ...); 65 | size_t chck_buffer_write_varg(struct chck_buffer *buf, const char *fmt, va_list args); 66 | 67 | ptrdiff_t chck_buffer_seek(struct chck_buffer *buf, long offset, int whence); 68 | bool chck_buffer_resize(struct chck_buffer *buf, size_t size); 69 | 70 | /* -DHAS_ZLIB=1 -lz */ 71 | bool chck_buffer_has_zlib(void); 72 | bool chck_buffer_compress_zlib(struct chck_buffer *buf); 73 | bool chck_buffer_decompress_zlib(struct chck_buffer *buf); 74 | 75 | #endif /* __chck_buffer__ */ 76 | -------------------------------------------------------------------------------- /chck/buffer/endianess.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_endianess__ 2 | #define __chck_endianess__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | enum chck_endianess { 10 | CHCK_ENDIANESS_LITTLE = 0, 11 | CHCK_ENDIANESS_BIG = 1, 12 | CHCK_ENDIANESS_NATIVE = 2, 13 | }; 14 | 15 | #if defined(__clang__) || (__GNUC__ >= 4 && __GNUC_MINOR__ >= 3 && !defined(__MINGW32__) && !defined(__MINGW64__)) 16 | # define bswap16 __builtin_bswap16 17 | # define bswap32 __builtin_bswap32 18 | # define bswap64 __builtin_bswap64 19 | # define HAS_BYTESWAP 1 20 | #elif defined(__GLIBC__) 21 | # include 22 | # define bswap16 __bswap_16 23 | # define bswap32 __bswap_32 24 | # define bswap64 __bswap_64 25 | # define HAS_BYTESWAP 1 26 | #elif defined(__NetBSD__) 27 | # include 28 | # include /* already named bswap16/32/64 */ 29 | # define HAS_BYTESWAP 1 30 | #elif defined(_MSC_VER) 31 | # define bswap16 _byteswap_ushort 32 | # define bswap32 _byteswap_ulong 33 | # define bswap64 _byteswap_uint64 34 | # define HAS_BYTESWAP 1 35 | #else 36 | # define HAS_BYTESWAP 0 37 | #endif 38 | 39 | #if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \ 40 | (defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN) || \ 41 | defined(__BIG_ENDIAN__) || \ 42 | defined(__ARMEB__) || \ 43 | defined(__THUMBEB__) || \ 44 | defined(__AARCH64EB__) || \ 45 | defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__) 46 | // compiletime big endian 47 | # define chck_endianess() CHCK_ENDIANESS_BIG 48 | #elif (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || \ 49 | (defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN) || \ 50 | defined(__LITTLE_ENDIAN__) || \ 51 | defined(__ARMEL__) || \ 52 | defined(__THUMBEL__) || \ 53 | defined(__AARCH64EL__) || \ 54 | defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) 55 | // compiletime little endian 56 | # define chck_endianess() CHCK_ENDIANESS_LITTLE 57 | #else 58 | // runtime endianess check 59 | static inline enum 60 | chck_endianess chck_endianess(void) 61 | { 62 | union { 63 | uint32_t i; 64 | char c[4]; 65 | } bint = { 0x01020304 }; 66 | return (bint.c[0] == 1 ? CHCK_ENDIANESS_BIG : CHCK_ENDIANESS_LITTLE); 67 | }; 68 | #endif 69 | 70 | static inline void 71 | chck_bswap_generic(void *p, size_t size) 72 | { 73 | if (size <= sizeof(uint8_t)) 74 | return; 75 | 76 | assert(size <= sizeof(intmax_t)); 77 | uint8_t b[sizeof(intmax_t)]; 78 | memcpy(b, p, size); 79 | 80 | for (size_t s = 0; s < size; ++s) 81 | memset((uint8_t*)p + s, b[size - s - 1], 1); 82 | } 83 | 84 | static inline void 85 | chck_bswap_single(void *p, size_t size) 86 | { 87 | #if HAS_BYTESWAP 88 | assert(p); 89 | switch (size) { 90 | case sizeof(uint32_t): 91 | *((uint32_t*)p) = bswap32(*((uint32_t*)p)); 92 | break; 93 | case sizeof(uint16_t): 94 | *((uint16_t*)p) = bswap16(*((uint16_t*)p)); 95 | break; 96 | case sizeof(uint64_t): 97 | *((uint64_t*)p) = bswap64(*((uint64_t*)p)); 98 | break; 99 | default: 100 | chck_bswap_generic(p, size); 101 | break; 102 | } 103 | #else 104 | chck_bswap_generic(p, size); 105 | #endif 106 | } 107 | 108 | static inline void 109 | chck_bswap(void *v, size_t size, size_t memb) 110 | { 111 | assert(v); 112 | 113 | for (uint8_t *p = v; size > sizeof(uint8_t) && p < (uint8_t*)v + (memb * size); p += size) 114 | chck_bswap_single(p, size); 115 | } 116 | 117 | /** define chck_bswap{16,32,64} for use **/ 118 | 119 | #if HAS_BYTESWAP 120 | # define generic_swap(T, n) \ 121 | static inline T chck_bswap##n(T v) { return bswap##n(v); } 122 | #else 123 | # define generic_swap(T, n) \ 124 | static inline T chck_bswap##n(T v) { chck_bswap_generic(&v, sizeof(v)); return v; } 125 | #endif 126 | 127 | generic_swap(uint16_t, 16) 128 | generic_swap(uint32_t, 32) 129 | generic_swap(uint64_t, 64) 130 | 131 | #undef generic_swap 132 | 133 | #endif /* __chck_endianess__ */ 134 | -------------------------------------------------------------------------------- /chck/buffer/test.c: -------------------------------------------------------------------------------- 1 | #include "buffer.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #undef NDEBUG 8 | #include 9 | 10 | int main(void) 11 | { 12 | /* TEST: ownership move */ 13 | { 14 | struct chck_buffer buf; 15 | assert(chck_buffer(&buf, 1, CHCK_ENDIANESS_NATIVE)); 16 | buf.copied = false; 17 | free(buf.buffer); 18 | chck_buffer_release(&buf); // should not SIGSEGV 19 | } 20 | 21 | /* TEST: over-read */ 22 | { 23 | struct chck_buffer buf; 24 | char data[] = "yolo I have only this many bytes"; 25 | assert(chck_buffer_from_pointer(&buf, data, sizeof(data), CHCK_ENDIANESS_NATIVE)); 26 | char bb[sizeof(data)]; 27 | assert(chck_buffer_read(bb, 1, sizeof(data), &buf) == sizeof(data)); 28 | chck_buffer_seek(&buf, 0, SEEK_SET); 29 | assert(chck_buffer_read(bb, 1, sizeof(data) * 4, &buf) == sizeof(data)); 30 | chck_buffer_seek(&buf, 0, SEEK_SET); 31 | assert(chck_buffer_read(bb, 8, sizeof(data), &buf) == 4); 32 | assert(chck_buffer_read(bb, 12, sizeof(data), &buf) == 0); 33 | assert(chck_buffer_read(bb, 1, sizeof(data), &buf) == 1); 34 | chck_buffer_release(&buf); 35 | } 36 | 37 | /* TEST: writing format */ 38 | { 39 | struct chck_buffer buf; 40 | assert(chck_buffer(&buf, 1, CHCK_ENDIANESS_NATIVE)); 41 | const char *result = "Hello buffer, I'm in your buffer at 10:00 clock!"; 42 | assert(chck_buffer_write_format(&buf, "Hello %s, I'm in your buffer at %d:%.2d clock!", "buffer", 10, 0) == strlen(result)); 43 | chck_buffer_seek(&buf, 0, SEEK_SET); 44 | assert(!memcmp(buf.buffer, result, strlen(result))); 45 | chck_buffer_release(&buf); 46 | 47 | } 48 | 49 | /* TEST: little endian buffer */ 50 | { 51 | const struct { 52 | char *data; 53 | size_t size; 54 | enum chck_endianess endianess; 55 | } tests[] = { 56 | { 57 | .data = "\x1\x12this integer is 5:\x12this integer is 5:\x5\x0\x0\0\1\0\1", 58 | .size = sizeof("\x0\x12this integer is 5:\x12this integer is 5:\x5\x0\x0\0\1\0\1") - 1, 59 | .endianess = CHCK_ENDIANESS_LITTLE, 60 | }, { 61 | .data = "\x1\x12this integer is 5:\x12this integer is 5:\x0\x0\0\x5\0\1\1", 62 | .size = sizeof("\x0\x12this integer is 5:\x12this integer is 5:\x0\x0\0\x5\0\1\1") - 1, 63 | .endianess = CHCK_ENDIANESS_BIG, 64 | }, 65 | }; 66 | 67 | for (int t = 0; t < 2; ++t) { 68 | int32_t i; 69 | int16_t s; 70 | int8_t c; 71 | char *str; 72 | struct chck_buffer buf; 73 | assert(chck_buffer_from_pointer(&buf, tests[t].data, tests[t].size, tests[t].endianess)); 74 | assert(buf.endianess == tests[t].endianess); 75 | size_t len; 76 | assert(chck_buffer_read_string(&str, &len, &buf)); 77 | assert(len == strlen("this integer is 5:")); 78 | assert(!strcmp(str, "this integer is 5:")); free(str); 79 | assert(chck_buffer_read_string_of_type(&str, NULL, sizeof(uint8_t), &buf)); 80 | assert(strcmp(str, "this integer is 5:") == 0); free(str); 81 | assert(chck_buffer_read_int(&i, sizeof(i), &buf)); 82 | assert(i == 5); 83 | assert(chck_buffer_read_int(&s, sizeof(s), &buf)); 84 | assert(s == 1); 85 | assert(chck_buffer_read_int(&c, sizeof(c), &buf)); 86 | assert(c == 1); 87 | assert((buf.curpos - buf.buffer) - buf.size == 0); 88 | chck_buffer_release(&buf); 89 | } 90 | } 91 | 92 | /* TEST: buffer write && read && resize */ 93 | { 94 | int64_t l = 32; 95 | int32_t i = 8; 96 | int16_t s = 6; 97 | enum chck_endianess tests[] = { CHCK_ENDIANESS_NATIVE, !chck_endianess() }; 98 | for (int t = 0; t < 2; ++t) { 99 | char *str; 100 | struct chck_buffer buf; 101 | assert(chck_buffer(&buf, 5+6, tests[t])); 102 | assert((tests[t] == CHCK_ENDIANESS_NATIVE) == chck_buffer_native_endianess(&buf)); 103 | 104 | assert(chck_buffer_write_string("test", 4, &buf) == 1); 105 | assert(chck_buffer_write_string_of_type("test", 4, sizeof(uint8_t), &buf)); 106 | assert(chck_buffer_resize(&buf, 5+6+sizeof(uint64_t)+sizeof(int32_t)+sizeof(int16_t))); 107 | assert(buf.size == 5+6+sizeof(uint64_t)+sizeof(int32_t)+sizeof(int16_t)); 108 | assert(buf.curpos - buf.buffer == 6+5); 109 | assert(buf.buffer != buf.curpos); 110 | 111 | assert(chck_buffer_write_int(&l, sizeof(l), &buf)); 112 | assert(chck_buffer_write_int(&i, sizeof(i), &buf)); 113 | assert(chck_buffer_write_int(&s, sizeof(s), &buf)); 114 | assert((buf.curpos - buf.buffer) - buf.size == 0); 115 | 116 | assert(chck_buffer_seek(&buf, 0, SEEK_SET) == buf.curpos - buf.buffer); 117 | assert(buf.curpos - buf.buffer == 0); 118 | 119 | assert(chck_buffer_read_string(&str, NULL, &buf)); 120 | assert(strcmp(str, "test") == 0); free(str); 121 | assert(chck_buffer_read_string_of_type(&str, NULL, sizeof(uint8_t), &buf)); 122 | assert(strcmp(str, "test") == 0); free(str); 123 | assert(chck_buffer_read_int(&l, sizeof(l), &buf)); 124 | assert(l == 32); 125 | assert(chck_buffer_read_int(&i, sizeof(i), &buf)); 126 | assert(i == 8); 127 | assert(chck_buffer_read_int(&s, sizeof(s), &buf)); 128 | assert(s == 6); 129 | assert((buf.curpos - buf.buffer) - buf.size == 0); 130 | chck_buffer_release(&buf); 131 | } 132 | } 133 | 134 | /* TEST: zlib compression && decompression */ 135 | { 136 | char uncompressed[] = "....................."; 137 | struct chck_buffer buf; 138 | chck_buffer_from_pointer(&buf, uncompressed, sizeof(uncompressed), CHCK_ENDIANESS_NATIVE); 139 | #if HAS_ZLIB 140 | uint8_t compressed[] = { 0x78, 0x9c, 0xd3, 0xd3, 0xc3, 0x2, 0x18, 0x0, 0x2d, 0x5e, 0x3, 0xc7 }; 141 | assert(chck_buffer_has_zlib()); 142 | assert(chck_buffer_compress_zlib(&buf)); 143 | assert(buf.size == sizeof(compressed)); 144 | assert(memcmp(buf.buffer, compressed, sizeof(compressed)) == 0); 145 | 146 | assert(chck_buffer_decompress_zlib(&buf)); 147 | assert(buf.size == sizeof(uncompressed)); 148 | assert(memcmp(buf.buffer, uncompressed, sizeof(uncompressed)) == 0); 149 | #else 150 | assert(!chck_buffer_has_zlib()); 151 | assert(!chck_buffer_compress_zlib(&buf)); 152 | assert(!chck_buffer_decompress_zlib(&buf)); 153 | #endif 154 | chck_buffer_release(&buf); 155 | } 156 | 157 | /* TEST: benchmark read/write (small writes, native && non-native) */ 158 | { 159 | const uint32_t iters = 0xFFFFF; 160 | enum chck_endianess tests[] = { CHCK_ENDIANESS_NATIVE, !chck_endianess() }; 161 | for (int i = 0; i < 2; ++i) { 162 | struct chck_buffer buf; 163 | assert(chck_buffer(&buf, iters * sizeof(uint64_t) * sizeof(uint32_t) * sizeof(uint16_t) * sizeof(uint8_t), tests[i])); 164 | for (uint64_t a = 0; a < iters; ++a) assert(chck_buffer_write_int(&a, sizeof(a), &buf)); 165 | for (uint32_t a = 0; a < iters; ++a) assert(chck_buffer_write_int(&a, sizeof(a), &buf)); 166 | for (uint16_t a = 0; a < 0xFFFF; ++a) assert(chck_buffer_write_int(&a, sizeof(a), &buf)); 167 | for (uint8_t a = 0; a < 0xFF; ++a) assert(chck_buffer_write_int(&a, sizeof(a), &buf)); 168 | for (uint32_t a = 0; a < iters; ++a) assert(chck_buffer_write_string("yolo", 4, &buf)); 169 | chck_buffer_seek(&buf, 0, SEEK_SET); 170 | for (uint64_t t, a = 0; a < iters; ++a) { assert(chck_buffer_read_int(&t, sizeof(t), &buf)); assert(a == t); } 171 | for (uint32_t t, a = 0; a < iters; ++a) { assert(chck_buffer_read_int(&t, sizeof(t), &buf)); assert(a == t); } 172 | for (uint16_t t, a = 0; a < 0xFFFF; ++a) { assert(chck_buffer_read_int(&t, sizeof(t), &buf)); assert(a == t); } 173 | for (uint8_t t, a = 0; a < 0xFF; ++a) { assert(chck_buffer_read_int(&t, sizeof(t), &buf)); assert(a == t); } 174 | for (uint32_t a = 0; a < iters; ++a) { 175 | char *t; 176 | size_t len; 177 | assert(chck_buffer_read_string(&t, &len, &buf)); 178 | assert(!strcmp(t, "yolo")); 179 | assert(len == 4); 180 | free(t); 181 | } 182 | chck_buffer_release(&buf); 183 | } 184 | } 185 | 186 | return EXIT_SUCCESS; 187 | } 188 | -------------------------------------------------------------------------------- /chck/dl/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(chck_dl dl.c) 2 | target_link_libraries(chck_dl PRIVATE ${CMAKE_DL_LIBS}) 3 | install_libraries(chck_dl) 4 | install_headers(dl.h) 5 | 6 | if (CHCK_BUILD_TESTS) 7 | find_library(DL_LIBRARY dl) 8 | add_definitions(-DDL_LIBRARY="${DL_LIBRARY}") 9 | add_executable(dl_test test.c) 10 | target_link_libraries(dl_test PRIVATE chck_dl) 11 | add_test_ex(dl_test) 12 | endif () 13 | -------------------------------------------------------------------------------- /chck/dl/dl.c: -------------------------------------------------------------------------------- 1 | #include "dl.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #if defined(_WIN32) || defined(_WIN64) 8 | # include 9 | #elif defined(__posix__) || defined(__unix__) || defined(__linux__) || defined(__APPLE__) 10 | # include 11 | #else 12 | # warning "dl: unsupported os" 13 | #endif 14 | 15 | void* 16 | chck_dl_load(const char *file, const char **out_error) 17 | { 18 | assert(file); 19 | 20 | void *handle = NULL; 21 | const char *error = NULL; 22 | #if defined(_WIN32) || defined(_WIN64) 23 | #if defined(__WINRT__) 24 | /** 25 | * WinRT only publically supports LoadPackagedLibrary() for loading .dll 26 | * files. LoadLibrary() is a private API, and not available for apps 27 | * (that can be published to MS' Windows Store.) 28 | */ 29 | handle = (void*)LoadPackagedLibrary(file, 0); 30 | #else 31 | handle = (void*)LoadLibrary(file); 32 | #endif 33 | 34 | if (!handle) 35 | error = "Failed to load dll file."; 36 | #elif defined(__posix__) || defined(__unix__) || defined(__linux__) || defined(__APPLE__) 37 | if (!(handle = dlopen(file, RTLD_NOW | RTLD_LOCAL))) 38 | error = dlerror(); 39 | #else 40 | error = "cdl: unsupported os"; 41 | #endif 42 | 43 | if (out_error) 44 | *out_error = error; 45 | 46 | return handle; 47 | } 48 | 49 | void* 50 | chck_dl_load_symbol(void *handle, const char *name, const char **out_error) 51 | { 52 | assert(handle); 53 | void *symbol = NULL; 54 | const char *error = NULL; 55 | 56 | #if defined(_WIN32) || defined(_WIN64) 57 | if (!(symbol = (void*)GetProcAddress((HMODULE)handle, name))) 58 | error = "Failed to load symbol."; 59 | #elif defined(__posix__) || defined(__unix__) || defined(__linux__) || defined(__APPLE__) 60 | if (!(symbol = dlsym(handle, name))) { 61 | const size_t len = strlen(name) + 1; 62 | char *nname = calloc(1, len + 1); 63 | 64 | if (nname) { 65 | /* append an underscore for platforms that need that. */ 66 | nname[0] = '_'; 67 | memcpy(nname + 1, name, len); 68 | symbol = dlsym(handle, nname); 69 | free(nname); 70 | } 71 | } 72 | 73 | if (!symbol) 74 | error = dlerror(); 75 | #else 76 | error = "cdl: unsupported os"; 77 | #endif 78 | 79 | if (out_error) 80 | *out_error = error; 81 | 82 | return symbol; 83 | } 84 | 85 | void 86 | chck_dl_unload(void *handle) 87 | { 88 | assert(handle); 89 | 90 | #if defined(_WIN32) || defined(_WIN64) 91 | FreeLibrary((HMODULE)handle); 92 | #elif defined(__posix__) || defined(__unix__) || defined(__linux__) || defined(__APPLE__) 93 | dlclose(handle); 94 | #endif 95 | } 96 | -------------------------------------------------------------------------------- /chck/dl/dl.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_cdl__ 2 | #define __chck_cdl__ 3 | 4 | #include 5 | 6 | void* chck_dl_load(const char *file, const char **out_error); 7 | void* chck_dl_load_symbol(void *handle, const char *name, const char **out_error); 8 | void chck_dl_unload(void *handle); 9 | 10 | #endif /* __chck_cdl__ */ 11 | -------------------------------------------------------------------------------- /chck/dl/test.c: -------------------------------------------------------------------------------- 1 | #include "dl.h" 2 | #include 3 | 4 | #undef NDEBUG 5 | #include 6 | 7 | int main(void) 8 | { 9 | /* TEST: library loading */ 10 | { 11 | void *handle = NULL; 12 | const char *error = NULL; 13 | 14 | #if defined(__APPLE__) 15 | assert((handle = chck_dl_load(DL_LIBRARY, &error)) != NULL); 16 | assert(error == NULL); 17 | assert(chck_dl_load_symbol(handle, "dlsym", &error) != NULL); 18 | assert(error == NULL); 19 | #elif __unix__ 20 | assert((handle = chck_dl_load(DL_LIBRARY, &error)) != NULL); 21 | assert(error == NULL); 22 | assert(chck_dl_load_symbol(handle, "dlsym", &error) != NULL); 23 | assert(error == NULL); 24 | #elif defined(_WIN32) || defined(_WIN64) 25 | assert((handle = chck_dl_load("C:/windows/system32/kernel32.dll", &error)) != NULL); 26 | assert(error == NULL); 27 | assert(chck_dl_load_symbol(handle, "GetNativeSystemInfo", &error) != NULL); 28 | assert(error == NULL); 29 | #else 30 | assert((handle = chck_dl_load("unsupported.os", &error)) == NULL); 31 | assert(error != NULL); 32 | return EXIT_SUCCESS; 33 | #endif 34 | 35 | assert(chck_dl_load_symbol(handle, "iWantSomeFrenchFriesWithHotChickenPlease", &error) == NULL); 36 | assert(error != NULL); 37 | 38 | assert(chck_dl_load_symbol(handle, "iWantSomeFrenchFriesWithHotChickenPlease", NULL) == NULL); 39 | assert(chck_dl_load("iWantSomeFrenchFriesWithHotChickenPlease", NULL) == NULL); 40 | chck_dl_unload(handle); 41 | } 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /chck/fs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(chck_fs fs.c) 2 | install_libraries(chck_fs) 3 | install_headers(fs.h) 4 | 5 | if (CHCK_BUILD_TESTS) 6 | add_executable(fs_test test.c) 7 | target_link_libraries(fs_test PRIVATE chck_fs) 8 | add_test_ex(fs_test) 9 | endif () 10 | -------------------------------------------------------------------------------- /chck/fs/README.md: -------------------------------------------------------------------------------- 1 | # Portable filesystem operations 2 | 3 | Delete all your stuff easily. 4 | 5 | ## TODO 6 | * mkdirp, rm -r, etc dangerous stuff... 7 | -------------------------------------------------------------------------------- /chck/fs/fs.c: -------------------------------------------------------------------------------- 1 | #include "fs.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #if defined(_WIN32) || defined(_WIN64) 10 | # include 11 | #endif 12 | 13 | #if defined(__APPLE__) && defined(__MACH__) 14 | # include 15 | #endif 16 | 17 | #if defined(BSD) || defined(__FreeBSD__) 18 | # include 19 | # include 20 | #endif 21 | 22 | #if defined(_WIN32) || defined(_WIN64) 23 | static inline char* 24 | ccopy(const char *str) 25 | { 26 | assert(str); 27 | const size_t size = strlen(str); 28 | char *cpy = chck_calloc_add_of(size, 1); 29 | return (cpy ? memcpy(cpy, str, size) : NULL); 30 | } 31 | #endif 32 | 33 | static inline bool 34 | resize(char **buf, size_t *size, size_t nsize) 35 | { 36 | assert(buf && size); 37 | 38 | if (nsize == *size) 39 | return true; 40 | 41 | void *tmp; 42 | if (!(tmp = realloc(*buf, nsize))) 43 | return false; 44 | 45 | *buf = tmp; 46 | *size = nsize; 47 | return true; 48 | } 49 | 50 | static inline bool 51 | resize_mul_of(char **buf, size_t *size, size_t nsize, size_t mul) 52 | { 53 | size_t r; 54 | if (chck_mul_ofsz(nsize, mul, &r)) 55 | return false; 56 | 57 | return resize(buf, size, nsize * mul); 58 | } 59 | 60 | static char* 61 | get_executable_path_from(const char *path) 62 | { 63 | char *buf = NULL; 64 | ssize_t rsize; 65 | size_t size = 1024; 66 | 67 | // here be dragons 68 | 69 | #if (defined(__APPLE__) && defined(__MACH__)) 70 | (void)path; 71 | unsigned int bsize = 0; 72 | _NSGetExecutablePath(NULL, &bsize); 73 | if (bsize == 0) goto fail; 74 | if (!(buf = malloc(bsize))) goto fail; 75 | _NSGetExecutablePath(buf, &bsize); 76 | buf[bsize] = 0; 77 | size = rsize = bsize; 78 | #elif defined(_WIN32) || defined(_WIN64) 79 | (void)path; 80 | if (!(buf = malloc(size))) goto fail; 81 | while ((size_t)(rsize = GetModuleFileName(NULL, buf, size)) > size) { 82 | if (rsize <= 0) goto fail; 83 | if (!resize_mul_of(&buf, &size, size, 2)) goto fail; 84 | } 85 | #else 86 | if (!(buf = malloc(size))) goto fail; 87 | while ((size_t)(rsize = readlink(path, buf, size)) > size) { 88 | if (rsize <= 0) goto fail; 89 | if (!resize_mul_of(&buf, &size, size, 2)) goto fail; 90 | } 91 | #endif 92 | 93 | if (rsize <= 0) goto fail; 94 | if (rsize != -1) { 95 | if (!resize(&buf, &size, (size_t)rsize + 1)) goto fail; 96 | buf[rsize] = 0; 97 | } 98 | 99 | #if (defined(__APPLE__) && defined(__MACH__)) 100 | char *tmp; 101 | if (!(tmp = realpath(buf, NULL))) goto fail; 102 | free(buf); buf = tmp; 103 | #endif 104 | return buf; 105 | 106 | fail: 107 | free(buf); 108 | return NULL; 109 | } 110 | 111 | char* 112 | chck_get_executable_path(void) 113 | { 114 | const char *path = NULL; 115 | char *exepath = NULL; 116 | 117 | #if defined(EMSCRIPTEN) 118 | (void)path; 119 | (void)exepath; 120 | return NULL; 121 | #elif defined(_WIN32) || defined(_WIN64) 122 | if (_pgmptr && !(exepath = ccopy(_pgmptr))) return NULL; 123 | if (exepath) return exepath; 124 | #elif defined(BSD) || defined(__FreeBSD__) /* works on all BSD's? */ 125 | int mib[4]; 126 | mib[0] = CTL_KERN; 127 | mib[1] = KERN_PROC; 128 | mib[2] = KERN_PROC_PATHNAME; 129 | mib[3] = -1; 130 | size_t cb = 0; 131 | sysctl(mib, 4, NULL, &cb, NULL, 0); 132 | if (cb > 0) { 133 | if (!(exepath = malloc(cb))) return NULL; 134 | sysctl(mib, 4, exepath, &cb, NULL, 0); 135 | return exepath; 136 | } 137 | #endif 138 | 139 | #if defined(__linux__) 140 | path = "/proc/self/exe"; 141 | #elif defined(__NetBSD__) 142 | path = "/proc/curproc/exe"; 143 | #elif defined(BSD) || defined(__FreeBSD__) 144 | path = "/proc/curproc/file"; 145 | #elif defined(__sun) 146 | path = "/proc/self/path/a.out"; 147 | #elif defined(_WIN32) || defined(_WIN64) 148 | path = NULL; 149 | #elif defined(__APPLE__) && defined(__MACH__) 150 | path = NULL; 151 | #elif defined(EMSCRIPTEN) 152 | path = NULL; 153 | #else 154 | # error insert your OS here 155 | #endif 156 | 157 | /* free this when not needed */ 158 | exepath = get_executable_path_from(path); 159 | return exepath; 160 | } 161 | -------------------------------------------------------------------------------- /chck/fs/fs.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_fs__ 2 | #define __chck_fs__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static inline const char* 11 | chck_basename(const char *path) 12 | { 13 | assert(path); 14 | const char *base = strrchr(path, '/'); 15 | return (base ? base + 1 : path); 16 | } 17 | 18 | static inline bool 19 | chck_filename_is_safe(const char *name) 20 | { 21 | if (!name || !*name) 22 | return false; 23 | 24 | if (strchr(name, '/') || !strcmp(name, ".") || !strcmp(name, "..") || strlen(name) > FILENAME_MAX) 25 | return false; 26 | 27 | return true; 28 | } 29 | 30 | char* chck_get_executable_path(void); 31 | 32 | #endif /* __chck_fs__ */ 33 | -------------------------------------------------------------------------------- /chck/fs/test.c: -------------------------------------------------------------------------------- 1 | #include "fs.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #undef NDEBUG 7 | #include 8 | 9 | int main(void) 10 | { 11 | /* TEST: executable path */ 12 | { 13 | char *path = chck_get_executable_path(); 14 | assert(path != NULL); 15 | #if __unix__ 16 | assert(path[0] == '/'); 17 | assert(!strcmp(path+strlen(path)-strlen("fs_test"), "fs_test")); 18 | #elif defined(_WIN32) || defined(_WIN64) 19 | assert(path[1] == ':'); 20 | assert(!strcmp(path+strlen(path)-strlen("fs_test.exe"), "fs_test.exe")); 21 | #endif 22 | printf("%s\n", path); 23 | free(path); 24 | } 25 | 26 | /* TEST: basename */ 27 | { 28 | assert(!strcmp(chck_basename("foo/bar"), "bar")); 29 | assert(!strcmp(chck_basename("foo"), "foo")); 30 | assert(!strcmp(chck_basename("foo/"), "")); 31 | assert(strcmp(chck_basename("foo/"), "foo")); 32 | assert(strcmp(chck_basename("foo/"), "/")); 33 | assert(!strcmp(chck_basename(""), "")); 34 | assert(strcmp(chck_basename("foo/bar"), "foo")); 35 | } 36 | 37 | /* TEST: safe filename */ 38 | { 39 | assert(!chck_filename_is_safe("..")); 40 | assert(!chck_filename_is_safe(".")); 41 | assert(!chck_filename_is_safe("foo/bar")); 42 | assert(chck_filename_is_safe("foo")); 43 | assert(chck_filename_is_safe("foo_01_bar")); 44 | 45 | char f[FILENAME_MAX + 2]; 46 | memset(f, 'a', sizeof(f)); 47 | f[FILENAME_MAX + 1] = 0; 48 | assert(!chck_filename_is_safe(f)); 49 | } 50 | 51 | return EXIT_SUCCESS; 52 | } 53 | -------------------------------------------------------------------------------- /chck/lut/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(chck_lut lut.c) 2 | install_libraries(chck_lut) 3 | install_headers(lut.h) 4 | 5 | if (CHCK_BUILD_TESTS) 6 | add_executable(lut_test test.c) 7 | target_link_libraries(lut_test PRIVATE chck_lut) 8 | add_test_ex(lut_test) 9 | endif () 10 | -------------------------------------------------------------------------------- /chck/lut/README.md: -------------------------------------------------------------------------------- 1 | # Lookup and hash tables 2 | 3 | Cheat codes for lazy programmers. 4 | -------------------------------------------------------------------------------- /chck/lut/lut.c: -------------------------------------------------------------------------------- 1 | #include "lut.h" 2 | #include 3 | #include /* for calloc, free, etc.. */ 4 | #include /* for memcpy/memset */ 5 | #include /* for assert */ 6 | 7 | static inline char* 8 | ccopy(const char *str) 9 | { 10 | assert(str); 11 | const size_t size = strlen(str); 12 | char *cpy = chck_calloc_add_of(size, 1); 13 | return (cpy ? memcpy(cpy, str, size) : NULL); 14 | } 15 | 16 | static inline bool 17 | lut_create_table(struct chck_lut *lut) 18 | { 19 | assert(lut); 20 | 21 | if (!(lut->table = chck_malloc_mul_of(lut->count, lut->member))) 22 | return false; 23 | 24 | memset(lut->table, lut->set, lut->count * lut->member); 25 | return true; 26 | } 27 | 28 | static inline void* 29 | lut_get_index(struct chck_lut *lut, uint32_t index) 30 | { 31 | assert(lut && index < lut->count); 32 | 33 | if (!lut->table && !lut_create_table(lut)) 34 | return NULL; 35 | 36 | if (index >= lut->count) 37 | return NULL; 38 | 39 | return lut->table + index * lut->member; 40 | } 41 | 42 | static inline bool 43 | lut_set_index(struct chck_lut *lut, uint32_t index, const void *data) 44 | { 45 | assert(lut && index < lut->count); 46 | 47 | if (!lut->table && !lut_create_table(lut)) 48 | return false; 49 | 50 | if (index >= lut->count) 51 | return NULL; 52 | 53 | if (data) { 54 | memcpy(lut->table + index * lut->member, data, lut->member); 55 | } else { 56 | memset(lut->table + index * lut->member, lut->set, lut->member); 57 | } 58 | 59 | return true; 60 | } 61 | 62 | bool 63 | chck_lut(struct chck_lut *lut, int set, size_t count, size_t member) 64 | { 65 | assert(lut && count > 0 && member > 0); 66 | 67 | if (!count || !member) 68 | return false; 69 | 70 | *lut = (struct chck_lut){ .set = set, .count = count, .member = member, .hashuint = chck_default_uint_hash, .hashstr = chck_default_str_hash }; 71 | return true; 72 | } 73 | 74 | void 75 | chck_lut_uint_algorithm(struct chck_lut *lut, uint32_t (*hashuint)(uint32_t uint)) 76 | { 77 | assert(lut && hashuint); 78 | lut->hashuint = hashuint; 79 | } 80 | 81 | void 82 | chck_lut_str_algorithm(struct chck_lut *lut, uint32_t (*hashstr)(const char *str, size_t len)) 83 | { 84 | assert(lut && hashstr); 85 | lut->hashstr = hashstr; 86 | } 87 | 88 | void 89 | chck_lut_flush(struct chck_lut *lut) 90 | { 91 | assert(lut); 92 | free(lut->table); 93 | lut->table = NULL; 94 | } 95 | 96 | void 97 | chck_lut_release(struct chck_lut *lut) 98 | { 99 | if (!lut) 100 | return; 101 | 102 | chck_lut_flush(lut); 103 | *lut = (struct chck_lut){0}; 104 | } 105 | 106 | bool 107 | chck_lut_set(struct chck_lut *lut, uint32_t lookup, const void *data) 108 | { 109 | assert(lut && lut->hashuint); 110 | return lut_set_index(lut, lut->hashuint(lookup) % lut->count, data); 111 | } 112 | 113 | void* 114 | chck_lut_get(struct chck_lut *lut, uint32_t lookup) 115 | { 116 | assert(lut && lut->hashuint); 117 | return lut_get_index(lut, lut->hashuint(lookup) % lut->count); 118 | } 119 | 120 | bool 121 | chck_lut_str_set(struct chck_lut *lut, const char *str, size_t len, const void *data) 122 | { 123 | assert(lut && lut->hashstr); 124 | return lut_set_index(lut, lut->hashstr(str, len) % lut->count, data); 125 | } 126 | 127 | void* 128 | chck_lut_str_get(struct chck_lut *lut, const char *str, size_t len) 129 | { 130 | assert(lut && lut->hashstr); 131 | return lut_get_index(lut, lut->hashstr(str, len) % lut->count); 132 | } 133 | 134 | void* 135 | chck_lut_iter(struct chck_lut *lut, size_t *iter) 136 | { 137 | assert(lut); 138 | 139 | if (*iter >= lut->count) 140 | return NULL; 141 | 142 | return lut->table + (*iter)++ * lut->member; 143 | } 144 | 145 | // metadata for resolving collisions 146 | struct header { 147 | bool placed; 148 | char *str_key; 149 | uint32_t uint_key; 150 | }; 151 | 152 | static bool 153 | header(struct header *hdr, const char *str_key, uint32_t uint_key) 154 | { 155 | void *str_copy = NULL; 156 | if (str_key && !(str_copy = ccopy(str_key))) { 157 | *hdr = (struct header){0}; 158 | return false; 159 | } 160 | 161 | hdr->placed = true; 162 | hdr->uint_key = uint_key; 163 | hdr->str_key = str_copy; 164 | return true; 165 | } 166 | 167 | static void 168 | header_release(struct header *hdr) 169 | { 170 | assert(hdr); 171 | 172 | if (hdr->str_key) { 173 | free(hdr->str_key); 174 | hdr->str_key = NULL; 175 | } 176 | 177 | hdr->placed = false; 178 | } 179 | 180 | static struct chck_hash_table* 181 | next_table(struct chck_hash_table *table) 182 | { 183 | assert(table); 184 | 185 | // create new table 186 | if (!(table->next = malloc(sizeof(*table->next)))) 187 | return false; 188 | 189 | if (!chck_hash_table(table->next, table->lut.set, table->lut.count, table->lut.member)) 190 | goto fail; 191 | 192 | chck_hash_table_uint_algorithm(table->next, table->lut.hashuint); 193 | chck_hash_table_str_algorithm(table->next, table->lut.hashstr); 194 | return table->next; 195 | 196 | fail: 197 | free(table->next); 198 | return (table->next = NULL); 199 | } 200 | 201 | static bool 202 | hash_table_set(struct chck_hash_table *table, struct chck_hash_table *l, struct header *h, uint32_t index, const char *str_key, uint32_t uint_key, const void *data) 203 | { 204 | // wanted to remove something that does not exist in hash table 205 | if (!table && !data) 206 | return true; 207 | 208 | // collision, we need new table 209 | if (!table && !(table = next_table(l))) 210 | return false; 211 | 212 | // release data of current header, if any in this slot 213 | if (h) 214 | header_release(h); 215 | 216 | // removal 217 | if (!data) { 218 | lut_set_index(&table->meta, index, NULL); 219 | return lut_set_index(&table->lut, index, NULL); 220 | } 221 | 222 | struct header hdr; 223 | if (!header(&hdr, str_key, uint_key) || !lut_set_index(&table->meta, index, &hdr)) { 224 | header_release(&hdr); 225 | return false; 226 | } 227 | 228 | return lut_set_index(&table->lut, index, data); 229 | } 230 | 231 | static bool 232 | hash_table_set_uint(struct chck_hash_table *table, uint32_t index, uint32_t key, const void *data) 233 | { 234 | assert(table); 235 | 236 | struct header *h; 237 | struct chck_hash_table *t = table, *l; 238 | do { 239 | l = t; 240 | if (!(h = lut_get_index(&t->meta, index)) || !h->placed || (!h->str_key && h->uint_key == key)) 241 | break; 242 | h = NULL; // Clear, in case we have collision. So we don't remove this header. (func: hash_table_set) 243 | } while ((t = t->next)); 244 | 245 | return hash_table_set(t, l, h, index, NULL, key, data); 246 | } 247 | 248 | static bool 249 | hash_table_set_str(struct chck_hash_table *table, uint32_t index, const char *key, const void *data) 250 | { 251 | assert(table && key); 252 | 253 | struct header *h; 254 | struct chck_hash_table *t = table, *l; 255 | do { 256 | l = t; 257 | if (!(h = lut_get_index(&t->meta, index)) || !h->placed || (h->str_key && !strcmp(h->str_key, key))) 258 | break; 259 | h = NULL; // Clear, in case we have collision. So we don't remove this header. (func: hash_table_set) 260 | } while ((t = t->next)); 261 | 262 | return hash_table_set(t, l, h, index, key, -1, data); 263 | } 264 | 265 | bool 266 | chck_hash_table(struct chck_hash_table *table, int set, size_t count, size_t member) 267 | { 268 | assert(table); 269 | *table = (struct chck_hash_table){0}; 270 | 271 | if (!chck_lut(&table->lut, set, count, member)) 272 | return false; 273 | 274 | if (!chck_lut(&table->meta, 0, count, sizeof(struct header))) 275 | goto fail; 276 | 277 | return true; 278 | 279 | fail: 280 | chck_lut_release(&table->lut); 281 | return false; 282 | } 283 | 284 | void 285 | chck_hash_table_uint_algorithm(struct chck_hash_table *table, uint32_t (*hashuint)(uint32_t uint)) 286 | { 287 | assert(table && hashuint); 288 | 289 | for (struct chck_hash_table *t = table; t; t = t->next) { 290 | chck_lut_uint_algorithm(&t->lut, hashuint); 291 | chck_lut_uint_algorithm(&t->meta, hashuint); 292 | } 293 | } 294 | 295 | void 296 | chck_hash_table_str_algorithm(struct chck_hash_table *table, uint32_t (*hashstr)(const char *str, size_t len)) 297 | { 298 | assert(table && hashstr); 299 | 300 | for (struct chck_hash_table *t = table; t; t = t->next) { 301 | chck_lut_str_algorithm(&t->lut, hashstr); 302 | chck_lut_str_algorithm(&t->meta, hashstr); 303 | } 304 | } 305 | 306 | void 307 | chck_hash_table_flush(struct chck_hash_table *table) 308 | { 309 | assert(table); 310 | 311 | struct chck_hash_table *n; 312 | for (struct chck_hash_table *t = table; t; t = n) { 313 | n = t->next; 314 | 315 | // release all metadata headers (in case of string keys) 316 | struct header *hdr; 317 | chck_lut_for_each(&t->meta, hdr) 318 | header_release(hdr); 319 | 320 | chck_lut_flush(&t->lut); 321 | chck_lut_flush(&t->meta); 322 | 323 | if (t != table) 324 | free(t); 325 | } 326 | 327 | table->next = NULL; 328 | } 329 | 330 | void 331 | chck_hash_table_release(struct chck_hash_table *table) 332 | { 333 | if (!table) 334 | return; 335 | 336 | chck_hash_table_flush(table); 337 | *table = (struct chck_hash_table){0}; 338 | } 339 | 340 | uint32_t 341 | chck_hash_table_collisions(struct chck_hash_table *table) 342 | { 343 | assert(table); 344 | 345 | uint32_t collisions = 0; 346 | for (struct chck_hash_table *t = table->next; t; t = t->next) { 347 | struct header *hdr; 348 | chck_lut_for_each(&t->meta, hdr) { 349 | if (hdr->placed) 350 | ++collisions; 351 | } 352 | } 353 | 354 | return collisions; 355 | } 356 | 357 | bool 358 | chck_hash_table_set(struct chck_hash_table *table, uint32_t key, const void *data) 359 | { 360 | assert(table); 361 | return hash_table_set_uint(table, table->lut.hashuint(key) % table->lut.count, key, data); 362 | } 363 | 364 | void* 365 | chck_hash_table_get(struct chck_hash_table *table, uint32_t key) 366 | { 367 | assert(table); 368 | 369 | if (!table->lut.table) 370 | return NULL; 371 | 372 | void *data; 373 | struct header *h; 374 | struct chck_hash_table *t = table; 375 | do { 376 | data = chck_lut_get(&t->lut, key); 377 | h = chck_lut_get(&t->meta, key); 378 | t = t->next; 379 | 380 | if (h && !h->str_key && h->uint_key == key) 381 | return data; 382 | 383 | // check if this item is a intersection, if so cycle from another set of luts 384 | } while (t); 385 | 386 | return NULL; 387 | } 388 | 389 | bool 390 | chck_hash_table_str_set(struct chck_hash_table *table, const char *str, size_t len, const void *data) 391 | { 392 | assert(table && str); 393 | return hash_table_set_str(table, table->lut.hashstr(str, len) % table->lut.count, str, data); 394 | } 395 | 396 | void* 397 | chck_hash_table_str_get(struct chck_hash_table *table, const char *str, size_t len) 398 | { 399 | assert(table && str); 400 | 401 | if (!table->lut.table) 402 | return NULL; 403 | 404 | void *data; 405 | struct header *h; 406 | struct chck_hash_table *t = table; 407 | do { 408 | data = chck_lut_str_get(&t->lut, str, len); 409 | h = chck_lut_str_get(&t->meta, str, len); 410 | t = t->next; 411 | 412 | if (h && h->str_key && !strcmp(h->str_key, str)) 413 | return data; 414 | 415 | // check if this item is a intersection, if so cycle from another set of luts 416 | } while (t); 417 | 418 | return NULL; 419 | } 420 | 421 | void* 422 | chck_hash_table_iter(struct chck_hash_table_iterator *iterator) 423 | { 424 | assert(iterator && iterator->table); 425 | 426 | iterator->str_key = NULL; 427 | iterator->uint_key = 0; 428 | 429 | bool placed = false; 430 | while (!placed) { 431 | if (iterator->iter >= iterator->table->lut.count) { 432 | if (!iterator->table->next) 433 | return NULL; 434 | 435 | // switch to another set of luts, since we have collisions 436 | iterator->table = iterator->table->next; 437 | iterator->iter = 0; 438 | } 439 | 440 | struct header *h; 441 | if ((h = lut_get_index(&iterator->table->meta, iterator->iter))) { 442 | iterator->str_key = h->str_key; 443 | iterator->uint_key = h->uint_key; 444 | placed = h->placed; 445 | } 446 | 447 | if (!placed) 448 | chck_lut_iter(&iterator->table->lut, &iterator->iter); 449 | } 450 | 451 | return chck_lut_iter(&iterator->table->lut, &iterator->iter); 452 | } 453 | -------------------------------------------------------------------------------- /chck/lut/lut.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_lut__ 2 | #define __chck_lut__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct chck_lut { 10 | uint8_t *table; 11 | 12 | // count and member size (lut size == count * member) 13 | size_t count, member; 14 | 15 | // the value lut was initialized with (memset(table, set, count * member)) 16 | int set; 17 | 18 | // pointers to hash functions 19 | uint32_t (*hashuint)(uint32_t uint); 20 | uint32_t (*hashstr)(const char *str, size_t len); 21 | }; 22 | 23 | struct chck_hash_table { 24 | struct chck_lut lut; 25 | struct chck_lut meta; 26 | 27 | // if there was collision, next table is created 28 | struct chck_hash_table *next; 29 | }; 30 | 31 | struct chck_hash_table_iterator { 32 | struct chck_hash_table *table; 33 | size_t iter; 34 | const char *str_key; 35 | uint32_t uint_key; 36 | }; 37 | 38 | // simply return the input, this is good for incrementing numbers 39 | // or, when you know that the input will be somewhere around range of your hash table's size 40 | static inline uint32_t 41 | chck_incremental_uint_hash(uint32_t uint) 42 | { 43 | return uint; 44 | } 45 | 46 | // default simple hash from 47 | static inline uint32_t 48 | chck_default_uint_hash(uint32_t uint) 49 | { 50 | uint = ((uint >> 16) ^ uint) * 0x45d9f3b; 51 | uint = ((uint >> 16) ^ uint) * 0x45d9f3b; 52 | return ((uint >> 16) ^ uint); 53 | } 54 | 55 | // default simple string hash 56 | static inline uint32_t 57 | chck_default_str_hash(const char *str, size_t len) 58 | { 59 | (void)len; 60 | int32_t c; 61 | uint32_t hash = 5381; 62 | while ((c = *str++)) hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ 63 | return hash; 64 | } 65 | 66 | /** 67 | * LUTs are manual lookup tables for your data. 68 | * Iterating LUT may not be effecient operation depending on the size of the lut. 69 | * 70 | * LUTs won't handle hash collisions at all, and stores the data in fixed size pool, thus references are copied. 71 | * This means, when collision happen, new data is copied over the intersecting data. 72 | * Thus you should not store anything allocated in luts (unless you can free the memory otherwise). 73 | */ 74 | 75 | #define chck_lut_for_each_call(lut, function, ...) \ 76 | { void *_P; for (size_t _I = 0; (_P = chck_lut_iter(lut, &_I));) function(_P, ##__VA_ARGS__); } 77 | 78 | #define chck_lut_for_each(lut, pos) \ 79 | for (size_t _I = 0; (pos = chck_lut_iter(lut, &_I));) 80 | 81 | bool chck_lut(struct chck_lut *lut, int set, size_t count, size_t member); 82 | void chck_lut_uint_algorithm(struct chck_lut *lut, uint32_t (*hashuint)(uint32_t uint)); 83 | void chck_lut_str_algorithm(struct chck_lut *lut, uint32_t (*hashstr)(const char *str, size_t len)); 84 | void chck_lut_release(struct chck_lut *lut); 85 | void chck_lut_flush(struct chck_lut *lut); 86 | bool chck_lut_set(struct chck_lut *lut, uint32_t lookup, const void *data); 87 | void* chck_lut_get(struct chck_lut *lut, uint32_t lookup); 88 | bool chck_lut_str_set(struct chck_lut *lut, const char *str, size_t len, const void *data); 89 | void* chck_lut_str_get(struct chck_lut *lut, const char *str, size_t len); 90 | void* chck_lut_iter(struct chck_lut *lut, size_t *iter); 91 | 92 | /** 93 | * Hash tables are wrappers around LUTs that does not have collisions. 94 | * Iterating Hash table may not be a effecient operation depending on the size of the hash table. 95 | * 96 | * Hash table uses internally LUTs. 97 | * When collision occurs it will push a new layer of luts for intersected items. 98 | * Thus the effeciency of the hash table decreases the more collisions/redirects there is. 99 | */ 100 | 101 | #define chck_hash_table_for_each_call(table, function, ...) \ 102 | { struct chck_hash_table_iterator _I = { table, 0, NULL, 0 }; void *_P; while ((_P = chck_hash_table_iter(&_I))) function(_P, ##__VA_ARGS__); } 103 | 104 | #define chck_hash_table_for_each(table, pos) \ 105 | for (struct chck_hash_table_iterator _I = { table, 0, NULL, 0 }; (pos = chck_hash_table_iter(&_I));) 106 | 107 | bool chck_hash_table(struct chck_hash_table *table, int set, size_t count, size_t member); 108 | void chck_hash_table_uint_algorithm(struct chck_hash_table *table, uint32_t (*hashuint)(uint32_t uint)); 109 | void chck_hash_table_str_algorithm(struct chck_hash_table *table, uint32_t (*hashstr)(const char *str, size_t len)); 110 | void chck_hash_table_release(struct chck_hash_table *table); 111 | void chck_hash_table_flush(struct chck_hash_table *table); 112 | uint32_t chck_hash_table_collisions(struct chck_hash_table *table); 113 | bool chck_hash_table_set(struct chck_hash_table *table, uint32_t key, const void *data); 114 | void* chck_hash_table_get(struct chck_hash_table *table, uint32_t key); 115 | bool chck_hash_table_str_set(struct chck_hash_table *table, const char *str, size_t len, const void *data); 116 | void* chck_hash_table_str_get(struct chck_hash_table *table, const char *str, size_t len); 117 | void* chck_hash_table_iter(struct chck_hash_table_iterator *iter); 118 | 119 | #endif /* __chck_lut__ */ 120 | -------------------------------------------------------------------------------- /chck/lut/test.c: -------------------------------------------------------------------------------- 1 | #include "lut.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #undef NDEBUG 7 | #include 8 | 9 | static void printstr(const char **str) 10 | { 11 | if (*str) 12 | printf("%s\n", *str); 13 | } 14 | 15 | int main(void) 16 | { 17 | /* TEST: lut */ 18 | { 19 | struct chck_lut lut; 20 | assert(chck_lut(&lut, 0, 32, sizeof(const char*))); 21 | 22 | const char *s0 = "(0) foobar"; 23 | const char *s1 = "(1) penguin"; 24 | const char *s2 = "(2) ismo"; 25 | 26 | assert(chck_lut_set(&lut, 0, &s0)); 27 | assert(chck_lut_set(&lut, 1, &s1)); 28 | assert(chck_lut_set(&lut, 2, &s2)); 29 | 30 | assert(*(const char**)chck_lut_get(&lut, 0) == s0); 31 | assert(*(const char**)chck_lut_get(&lut, 1) == s1); 32 | assert(*(const char**)chck_lut_get(&lut, 2) == s2); 33 | 34 | { 35 | const char **p; 36 | uint32_t i = 0; 37 | chck_lut_for_each(&lut, p) if (*p) ++i; 38 | assert(i == 3); 39 | } 40 | 41 | chck_lut_for_each_call(&lut, printstr); 42 | chck_lut_flush(&lut); 43 | 44 | assert(chck_lut_str_set(&lut, "s0", 2, &s0)); 45 | assert(chck_lut_str_set(&lut, "s1", 2, &s1)); 46 | assert(chck_lut_str_set(&lut, "s2", 2, &s2)); 47 | 48 | assert(*(const char**)chck_lut_str_get(&lut, "s0", 2) == s0); 49 | assert(*(const char**)chck_lut_str_get(&lut, "s1", 2) == s1); 50 | assert(*(const char**)chck_lut_str_get(&lut, "s2",2 ) == s2); 51 | 52 | chck_lut_for_each_call(&lut, printstr); 53 | 54 | { 55 | const char **p; 56 | uint32_t i = 0; 57 | chck_lut_for_each(&lut, p) if (*p) ++i; 58 | assert(i == 3); 59 | } 60 | 61 | chck_lut_str_set(&lut, "s0", 2, &s1); 62 | assert(*(const char**)chck_lut_str_get(&lut, "s0", 2) == s1); 63 | 64 | { 65 | const char **p; 66 | uint32_t i = 0; 67 | chck_lut_for_each(&lut, p) if (*p) ++i; 68 | assert(i == 3); 69 | } 70 | 71 | chck_lut_release(&lut); 72 | } 73 | 74 | /* TEST: hash table */ 75 | { 76 | struct chck_hash_table table; 77 | assert(chck_hash_table(&table, 0, 32, sizeof(const char*))); 78 | 79 | const char *s0 = "(0) foobar"; 80 | const char *s1 = "(1) penguin"; 81 | const char *s2 = "(2) ismo"; 82 | 83 | chck_hash_table_set(&table, 0, &s0); 84 | chck_hash_table_set(&table, 1, &s1); 85 | chck_hash_table_set(&table, 2, &s2); 86 | 87 | assert(*(const char**)chck_hash_table_get(&table, 0) == s0); 88 | assert(*(const char**)chck_hash_table_get(&table, 1) == s1); 89 | assert(*(const char**)chck_hash_table_get(&table, 2) == s2); 90 | 91 | { 92 | const char **p; 93 | uint32_t i = 0; 94 | chck_hash_table_for_each(&table, p) if (*p) ++i; 95 | assert(i == 3); 96 | } 97 | 98 | chck_hash_table_str_set(&table, "s0", 2, &s0); 99 | chck_hash_table_str_set(&table, "s1", 2, &s1); 100 | chck_hash_table_str_set(&table, "s2", 2, &s2); 101 | 102 | { 103 | const char **p; 104 | uint32_t i = 0; 105 | chck_hash_table_for_each(&table, p) if (*p) ++i; 106 | assert(i == 6); 107 | } 108 | 109 | chck_hash_table_for_each_call(&table, printstr); 110 | printf("[1] collisions: %u\n", chck_hash_table_collisions(&table)); 111 | chck_hash_table_flush(&table); 112 | 113 | chck_hash_table_str_set(&table, "s0", 2, &s0); 114 | chck_hash_table_str_set(&table, "s1", 2, &s1); 115 | chck_hash_table_str_set(&table, "s2", 2, &s2); 116 | 117 | assert(*(const char**)chck_hash_table_str_get(&table, "s0", 2) == s0); 118 | assert(*(const char**)chck_hash_table_str_get(&table, "s1", 2) == s1); 119 | assert(*(const char**)chck_hash_table_str_get(&table, "s2", 2) == s2); 120 | 121 | chck_hash_table_for_each_call(&table, printstr); 122 | 123 | { 124 | const char **p; 125 | uint32_t i = 0; 126 | chck_hash_table_for_each(&table, p) if (*p) ++i; 127 | assert(i == 3); 128 | } 129 | 130 | chck_hash_table_str_set(&table, "s0", 2, &s1); 131 | assert(*(const char**)chck_hash_table_str_get(&table, "s0", 2) == s1); 132 | 133 | { 134 | const char **p; 135 | uint32_t i = 0; 136 | chck_hash_table_for_each(&table, p) if (*p) ++i; 137 | assert(i == 3); 138 | } 139 | 140 | printf("[2] collisions: %u\n", chck_hash_table_collisions(&table)); 141 | chck_hash_table_flush(&table); 142 | } 143 | 144 | /* TEST: benchmark (default algorithm, number of collisions) */ 145 | { 146 | const uint32_t iters = 24; 147 | struct chck_hash_table table; 148 | assert(chck_hash_table(&table, -1, 128, sizeof(uint32_t))); 149 | 150 | uint32_t r[24] = { 151 | 0xFFFFFF, 0xFFFF, 0xFF, 0xEFEF, 0xDEAD, 0xFFFFF, 152 | 0x5B1E18, 0x7225E4, 0x17A414, 0xDC183C, 0xF8A0B8, 0xEEC494, 153 | 0x371685, 0x0FB872, 0x414034, 0xDC4684, 0x404076, 0x9E3E7E, 154 | 0x0C2555, 0x9DA908, 0xAC6E58, 0x773527, 0x860D2, 0x9F3CA9 155 | }; 156 | 157 | for (uint32_t i = 0; i < iters; ++i) { 158 | chck_hash_table_set(&table, r[i], &i); 159 | } 160 | 161 | for (uint32_t i = iters / 2, d = iters / 2; i < iters; ++i, --d) { 162 | assert(*(uint32_t*)chck_hash_table_get(&table, r[i]) == i); 163 | assert(*(uint32_t*)chck_hash_table_get(&table, r[d]) == d); 164 | } 165 | 166 | printf("[3] collisions: %u\n", chck_hash_table_collisions(&table)); 167 | chck_hash_table_flush(&table); 168 | } 169 | 170 | /* TEST: benchmark (incremental algorithm with known range) 171 | * mainly tests insert/get speed */ 172 | { 173 | const uint32_t iters = 0xFFFFF; 174 | struct chck_hash_table table; 175 | assert(chck_hash_table(&table, -1, iters, sizeof(uint32_t))); 176 | chck_hash_table_uint_algorithm(&table, chck_incremental_uint_hash); 177 | 178 | for (uint32_t i = 0; i < iters; ++i) 179 | chck_hash_table_set(&table, i, &i); 180 | 181 | for (uint32_t i = iters / 2, d = iters / 2; i < iters; ++i, --d) { 182 | assert(*(uint32_t*)chck_hash_table_get(&table, i) == i); 183 | assert(*(uint32_t*)chck_hash_table_get(&table, d) == d); 184 | } 185 | 186 | printf("[4] collisions: %u\n", chck_hash_table_collisions(&table)); 187 | chck_hash_table_flush(&table); 188 | } 189 | 190 | return EXIT_SUCCESS; 191 | } 192 | -------------------------------------------------------------------------------- /chck/macros.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_macros_h__ 2 | #define __chck_macros_h__ 3 | 4 | #if __GNUC__ 5 | # if !defined(likely) && !defined(unlikely) 6 | # define likely(x) __builtin_expect(!!(x), 1) 7 | # define unlikely(x) __builtin_expect(!!(x), 0) 8 | # endif 9 | # define CHCK_FORMAT(f, x, y) __attribute__((format(f, x, y))) 10 | # define CHCK_MALLOC __attribute__((malloc)) 11 | #else 12 | # if !defined(likely) && !defined(unlikely) 13 | # define likely(x) !!(x) 14 | # define unlikely(x) !!(x) 15 | # endif 16 | # define CHCK_FORMAT(f, x, y) 17 | # define CHCK_MALLOC 18 | #endif 19 | 20 | #endif /* __chck_macros_h__ */ 21 | -------------------------------------------------------------------------------- /chck/math/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | install_headers(math.h) 2 | 3 | if (CHCK_BUILD_TESTS) 4 | find_package(Math REQUIRED) 5 | add_executable(math_test test.c) 6 | target_link_libraries(math_test PRIVATE ${MATH_LIBRARY}) 7 | add_test_ex(math_test) 8 | endif () 9 | -------------------------------------------------------------------------------- /chck/math/README.md: -------------------------------------------------------------------------------- 1 | # Simple math header 2 | 3 | Math including standard math, and defining some constants if not already defined. 4 | Also generates typesafe versions of min/max functions in addition to clamp, and nearest integer rounding modulus function. 5 | -------------------------------------------------------------------------------- /chck/math/math.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_math_h__ 2 | #define __chck_math_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /** */ 12 | 13 | #ifndef M_PI 14 | # define M_PI 3.14159265358979323846 15 | #endif 16 | 17 | #ifndef M_PI_2 18 | # define M_PI_2 1.57079632679489661923 19 | #endif 20 | 21 | #ifndef M_PI_4 22 | # define M_PI_4 0.78539816339744830962 23 | #endif 24 | 25 | #ifndef M_SQRT2 26 | # define M_SQRT2 1.41421356237309504880 27 | #endif 28 | 29 | #ifndef M_SQRT1_2 30 | # define M_SQRT1_2 0.70710678118654752440 31 | #endif 32 | 33 | #ifndef M_SQRT3 34 | # define M_SQRT3 1.73205080756887729352 35 | #endif 36 | 37 | #ifndef M_SQRT1_3 38 | # define M_SQRT1_3 0.57735026918962576450 39 | #endif 40 | 41 | #ifndef M_1_PI 42 | # define M_1_PI 0.318309886183790671538 43 | #endif 44 | 45 | #ifndef M_E 46 | # define M_E 2.7182818284590452354 47 | #endif 48 | 49 | #ifndef M_LOG2E 50 | # define M_LOG2E 1.4426950408889634074 51 | #endif 52 | 53 | #ifndef M_LOG10E 54 | # define M_LOG10E 0.43429448190325182765 55 | #endif 56 | 57 | #ifndef M_LN2 58 | # define M_LN2 0.69314718055994530942 59 | #endif 60 | 61 | #ifndef M_LN10 62 | # define M_LN10 2.30258509299404568402 63 | #endif 64 | 65 | #if defined(__GNUC__) 66 | # define NAN_FLT __builtin_nanf("") 67 | #else 68 | # define NAN_FLT (*((float*)(&(int){0x7FC00000}))) 69 | #endif 70 | 71 | /** declare generic math functions */ 72 | 73 | // T = type name, n = function suffix 74 | #define decl_generics_for_type(T, n) \ 75 | static inline T chck_min##n(T a, T b) { return (a < b ? a : b); } \ 76 | static inline T chck_max##n(T a, T b) { return (a > b ? a : b); } \ 77 | static inline T chck_clamp##n(T a, T min, T max) { return (chck_min##n(chck_max##n(a, min), max)); } 78 | 79 | // T = type name, n = function suffix 80 | #define decl_unsigned_generics_for_type(T, n) \ 81 | static inline T chck_npot##n(T v) { if (v && !(v & (v - 1))) return v; T p; for (p = 1; p < v; p *= 2); return p; } 82 | 83 | // T = signed type, FT = floating point type, n = signed function suffix, fs = floating point suffix 84 | #define decl_signed_generics(T, FT, n, fs) \ 85 | decl_generics_for_type(T, n) \ 86 | static inline T chck_modn##n(T x, T m) { return x - m * round##fs((FT)x / (FT)m); } // modulus rounding to nearest int (-m/2, +m/2 range) 87 | 88 | // UT = unsigned type, un = unsigned function suffix 89 | // T = signed type, FT = floating point type, n = signed function suffix 90 | #define decl_generics(UT, un, T, FT, n) \ 91 | decl_generics_for_type(UT, un) \ 92 | decl_signed_generics(T, FT, n, ) \ 93 | decl_unsigned_generics_for_type(UT, un) \ 94 | 95 | decl_generics_for_type(size_t, sz) 96 | decl_unsigned_generics_for_type(size_t, sz) 97 | decl_generics(uint64_t, u64, int64_t, double, 64) 98 | decl_generics(uint32_t, u32, int32_t, float, 32) 99 | decl_generics(uint16_t, u16, int16_t, float, 16) 100 | decl_generics(uint8_t, u8, int8_t, float, 8) 101 | decl_signed_generics(long double, long double, ld, l) 102 | decl_signed_generics(double, double, , ) 103 | decl_signed_generics(float, float, f, f) 104 | 105 | #undef decl_generics 106 | #undef decl_signed_generics 107 | #undef decl_generics_for_type 108 | 109 | /** floating point almost equality checks */ 110 | 111 | static inline bool chck_equalld(long double a, long double b, long double error) { 112 | return (fabsl(a - b) < error * LDBL_EPSILON * fabsl(a + b) || fabsl(a - b) < LDBL_MIN); 113 | } 114 | 115 | static inline bool chck_equal(double a, double b, double error) { 116 | return (fabs(a - b) < error * DBL_EPSILON * fabs(a + b) || fabs(a - b) < DBL_MIN); 117 | } 118 | 119 | static inline bool chck_equalf(float a, float b, float error) { 120 | return (fabsf(a - b) < error * FLT_EPSILON * fabsf(a + b) || fabsf(a - b) < FLT_MIN); 121 | } 122 | 123 | #endif /* __chck_math_h__ */ 124 | -------------------------------------------------------------------------------- /chck/math/test.c: -------------------------------------------------------------------------------- 1 | #include "math.h" 2 | #include 3 | 4 | #undef NDEBUG 5 | #include 6 | 7 | #pragma GCC diagnostic ignored "-Wsuggest-attribute=pure" 8 | 9 | int main(void) 10 | { 11 | assert(chck_equalld(LDBL_MIN, LDBL_MIN + LDBL_MIN, LDBL_DIG / LDBL_EPSILON)); 12 | assert(!chck_equalld(LDBL_MIN, LDBL_MIN + LDBL_MIN, 1.0l)); 13 | assert(chck_equal(DBL_MIN, DBL_MIN + DBL_MIN, DBL_DIG / DBL_EPSILON)); 14 | assert(!chck_equal(DBL_MIN, DBL_MIN + DBL_MIN, 1.0)); 15 | assert(chck_equalf(FLT_MIN, FLT_MIN + FLT_MIN, FLT_DIG / FLT_EPSILON)); 16 | assert(!chck_equalf(FLT_MIN, FLT_MIN + FLT_MIN, 1.0f)); 17 | 18 | for (uint32_t i = 0xFFFF; i < 0xFFFFFF; ++i) { 19 | assert(chck_minu32(i, i - 20) == i - 20); 20 | assert(chck_minu32(i, i + 20) == i); 21 | assert(chck_maxu32(i, i - 20) == i); 22 | assert(chck_maxu32(i, i + 20) == i + 20); 23 | assert(chck_clampu32(i, i - 20, i + 20) == i); 24 | assert(chck_clampu32(i, i - 40, i - 20) == i - 20); 25 | assert(chck_clampu32(i, i + 20, i + 40) == i + 20); 26 | assert(chck_modn32(i, i * 2 + 20) == (int32_t)i); 27 | assert(chck_modn32(i, i * 2 - 20) == -(int32_t)(i - 20)); 28 | assert((int32_t)chck_modnf(i, i * 2 + 20) == (int32_t)i); 29 | assert((int32_t)chck_modnf(i, i * 2 - 20) == -(int32_t)(i - 20)); 30 | assert((int32_t)chck_modn(i, i * 2 + 20) == (int32_t)i); 31 | assert((int32_t)chck_modn(i, i * 2 - 20) == -(int32_t)(i - 20)); 32 | } 33 | 34 | assert(chck_max32(-20, 20) == 20); 35 | assert(chck_max32(40, 20) == 40); 36 | assert(chck_min32(-20, 20) == -20); 37 | assert(chck_min32(-40, 20) == -40); 38 | assert(chck_clamp32(40, -20, 60) == 40); 39 | assert(chck_clamp32(40, 50, 60) == 50); 40 | assert(chck_clamp32(40, -20, 20) == 20); 41 | 42 | assert((int32_t)chck_modn(20 - 340, 360) == 40); 43 | assert((int32_t)chck_modn(340 - 20, 360) == -40); 44 | assert((int32_t)chck_modnf(20 - 340, 360) == 40); 45 | assert((int32_t)chck_modnf(340 - 20, 360) == -40); 46 | 47 | assert(chck_npotu8(3) == 4); 48 | assert(chck_npotu8(1) == 1); 49 | assert(chck_npotu16(25) == 32); 50 | assert(chck_npotu16(48) == 64); 51 | assert(chck_npotu32(0) == 1); 52 | assert(chck_npotu32(4097) == 8192); 53 | assert(chck_npotsz(1025) == 2048); 54 | return EXIT_SUCCESS; 55 | } 56 | -------------------------------------------------------------------------------- /chck/overflow/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | install_headers(overflow.h) 2 | 3 | if (CHCK_BUILD_TESTS) 4 | add_executable(overflow_test test.c) 5 | add_test_ex(overflow_test) 6 | endif () 7 | -------------------------------------------------------------------------------- /chck/overflow/overflow.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_overflow_h__ 2 | #define __chck_overflow_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /** clang feature detection. */ 12 | #ifndef __has_builtin 13 | # define __has_builtin(x) 0 14 | #endif 15 | 16 | #if __GNUC__ >= 5 || __has_builtin(__builtin_add_overflow) 17 | /** assume clang and gcc (>=5) to only have builtins for now. */ 18 | # define add_of(a, b, r) __builtin_add_overflow(a, b, r) 19 | # define sub_of(a, b, r) __builtin_sub_overflow(a, b, r) 20 | # define mul_of(a, b, r) __builtin_mul_overflow(a, b, r) 21 | # define of_attr 22 | #else 23 | /** else use these generics, note behaviour of these is not strictly defined. */ 24 | # define add_of(a, b, r) ((*(r) = ((a) + (b))) < (a)) 25 | # define sub_of(a, b, r) ((*(r) = ((a) - (b))) > (a)) 26 | # define mul_of(a, b, r) (((*(r) = ((a) * (b))) || *(r) == 0) && ((a) != 0 && (b) > *(r) / (a))) 27 | # if __clang__ 28 | # define of_attr __attribute__((optnone)) // Do not optimize above checks, in most systems this will work, but not defined. 29 | # warning "Using non compiler builtins for overflow checks, this will be undefined for signed integers" 30 | # elif __GNUC__ 31 | # define of_attr __attribute__((optimize("wrapv"))) // in older GCC we can make this behavior defined 32 | # else 33 | # warning "Using non compiler builtins for overflow checks, this will be undefined for signed integers" 34 | # endif 35 | #endif 36 | 37 | /** declare overflow functions */ 38 | 39 | // T = type name, n = function suffix, s = is type signed? 40 | #define decl_generics_for_type(T, n, s) \ 41 | of_attr static inline bool chck_add_of##n(T a, T b, T *r) { assert((!s || b >= 0) && r); return add_of(a, b, r); } \ 42 | of_attr static inline bool chck_sub_of##n(T a, T b, T *r) { assert((!s || b >= 0) && r); return sub_of(a, b, r); } \ 43 | of_attr static inline bool chck_mul_of##n(T a, T b, T *r) { assert(r); return mul_of(a, b, r); } 44 | 45 | // UT = unsigned type, un = unsigned function suffix 46 | // T = signed type, n = signed function suffix 47 | #define decl_generics(UT, un, T, n) \ 48 | decl_generics_for_type(UT, un, false) \ 49 | decl_generics_for_type(T, n, true) 50 | 51 | #pragma GCC diagnostic push 52 | #pragma GCC diagnostic ignored "-Wtype-limits" 53 | 54 | decl_generics_for_type(size_t, sz, false) 55 | decl_generics(uint64_t, u64, int64_t, 64) 56 | decl_generics(uint32_t, u32, int32_t, 32) 57 | decl_generics(uint16_t, u16, int16_t, 16) 58 | decl_generics(uint8_t, u8, int8_t, 8) 59 | 60 | #pragma GCC diagnostic pop 61 | 62 | #undef decl_generics 63 | #undef decl_generics_for_type 64 | #undef add_of 65 | #undef sub_of 66 | #undef mul_of 67 | #undef of_attr 68 | 69 | CHCK_MALLOC static inline void* 70 | chck_malloc_add_of(size_t size, size_t add) 71 | { 72 | size_t r; 73 | if (unlikely(chck_add_ofsz(size, add, &r)) || !r) 74 | return NULL; 75 | 76 | return malloc(r); 77 | } 78 | 79 | CHCK_MALLOC static inline void* 80 | chck_malloc_sub_of(size_t size, size_t sub) 81 | { 82 | size_t r; 83 | if (unlikely(chck_sub_ofsz(size, sub, &r)) || !r) 84 | return NULL; 85 | 86 | return malloc(r); 87 | } 88 | 89 | CHCK_MALLOC static inline void* 90 | chck_malloc_mul_of(size_t size, size_t mul) 91 | { 92 | size_t r; 93 | if (unlikely(chck_mul_ofsz(size, mul, &r)) || !r) 94 | return NULL; 95 | 96 | return malloc(r); 97 | } 98 | 99 | CHCK_MALLOC static inline void* 100 | chck_calloc_of(size_t nmemb, size_t size) 101 | { 102 | size_t r; 103 | if (unlikely(chck_mul_ofsz(nmemb, size, &r)) || !r) 104 | return NULL; 105 | 106 | return calloc(nmemb, size); 107 | } 108 | 109 | CHCK_MALLOC static inline void* 110 | chck_calloc_add_of(size_t size, size_t add) 111 | { 112 | size_t r; 113 | if (unlikely(chck_add_ofsz(size, add, &r)) || !r) 114 | return NULL; 115 | 116 | return calloc(1, r); 117 | } 118 | 119 | CHCK_MALLOC static inline void* 120 | chck_calloc_sub_of(size_t size, size_t sub) 121 | { 122 | size_t r; 123 | if (unlikely(chck_sub_ofsz(size, sub, &r)) || !r) 124 | return NULL; 125 | 126 | return calloc(1, r); 127 | } 128 | 129 | static inline void* 130 | chck_realloc_add_of(void *ptr, size_t size, size_t add) 131 | { 132 | size_t r; 133 | if (unlikely(chck_add_ofsz(size, add, &r)) || !r) 134 | return NULL; 135 | 136 | return realloc(ptr, r); 137 | } 138 | 139 | static inline void* 140 | chck_realloc_sub_of(void *ptr, size_t size, size_t sub) 141 | { 142 | size_t r; 143 | if (unlikely(chck_sub_ofsz(size, sub, &r)) || !r) 144 | return NULL; 145 | 146 | return realloc(ptr, r); 147 | } 148 | 149 | static inline void* 150 | chck_realloc_mul_of(void *ptr, size_t size, size_t mul) 151 | { 152 | size_t r; 153 | if (unlikely(chck_mul_ofsz(size, mul, &r)) || !r) 154 | return NULL; 155 | 156 | return realloc(ptr, r); 157 | } 158 | 159 | #endif /* __chck_overflow_h__ */ 160 | -------------------------------------------------------------------------------- /chck/overflow/test.c: -------------------------------------------------------------------------------- 1 | #include "overflow.h" 2 | #include 3 | #include 4 | 5 | #undef NDEBUG 6 | #include 7 | 8 | int main(void) 9 | { 10 | #define test_of(T, n, s) \ 11 | { \ 12 | T r; \ 13 | assert(chck_add_of##n(1, s, &r)); \ 14 | assert(chck_add_of##n(s, 1, &r)); \ 15 | assert(chck_sub_of##n(((T)~0 == s ? 0 : -2), s, &r)); \ 16 | assert(chck_mul_of##n(3, s, &r)); \ 17 | assert(chck_mul_of##n(-3, s, &r)); \ 18 | assert(!chck_add_of##n(4, 4, &r)); \ 19 | assert(!chck_sub_of##n(8, 4, &r)); \ 20 | assert(!chck_mul_of##n(5, 5, &r)); \ 21 | assert(!chck_add_of##n(s, 0, &r)); \ 22 | assert(!chck_sub_of##n(s, 0, &r)); \ 23 | assert(!chck_mul_of##n(s, 0, &r)); \ 24 | } 25 | 26 | // TEST: overflow functions 27 | test_of(size_t, sz, SIZE_MAX); 28 | test_of(uint64_t, u64, UINT64_MAX); 29 | test_of(int64_t, 64, INT64_MAX); 30 | test_of(uint32_t, u32, UINT32_MAX); 31 | test_of(int32_t, 32, INT32_MAX); 32 | test_of(uint16_t, u16, UINT16_MAX); 33 | test_of(int16_t, 16, INT16_MAX); 34 | test_of(uint8_t, u8, UINT8_MAX); 35 | test_of(int8_t, 8, INT8_MAX); 36 | 37 | // TEST: overflow aware alloc functions 38 | { 39 | void *ptr; 40 | assert((ptr = chck_malloc_add_of(8, 8))); free(ptr); 41 | assert(!(ptr = chck_malloc_add_of(SIZE_MAX, 8))); free(ptr); 42 | assert(!(ptr = chck_malloc_add_of(8, SIZE_MAX))); free(ptr); 43 | 44 | assert((ptr = chck_malloc_sub_of(8, 4))); free(ptr); 45 | assert(!(ptr = chck_malloc_sub_of(4, 8))); free(ptr); 46 | 47 | assert((ptr = chck_malloc_mul_of(8, 8))); free(ptr); 48 | assert(!(ptr = chck_malloc_mul_of(8, SIZE_MAX))); free(ptr); 49 | assert(!(ptr = chck_malloc_mul_of(SIZE_MAX, 8))); free(ptr); 50 | 51 | assert((ptr = chck_calloc_add_of(8, 8))); free(ptr); 52 | assert(!(ptr = chck_calloc_add_of(SIZE_MAX, 8))); free(ptr); 53 | assert(!(ptr = chck_calloc_add_of(8, SIZE_MAX))); free(ptr); 54 | 55 | assert((ptr = chck_malloc_sub_of(8, 4))); free(ptr); 56 | assert(!(ptr = chck_malloc_sub_of(4, 8))); free(ptr); 57 | 58 | assert((ptr = chck_calloc_of(8, 8))); free(ptr); 59 | assert(!(ptr = chck_calloc_of(8, SIZE_MAX))); free(ptr); 60 | assert(!(ptr = chck_calloc_of(SIZE_MAX, 8))); free(ptr); 61 | 62 | assert((ptr = chck_realloc_add_of(ptr, 8, 8))); free(ptr); ptr = NULL; 63 | assert(!(ptr = chck_realloc_add_of(ptr, SIZE_MAX, 8))); free(ptr); ptr = NULL; 64 | assert(!(ptr = chck_realloc_add_of(ptr, 8, SIZE_MAX))); free(ptr); 65 | ptr = NULL; 66 | 67 | assert((ptr = chck_realloc_sub_of(ptr, 8, 4))); free(ptr); ptr = NULL; 68 | assert(!(ptr = chck_realloc_sub_of(ptr, 4, 8))); free(ptr); 69 | ptr = NULL; 70 | 71 | assert((ptr = chck_realloc_mul_of(ptr, 8, 8))); free(ptr); ptr = NULL; 72 | assert(!(ptr = chck_realloc_mul_of(ptr, 8, SIZE_MAX))); free(ptr); ptr = NULL; 73 | assert(!(ptr = chck_realloc_mul_of(ptr, SIZE_MAX, 8))); free(ptr); 74 | } 75 | 76 | return EXIT_SUCCESS; 77 | } 78 | -------------------------------------------------------------------------------- /chck/pool/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(chck_pool pool.c) 2 | install_libraries(chck_pool) 3 | install_headers(pool.h) 4 | 5 | if (CHCK_BUILD_TESTS) 6 | add_executable(pool_test test.c) 7 | target_link_libraries(pool_test PRIVATE chck_pool) 8 | add_test_ex(pool_test) 9 | endif () 10 | -------------------------------------------------------------------------------- /chck/pool/README.md: -------------------------------------------------------------------------------- 1 | # Memory object pools 2 | 3 | Cache friendly pools for any type. 4 | -------------------------------------------------------------------------------- /chck/pool/pool.c: -------------------------------------------------------------------------------- 1 | #include "pool.h" 2 | #include 3 | #include /* for calloc, free, etc.. */ 4 | #include /* for memcpy */ 5 | #include /* for assert */ 6 | 7 | static void 8 | pool_buffer_flush(struct chck_pool_buffer *pb, bool release) 9 | { 10 | assert(pb); 11 | 12 | if (release){ 13 | free(pb->buffer); 14 | pb->allocated = 0; 15 | pb->buffer = NULL; 16 | } 17 | 18 | pb->count = pb->used = 0; 19 | } 20 | 21 | static void 22 | pool_buffer_release(struct chck_pool_buffer *pb) 23 | { 24 | if (!pb) 25 | return; 26 | 27 | pool_buffer_flush(pb, true); 28 | *pb = (struct chck_pool_buffer){0}; 29 | } 30 | 31 | static bool 32 | pool_buffer_resize(struct chck_pool_buffer *pb, size_t size) 33 | { 34 | assert(pb); 35 | 36 | if (unlikely(size == pb->allocated)) 37 | return true; 38 | 39 | if (unlikely(size == 0)) { 40 | pool_buffer_flush(pb, true); 41 | return true; 42 | } 43 | 44 | uint8_t *tmp; 45 | if (!(tmp = realloc(pb->buffer, size))) 46 | return false; 47 | 48 | // make sure our buffer is always initialized, to avoid complexity 49 | if (size > pb->allocated) 50 | memset(tmp + pb->allocated, 0, size - pb->allocated); 51 | 52 | pb->buffer = tmp; 53 | pb->allocated = size; 54 | pb->used = (pb->allocated < pb->used ? pb->allocated : pb->used); 55 | return true; 56 | } 57 | 58 | static bool 59 | pool_buffer_resize_add(struct chck_pool_buffer *pb, size_t base, size_t add) 60 | { 61 | size_t sz; 62 | if (unlikely(chck_add_ofsz(base, add, &sz))) 63 | return false; 64 | 65 | return pool_buffer_resize(pb, sz); 66 | } 67 | 68 | static bool 69 | pool_buffer_resize_mul(struct chck_pool_buffer *pb, size_t base, size_t mul) 70 | { 71 | size_t sz; 72 | if (unlikely(chck_mul_ofsz(base, mul, &sz))) 73 | return false; 74 | 75 | return pool_buffer_resize(pb, sz); 76 | } 77 | 78 | static bool 79 | pool_buffer(struct chck_pool_buffer *pb, size_t grow, size_t capacity, size_t member_size) 80 | { 81 | assert(pb && member_size > 0); 82 | 83 | if (unlikely(!member_size) || unlikely(chck_mul_ofsz((grow ? grow : 32), member_size, &pb->step))) 84 | return false; 85 | 86 | pb->member = member_size; 87 | 88 | if (capacity > 0) 89 | pool_buffer_resize_mul(pb, capacity, member_size); 90 | 91 | return true; 92 | } 93 | 94 | static void* 95 | pool_buffer_add(struct chck_pool_buffer *pb, const void *data, size_t pos, size_t *out_index) 96 | { 97 | assert(pb && pb->member > 0); 98 | 99 | size_t tail; 100 | if (unlikely(chck_add_ofsz(pos, pb->member, &tail))) 101 | return NULL; 102 | 103 | while (pb->allocated < pos + pb->member) { 104 | if (unlikely(!pool_buffer_resize_add(pb, pb->allocated, pb->step))) 105 | return NULL; 106 | } 107 | 108 | if (!pb->buffer) 109 | return NULL; 110 | 111 | if (data) { 112 | memcpy(pb->buffer + pos, data, pb->member); 113 | } else { 114 | memset(pb->buffer + pos, 0, pb->member); 115 | } 116 | 117 | if (tail > pb->used) 118 | pb->used = tail; 119 | 120 | if (out_index) 121 | *out_index = pos / pb->member; 122 | 123 | pb->count++; 124 | return pb->buffer + pos; 125 | } 126 | 127 | static void* 128 | pool_buffer_add_move(struct chck_pool_buffer *pb, const void *data, size_t pos, size_t *out_index) 129 | { 130 | if (pos > pb->used) 131 | pos = pb->used; 132 | 133 | void *ptr; 134 | if (!(ptr = pool_buffer_add(pb, data, pb->used, out_index))) 135 | return NULL; 136 | 137 | assert(pb->used >= pb->member); 138 | assert(pb->used > pos); 139 | 140 | if (pb->used > pb->member) { 141 | const size_t shift = pb->used - (pos + pb->member); 142 | memmove(pb->buffer + pos + pb->member, pb->buffer + pos, shift); 143 | ptr = pb->buffer + pos; 144 | } 145 | 146 | if (data) { 147 | memcpy(ptr, data, pb->member); 148 | } else { 149 | memset(ptr, 0, pb->member); 150 | } 151 | 152 | return ptr; 153 | } 154 | 155 | static void 156 | pool_buffer_remove(struct chck_pool_buffer *pb, size_t index, size_t (*get_used)(), void *userdata) 157 | { 158 | assert(pb && get_used); 159 | 160 | size_t slot; 161 | if (unlikely(chck_mul_ofsz(index, pb->member, &slot)) || unlikely(slot >= pb->used)) 162 | return; 163 | 164 | if (slot + pb->member >= pb->used) 165 | pb->used = get_used(pb, index, userdata); 166 | 167 | if (pb->used + pb->step < pb->allocated) 168 | pool_buffer_resize_add(pb, pb->used, pb->member); 169 | 170 | assert(pb->count > 0); 171 | pb->count--; 172 | assert((pb->count > 0 && pb->used > 0) || (!pb->count && !pb->used)); 173 | } 174 | 175 | static void 176 | pool_buffer_remove_move(struct chck_pool_buffer *pb, size_t index) 177 | { 178 | assert(pb); 179 | 180 | size_t slot; 181 | if (unlikely(chck_mul_ofsz(index, pb->member, &slot)) || unlikely(slot >= pb->used)) 182 | return; 183 | 184 | if (slot + pb->member < pb->used) 185 | memmove(pb->buffer + slot, pb->buffer + slot + pb->member, pb->used - slot - pb->member); 186 | 187 | pb->used -= pb->member; 188 | 189 | if (pb->used + pb->step < pb->allocated) 190 | pool_buffer_resize_add(pb, pb->used, pb->member); 191 | 192 | assert(pb->count > 0); 193 | pb->count--; 194 | assert((pb->count > 0 && pb->used > 0) || (!pb->count && !pb->used)); 195 | } 196 | 197 | static void* 198 | pool_buffer_get(const struct chck_pool_buffer *pb, size_t index) 199 | { 200 | assert(pb); 201 | 202 | size_t slot; 203 | if (unlikely(chck_mul_ofsz(index, pb->member, &slot)) || unlikely(slot >= pb->used)) 204 | return NULL; 205 | 206 | return pb->buffer + slot; 207 | } 208 | 209 | static void* 210 | pool_buffer_iter(const struct chck_pool_buffer *pb, size_t *iter, bool reverse) 211 | { 212 | assert(pb && iter); 213 | 214 | if (!pb->member || *iter >= pb->used / pb->member) 215 | return NULL; 216 | 217 | void *ptr = pool_buffer_get(pb, *iter); 218 | (*iter) += (reverse ? -1 : 1); 219 | return ptr; 220 | } 221 | 222 | static bool 223 | pool_buffer_set_c_array(struct chck_pool_buffer *pb, const void *items, size_t memb) 224 | { 225 | assert(pb); 226 | 227 | if (items && memb > 0) { 228 | if (!pool_buffer_resize_mul(pb, memb, pb->member)) 229 | return false; 230 | 231 | memcpy(pb->buffer, items, pb->allocated); 232 | } else { 233 | pool_buffer_release(pb); 234 | memb = 0; 235 | } 236 | 237 | pb->used = pb->allocated; 238 | pb->count = memb; 239 | return true; 240 | } 241 | 242 | static void* 243 | pool_buffer_to_c_array(struct chck_pool_buffer *pb, size_t *out_memb) 244 | { 245 | assert(pb); 246 | 247 | if (!pb->member) { 248 | if (out_memb) 249 | *out_memb = 0; 250 | return NULL; 251 | } 252 | 253 | if (out_memb) 254 | *out_memb = (pb->used / pb->member); 255 | 256 | return pb->buffer; 257 | } 258 | 259 | static bool 260 | pool_is_mapped(const struct chck_pool *pool, size_t index) 261 | { 262 | assert(pool); 263 | const bool *mapped = pool_buffer_get(&pool->map, index); 264 | return (mapped ? *mapped : false); 265 | } 266 | 267 | static size_t 268 | pool_get_free_slot(struct chck_pool *pool) 269 | { 270 | assert(pool); 271 | 272 | if (pool->removed.count > 0) { 273 | const size_t last = *(size_t*)pool_buffer_get(&pool->removed, pool->removed.count - 1); 274 | pool_buffer_remove_move(&pool->removed, pool->removed.count - 1); 275 | return last; 276 | } 277 | 278 | return pool->items.count; 279 | } 280 | 281 | bool 282 | chck_pool(struct chck_pool *pool, size_t grow, size_t capacity, size_t member_size) 283 | { 284 | assert(pool && member_size > 0); 285 | 286 | if (unlikely(!member_size)) 287 | return false; 288 | 289 | *pool = (struct chck_pool){0}; 290 | return (pool_buffer(&pool->items, grow, capacity, member_size) && 291 | pool_buffer(&pool->map, grow, capacity, sizeof(bool)) && 292 | pool_buffer(&pool->removed, grow, 0, sizeof(size_t))); 293 | } 294 | 295 | bool 296 | chck_pool_from_c_array(struct chck_pool *pool, const void *items, size_t memb, size_t grow, size_t member_size) 297 | { 298 | return (chck_pool(pool, grow, 0, member_size) && chck_pool_set_c_array(pool, items, memb)); 299 | } 300 | 301 | void 302 | chck_pool_release(struct chck_pool *pool) 303 | { 304 | if (!pool) 305 | return; 306 | 307 | pool_buffer_release(&pool->items); 308 | pool_buffer_release(&pool->map); 309 | pool_buffer_release(&pool->removed); 310 | } 311 | 312 | void 313 | chck_pool_flush(struct chck_pool *pool) 314 | { 315 | assert(pool); 316 | pool_buffer_flush(&pool->items, true); 317 | pool_buffer_flush(&pool->map, true); 318 | pool_buffer_flush(&pool->removed, true); 319 | } 320 | 321 | void* 322 | chck_pool_get(const struct chck_pool *pool, size_t index) 323 | { 324 | assert(pool); 325 | 326 | void *ptr; 327 | if (!(ptr = pool_buffer_get(&pool->items, index))) 328 | return NULL; 329 | 330 | return (likely(pool_is_mapped(pool, index)) ? ptr : NULL); 331 | } 332 | 333 | void* 334 | chck_pool_get_last(const struct chck_pool *pool) 335 | { 336 | return chck_pool_get(pool, pool->items.count - 1); 337 | } 338 | 339 | void 340 | chck_pool_print(const struct chck_pool *pool, FILE *out) 341 | { 342 | assert(pool && out); 343 | 344 | fprintf(out, "pool: %p member: %zu items: %zu reversed: %zu used: %zu allocated: %zu\n", 345 | pool, pool->items.member, pool->map.used, pool->map.allocated, pool->items.used, pool->items.allocated); 346 | 347 | for (size_t i = 0; i < pool->map.used; ++i) 348 | fprintf(out, "%s%s", (pool_is_mapped(pool, i) ? "1" : "0"), ((i + 1) % 80 == 0 ? "\n" : "")); 349 | 350 | fprintf(out, "%s^^^\n", (pool->map.used % 80 == 0 ? "" : "\n")); 351 | } 352 | 353 | static size_t 354 | pool_get_used(struct chck_pool_buffer *pb, size_t removed, struct chck_pool *pool) 355 | { 356 | assert(pb && pool); 357 | assert(removed + 1 <= pool->map.used / pool->map.member); 358 | assert(pb->used > 0); 359 | 360 | // for chck_pool's, chck_pool_buffer can not know alone the used size, 361 | // so we need to help a bit with this function. 362 | 363 | size_t largest = removed + 1; 364 | for (; largest > 0 && (largest - 1 == removed || !pool_is_mapped(pool, largest - 1)); --largest); 365 | return largest * pb->member; 366 | } 367 | 368 | void* 369 | chck_pool_add(struct chck_pool *pool, const void *data, size_t *out_index) 370 | { 371 | assert(pool); 372 | const size_t slot = pool_get_free_slot(pool); 373 | 374 | if (!pool_buffer_add(&pool->map, (bool[]){true}, slot * pool->map.member, NULL)) 375 | return NULL; 376 | 377 | void *p; 378 | if (!(p = pool_buffer_add(&pool->items, data, slot * pool->items.member, out_index))) { 379 | pool_buffer_remove(&pool->map, slot * pool->map.member, pool_get_used, pool); 380 | return NULL; 381 | } 382 | 383 | return p; 384 | } 385 | 386 | void 387 | chck_pool_remove(struct chck_pool *pool, size_t index) 388 | { 389 | assert(pool); 390 | 391 | if (unlikely(!pool_is_mapped(pool, index))) 392 | return; 393 | 394 | const bool last = (index == pool->items.used / pool->items.member); 395 | pool_buffer_remove(&pool->items, index, pool_get_used, pool); 396 | 397 | *((bool*)pool_buffer_get(&pool->map, index)) = false; 398 | 399 | pool_buffer_resize_mul(&pool->map, (pool->items.allocated / pool->items.member), pool->map.member); 400 | pool->map.used = (pool->items.used / pool->items.member) * pool->map.member; 401 | 402 | if (!last) { 403 | // Some heuristics to avoid large amount of heap allocations 404 | pool->removed.step = (pool->items.step < pool->items.count / 2 ? pool->items.count / 2 : pool->items.step); 405 | pool_buffer_add(&pool->removed, &index, pool->removed.used, NULL); 406 | } 407 | } 408 | 409 | void* 410 | chck_pool_iter(const struct chck_pool *pool, size_t *iter, bool reverse) 411 | { 412 | assert(pool && iter); 413 | 414 | if (!pool->items.member) 415 | return NULL; 416 | 417 | void *current = NULL; 418 | do { 419 | const bool mapped = pool_is_mapped(pool, *iter); 420 | current = pool_buffer_iter(&pool->items, iter, reverse); 421 | current = (mapped ? current : NULL); 422 | } while (!current && *iter < pool->map.used / pool->map.member); 423 | 424 | return current; 425 | } 426 | 427 | bool 428 | chck_pool_set_c_array(struct chck_pool *pool, const void *items, size_t memb) 429 | { 430 | assert(pool); 431 | 432 | if (unlikely(!pool_buffer_set_c_array(&pool->items, items, memb))) 433 | return false; 434 | 435 | pool_buffer_flush(&pool->removed, true); 436 | return true; 437 | } 438 | 439 | void* 440 | chck_pool_to_c_array(struct chck_pool *pool, size_t *out_memb) 441 | { 442 | assert(pool); 443 | return pool_buffer_to_c_array(&pool->items, out_memb); 444 | } 445 | 446 | bool 447 | chck_iter_pool(struct chck_iter_pool *pool, size_t grow, size_t capacity, size_t member_size) 448 | { 449 | assert(pool && member_size > 0); 450 | 451 | if (unlikely(!member_size)) 452 | return false; 453 | 454 | *pool = (struct chck_iter_pool){0}; 455 | return pool_buffer(&pool->items, grow, capacity, member_size); 456 | } 457 | 458 | bool 459 | chck_iter_pool_from_c_array(struct chck_iter_pool *pool, const void *items, size_t memb, size_t grow, size_t member_size) 460 | { 461 | return unlikely(chck_iter_pool(pool, grow, 0, member_size) && chck_iter_pool_set_c_array(pool, items, memb)); 462 | } 463 | 464 | void 465 | chck_iter_pool_release(struct chck_iter_pool *pool) 466 | { 467 | if (!pool) 468 | return; 469 | 470 | pool_buffer_release(&pool->items); 471 | } 472 | 473 | void 474 | chck_iter_pool_flush(struct chck_iter_pool *pool) 475 | { 476 | assert(pool); 477 | pool_buffer_flush(&pool->items, true); 478 | } 479 | 480 | void 481 | chck_iter_pool_empty(struct chck_iter_pool *pool) 482 | { 483 | assert(pool); 484 | pool_buffer_flush(&pool->items, false); 485 | } 486 | 487 | void* 488 | chck_iter_pool_get(const struct chck_iter_pool *pool, size_t index) 489 | { 490 | assert(pool); 491 | return pool_buffer_get(&pool->items, index); 492 | } 493 | 494 | void* 495 | chck_iter_pool_get_last(const struct chck_iter_pool *pool) 496 | { 497 | return chck_iter_pool_get(pool, pool->items.count - 1); 498 | } 499 | 500 | void* 501 | chck_iter_pool_insert(struct chck_iter_pool *pool, size_t index, const void *data) 502 | { 503 | assert(pool); 504 | return pool_buffer_add_move(&pool->items, data, index * pool->items.member, NULL); 505 | } 506 | 507 | void* 508 | chck_iter_pool_push_front(struct chck_iter_pool *pool, const void *data) 509 | { 510 | assert(pool); 511 | return pool_buffer_add_move(&pool->items, data, 0, NULL); 512 | } 513 | 514 | void* 515 | chck_iter_pool_push_back(struct chck_iter_pool *pool, const void *data) 516 | { 517 | assert(pool); 518 | return pool_buffer_add(&pool->items, data, pool->items.used, NULL); 519 | } 520 | 521 | void 522 | chck_iter_pool_remove(struct chck_iter_pool *pool, size_t index) 523 | { 524 | assert(pool); 525 | pool_buffer_remove_move(&pool->items, index); 526 | } 527 | 528 | void* 529 | chck_iter_pool_iter(const struct chck_iter_pool *pool, size_t *iter, bool reverse) 530 | { 531 | assert(pool && iter); 532 | return pool_buffer_iter(&pool->items, iter, reverse); 533 | } 534 | 535 | bool 536 | chck_iter_pool_set_c_array(struct chck_iter_pool *pool, const void *items, size_t memb) 537 | { 538 | assert(pool); 539 | return pool_buffer_set_c_array(&pool->items, items, memb); 540 | } 541 | 542 | void* 543 | chck_iter_pool_to_c_array(struct chck_iter_pool *pool, size_t *out_memb) 544 | { 545 | assert(pool); 546 | return pool_buffer_to_c_array(&pool->items, out_memb); 547 | } 548 | -------------------------------------------------------------------------------- /chck/pool/pool.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_pool__ 2 | #define __chck_pool__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct chck_pool_buffer { 11 | // pointer to contents 12 | uint8_t *buffer; 13 | 14 | // growth step and member size (step == grow * member_size) 15 | size_t step, member; 16 | 17 | // how many bytes are used and allocated 18 | size_t used, allocated; 19 | 20 | // number of items in the buffer 21 | size_t count; 22 | }; 23 | 24 | struct chck_pool { 25 | struct chck_pool_buffer items; 26 | struct chck_pool_buffer map; 27 | struct chck_pool_buffer removed; 28 | }; 29 | 30 | struct chck_iter_pool { 31 | struct chck_pool_buffer items; 32 | }; 33 | 34 | struct chck_ring_pool { 35 | struct chck_pool_buffer items; 36 | 37 | // storage for popped element so we can return it 38 | void *popped; 39 | }; 40 | 41 | /** 42 | * Pools are manual memory buffers for your data (usually structs). 43 | * Pools may contain holes as whenever you remove item, the space is not removed, but instead marked as unused. 44 | * To access pool items, dont rely on the returned pointers, but use the indices instead. 45 | * The pointers may point to garbage whenever you add/remove item (as the buffer may be resized). 46 | * 47 | * Pools have very fast add/remove operation O(1) with expense of buffer of booleans and free list. 48 | */ 49 | 50 | #define chck_pool_for_each_call(pool, function, ...) \ 51 | { void *_P; for (size_t _I = 0; (_P = chck_pool_iter(pool, &_I, false));) function(_P, ##__VA_ARGS__); } 52 | 53 | #define chck_pool_for_each_call_reverse(pool, function, ...) \ 54 | { void *_P; for (size_t _I = (pool)->items.count - 1; (_P = chck_pool_iter(pool, &_I, true));) function(_P, ##__VA_ARGS__); } 55 | 56 | #define chck_pool_for_each(pool, pos) \ 57 | for (size_t _I = 0; (pos = chck_pool_iter(pool, &_I, false));) 58 | 59 | #define chck_pool_for_each_reverse(pool, pos) \ 60 | for (size_t _I = (pool)->items.count - 1; (pos = chck_pool_iter(pool, &_I, true));) 61 | 62 | bool chck_pool(struct chck_pool *pool, size_t grow, size_t capacity, size_t member_size); 63 | bool chck_pool_from_c_array(struct chck_pool *pool, const void *items, size_t memb, size_t grow, size_t member_size); 64 | void chck_pool_release(struct chck_pool *pool); 65 | void chck_pool_flush(struct chck_pool *pool); 66 | void chck_pool_print(const struct chck_pool *pool, FILE *out); 67 | void* chck_pool_get(const struct chck_pool *pool, size_t index); 68 | void* chck_pool_get_last(const struct chck_pool *pool); 69 | void* chck_pool_add(struct chck_pool *pool, const void *data, size_t *out_index); 70 | void chck_pool_remove(struct chck_pool *pool, size_t index); 71 | void* chck_pool_iter(const struct chck_pool *pool, size_t *iter, bool reverse); 72 | bool chck_pool_set_c_array(struct chck_pool *pool, const void *items, size_t memb); /* struct item *c_array; */ 73 | void* chck_pool_to_c_array(struct chck_pool *pool, size_t *memb); /* struct item *c_array; (contains holes) */ 74 | 75 | /** 76 | * IterPools don't have holes in buffer. 77 | * Whenever you remove a item from IterPool, the items after that get memmoved. 78 | * Thus the indices returned by IterPool functions are _not_ safe. 79 | * 80 | * As the name implies, use this pool only if you need to access items by iteration. 81 | */ 82 | 83 | #define chck_iter_pool_for_each_call(pool, function, ...) \ 84 | { void *_P; for (size_t _I = 0; (_P = chck_iter_pool_iter(pool, &_I, false));) function(_P, ##__VA_ARGS__); } 85 | 86 | #define chck_iter_pool_for_each_call_reverse(pool, function, ...) \ 87 | { void *_P; for (size_t _I = (pool)->items.count - 1; (_P = chck_iter_pool_iter(pool, &_I, true));) function(_P, ##__VA_ARGS__); } 88 | 89 | #define chck_iter_pool_for_each(pool, pos) \ 90 | for (size_t _I = 0; (pos = chck_iter_pool_iter(pool, &_I, false));) 91 | 92 | #define chck_iter_pool_for_each_reverse(pool, pos) \ 93 | for (size_t _I = (pool)->items.count - 1; (pos = chck_iter_pool_iter(pool, &_I, true));) 94 | 95 | bool chck_iter_pool(struct chck_iter_pool *pool, size_t grow, size_t capacity, size_t member_size); 96 | bool chck_iter_pool_from_c_array(struct chck_iter_pool *pool, const void *items, size_t memb, size_t grow_step, size_t member_size); 97 | void chck_iter_pool_release(struct chck_iter_pool *pool); 98 | void chck_iter_pool_flush(struct chck_iter_pool *pool); 99 | void chck_iter_pool_empty(struct chck_iter_pool *pool); 100 | void* chck_iter_pool_get(const struct chck_iter_pool *pool, size_t index); 101 | void* chck_iter_pool_get_last(const struct chck_iter_pool *pool); 102 | void* chck_iter_pool_push_front(struct chck_iter_pool *pool, const void *data); 103 | void* chck_iter_pool_push_back(struct chck_iter_pool *pool, const void *data); 104 | void* chck_iter_pool_insert(struct chck_iter_pool *pool, size_t index, const void *data); 105 | void chck_iter_pool_remove(struct chck_iter_pool *pool, size_t index); 106 | void* chck_iter_pool_iter(const struct chck_iter_pool *pool, size_t *iter, bool reverse); 107 | bool chck_iter_pool_set_c_array(struct chck_iter_pool *pool, const void *items, size_t memb); /* struct item *c_array; */ 108 | void* chck_iter_pool_to_c_array(struct chck_iter_pool *pool, size_t *memb); /* struct item *c_array; */ 109 | 110 | #endif /* __chck_pool__ */ 111 | -------------------------------------------------------------------------------- /chck/pool/test.c: -------------------------------------------------------------------------------- 1 | #include "pool.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #undef NDEBUG 8 | #include 9 | 10 | struct item { 11 | uint32_t a; 12 | void *b; 13 | }; 14 | 15 | static void printa(struct item *item) 16 | { 17 | printf("item::%d\n", item->a); 18 | } 19 | 20 | int main(void) 21 | { 22 | struct item dummy = {0}; 23 | 24 | /* TEST: pool */ 25 | { 26 | struct chck_pool pool = {0}; 27 | 28 | { 29 | size_t iter = 0; 30 | assert(!chck_pool_iter(&pool, &iter, false)); 31 | assert(!iter); 32 | assert(!chck_pool_iter(&pool, &iter, true)); 33 | assert(!iter); 34 | } 35 | 36 | assert(chck_pool(&pool, 32, 3, sizeof(struct item))); 37 | assert(pool.items.step == 32 * sizeof(struct item)); 38 | assert(pool.items.member == sizeof(struct item)); 39 | assert(pool.items.allocated == 3 * sizeof(struct item)); 40 | assert(pool.items.used == 0); 41 | assert(pool.items.count == 0); 42 | assert(pool.items.buffer); 43 | 44 | for (uint32_t i = 0; i < 3; ++i) 45 | assert(!chck_pool_get(&pool, i)); 46 | 47 | assert(chck_pool_add(&pool, (&(struct item){1, NULL}), NULL)); 48 | assert(chck_pool_add(&pool, (&(struct item){2, NULL}), NULL)); 49 | chck_pool_remove(&pool, 0); 50 | assert(((struct item*)chck_pool_get(&pool, 1))->a == 2); 51 | chck_pool_flush(&pool); 52 | 53 | size_t a, b, c; 54 | assert(chck_pool_add(&pool, (&(struct item){1, NULL}), &a)); 55 | assert(chck_pool_add(&pool, (&(struct item){2, NULL}), &b)); 56 | assert(chck_pool_add(&pool, (&(struct item){3, NULL}), &c)); 57 | assert(memcmp(chck_pool_get(&pool, 0), &dummy, sizeof(dummy))); 58 | 59 | assert(a == 0 && b == 1 && c == 2); 60 | assert(a != b && b != c && a != c); 61 | 62 | { 63 | size_t iter = 0; 64 | struct item *current; 65 | while ((current = chck_pool_iter(&pool, &iter, false))) 66 | assert(current != NULL); 67 | 68 | assert(pool.items.count == 3); 69 | assert(pool.items.used == 3 * sizeof(struct item)); 70 | assert(pool.items.allocated == 32 * sizeof(struct item)); 71 | assert(iter == 3); 72 | } 73 | 74 | chck_pool_remove(&pool, b); 75 | 76 | { 77 | size_t iter = 0; 78 | struct item *current; 79 | while ((current = chck_pool_iter(&pool, &iter, false))) 80 | assert(current != NULL); 81 | 82 | assert(pool.items.count == 2); 83 | assert(pool.items.used == 3 * sizeof(struct item)); 84 | assert(pool.items.allocated == 32 * sizeof(struct item)); 85 | assert(iter == 3); 86 | } 87 | 88 | assert(chck_pool_add(&pool, NULL, NULL)); 89 | assert(!memcmp(chck_pool_get(&pool, 1), &dummy, sizeof(dummy))); 90 | 91 | { 92 | size_t iter = 0; 93 | struct item *current; 94 | while ((current = chck_pool_iter(&pool, &iter, false))) 95 | assert(current != NULL); 96 | 97 | assert(pool.items.count == 3); 98 | assert(pool.items.used == 3 * sizeof(struct item)); 99 | assert(pool.items.allocated == 32 * sizeof(struct item)); 100 | assert(iter == 3); 101 | } 102 | 103 | chck_pool_remove(&pool, c); 104 | 105 | { 106 | size_t iter = 0; 107 | struct item *current; 108 | while ((current = chck_pool_iter(&pool, &iter, true))) 109 | assert(current != NULL); 110 | 111 | assert(pool.items.count == 2); 112 | assert(pool.items.used == 2 * sizeof(struct item)); 113 | assert(pool.items.allocated == 32 * sizeof(struct item)); 114 | assert(iter == (size_t)-1); 115 | } 116 | 117 | chck_pool_add(&pool, NULL, NULL); 118 | 119 | { 120 | size_t iter = 0; 121 | struct item *current; 122 | while ((current = chck_pool_iter(&pool, &iter, false))) 123 | assert(current != NULL); 124 | 125 | assert(pool.items.count == 3); 126 | assert(pool.items.used == 3 * sizeof(struct item)); 127 | assert(pool.items.allocated == 32 * sizeof(struct item)); 128 | assert(iter == 3); 129 | } 130 | 131 | struct item *itemA = chck_pool_get(&pool, a); 132 | struct item *itemB = chck_pool_get(&pool, b); 133 | struct item *itemC = chck_pool_get(&pool, c); 134 | itemA->a = 1; 135 | itemB->a = 2; 136 | itemC->a = 3; 137 | 138 | assert(chck_pool_get(&pool, 0) == itemA); 139 | assert(chck_pool_get(&pool, 1) == itemB); 140 | assert(chck_pool_get(&pool, 2) == itemC); 141 | assert(chck_pool_get_last(&pool) == itemC); 142 | assert(&((struct item*)chck_pool_to_c_array(&pool, NULL))[0] == itemA); 143 | assert(&((struct item*)chck_pool_to_c_array(&pool, NULL))[1] == itemB); 144 | assert(&((struct item*)chck_pool_to_c_array(&pool, NULL))[2] == itemC); 145 | chck_pool_for_each_call(&pool, printa); 146 | 147 | chck_pool_flush(&pool); 148 | 149 | chck_pool_add(&pool, (&(struct item){1, NULL}), &a); 150 | chck_pool_add(&pool, (&(struct item){2, NULL}), &b); 151 | chck_pool_add(&pool, (&(struct item){3, NULL}), &c); 152 | chck_pool_add(&pool, (&(struct item){4, NULL}), &a); 153 | chck_pool_add(&pool, (&(struct item){5, NULL}), &b); 154 | chck_pool_add(&pool, (&(struct item){6, NULL}), &c); 155 | chck_pool_remove(&pool, a); 156 | chck_pool_remove(&pool, b); 157 | chck_pool_add(&pool, (&(struct item){7, NULL}), &c); 158 | chck_pool_add(&pool, (&(struct item){8, NULL}), &a); 159 | chck_pool_add(&pool, (&(struct item){9, NULL}), &b); 160 | 161 | assert(pool.items.used == 7 * sizeof(struct item)); 162 | assert(pool.items.allocated == 32 * sizeof(struct item)); 163 | 164 | { 165 | size_t aa = 0; 166 | struct item *current; 167 | chck_pool_for_each(&pool, current) { 168 | ++aa; 169 | assert(current->a != 4); 170 | assert(current->a != 5); 171 | assert(current != NULL); 172 | } 173 | 174 | assert(pool.items.count == aa); 175 | } 176 | 177 | { 178 | for (uint32_t i = 0; i < 32; ++i) 179 | chck_pool_add(&pool, NULL, NULL); 180 | 181 | assert(pool.items.used == 39 * sizeof(struct item)); 182 | assert(pool.items.allocated == 64 * sizeof(struct item)); 183 | 184 | for (uint32_t i = 0; i < 32; ++i) 185 | chck_pool_remove(&pool, 7 + i); 186 | 187 | assert(pool.items.used == 7 * sizeof(struct item)); 188 | assert(pool.items.allocated == 8 * sizeof(struct item)); 189 | } 190 | 191 | struct item bars[4] = {{.a = 1}}; 192 | assert(chck_pool_set_c_array(&pool, &bars, 4)); 193 | assert(pool.items.allocated == 4 * sizeof(struct item)); 194 | assert(pool.items.used == 4 * sizeof(struct item)); 195 | assert(!memcmp(pool.items.buffer, bars, sizeof(bars))); 196 | 197 | chck_pool_release(&pool); 198 | assert(pool.items.allocated == 0); 199 | assert(pool.items.used == 0); 200 | } 201 | 202 | /* TEST: iter pool */ 203 | { 204 | struct chck_iter_pool pool = {0}; 205 | 206 | { 207 | size_t iter = 0; 208 | assert(!chck_iter_pool_iter(&pool, &iter, false)); 209 | assert(!iter); 210 | assert(!chck_iter_pool_iter(&pool, &iter, true)); 211 | assert(!iter); 212 | } 213 | 214 | { 215 | size_t memb; 216 | assert(!chck_iter_pool_to_c_array(&pool, &memb) && memb == 0); 217 | } 218 | 219 | assert(chck_iter_pool(&pool, 32, 2, sizeof(struct item))); 220 | assert(pool.items.step == 32 * sizeof(struct item)); 221 | assert(pool.items.member == sizeof(struct item)); 222 | assert(pool.items.allocated == 2 * sizeof(struct item)); 223 | assert(pool.items.used == 0); 224 | assert(pool.items.count == 0); 225 | assert(pool.items.buffer); 226 | 227 | assert(chck_iter_pool_push_back(&pool, (&(struct item){1, NULL}))); 228 | assert(chck_iter_pool_push_back(&pool, (&(struct item){2, NULL}))); 229 | chck_iter_pool_remove(&pool, 0); 230 | assert(((struct item*)chck_iter_pool_get(&pool, 0))->a == 2); 231 | chck_iter_pool_empty(&pool); 232 | assert(pool.items.allocated == 2 * sizeof(struct item)); 233 | 234 | assert(chck_iter_pool_push_front(&pool, (&(struct item){1, NULL}))); 235 | assert(chck_iter_pool_insert(&pool, 55, (&(struct item){2, NULL}))); // same as push_back when index > count 236 | assert(chck_iter_pool_insert(&pool, 1, (&(struct item){3, NULL}))); 237 | assert(((struct item*)chck_iter_pool_get(&pool, 0))->a == 1); 238 | assert(((struct item*)chck_iter_pool_get(&pool, 1))->a == 3); 239 | assert(((struct item*)chck_iter_pool_get(&pool, 2))->a == 2); 240 | 241 | { 242 | size_t iter = 0; 243 | struct item *current; 244 | while ((current = chck_iter_pool_iter(&pool, &iter, false))) 245 | assert(current != NULL); 246 | 247 | assert(pool.items.count == 3); 248 | assert(pool.items.used == 3 * sizeof(struct item)); 249 | assert(pool.items.allocated == 34 * sizeof(struct item)); 250 | assert(iter == 3); 251 | } 252 | 253 | chck_iter_pool_remove(&pool, 1); 254 | 255 | { 256 | size_t iter = 0; 257 | struct item *current; 258 | while ((current = chck_iter_pool_iter(&pool, &iter, true))) 259 | assert(current != NULL); 260 | 261 | assert(pool.items.count == 2); 262 | assert(pool.items.used == 2 * sizeof(struct item)); 263 | assert(pool.items.allocated == 34 * sizeof(struct item)); 264 | assert(iter == (size_t)-1); 265 | } 266 | 267 | void *f; 268 | assert((f = chck_iter_pool_push_front(&pool, NULL))); 269 | assert(chck_iter_pool_get(&pool, 0) == f); 270 | 271 | { 272 | size_t iter = 0; 273 | struct item *current; 274 | while ((current = chck_iter_pool_iter(&pool, &iter, false))) 275 | assert(current != NULL); 276 | 277 | assert(pool.items.count == 3); 278 | assert(pool.items.used == 3 * sizeof(struct item)); 279 | assert(pool.items.allocated == 34 * sizeof(struct item)); 280 | assert(iter == 3); 281 | } 282 | 283 | chck_iter_pool_remove(&pool, 2); 284 | 285 | { 286 | size_t iter = 0; 287 | struct item *current; 288 | while ((current = chck_iter_pool_iter(&pool, &iter, false))) 289 | assert(current != NULL); 290 | 291 | assert(pool.items.count == 2); 292 | assert(pool.items.used == 2 * sizeof(struct item)); 293 | assert(pool.items.allocated == 34 * sizeof(struct item)); 294 | assert(iter == 2); 295 | } 296 | 297 | chck_iter_pool_push_back(&pool, NULL); 298 | 299 | { 300 | size_t iter = 0; 301 | struct item *current; 302 | while ((current = chck_iter_pool_iter(&pool, &iter, false))) 303 | assert(current != NULL); 304 | 305 | assert(pool.items.count == 3); 306 | assert(pool.items.used == 3 * sizeof(struct item)); 307 | assert(pool.items.allocated == 34 * sizeof(struct item)); 308 | assert(iter == 3); 309 | } 310 | 311 | struct item *itemA = chck_iter_pool_get(&pool, 0); 312 | struct item *itemB = chck_iter_pool_get(&pool, 1); 313 | struct item *itemC = chck_iter_pool_get(&pool, 2); 314 | itemA->a = 1; 315 | itemB->a = 2; 316 | itemC->a = 3; 317 | 318 | assert(chck_iter_pool_get(&pool, 0) == itemA); 319 | assert(chck_iter_pool_get(&pool, 1) == itemB); 320 | assert(chck_iter_pool_get(&pool, 2) == itemC); 321 | assert(chck_iter_pool_get_last(&pool) == itemC); 322 | assert(&((struct item*)chck_iter_pool_to_c_array(&pool, NULL))[0] == itemA); 323 | assert(&((struct item*)chck_iter_pool_to_c_array(&pool, NULL))[1] == itemB); 324 | assert(&((struct item*)chck_iter_pool_to_c_array(&pool, NULL))[2] == itemC); 325 | chck_iter_pool_for_each_call(&pool, printa); 326 | 327 | { 328 | for (uint32_t i = 0; i < 32; ++i) 329 | chck_iter_pool_push_back(&pool, NULL); 330 | 331 | assert(pool.items.used == 35 * sizeof(struct item)); 332 | assert(pool.items.allocated == 66 * sizeof(struct item)); 333 | 334 | for (uint32_t i = 0; i < 32; ++i) 335 | chck_iter_pool_remove(&pool, pool.items.count - 1); 336 | 337 | assert(pool.items.used == 3 * sizeof(struct item)); 338 | assert(pool.items.allocated == 34 * sizeof(struct item)); 339 | } 340 | 341 | struct item bars[4] = {{.a = 1}}; 342 | assert(chck_iter_pool_set_c_array(&pool, &bars, 4)); 343 | assert(pool.items.allocated == 4 * sizeof(struct item)); 344 | assert(pool.items.used == 4 * sizeof(struct item)); 345 | assert(!memcmp(pool.items.buffer, bars, sizeof(bars))); 346 | 347 | chck_iter_pool_release(&pool); 348 | assert(pool.items.allocated == 0); 349 | assert(pool.items.used == 0); 350 | } 351 | 352 | /* TEST: benchmark (many insertions, and removal expanding from center) */ 353 | { 354 | const uint32_t iters = 0xFFFFF; 355 | struct chck_pool pool; 356 | assert(chck_pool(&pool, 32, iters, sizeof(struct item))); 357 | for (uint32_t i = 0; i < iters; ++i) 358 | assert(chck_pool_add(&pool, (&(struct item){i, NULL}), NULL)); 359 | assert(pool.items.count == iters); 360 | assert(pool.items.used == iters * sizeof(struct item)); 361 | for (uint32_t i = iters / 2, d = iters / 2; i < iters; ++i, --d) { 362 | assert(((struct item*)chck_pool_get(&pool, i))->a == i); 363 | assert(((struct item*)chck_pool_get(&pool, d))->a == d); 364 | chck_pool_remove(&pool, i); 365 | chck_pool_remove(&pool, d); 366 | } 367 | assert(pool.items.count == 0); 368 | assert(pool.items.used == 0); 369 | chck_pool_release(&pool); 370 | assert(pool.items.allocated == 0); 371 | } 372 | 373 | return EXIT_SUCCESS; 374 | } 375 | -------------------------------------------------------------------------------- /chck/sjis/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(chck_sjis sjis.c) 2 | install_libraries(chck_sjis) 3 | install_headers(sjis.h) 4 | 5 | if (CHCK_BUILD_TESTS) 6 | add_executable(sjis_test test.c) 7 | target_link_libraries(sjis_test PRIVATE chck_sjis) 8 | add_test_ex(sjis_test) 9 | endif () 10 | -------------------------------------------------------------------------------- /chck/sjis/README.md: -------------------------------------------------------------------------------- 1 | # SJIS<->UTF8 converter 2 | 3 | Sometimes we need to show that SJIS encoded text 4 | -------------------------------------------------------------------------------- /chck/sjis/sjis.c: -------------------------------------------------------------------------------- 1 | #include "sjis.h" 2 | #include "utf8sjis.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #pragma GCC diagnostic ignored "-Woverflow" 11 | 12 | static inline bool 13 | resize(uint8_t **buf, size_t *size, size_t nsize) 14 | { 15 | assert(buf && size); 16 | 17 | if (nsize == *size) 18 | return true; 19 | 20 | void *tmp; 21 | if (!(tmp = realloc(*buf, nsize))) 22 | return false; 23 | 24 | *buf = tmp; 25 | *size = nsize; 26 | return true; 27 | } 28 | 29 | static inline bool 30 | resize_mul(uint8_t **buf, size_t *size, size_t nsize, size_t mul) 31 | { 32 | size_t nsz; 33 | if (unlikely(chck_mul_ofsz(nsize, mul, &nsz))) 34 | return false; 35 | 36 | return resize(buf, size, nsz); 37 | } 38 | 39 | static inline bool 40 | put(uint8_t **buf, size_t *o, size_t *size, const char *bytes) 41 | { 42 | assert(buf && o && size && bytes && *size > *o); 43 | 44 | size_t len = strlen(bytes); 45 | if (len >= *size - *o && !resize_mul(buf, size, *size, 2)) 46 | return false; 47 | 48 | memcpy(*buf + *o, bytes, len); 49 | *o += len; 50 | return true; 51 | } 52 | 53 | char* 54 | chck_sjis_to_utf8(const uint8_t *sjis, size_t size, size_t *out_size, bool terminate) 55 | { 56 | assert(sjis && size != 0); 57 | 58 | size_t dsize; 59 | uint8_t *dec; 60 | if (!(dec = malloc((dsize = size)))) 61 | return NULL; 62 | 63 | size_t d = 0; 64 | for (size_t i = 0; i < size; ++i) { 65 | dec[d] = 0x00; /* this is the unexpected case */ 66 | 67 | /* modified ASCII */ 68 | if (sjis[i] <= 0x7f) { 69 | if (sjis[i] == 0x5c) { // YEN 70 | put(&dec, &d, &dsize, "\xc2\xa5"); 71 | } else if (sjis[i] == 0x7e) { // OVERLINE 72 | put(&dec, &d, &dsize, "\xe2\x80\xbe"); 73 | } else { 74 | put(&dec, &d, &dsize, (const char[]){ sjis[i], 0 }); 75 | } 76 | } 77 | 78 | /* single byte half-width katakana */ 79 | if (sjis[i] >= 0xa1 && sjis[i] <= 0xdf) { 80 | char hw_katakana[4] = { 0xef, 0xbd, sjis[i], 0x00 }; 81 | if (sjis[i] >= 0xc0) { 82 | hw_katakana[2] = 0xbe; 83 | hw_katakana[3] = 0x80 + sjis[i] - 0xc0; 84 | } 85 | put(&dec, &d, &dsize, hw_katakana); 86 | } 87 | 88 | /* multibyte */ 89 | if (sjis[i] >= 0x81 && sjis[i] <= 0xfc) { 90 | const struct map_sjis_utf8 *data = NULL; 91 | for (uint16_t mb = 0; mb < map_sjis_utf8_size; ++mb) { 92 | if (!memcmp(map_sjis_utf8[mb].sjis, sjis + i, 2)) { 93 | data = &map_sjis_utf8[mb]; 94 | break; 95 | } 96 | } 97 | 98 | if (data) { 99 | put(&dec, &d, &dsize, data->utf8); 100 | } else { 101 | put(&dec, &d, &dsize, "\xef\xbf\xbd"); // invalid 102 | } 103 | 104 | i += 1; // skip byte 105 | } 106 | } 107 | 108 | if (d > 0) { 109 | /* resize buffer to real size */ 110 | terminate = (terminate ? true : false); 111 | size = d + (dec[d - 1] != 0x00 ? terminate : 0); 112 | resize(&dec, &dsize, size); 113 | if (terminate && dec[d - 1] != 0x00) dec[d] = 0x00; 114 | } else { 115 | size = 0; 116 | free(dec); 117 | dec = NULL; 118 | } 119 | 120 | if (out_size) 121 | *out_size = size; 122 | 123 | return (char*)dec; 124 | } 125 | 126 | uint8_t* 127 | chck_utf8_to_sjis(const char *input, size_t size, size_t *out_size, bool terminate) 128 | { 129 | const uint8_t *utf8 = (const uint8_t*)input; 130 | assert(input && size != 0); 131 | 132 | size_t dsize; 133 | uint8_t *dec; 134 | if (!(dec = malloc((dsize = size)))) 135 | return NULL; 136 | 137 | size_t d = 0; 138 | for (size_t i = 0; i < size; ++i) { 139 | dec[d] = 0x00; /* this is the unexpected case */ 140 | 141 | /* ASCII */ 142 | if (utf8[i] <= 0x7f) { 143 | if (utf8[i] == 0x5c) { // BACKSLASH 144 | put(&dec, &d, &dsize, "\x81\x5f"); 145 | } else if (utf8[i] == 0x7e) { // TILDE 146 | put(&dec, &d, &dsize, "\x81\x60"); 147 | } else { 148 | put(&dec, &d, &dsize, (const char[]){ utf8[i], 0 }); 149 | } 150 | } 151 | 152 | /* half-width katakana */ 153 | if (utf8[i + 1] >= 0xbd && utf8[i + 1] <= 0xbe) { 154 | char hw_katakana[2] = { utf8[i + 2], 0x00 }; 155 | if (utf8[i + 1] >= 0xbe) hw_katakana[0] = 0xc0 + utf8[i + 2] - 0x80; 156 | put(&dec, &d, &dsize, hw_katakana); 157 | } 158 | 159 | /* multibyte */ 160 | if ((utf8[i + 1] & 0xc0) == 0x80) { 161 | uint8_t mblen = 0; 162 | const struct map_sjis_utf8 *data = NULL; 163 | while (i + mblen + 1 < size && (utf8[i + mblen + 1] & 0xc0) == 0x80) 164 | ++mblen; 165 | 166 | for (uint16_t mb = 0; mb < map_sjis_utf8_size; ++mb) { 167 | if (!memcmp(map_sjis_utf8[mb].utf8, utf8 + i, mblen + 1)) { 168 | data = &map_sjis_utf8[mb]; 169 | break; 170 | } 171 | } 172 | 173 | if (data) { 174 | put(&dec, &d, &dsize, data->sjis); 175 | } else { 176 | put(&dec, &d, &dsize, "\x81\x9f"); // invalid 177 | } 178 | 179 | i += mblen; // skip bytes 180 | } 181 | } 182 | 183 | if (d > 0) { 184 | /* resize buffer to real size */ 185 | terminate = (terminate ? true : false); 186 | size = d + (dec[d - 1] != 0x00 ? terminate : 0); 187 | resize(&dec, &dsize, size); 188 | if (terminate && dec[d - 1] != 0) dec[d] = 0x00; 189 | } else { 190 | size = 0; 191 | free(dec); 192 | dec = NULL; 193 | } 194 | 195 | if (out_size) 196 | *out_size = size; 197 | 198 | return dec; 199 | } 200 | -------------------------------------------------------------------------------- /chck/sjis/sjis.h: -------------------------------------------------------------------------------- 1 | #ifndef __sjis_h__ 2 | #define __sjis_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | char* chck_sjis_to_utf8(const unsigned char *sjis, size_t size, size_t *out_size, bool terminate); 10 | uint8_t* chck_utf8_to_sjis(const char *input, size_t size, size_t *out_size, bool terminate); 11 | 12 | #endif /* __sjis_h__ */ 13 | -------------------------------------------------------------------------------- /chck/sjis/test.c: -------------------------------------------------------------------------------- 1 | #include "sjis.h" 2 | #include 3 | #include 4 | 5 | #undef NDEBUG 6 | #include 7 | 8 | #pragma GCC diagnostic ignored "-Woverflow" 9 | 10 | int main(void) 11 | { 12 | uint8_t sjis[] = { 13 | 0x82, 0xa8, 0x8c, 0x5a, 0x82, 0xbf, 0x82, 0xe1, 0x82, 0xf1, 0x82, 0xa0, 14 | 0x82, 0xe8, 0x82, 0xaa, 0x82, 0xc6, 0x81, 0x49, 0x81, 0x69, 0x81, 0x8b, 15 | 0x2e, 0x81, 0x8b, 0x81, 0x6a, 0x20, 0x84, 0xb3, 0x84, 0xaa, 0x84, 0xb3 16 | }; 17 | 18 | char utf8[] = { 19 | 0xe3, 0x81, 0x8a, 0xe5, 0x85, 0x84, 0xe3, 0x81, 0xa1, 0xe3, 0x82, 0x83, 20 | 0xe3, 0x82, 0x93, 0xe3, 0x81, 0x82, 0xe3, 0x82, 0x8a, 0xe3, 0x81, 0x8c, 21 | 0xe3, 0x81, 0xa8, 0xef, 0xbc, 0x81, 0xef, 0xbc, 0x88, 0xc2, 0xb0, 0x2e, 22 | 0xc2, 0xb0, 0xef, 0xbc, 0x89, 0x20, 0xe2, 0x94, 0xbb, 0xe2, 0x94, 0x81, 23 | 0xe2, 0x94, 0xbb 24 | }; 25 | 26 | /* TEST: SJIS to UTF8 */ 27 | { 28 | char *u8; 29 | u8 = chck_sjis_to_utf8(sjis, sizeof(sjis), NULL, 1); 30 | assert(memcmp(utf8, u8, sizeof(utf8)) == 0); 31 | free(u8); 32 | } 33 | 34 | /* TEST: UTF8 to SJIS */ 35 | { 36 | uint8_t *sj; 37 | sj = chck_utf8_to_sjis(utf8, sizeof(utf8), NULL, 1); 38 | assert(memcmp(sjis, sj, sizeof(sjis)) == 0); 39 | free(sj); 40 | } 41 | 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /chck/string/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(chck_string string.c) 2 | install_libraries(chck_string) 3 | install_headers(string.h) 4 | 5 | if (CHCK_BUILD_TESTS) 6 | add_executable(string_test test.c) 7 | target_link_libraries(string_test PRIVATE chck_string) 8 | add_test_ex(string_test) 9 | endif () 10 | -------------------------------------------------------------------------------- /chck/string/string.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define WHITESPACE " \t\n\v\f\r" 9 | 10 | static inline char* 11 | ccopy(const char *str, size_t len) 12 | { 13 | assert(str); 14 | char *cpy = chck_calloc_add_of(len, 1); 15 | return (cpy ? memcpy(cpy, str, len) : NULL); 16 | } 17 | 18 | void 19 | chck_string_release(struct chck_string *string) 20 | { 21 | if (!string) 22 | return; 23 | 24 | if (string->is_heap) 25 | free(string->data); 26 | 27 | *string = (struct chck_string){0}; 28 | } 29 | 30 | bool 31 | chck_string_set_cstr_with_length(struct chck_string *string, const char *data, size_t len, bool is_heap) 32 | { 33 | assert(string); 34 | 35 | char *copy = (char*)data; 36 | if (is_heap && data && len > 0 && !(copy = ccopy(data, len))) 37 | return false; 38 | 39 | chck_string_release(string); 40 | string->is_heap = is_heap; 41 | string->data = (len > 0 ? copy : NULL); 42 | string->size = len; 43 | return true; 44 | } 45 | 46 | bool 47 | chck_string_set_cstr(struct chck_string *string, const char *data, bool is_heap) 48 | { 49 | assert(string); 50 | return chck_string_set_cstr_with_length(string, data, (data ? strlen(data) : 0), is_heap); 51 | } 52 | 53 | #pragma GCC diagnostic push 54 | #pragma GCC diagnostic ignored "-Wformat-nonliteral" 55 | 56 | bool 57 | chck_string_set_varg(struct chck_string *string, const char *fmt, va_list args) 58 | { 59 | assert(string && fmt); 60 | 61 | va_list cpy; 62 | va_copy(cpy, args); 63 | 64 | char *str = NULL; 65 | const size_t len = vsnprintf(NULL, 0, fmt, args); 66 | if (len > 0 && !(str = chck_malloc_add_of(len, 1))) { 67 | va_end(cpy); 68 | return false; 69 | } 70 | 71 | vsnprintf(str, len + 1, fmt, cpy); 72 | va_end(cpy); 73 | 74 | chck_string_release(string); 75 | string->is_heap = true; 76 | string->data = (len > 0 ? str : NULL); 77 | string->size = len; 78 | return true; 79 | } 80 | 81 | bool 82 | chck_string_set_format(struct chck_string *string, const char *fmt, ...) 83 | { 84 | va_list argp; 85 | va_start(argp, fmt); 86 | const bool ret = chck_string_set_varg(string, fmt, argp); 87 | va_end(argp); 88 | return ret; 89 | } 90 | 91 | #pragma GCC diagnostic pop 92 | 93 | bool 94 | chck_string_set(struct chck_string *string, const struct chck_string *other, bool is_heap) 95 | { 96 | assert(string && other); 97 | 98 | if (!is_heap && string->data == other->data) { 99 | string->size = other->size; 100 | return true; 101 | } 102 | 103 | return chck_string_set_cstr_with_length(string, other->data, other->size, is_heap); 104 | } 105 | 106 | char* 107 | chck_cstr_strip(char *cstr) 108 | { 109 | assert(cstr); 110 | 111 | char *e; 112 | cstr += strspn(cstr, WHITESPACE); 113 | for (e = strchr(cstr, 0); e > cstr; --e) 114 | if (!strchr(WHITESPACE, e[-1])) 115 | break; 116 | 117 | *e = 0; 118 | return cstr; 119 | } 120 | 121 | char* 122 | chck_cstr_remove_chars(char *cstr, const char *bad) 123 | { 124 | assert(cstr && bad); 125 | 126 | char *t, *f; 127 | for (f = cstr, t = cstr; *f; ++f) { 128 | if (strchr(bad, *f)) 129 | continue; 130 | 131 | *(t++) = *f; 132 | } 133 | 134 | *t = 0; 135 | return cstr; 136 | } 137 | 138 | char* 139 | chck_cstr_replace_char(char *cstr, char replace, char with) 140 | { 141 | assert(cstr && replace != with); 142 | 143 | if (replace == with) 144 | return cstr; 145 | 146 | char *s = cstr; 147 | while ((s = strchr(s, replace))) 148 | *s = with; 149 | return cstr; 150 | } 151 | 152 | const char* 153 | chck_cstr_tokenize(const char *cstr, size_t *out_len, const char *separator, bool skip_whitespace, const char **state) 154 | { 155 | assert(out_len && separator && state); 156 | const char *current = (state && *state ? *state : cstr); 157 | 158 | if (chck_cstr_is_empty(current) || chck_cstr_is_empty(cstr)) 159 | return NULL; 160 | 161 | current += strspn(current, separator); 162 | 163 | if (skip_whitespace) 164 | current += strspn(current, WHITESPACE); 165 | 166 | *out_len = strcspn(current, separator); 167 | *state = current + *out_len; 168 | 169 | if (**state != 0) 170 | ++(*state); 171 | 172 | if (skip_whitespace) { 173 | const size_t ws = strcspn(current, WHITESPACE); 174 | *out_len -= (ws < *out_len ? *out_len - ws : 0); 175 | } 176 | 177 | return current; 178 | } 179 | 180 | const char* 181 | chck_cstr_tokenize_quoted(const char *cstr, size_t *out_len, const char *separator, const char *quotes, const char **state) 182 | { 183 | assert(out_len && separator && quotes && state); 184 | const char *e, *current = chck_cstr_tokenize(cstr, out_len, separator, true, state); 185 | 186 | if (!current) 187 | return NULL; 188 | 189 | for (const char *q = quotes; *q; ++q) { 190 | if (*current != *q) 191 | continue; 192 | 193 | bool escaped = false; 194 | for (e = ++current; *e; ++e) { 195 | if (escaped) 196 | escaped = false; 197 | else if (*e == '\\') 198 | escaped = true; 199 | else if (*e == *q) 200 | break; 201 | } 202 | 203 | *out_len = e - current; 204 | e = (!*e ? e : e + 1); 205 | 206 | if (*e) { 207 | size_t tmp; 208 | const char *state2 = NULL; 209 | *state = chck_cstr_tokenize(e, &tmp, separator, true, &state2); 210 | } else { 211 | *state = e; 212 | } 213 | 214 | break; 215 | } 216 | 217 | return current; 218 | } 219 | -------------------------------------------------------------------------------- /chck/string/string.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_string_h__ 2 | #define __chck_string_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define CSTRE(x) (x ? x : "") 13 | 14 | struct chck_string { 15 | char *data; 16 | size_t size; 17 | bool is_heap; 18 | }; 19 | 20 | static inline bool 21 | chck_cstr_is_empty(const char *data) 22 | { 23 | return (!data || *data == 0); 24 | } 25 | 26 | static inline bool 27 | chck_cstr_ends_with(const char *a, const char *b) 28 | { 29 | const size_t lena = (a ? strlen(a) : 0), lenb = (b ? strlen(b) : 0); 30 | return (lena >= lenb && !memcmp(a + lena - lenb, CSTRE(b), lenb)); 31 | } 32 | 33 | static inline bool 34 | chck_cstr_starts_with(const char *a, const char *b) 35 | { 36 | const size_t lena = (a ? strlen(a) : 0), lenb = (b ? strlen(b) : 0); 37 | return (lena >= lenb && !memcmp(CSTRE(a), CSTRE(b), lenb)); 38 | } 39 | 40 | static inline bool 41 | chck_cstreq(const char *a, const char *b) 42 | { 43 | return (a == b) || (a && b && !strcmp(a, b)); 44 | } 45 | 46 | static inline bool 47 | chck_cstrneq(const char *a, const char *b, size_t len) 48 | { 49 | return (a == b) || (a && b && !strncmp(a, b, len)); 50 | } 51 | 52 | static inline bool 53 | chck_string_is_empty(const struct chck_string *string) 54 | { 55 | return chck_cstr_is_empty(string->data); 56 | } 57 | 58 | static inline bool 59 | chck_string_ends_with_cstr(const struct chck_string *a, const char *cstr) 60 | { 61 | const size_t len = (cstr ? strlen(cstr) : 0); 62 | return (a->size >= len && !memcmp(a->data + a->size - len, CSTRE(cstr), len)); 63 | } 64 | 65 | static inline bool 66 | chck_string_starts_with_cstr(const struct chck_string *a, const char *cstr) 67 | { 68 | const size_t len = (cstr ? strlen(cstr) : 0); 69 | return (a->size >= len && !memcmp(a->data, CSTRE(cstr), len)); 70 | } 71 | 72 | static inline bool 73 | chck_string_ends_with(const struct chck_string *a, const struct chck_string *b) 74 | { 75 | return (a->size >= b->size && !memcmp(a->data + a->size - b->size, CSTRE(b->data), b->size)); 76 | } 77 | 78 | static inline bool 79 | chck_string_starts_with(const struct chck_string *a, const struct chck_string *b) 80 | { 81 | return (a->size >= b->size && !memcmp(CSTRE(a->data), CSTRE(b->data), b->size)); 82 | } 83 | 84 | static inline bool 85 | chck_string_eq(const struct chck_string *a, const struct chck_string *b) 86 | { 87 | return (a->data == b->data) || (a->size == b->size && !memcmp(CSTRE(a->data), CSTRE(b->data), a->size)); 88 | } 89 | 90 | static inline bool 91 | chck_string_eq_cstr(const struct chck_string *a, const char *cstr) 92 | { 93 | const size_t len = (cstr ? strlen(cstr) : 0); 94 | return (len == a->size) && (cstr == a->data || !memcmp(CSTRE(a->data), CSTRE(cstr), a->size)); 95 | } 96 | 97 | #undef CSTRE 98 | 99 | #define decl_int_conv(T, PT, n, fun, range) \ 100 | static inline bool chck_cstr_to_##n(const char *data, T *out) { \ 101 | if (chck_cstr_is_empty(data)) \ 102 | return false; \ 103 | char *end; \ 104 | errno = 0; \ 105 | const PT num = fun(data, &end, 10); \ 106 | if (!end || *end != 0 || errno == ERANGE || errno == EINVAL || (range)) \ 107 | return false; \ 108 | if (out) \ 109 | *out = num; \ 110 | return true; \ 111 | } 112 | 113 | #define decl_flt_conv(T, n, fun) \ 114 | static inline bool chck_cstr_to_##n(const char *data, T *out) { \ 115 | if (chck_cstr_is_empty(data)) \ 116 | return false; \ 117 | char *end; \ 118 | errno = 0; \ 119 | const T num = fun(data, &end); \ 120 | if (!end || *end != 0 || errno == ERANGE) \ 121 | return false; \ 122 | if (out) \ 123 | *out = num; \ 124 | return true; \ 125 | } 126 | 127 | decl_int_conv(uint32_t, uint64_t, u32, strtoull, num > UINT32_MAX); 128 | decl_int_conv(uint16_t, uint32_t, u16, strtoul, num > UINT16_MAX); 129 | decl_int_conv(uint8_t, uint16_t, u8, strtoul, num > UINT8_MAX); 130 | decl_int_conv(int64_t, int64_t, i64, strtoll, num > INT64_MAX || num < INT64_MIN); 131 | decl_int_conv(int32_t, int64_t, i32, strtol, num > INT32_MAX || num < INT32_MIN); 132 | decl_int_conv(int16_t, int32_t, i16, strtol, num > INT16_MAX || num < INT16_MIN); 133 | decl_int_conv(int8_t, int16_t, i8, strtol, num > INT8_MAX || num < INT8_MIN); 134 | 135 | decl_flt_conv(double, d, strtod); 136 | decl_flt_conv(float, f, strtof); 137 | decl_flt_conv(long double, ld, strtold); 138 | 139 | #undef decl_int_conv 140 | #undef decl_flt_conv 141 | 142 | static inline bool 143 | chck_cstr_to_u64(const char *data, uint64_t *out) 144 | { 145 | if (chck_cstr_is_empty(data) || strcspn(data, "-") == strspn(data, " ")) 146 | return false; 147 | 148 | char *end; 149 | errno = 0; 150 | const uint64_t num = strtoull(data, &end, 10); 151 | if (!end || *end != 0 || errno == ERANGE) 152 | return false; 153 | 154 | if (out) 155 | *out = num; 156 | 157 | return true; 158 | } 159 | 160 | static inline bool 161 | chck_cstr_to_bool(const char *data, bool *out) 162 | { 163 | if (!data || (!chck_cstreq(data, "true") && !chck_cstreq(data, "false") && !chck_cstreq(data, "1") && !chck_cstreq(data, "0"))) 164 | return false; 165 | 166 | if (out) 167 | *out = (chck_cstreq(data, "true") || chck_cstreq(data, "1")); 168 | 169 | return true; 170 | } 171 | 172 | void chck_string_release(struct chck_string *string); 173 | bool chck_string_set_cstr(struct chck_string *string, const char *data, bool is_heap); 174 | bool chck_string_set_cstr_with_length(struct chck_string *string, const char *data, size_t len, bool is_heap); 175 | bool chck_string_set(struct chck_string *string, const struct chck_string *other, bool is_heap); 176 | CHCK_FORMAT(printf, 2, 3) bool chck_string_set_format(struct chck_string *string, const char *fmt, ...); 177 | bool chck_string_set_varg(struct chck_string *string, const char *fmt, va_list args); 178 | 179 | char* chck_cstr_strip(char *cstr); /* modifies inplace */ 180 | char* chck_cstr_remove_chars(char *cstr, const char *bad); /* modifies inplace */ 181 | char* chck_cstr_replace_char(char *cstr, char replace, char with); /* modifies inplace */ 182 | const char* chck_cstr_tokenize(const char *cstr, size_t *out_len, const char *separator, bool skip_whitespace, const char **state); 183 | const char* chck_cstr_tokenize_quoted(const char *cstr, size_t *out_len, const char *separator, const char *quotes, const char **state); 184 | 185 | #endif /* __chck_string_h__ */ 186 | -------------------------------------------------------------------------------- /chck/string/test.c: -------------------------------------------------------------------------------- 1 | #include "string.h" 2 | #include 3 | #include 4 | 5 | #undef NDEBUG 6 | #include 7 | 8 | int main(void) 9 | { 10 | /* TEST: stripping */ 11 | { 12 | struct chck_string v = {0}; 13 | assert(chck_string_set_cstr(&v, " contains some whitespace ", true)); 14 | assert(chck_cstr_strip(v.data) == v.data + 3); 15 | assert(strlen(v.data + 3) == 24); 16 | assert(chck_cstreq(v.data + 3, "contains some whitespace")); 17 | 18 | assert(chck_string_set_cstr(&v, "asd", true)); 19 | assert(chck_cstr_strip(v.data) == v.data); 20 | assert(strlen(v.data) == 3); 21 | assert(chck_cstreq(v.data, "asd")); 22 | 23 | assert(chck_string_set_cstr(&v, "foo baz lol", true)); 24 | assert(chck_cstr_remove_chars(v.data, "baz") == v.data); 25 | assert(chck_cstreq(v.data, "foo lol")); 26 | chck_string_release(&v); 27 | 28 | assert(chck_string_set_cstr(&v, "foo baz lol", true)); 29 | assert(chck_cstr_remove_chars(v.data, "qwerty") == v.data); 30 | assert(chck_cstreq(v.data, "foo baz lol")); 31 | chck_string_release(&v); 32 | 33 | assert(chck_string_set_cstr(&v, "foo --- bar", true)); 34 | assert(chck_cstr_replace_char(v.data, '-', '.') == v.data); 35 | assert(chck_cstreq(v.data, "foo ... bar")); 36 | chck_string_release(&v); 37 | 38 | assert(chck_string_set_cstr(&v, "foo --- bar", true)); 39 | assert(chck_cstr_replace_char(v.data, '.', '-') == v.data); 40 | assert(chck_cstreq(v.data, "foo --- bar")); 41 | chck_string_release(&v); 42 | } 43 | 44 | /* TEST: tokenizing */ 45 | { 46 | const char *v = " token: this :please "; 47 | 48 | { 49 | size_t len, i = 0; 50 | const char *t, *state = NULL; 51 | const char *except[] = { " token", " this ", "please " }; 52 | while ((t = chck_cstr_tokenize(v, &len, ":", false, &state))) { 53 | assert(i < 3); 54 | assert(len == strlen(except[i])); 55 | assert(chck_cstrneq(except[i], t, len)); 56 | ++i; 57 | } 58 | assert(i == 3); 59 | } 60 | 61 | { 62 | size_t len, i = 0; 63 | const char *t, *state = NULL; 64 | const char *except[] = { "token", "this", "please" }; 65 | while ((t = chck_cstr_tokenize(v, &len, ":", true, &state))) { 66 | assert(i < 3); 67 | assert(len == strlen(except[i])); 68 | assert(chck_cstrneq(except[i], t, len)); 69 | ++i; 70 | } 71 | assert(i == 3); 72 | } 73 | 74 | v = "some : words : \" but this is included \" : \"yay : yoy\" : \"foo\""; 75 | 76 | { 77 | size_t len, i = 0; 78 | const char *t, *state = NULL; 79 | const char *except[] = { "some", "words", " but this is included ", "yay : yoy", "foo" }; 80 | while ((t = chck_cstr_tokenize_quoted(v, &len, ":", "\"", &state))) { 81 | assert(i < 5); 82 | assert(len == strlen(except[i])); 83 | assert(chck_cstrneq(except[i], t, len)); 84 | ++i; 85 | } 86 | assert(i == 5); 87 | } 88 | 89 | v = "some words \" but this is included \" \"yay yoy\" \"foo\""; 90 | 91 | { 92 | size_t len, i = 0; 93 | const char *t, *state = NULL; 94 | const char *except[] = { "some", "words", " but this is included ", "yay yoy", "foo" }; 95 | while ((t = chck_cstr_tokenize_quoted(v, &len, " ", "\"", &state))) { 96 | assert(i < 5); 97 | assert(len == strlen(except[i])); 98 | assert(chck_cstrneq(except[i], t, len)); 99 | ++i; 100 | } 101 | assert(i == 5); 102 | } 103 | } 104 | 105 | /* TEST: bool conversion tests */ 106 | { 107 | bool v; 108 | assert(chck_cstr_to_bool("true", &v) && v == true); 109 | assert(chck_cstr_to_bool("false", &v) && v == false); 110 | assert(chck_cstr_to_bool("1", &v) && v == true); 111 | assert(chck_cstr_to_bool("0", &v) && v == false); 112 | assert(!chck_cstr_to_bool("falsef", NULL)); 113 | assert(!chck_cstr_to_bool("fals", NULL)); 114 | assert(!chck_cstr_to_bool("truee", NULL)); 115 | assert(!chck_cstr_to_bool("tru", NULL)); 116 | assert(!chck_cstr_to_bool("not-a-bool", NULL)); 117 | assert(!chck_cstr_to_bool("5", NULL)); 118 | } 119 | 120 | /* TEST: float conversion tests */ 121 | { 122 | float v; 123 | assert(chck_cstr_to_f("0.123", &v) && chck_equalf(v, 0.123, 1.0f)); 124 | assert(chck_cstr_to_f("0.1e2", &v) && chck_equalf(v, 0.1e2, 1.0f)); 125 | assert(!chck_cstr_to_f("0.1e1000", NULL)); 126 | assert(!chck_cstr_to_f("not-float", NULL)); 127 | } 128 | 129 | /* TEST: double conversion tests */ 130 | { 131 | double v; 132 | assert(chck_cstr_to_d("0.123", &v) && chck_equal(v, 0.123, 1.0)); 133 | assert(chck_cstr_to_d("0.1e100", &v) && chck_equal(v, 0.1e100, 1.0)); 134 | assert(!chck_cstr_to_d("0.1e1000", NULL)); 135 | assert(!chck_cstr_to_d("not-double", NULL)); 136 | } 137 | 138 | /* TEST: long double conversion tests */ 139 | { 140 | long double v; // valgrind does not handle long double so we don't do 1e1000 here 141 | assert(chck_cstr_to_ld("0.123", &v) && chck_equalld(v, 0.123l, 1.0l)); 142 | assert(chck_cstr_to_ld("0.1e100", &v) && chck_equalld(v, 0.1e100l, 1.0)); 143 | assert(!chck_cstr_to_ld("0.1e100000", NULL)); 144 | assert(!chck_cstr_to_ld("not-long-double", NULL)); 145 | } 146 | 147 | /* TEST: i64 conversion tests */ 148 | { 149 | int64_t v; 150 | assert(chck_cstr_to_i64("1", &v) && v == 1); 151 | assert(chck_cstr_to_i64("-1", &v) && v == -1); 152 | assert(chck_cstr_to_i64("9223372036854775807", &v) && v == INT64_MAX); 153 | assert(chck_cstr_to_i64("-9223372036854775808", &v) && v == INT64_MIN); 154 | assert(!chck_cstr_to_i64("9223372036854775808", &v)); 155 | assert(!chck_cstr_to_i64("-9223372036854775809", &v)); 156 | assert(!chck_cstr_to_i64("1.0", &v)); 157 | 158 | uint64_t uv; 159 | assert(chck_cstr_to_u64("1", &uv) && uv == 1); 160 | assert(!chck_cstr_to_u64("-1", &uv)); 161 | assert(chck_cstr_to_u64("18446744073709551615", &uv) && uv == UINT64_MAX); 162 | assert(!chck_cstr_to_u64("18446744073709551616", &uv)); 163 | assert(!chck_cstr_to_u64("1.0", &uv)); 164 | } 165 | 166 | /* TEST: i32 conversion tests */ 167 | { 168 | int32_t v; 169 | assert(chck_cstr_to_i32("1", &v) && v == 1); 170 | assert(chck_cstr_to_i32("-1", &v) && v == -1); 171 | assert(chck_cstr_to_i32("2147483647", &v) && v == INT32_MAX); 172 | assert(chck_cstr_to_i32("-2147483648", &v) && v == INT32_MIN); 173 | assert(!chck_cstr_to_i32("2147483648", &v)); 174 | assert(!chck_cstr_to_i32("-2147483649", &v)); 175 | assert(!chck_cstr_to_i32("1.0", &v)); 176 | 177 | uint32_t uv; 178 | assert(chck_cstr_to_u32("1", &uv) && uv == 1); 179 | assert(!chck_cstr_to_u32("-1", &uv)); 180 | assert(chck_cstr_to_u32("4294967295", &uv) && uv == UINT32_MAX); 181 | assert(!chck_cstr_to_u32("4294967296", &uv)); 182 | assert(!chck_cstr_to_u32("1.0", &uv)); 183 | } 184 | 185 | /* TEST: i16 conversion tests */ 186 | { 187 | int16_t v; 188 | assert(chck_cstr_to_i16("1", &v) && v == 1); 189 | assert(chck_cstr_to_i16("-1", &v) && v == -1); 190 | assert(chck_cstr_to_i16("32767", &v) && v == INT16_MAX); 191 | assert(chck_cstr_to_i16("-32768", &v) && v == INT16_MIN); 192 | assert(!chck_cstr_to_i16("32768", &v)); 193 | assert(!chck_cstr_to_i16("-32769", &v)); 194 | assert(!chck_cstr_to_i16("1.0", &v)); 195 | 196 | uint16_t uv; 197 | assert(chck_cstr_to_u16("1", &uv) && uv == 1); 198 | assert(!chck_cstr_to_u16("-1", &uv)); 199 | assert(chck_cstr_to_u16("65535", &uv) && uv == UINT16_MAX); 200 | assert(!chck_cstr_to_u16("65536", &uv)); 201 | assert(!chck_cstr_to_u16("1.0", &uv)); 202 | } 203 | 204 | /* TEST: i8 conversion tests */ 205 | { 206 | int8_t v; 207 | assert(chck_cstr_to_i8("1", &v) && v == 1); 208 | assert(chck_cstr_to_i8("-1", &v) && v == -1); 209 | assert(chck_cstr_to_i8("127", &v) && v == INT8_MAX); 210 | assert(chck_cstr_to_i8("-128", &v) && v == INT8_MIN); 211 | assert(!chck_cstr_to_i8("128", &v)); 212 | assert(!chck_cstr_to_i8("-129", &v)); 213 | assert(!chck_cstr_to_i8("1.0", &v)); 214 | 215 | uint8_t uv; 216 | assert(chck_cstr_to_u8("1", &uv) && uv == 1); 217 | assert(!chck_cstr_to_u8("-1", &uv)); 218 | assert(chck_cstr_to_u8("255", &uv) && uv == UINT8_MAX); 219 | assert(!chck_cstr_to_u8("257", &uv)); 220 | assert(!chck_cstr_to_u8("1.0", &uv)); 221 | } 222 | 223 | /* TEST: string tests */ 224 | { 225 | struct chck_string str = {0}; 226 | assert(chck_string_set_cstr(&str, "foobar", false)); 227 | assert(str.data && str.size == strlen("foobar") && chck_string_eq_cstr(&str, "foobar")); 228 | assert(chck_string_ends_with_cstr(&str, "") && chck_string_ends_with_cstr(&str, NULL) && chck_string_ends_with_cstr(&str, "foobar") && chck_string_ends_with_cstr(&str, "bar") && !chck_string_ends_with_cstr(&str, "bur")); 229 | assert(chck_string_starts_with_cstr(&str, "") && chck_string_starts_with_cstr(&str, NULL) && chck_string_starts_with_cstr(&str, "foobar") && chck_string_starts_with_cstr(&str, "foo") && !chck_string_starts_with_cstr(&str, "fuo")); 230 | assert(!chck_string_is_empty(&str)); 231 | assert(chck_string_set_cstr(&str, "", false)); 232 | assert(!str.data && str.size == strlen("") && chck_string_eq_cstr(&str, "")); 233 | assert(!str.data && str.size == 0 && chck_string_eq_cstr(&str, NULL)); // string_eq_cstr treats empty string as NULL 234 | assert(chck_string_ends_with_cstr(&str, "") && chck_string_ends_with_cstr(&str, NULL) && !chck_string_ends_with_cstr(&str, "foobar")); 235 | assert(chck_string_starts_with_cstr(&str, "") && chck_string_starts_with_cstr(&str, NULL) && !chck_string_starts_with_cstr(&str, "foobar")); 236 | assert(chck_string_is_empty(&str)); 237 | assert(chck_string_set_cstr(&str, NULL, false)); 238 | assert(!str.data && str.size == 0 && !chck_string_eq_cstr(&str, "foobar")); 239 | assert(chck_string_ends_with_cstr(&str, "") && chck_string_ends_with_cstr(&str, NULL) && !chck_string_ends_with_cstr(&str, "foobar")); 240 | assert(chck_string_starts_with_cstr(&str, "") && chck_string_starts_with_cstr(&str, NULL) && !chck_string_starts_with_cstr(&str, "foobar")); 241 | assert(chck_string_is_empty(&str)); 242 | 243 | // is_heap is false, so nothing is copied. 244 | // since chck_strings do not copy when is_heap is false, printf for .data would print whole "foobar" in this case. 245 | // if is_heap is set to true, the string is copied and terminated correctly. 246 | assert(chck_string_set_cstr_with_length(&str, "foobar", 3, false)); 247 | assert(str.data && str.size == strlen("foo") && chck_string_eq_cstr(&str, "foo") && !chck_string_eq_cstr(&str, "foobar")); 248 | 249 | struct chck_string str2 = {0}; 250 | assert(chck_string_set(&str2, &str, false)); 251 | assert(str.data == str2.data && str2.size == strlen("foo") && chck_string_eq(&str, &str2)); 252 | assert(chck_string_ends_with(&str, &str2)); 253 | assert(chck_string_starts_with(&str, &str2)); 254 | assert(chck_string_set(&str2, &str, true)); 255 | assert(str.data != str2.data && str2.size == strlen("foo") && chck_string_eq(&str, &str2)); 256 | assert(chck_string_ends_with(&str, &str2)); 257 | assert(chck_string_starts_with(&str, &str2)); 258 | assert(chck_string_set_cstr(&str2, "foobar2", false)); 259 | assert(str.data != str2.data && str2.size == strlen("foobar2") && !chck_string_eq(&str, &str2)); 260 | assert(!chck_string_ends_with(&str, &str2)); 261 | assert(!chck_string_starts_with(&str, &str2)); 262 | 263 | assert(chck_string_set_format(&str, "%s-%s", "test", str2.data)); 264 | assert(str.data && str.size == strlen("test-foobar2") && str.is_heap && chck_string_eq_cstr(&str, "test-foobar2")); 265 | 266 | chck_string_release(&str); 267 | chck_string_release(&str2); 268 | assert(!str.data && !str2.data && str.size + str2.size == 0); 269 | assert(chck_string_eq(&str, &str2)); 270 | 271 | assert(chck_cstreq("foobar", "foobar")); 272 | assert(!chck_cstreq("foobar", "foo")); 273 | assert(!chck_cstreq("foobar", NULL)); 274 | assert(chck_cstreq(NULL, NULL)); 275 | 276 | assert(chck_cstrneq("foobar", "foo", 3)); 277 | assert(chck_cstrneq("fo", "fo", 3)); 278 | assert(!chck_cstrneq("foobar", "foa", 3)); 279 | assert(chck_cstrneq(NULL, NULL, 3)); 280 | assert(chck_cstrneq(NULL, NULL, 0)); 281 | 282 | assert(!chck_cstr_is_empty("foobar")); 283 | assert(chck_cstr_ends_with("foobar", "foobar") && chck_cstr_ends_with("foobar", "bar") && !chck_cstr_ends_with("foobar", "bur")); 284 | assert(chck_cstr_starts_with("foobar", "foobar") && chck_cstr_starts_with("foobar", "foo") && !chck_cstr_starts_with("foobar", "fur")); 285 | assert(chck_cstr_is_empty("") && chck_cstr_is_empty(NULL)); 286 | assert(chck_cstr_ends_with("", "") && chck_cstr_ends_with(NULL, NULL)); 287 | assert(chck_cstr_starts_with("", "") && chck_cstr_starts_with(NULL, NULL)); 288 | } 289 | return EXIT_SUCCESS; 290 | } 291 | -------------------------------------------------------------------------------- /chck/thread/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(CMAKE_THREAD_PREFER_PTHREAD 1) 2 | find_package(Threads) 3 | set_package_properties(Threads PROPERTIES TYPE PURPOSE "Enables threading support") 4 | 5 | if (THREADS_FOUND) 6 | find_package(Valgrind) 7 | if (VALGRIND_FOUND) 8 | add_definitions(-DHAS_VALGRIND=1) 9 | include_directories(${VALGRIND_INCLUDE_DIRS}) 10 | endif (VALGRIND_FOUND) 11 | 12 | if ("${CMAKE_THREAD_LIBS_INIT}" STREQUAL "") 13 | find_library(THREAD_LIB NAMES pthread) 14 | else () 15 | set(THREAD_LIB ${CMAKE_THREAD_LIBS_INIT}) 16 | endif () 17 | 18 | add_subdirectory(queue) 19 | endif (THREADS_FOUND) 20 | 21 | set(CHCK_COMPONENTS ${CHCK_COMPONENTS} PARENT_SCOPE) 22 | -------------------------------------------------------------------------------- /chck/thread/README.md: -------------------------------------------------------------------------------- 1 | # Threading utilities 2 | 3 | Thread queue, dispatch, etc.. 4 | -------------------------------------------------------------------------------- /chck/thread/queue/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(chck_tqueue queue.c) 2 | target_link_libraries(chck_tqueue PRIVATE ${THREAD_LIB}) 3 | install_libraries(chck_tqueue) 4 | install_headers(queue.h) 5 | 6 | if (CHCK_BUILD_TESTS) 7 | add_executable(thread_queue_test test.c) 8 | target_link_libraries(thread_queue_test PRIVATE chck_tqueue) 9 | add_test_ex(thread_queue_test) 10 | endif () 11 | -------------------------------------------------------------------------------- /chck/thread/queue/README.md: -------------------------------------------------------------------------------- 1 | # Thread pools 2 | 3 | Execute tasks on given number of threads. 4 | Optional fd signaling to integrate in event loops on unix systems. 5 | -------------------------------------------------------------------------------- /chck/thread/queue/queue.c: -------------------------------------------------------------------------------- 1 | #include "queue.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #if HAS_VALGRIND 11 | # include 12 | #else 13 | # define VALGRIND_HG_DISABLE_CHECKING(x, y) ; 14 | # define VALGRIND_HG_ENABLE_CHECKING(x, y) ; 15 | #endif 16 | 17 | static bool 18 | creator_thread(const struct chck_tqueue *tqueue, const char *function) 19 | { 20 | if (tqueue->threads.self != pthread_self()) { 21 | fprintf(stderr, "chck: Function '%s' should be only called from same thread where tqueue was created in.\n", function); 22 | abort(); 23 | return false; 24 | } 25 | return true; 26 | } 27 | #define creator_thread(x) creator_thread(x, __func__) 28 | 29 | static void* 30 | get_data(struct chck_tasks *tasks, size_t index) 31 | { 32 | assert(tasks && tasks->buffer); 33 | 34 | if (index >= tasks->qsize) 35 | return NULL; 36 | 37 | return tasks->buffer + (index * tasks->msize); 38 | } 39 | 40 | static void* 41 | on_thread(void *arg) 42 | { 43 | assert(arg); 44 | struct chck_tasks *tasks = arg; 45 | 46 | while (true) { 47 | // XXX: We could use seperate lock here for dequeueing, and have own lock for insertion. 48 | pthread_mutex_lock(&tasks->mutex); 49 | 50 | if (!tasks->cancel && !tasks->tcount) 51 | pthread_cond_wait(&tasks->notify, &tasks->mutex); 52 | 53 | if (tasks->cancel) 54 | break; 55 | 56 | if (!tasks->tcount) { 57 | pthread_mutex_unlock(&tasks->mutex); 58 | continue; 59 | } 60 | 61 | assert(tasks->tcount > 0); 62 | bool *processed = &tasks->processed[tasks->thead]; 63 | void *data = get_data(tasks, tasks->thead); 64 | void (*work)() = tasks->work; 65 | 66 | assert(tasks->qsize > 0); 67 | tasks->thead = (tasks->thead + 1) % tasks->qsize; 68 | tasks->tcount -= 1; 69 | 70 | pthread_mutex_unlock(&tasks->mutex); 71 | 72 | // We only may read race against these. That's okay. 73 | // The user should not meddle with the input outside of the callbacks. 74 | // And tqueue won't touch the item when worker is working on it. 75 | VALGRIND_HG_DISABLE_CHECKING(data, tasks->msize); 76 | 77 | work(data); 78 | 79 | // Collectless mode when no callback specified. 80 | if (!tasks->callback) { 81 | if (tasks->destructor) 82 | tasks->destructor(data); 83 | 84 | memset(data, 0, tasks->msize); 85 | 86 | pthread_mutex_lock(&tasks->mutex); 87 | assert(tasks->count >= 1); 88 | tasks->head = (tasks->head + 1) % tasks->qsize; 89 | tasks->count -= 1; 90 | pthread_mutex_unlock(&tasks->mutex); 91 | } else { 92 | *processed = true; 93 | } 94 | 95 | VALGRIND_HG_ENABLE_CHECKING(data, tasks->msize); 96 | 97 | if (tasks->fd >= 0) 98 | write(tasks->fd, (uint64_t[]){1}, sizeof(uint64_t)); 99 | } 100 | 101 | pthread_mutex_unlock(&tasks->mutex); 102 | return NULL; 103 | } 104 | 105 | static void 106 | stop(struct chck_tqueue *tqueue) 107 | { 108 | assert(tqueue); 109 | 110 | if (!tqueue->threads.running) 111 | return; 112 | 113 | pthread_mutex_lock(&tqueue->tasks.mutex); 114 | tqueue->tasks.cancel = true; 115 | pthread_cond_broadcast(&tqueue->tasks.notify); 116 | pthread_mutex_unlock(&tqueue->tasks.mutex); 117 | 118 | for (size_t i = 0; i < tqueue->threads.count; ++i) 119 | pthread_join(tqueue->threads.t[i], NULL); 120 | 121 | memset(tqueue->threads.t, 0, sizeof(*tqueue->threads.t) * tqueue->threads.count); 122 | tqueue->threads.running = false; 123 | } 124 | 125 | static bool 126 | start(struct chck_tqueue *tqueue) 127 | { 128 | assert(tqueue); 129 | 130 | if (tqueue->threads.running) 131 | return true; 132 | 133 | tqueue->tasks.cancel = false; 134 | 135 | for (size_t i = 0; i < tqueue->threads.count; ++i) { 136 | if (pthread_create(&tqueue->threads.t[i], NULL, on_thread, &tqueue->tasks) != 0) 137 | return false; 138 | } 139 | 140 | tqueue->threads.running = true; 141 | return true; 142 | } 143 | 144 | bool 145 | chck_tqueue_add_task(struct chck_tqueue *tqueue, void *data, useconds_t block) 146 | { 147 | assert(tqueue && data); 148 | 149 | if (!tqueue->threads.running && !start(tqueue)) 150 | return false; 151 | 152 | assert(tqueue->tasks.qsize > 0); 153 | const size_t next = (tqueue->tasks.tail + 1) % tqueue->tasks.qsize; 154 | assert(next < tqueue->tasks.qsize); 155 | 156 | bool ret = false; 157 | while (true) { 158 | pthread_mutex_lock(&tqueue->tasks.mutex); 159 | 160 | if (tqueue->tasks.count >= tqueue->tasks.qsize) { 161 | if (block) { 162 | pthread_mutex_unlock(&tqueue->tasks.mutex); 163 | 164 | if (tqueue->threads.self == pthread_self() && tqueue->tasks.callback) 165 | chck_tqueue_collect(tqueue); 166 | 167 | usleep(block); 168 | continue; 169 | } 170 | break; 171 | } 172 | 173 | if (tqueue->tasks.cancel) 174 | break; 175 | 176 | void *ptr = get_data(&tqueue->tasks, tqueue->tasks.tail); 177 | memcpy(ptr, data, tqueue->tasks.msize); 178 | tqueue->tasks.processed[tqueue->tasks.tail] = false; 179 | tqueue->tasks.tail = next; 180 | tqueue->tasks.count += 1; 181 | tqueue->tasks.tcount += 1; 182 | 183 | pthread_cond_signal(&tqueue->tasks.notify); 184 | ret = true; 185 | break; 186 | } 187 | 188 | pthread_mutex_unlock(&tqueue->tasks.mutex); 189 | return ret; 190 | } 191 | 192 | size_t 193 | chck_tqueue_collect(struct chck_tqueue *tqueue) 194 | { 195 | assert(tqueue); 196 | 197 | // Collect is unneccessary when no callback specified 198 | assert(tqueue->tasks.callback); 199 | 200 | // For simplicity, we only allow collection on creator thread. 201 | // Collecting anywhere else does not make sense anyways. 202 | if (!tqueue || !creator_thread(tqueue)) 203 | return 0; 204 | 205 | if (tqueue->tasks.fd >= 0) { 206 | char buf[sizeof(uint64_t)]; 207 | read(tqueue->tasks.fd, buf, sizeof(buf)); 208 | } 209 | 210 | pthread_mutex_lock(&tqueue->tasks.mutex); 211 | const size_t head = tqueue->tasks.head; 212 | const size_t count = tqueue->tasks.count; 213 | pthread_mutex_unlock(&tqueue->tasks.mutex); 214 | 215 | // Read races again fine here. 216 | // We can't enter inside until the thread is done with the item. 217 | size_t processed = 0; 218 | for (size_t i = head, x = 0; x < count; i = (i + 1) % tqueue->tasks.qsize, ++x) { 219 | if (!tqueue->tasks.processed[i]) 220 | continue; 221 | 222 | void *data = get_data(&tqueue->tasks, i); 223 | VALGRIND_HG_DISABLE_CHECKING(data, tqueue->tasks.msize); 224 | 225 | if (tqueue->tasks.callback) 226 | tqueue->tasks.callback(data); 227 | 228 | if (tqueue->tasks.destructor) 229 | tqueue->tasks.destructor(data); 230 | 231 | memset(data, 0, tqueue->tasks.msize); 232 | VALGRIND_HG_ENABLE_CHECKING(data, tqueue->tasks.msize); 233 | 234 | tqueue->tasks.processed[i] = false; 235 | ++processed; 236 | } 237 | 238 | // We need to lock here however 239 | pthread_mutex_lock(&tqueue->tasks.mutex); 240 | assert(tqueue->tasks.count >= processed); 241 | tqueue->tasks.head = (tqueue->tasks.head + processed) % tqueue->tasks.qsize; 242 | tqueue->tasks.count -= processed; 243 | const size_t rcount = tqueue->tasks.count; 244 | pthread_mutex_unlock(&tqueue->tasks.mutex); 245 | 246 | if (!tqueue->threads.keep_alive && !rcount) 247 | stop(tqueue); 248 | 249 | return rcount; 250 | } 251 | 252 | void 253 | chck_tqueue_release(struct chck_tqueue *tqueue) 254 | { 255 | // Allowed only on creator thread. 256 | if (!tqueue || (tqueue->threads.self && !creator_thread(tqueue))) 257 | return; 258 | 259 | stop(tqueue); 260 | pthread_mutex_destroy(&tqueue->tasks.mutex); 261 | pthread_cond_destroy(&tqueue->tasks.notify); 262 | 263 | if (tqueue->tasks.destructor) { 264 | for (size_t i = tqueue->tasks.head, x = 0; x < tqueue->tasks.count; i = (i + 1) % tqueue->tasks.qsize, ++x) 265 | tqueue->tasks.destructor(get_data(&tqueue->tasks, i)); 266 | } 267 | 268 | if (tqueue->tasks.fd >= 0) 269 | close(tqueue->tasks.fd); 270 | 271 | free(tqueue->tasks.processed); 272 | free(tqueue->tasks.buffer); 273 | free(tqueue->threads.t); 274 | *tqueue = (struct chck_tqueue){0}; 275 | } 276 | 277 | void 278 | chck_tqueue_set_fd(struct chck_tqueue *tqueue, int fd) 279 | { 280 | assert(tqueue); 281 | 282 | // Allowed only on creator thread. 283 | if (!tqueue || !creator_thread(tqueue)) 284 | return; 285 | 286 | if (tqueue->tasks.fd >= 0) 287 | close(tqueue->tasks.fd); 288 | 289 | tqueue->tasks.fd = dup(fd); 290 | } 291 | 292 | int 293 | chck_tqueue_get_fd(struct chck_tqueue *tqueue) 294 | { 295 | assert(tqueue); 296 | 297 | // Allowed only on creator thread. 298 | if (!tqueue || !creator_thread(tqueue)) 299 | return -1; 300 | 301 | return tqueue->tasks.fd; 302 | } 303 | 304 | void 305 | chck_tqueue_set_keep_alive(struct chck_tqueue *tqueue, bool keep_alive) 306 | { 307 | assert(tqueue); 308 | // Whether to destroy or keep thread alive when no tasks. 309 | tqueue->threads.keep_alive = keep_alive; 310 | } 311 | 312 | bool 313 | chck_tqueue_get_keep_alive(struct chck_tqueue *tqueue) 314 | { 315 | assert(tqueue); 316 | return tqueue->threads.keep_alive; 317 | } 318 | 319 | bool 320 | chck_tqueue(struct chck_tqueue *tqueue, size_t nthreads, size_t qsize, size_t msize, void (*work)(), void (*callback)(), void (*destructor)()) 321 | { 322 | assert(tqueue && work && msize > 0); 323 | *tqueue = (struct chck_tqueue){ .tasks = { .fd = - 1 } }; 324 | 325 | if (!msize || !work) 326 | return false; 327 | 328 | if (!(tqueue->tasks.buffer = chck_calloc_of(qsize, msize)) || 329 | !(tqueue->tasks.processed = chck_calloc_of(qsize, sizeof(bool)))) 330 | return false; 331 | 332 | // We allow racy reads on this array. 333 | // However we don't allow racy writes. 334 | // Unfortunately some helgrind macros are unimplemented that would allow turning this off just for reads. 335 | VALGRIND_HG_DISABLE_CHECKING(tqueue->tasks.processed, qsize); 336 | 337 | if (pthread_mutex_init(&tqueue->tasks.mutex, NULL) != 0 || 338 | pthread_cond_init(&tqueue->tasks.notify, NULL) != 0) 339 | goto fail; 340 | 341 | if (!(tqueue->threads.t = chck_calloc_of(nthreads, sizeof(pthread_t)))) 342 | goto fail; 343 | 344 | tqueue->threads.self = pthread_self(); 345 | tqueue->threads.count = nthreads; 346 | tqueue->tasks.msize = msize; 347 | tqueue->tasks.qsize = qsize; 348 | tqueue->tasks.work = work; 349 | tqueue->tasks.callback = callback; 350 | tqueue->tasks.destructor = destructor; 351 | return true; 352 | 353 | fail: 354 | chck_tqueue_release(tqueue); 355 | return false; 356 | } 357 | -------------------------------------------------------------------------------- /chck/thread/queue/queue.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_dispatch_h__ 2 | #define __chck_dispatch_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct chck_tqueue { 11 | struct chck_tasks { 12 | uint8_t *buffer; 13 | bool *processed; 14 | void (*work)(); 15 | void (*callback)(); 16 | void (*destructor)(); 17 | size_t msize; 18 | size_t qsize; 19 | size_t head, thead, tail, count, tcount; 20 | pthread_mutex_t mutex; 21 | pthread_cond_t notify; 22 | int fd; 23 | bool cancel; 24 | } tasks; 25 | 26 | struct { 27 | pthread_t *t; 28 | pthread_t self; 29 | size_t count; 30 | bool running; 31 | bool keep_alive; 32 | } threads; 33 | }; 34 | 35 | bool chck_tqueue_add_task(struct chck_tqueue *tqueue, void *data, useconds_t block); 36 | size_t chck_tqueue_collect(struct chck_tqueue *tqueue); 37 | void chck_tqueue_set_fd(struct chck_tqueue *tqueue, int fd); 38 | int chck_tqueue_get_fd(struct chck_tqueue *tqueue); 39 | void chck_tqueue_set_keep_alive(struct chck_tqueue *tqueue, bool keep_alive); 40 | bool chck_tqueue_get_keep_alive(struct chck_tqueue *tqueue); 41 | void chck_tqueue_release(struct chck_tqueue *tqueue); 42 | bool chck_tqueue(struct chck_tqueue *tqueue, size_t nthreads, size_t qsize, size_t msize, void (*work)(), void (*callback)(), void (*destructor)()); 43 | 44 | #endif /* __chck_dispatch_h__ */ 45 | -------------------------------------------------------------------------------- /chck/thread/queue/test.c: -------------------------------------------------------------------------------- 1 | #include "queue.h" 2 | #include 3 | #include 4 | 5 | #ifdef __linux__ 6 | # include 7 | # include 8 | #endif 9 | 10 | #undef NDEBUG 11 | #include 12 | 13 | struct item { 14 | int a; 15 | int c; 16 | }; 17 | 18 | static void 19 | work(struct item *item) 20 | { 21 | assert(item); 22 | item->c /= 5; 23 | } 24 | 25 | static void 26 | callback(struct item *item) 27 | { 28 | assert(item); 29 | assert((item->a == 1 && item->c == 2) || (item->a == 2 && item->c == 1)); 30 | } 31 | 32 | static void 33 | destructor(struct item *item) 34 | { 35 | assert(item); 36 | assert((item->a == 1 && item->c == 2) || (item->a == 2 && item->c == 1)); 37 | } 38 | 39 | int main(void) 40 | { 41 | /* TEST: thread pools */ 42 | { 43 | struct chck_tqueue tqueue; 44 | assert(chck_tqueue(&tqueue, 1, 2, sizeof(struct item), work, callback, destructor)); 45 | 46 | struct item a = { 1, 10 }; 47 | assert(chck_tqueue_add_task(&tqueue, &a, 0)); 48 | 49 | struct item b = { 2, 5 }; 50 | assert(chck_tqueue_add_task(&tqueue, &b, 0)); 51 | 52 | while (chck_tqueue_collect(&tqueue)); 53 | chck_tqueue_release(&tqueue); 54 | } 55 | 56 | /* TEST: thread pools, no collect */ 57 | { 58 | struct chck_tqueue tqueue; 59 | assert(chck_tqueue(&tqueue, 1, 1, sizeof(struct item), work, NULL, destructor)); 60 | assert(!chck_tqueue_get_keep_alive(&tqueue)); 61 | chck_tqueue_set_keep_alive(&tqueue, true); 62 | assert(chck_tqueue_get_keep_alive(&tqueue)); 63 | 64 | struct item a = { 1, 10 }; 65 | assert(chck_tqueue_add_task(&tqueue, &a, 100)); 66 | 67 | struct item b = { 2, 5 }; 68 | assert(chck_tqueue_add_task(&tqueue, &b, 100)); 69 | 70 | usleep(100); 71 | chck_tqueue_release(&tqueue); 72 | } 73 | 74 | #ifdef __linux__ 75 | /* TEST: fd support */ 76 | { 77 | struct chck_tqueue tqueue; 78 | assert(chck_tqueue(&tqueue, 1, 2, sizeof(struct item), work, callback, destructor)); 79 | 80 | assert(chck_tqueue_get_fd(&tqueue) == -1); 81 | chck_tqueue_set_fd(&tqueue, 100); // fd that doesn't exist 82 | assert(chck_tqueue_get_fd(&tqueue) == -1); 83 | 84 | int fd; 85 | assert((fd = eventfd(0, EFD_CLOEXEC)) >= 0); 86 | chck_tqueue_set_fd(&tqueue, fd); 87 | assert((chck_tqueue_get_fd(&tqueue) != -1)); // dup'd 88 | close(fd); 89 | 90 | struct item a = { 1, 10 }; 91 | assert(chck_tqueue_add_task(&tqueue, &a, 0)); 92 | 93 | struct item b = { 2, 5 }; 94 | assert(chck_tqueue_add_task(&tqueue, &b, 0)); 95 | 96 | struct pollfd fds[1] = { { .fd = chck_tqueue_get_fd(&tqueue), .events = POLLIN } }; 97 | while (true) { 98 | if (poll(fds, 1, -1) <= 0) 99 | continue; 100 | 101 | for (size_t i = 0; i < 1; ++i) { 102 | if (fds[i].revents & POLLIN) { 103 | if (!chck_tqueue_collect(&tqueue)) 104 | goto out; 105 | } 106 | } 107 | } 108 | 109 | out: 110 | chck_tqueue_release(&tqueue); 111 | } 112 | #endif 113 | 114 | /* TEST: throughput on single thread */ 115 | { 116 | struct chck_tqueue tqueue; 117 | const size_t iters = 0xFFFFF; 118 | assert(chck_tqueue(&tqueue, 1, iters, sizeof(struct item), work, callback, destructor)); 119 | 120 | for (size_t i = 0; i < iters / 2; ++i) { 121 | struct item a = { 1, 10 }; 122 | assert(chck_tqueue_add_task(&tqueue, &a, 0)); 123 | 124 | struct item b = { 2, 5 }; 125 | assert(chck_tqueue_add_task(&tqueue, &b, 0)); 126 | } 127 | 128 | while (chck_tqueue_collect(&tqueue)) usleep(1000); 129 | chck_tqueue_release(&tqueue); 130 | } 131 | 132 | return EXIT_SUCCESS; 133 | } 134 | -------------------------------------------------------------------------------- /chck/unicode/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(chck_unicode unicode.c) 2 | install_libraries(chck_unicode) 3 | install_headers(unicode.h) 4 | 5 | if (CHCK_BUILD_TESTS) 6 | add_executable(unicode_test test.c) 7 | target_link_libraries(unicode_test PRIVATE chck_unicode) 8 | add_test_ex(unicode_test) 9 | endif () 10 | -------------------------------------------------------------------------------- /chck/unicode/README.md: -------------------------------------------------------------------------------- 1 | # Unicode helpers 2 | 3 | Encoding, decoding and other utility functions 4 | -------------------------------------------------------------------------------- /chck/unicode/test.c: -------------------------------------------------------------------------------- 1 | #include "unicode.h" 2 | #include 3 | #include 4 | 5 | #undef NDEBUG 6 | #include 7 | 8 | int main(void) 9 | { 10 | // test: UTF32 love hotel 11 | { 12 | char out[4]; 13 | assert(chck_utf32_encode('a', out) == 1); 14 | assert(!strncmp(out, "a", 1)); 15 | assert(chck_utf32_encode(0x1f3e9, out) == 4); 16 | assert(!strncmp(out, "🏩", 4)); 17 | assert(chck_utf32_encode(0, out) == 0); 18 | } 19 | 20 | // test: UTF16 love hotel 21 | { 22 | char out[4]; 23 | uint16_t hi = 0; 24 | enum chck_utf16_error error; 25 | assert(chck_utf16_encode('a', out, &hi, &error) == 1); 26 | assert(error == CHCK_UTF16_OK); 27 | assert(hi == 0); 28 | assert(!strncmp(out, "a", 1)); 29 | 30 | assert(chck_utf16_encode(0, out, &hi, &error) == 0); 31 | assert(error == CHCK_UTF16_OK); 32 | assert(hi == 0); 33 | 34 | assert(chck_utf16_encode(0xDFE9, out, &hi, &error) == 0); 35 | assert(error == CHCK_UTF16_UNEXPECTED_LOW); 36 | assert(hi == 0); 37 | 38 | assert(chck_utf16_encode(0xD83C, out, &hi, &error) == 0); 39 | assert(error == CHCK_UTF16_OK); 40 | assert(hi != 0); 41 | assert(chck_utf16_encode(0xD83C, out, &hi, &error) == 0); 42 | assert(error == CHCK_UTF16_UNEXPECTED_HIGH); 43 | assert(hi == 0); 44 | 45 | assert(chck_utf16_encode(0xD83C, out, &hi, &error) == 0); 46 | assert(error == CHCK_UTF16_OK); 47 | assert(hi != 0); 48 | assert(chck_utf16_encode(0xDFE9, out, &hi, &error) == 4); 49 | assert(error == CHCK_UTF16_OK); 50 | assert(!strncmp(out, "🏩", 4)); 51 | } 52 | 53 | // test: mblen 54 | { 55 | assert(chck_utf8_mblen("🏩") == 4); 56 | assert(chck_utf8_mblen("a") == 1); 57 | assert(chck_utf8_mblen("\xb1") == 0); 58 | } 59 | 60 | // test: validate 61 | { 62 | assert(chck_utf8_validate("🏩")); 63 | assert(chck_utf8_validate("a")); 64 | assert(chck_utf8_validate("\xc3\xb1")); 65 | assert(!chck_utf8_validate("\xc3\x28")); 66 | assert(!chck_utf8_validate("\xa0\xa1")); 67 | assert(chck_utf8_validate("\xe2\x82\xa1")); 68 | assert(!chck_utf8_validate("\xe2\x28\xa1")); 69 | assert(!chck_utf8_validate("\xe2\x82\x28")); 70 | assert(!chck_utf8_validate("\xf0\x28\x8c\xbc")); 71 | assert(!chck_utf8_validate("\xf0\x90\x28\xbc")); 72 | assert(chck_utf8_validate("\xf0\x90\x8c\xbc")); 73 | assert(!chck_utf8_validate("\xf0\x28\x8c\x28")); 74 | } 75 | 76 | // test: codepoint 77 | { 78 | assert(chck_utf8_codepoint("🏩") == 0x1f3e9); 79 | assert(chck_utf8_codepoint("\xc3\x28") == CHCK_REPLACEMENT_CHAR); 80 | assert(chck_utf8_codepoint("a") == 'a'); 81 | } 82 | 83 | // test: utf8 strlen 84 | { 85 | size_t len; 86 | assert(chck_utf8_strlen("🏩", strlen("🏩"), &len) && len == 1); 87 | assert(chck_utf8_strlen("a", strlen("a"), &len) && len == 1); 88 | assert(chck_utf8_strlen("ab", strlen("ab"), &len) && len == 2); 89 | assert(!chck_utf8_strlen("\xc3\x28", strlen("\xc3\x28"), &len) && len == 0); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /chck/unicode/unicode.c: -------------------------------------------------------------------------------- 1 | #include "unicode.h" 2 | #include 3 | 4 | uint8_t 5 | chck_utf32_encode(uint32_t dec, char out[4]) 6 | { 7 | assert(out); 8 | 9 | if (!dec) 10 | return 0; 11 | 12 | const uint8_t bits[4] = { 0x00, 0xC0, 0xE0, 0xF0 }; 13 | const uint8_t mb = (dec < 0x80 ? 0 : (dec < 0x800 ? 1 : (dec < 0x10000 ? 2 : 3))); 14 | 15 | out[0] = (dec >> (mb * 6)) | bits[mb]; 16 | for (int32_t i = mb * 6 - 6, c = 1; i >= 0 && c < mb + 1; i -= 6, ++c) 17 | out[c] = ((dec >> i) & 0x3F) | 0x80; 18 | 19 | return mb + 1; 20 | } 21 | 22 | uint8_t 23 | chck_utf16_encode(uint16_t dec, char out[4], uint16_t *in_out_hi, enum chck_utf16_error *out_error) 24 | { 25 | assert(out && in_out_hi); 26 | 27 | if (out_error) 28 | *out_error = CHCK_UTF16_OK; 29 | 30 | uint32_t u32dec = dec; 31 | const uint16_t hi = *in_out_hi; 32 | 33 | #define IS_HIGH_SURROGATE(dec) (((dec) & 0xFC00) == 0xD800) 34 | #define IS_LOW_SURROGATE(dec) (((dec) & 0xFC00) == 0xDC00) 35 | #define DECODE_SURROGATE_PAIR(hi, lo) ((((hi) & 0x3FF) << 10) + ((lo) & 0x3FF) + 0x10000) 36 | 37 | if (hi) { 38 | if (IS_LOW_SURROGATE(dec)) { 39 | u32dec = DECODE_SURROGATE_PAIR(hi, dec); 40 | *in_out_hi = 0; 41 | } else { 42 | if (out_error) 43 | *out_error = CHCK_UTF16_UNEXPECTED_HIGH; 44 | *in_out_hi = 0; 45 | return 0; 46 | } 47 | } else { 48 | if (IS_HIGH_SURROGATE(dec)) { 49 | *in_out_hi = dec; 50 | return 0; 51 | } else if (IS_LOW_SURROGATE(dec)) { 52 | if (out_error) 53 | *out_error = CHCK_UTF16_UNEXPECTED_LOW; 54 | return 0; 55 | } 56 | } 57 | 58 | #undef DECODE_SURROGATE_PAIR 59 | #undef IS_LOW_SURROGATE 60 | #undef IS_HIGH_SURROGATE 61 | 62 | return chck_utf32_encode(u32dec, out); 63 | } 64 | 65 | // Copyright (c) 2008-2009 Bjoern Hoehrmann 66 | // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. 67 | 68 | static const uint8_t utf8d[] = { 69 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f 70 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f 71 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f 72 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f 73 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f 74 | 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf 75 | 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df 76 | 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef 77 | 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff 78 | 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 79 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 80 | 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 81 | 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 82 | 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 83 | }; 84 | 85 | uint32_t 86 | chck_utf8_decode(uint32_t *state, uint32_t *codep, uint8_t byte) 87 | { 88 | assert(state && codep); 89 | const uint32_t type = utf8d[byte]; 90 | *codep = (*state != CHCK_UTF8_ACCEPT ? (byte & 0x3fu) | (*codep << 6) : (0xff >> type) & byte); 91 | *state = utf8d[256 + *state * 16 + type]; 92 | return *state; 93 | } 94 | 95 | uint8_t 96 | chck_utf8_mblen(const char u8[4]) 97 | { 98 | assert(u8); 99 | 100 | struct { 101 | uint8_t lo, hi, mb; 102 | } map[] = { 103 | { 0xC0, 0x80, 0 }, 104 | { 0x80, 0, 1 }, 105 | { 0xE0, 0xC0, 2 }, 106 | { 0xF0, 0xE0, 3 }, 107 | { 0xF8, 0xF0, 4 }, 108 | }; 109 | 110 | for (uint8_t i = 0; i < 5; ++i) 111 | if (!((u8[0] & map[i].lo) ^ map[i].hi)) 112 | return map[i].mb; 113 | 114 | return 0; 115 | } 116 | 117 | bool 118 | chck_utf8_validate(const char u8[4]) 119 | { 120 | assert(u8); 121 | 122 | const uint8_t mb = chck_utf8_mblen(u8); 123 | uint32_t state = CHCK_UTF8_ACCEPT, cp; 124 | for (uint8_t i = 0; i < mb; ++i) { 125 | if (chck_utf8_decode(&state, &cp, u8[i]) == CHCK_UTF8_REJECT) 126 | return false; 127 | } 128 | 129 | return (mb > 0); 130 | } 131 | 132 | uint32_t 133 | chck_utf8_codepoint(const char u8[4]) 134 | { 135 | assert(u8); 136 | 137 | const uint8_t mb = chck_utf8_mblen(u8); 138 | uint32_t state = CHCK_UTF8_ACCEPT, cp = u8[0]; 139 | for (uint8_t i = 0; i < mb; ++i) { 140 | if (chck_utf8_decode(&state, &cp, u8[i]) == CHCK_UTF8_REJECT) 141 | return CHCK_REPLACEMENT_CHAR; 142 | } 143 | 144 | return (mb > 0 ? cp : CHCK_REPLACEMENT_CHAR); 145 | } 146 | 147 | bool 148 | chck_utf8_strlen(const char *u8, size_t len, size_t *out_len) 149 | { 150 | assert(u8 && out_len); 151 | *out_len = 0; 152 | 153 | uint32_t state = CHCK_UTF8_ACCEPT, cp; 154 | for (size_t i = 0; i < len; ++i) { 155 | if (chck_utf8_decode(&state, &cp, u8[i]) == CHCK_UTF8_ACCEPT) 156 | ++*out_len; 157 | } 158 | 159 | return (state == CHCK_UTF8_ACCEPT); 160 | } 161 | -------------------------------------------------------------------------------- /chck/unicode/unicode.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_unicode_h__ 2 | #define __chck_unicode_h__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define CHCK_REPLACEMENT_CHAR 0xFFFD 10 | 11 | #define CHCK_UTF8_ACCEPT 0 12 | #define CHCK_UTF8_REJECT 1 13 | 14 | enum chck_utf16_error 15 | { 16 | CHCK_UTF16_OK, 17 | CHCK_UTF16_UNEXPECTED_HIGH, 18 | CHCK_UTF16_UNEXPECTED_LOW, 19 | }; 20 | 21 | uint8_t chck_utf32_encode(uint32_t dec, char out[4]); 22 | uint8_t chck_utf16_encode(uint16_t dec, char out[4], uint16_t *in_out_hi, enum chck_utf16_error *out_error); 23 | uint32_t chck_utf8_decode(uint32_t *state /* accept, reject */, uint32_t *codep, uint8_t byte); 24 | uint8_t chck_utf8_mblen(const char u8[4]); 25 | bool chck_utf8_validate(const char u8[4]); 26 | uint32_t chck_utf8_codepoint(const char u8[4]); 27 | bool chck_utf8_strlen(const char *u8, size_t len, size_t *out_len); 28 | 29 | #endif /* __chck_unicode_h__ */ 30 | -------------------------------------------------------------------------------- /chck/xdg/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Not supporting other platforms atm 2 | if (UNIX) 3 | 4 | add_library(chck_xdg xdg.c) 5 | install_libraries(chck_xdg) 6 | install_headers(xdg.h) 7 | 8 | if (CHCK_BUILD_TESTS) 9 | add_executable(xdg_test test.c) 10 | target_link_libraries(xdg_test PRIVATE chck_xdg) 11 | add_test_ex(xdg_test) 12 | endif () 13 | 14 | endif () 15 | -------------------------------------------------------------------------------- /chck/xdg/test.c: -------------------------------------------------------------------------------- 1 | #include "xdg.h" 2 | #include 3 | #include 4 | #include 5 | 6 | #undef NDEBUG 7 | #include 8 | 9 | int 10 | main(void) 11 | { 12 | // OUTPUT: current variables 13 | { 14 | char *ret; 15 | printf("%s\n", (ret = xdg_get_path("XDG_CONFIG_HOME", ".config"))); free(ret); 16 | printf("%s\n", (ret = xdg_get_path("XDG_DATA_HOME", ".local/share"))); free(ret); 17 | printf("%s\n", (ret = xdg_get_path("XDG_CACHE_HOME", ".cache"))); free(ret); 18 | printf("%s\n", (ret = xdg_get_path("XDG_THIS_DOES_NOT_EXIST", "but_mah_path_is_still_here"))); free(ret); 19 | 20 | const char *path; 21 | struct xdg_paths state = {0}; 22 | while ((path = xdg_get_paths("XDG_DATA_DIRS", "/usr/share:/foo/bar:asd:/the/relative/got/skipped:asd", &state, -1))) 23 | printf("=> %s\n", path); 24 | } 25 | 26 | // TEST: good set 27 | { 28 | const char *home = "/home/dir/.configs" ; 29 | setenv("XDG_CONFIG_HOME", home, 1); 30 | char *ret = xdg_get_path("XDG_CONFIG_HOME", "default_is_not_used"); 31 | assert(!strcmp(ret, home)); 32 | free(ret); 33 | } 34 | 35 | // TEST: default value 36 | { 37 | unsetenv("XDG_CONFIG_HOME"); 38 | char *ret = xdg_get_path("XDG_CONFIG_HOME", "default_path"); 39 | assert(strstr(ret, "default_path")); 40 | free(ret); 41 | } 42 | 43 | // TEST: non relative 44 | { 45 | const char *home = "\\o// $24$@£ DID I SET MY CONFIG_HOME RIGHT!?" ; 46 | setenv("XDG_CONFIG_HOME", home, 1); 47 | char *ret = xdg_get_path("XDG_CONFIG_HOME", "that_does_not_get_set"); 48 | assert(strcmp(ret, home)); 49 | free(ret); 50 | } 51 | 52 | // TEST: good paths 53 | { 54 | int i = 0; 55 | const char *paths[3] = { "/test", "/test2", "/relative/skipped" }; 56 | setenv("XDG_DATA_DIRS", "/test:/test2:relative:/relative/skipped:/ignored/by/max/iter/arg", 1); 57 | 58 | const char *path; 59 | struct xdg_paths state = {0}; 60 | while ((path = xdg_get_paths("XDG_DATA_DIRS", "/does:/not:/trigger", &state, 3))) { 61 | assert(i < 3 && !strcmp(path, paths[i])); 62 | ++i; 63 | } 64 | } 65 | 66 | // TEST: default paths 67 | { 68 | int i = 0; 69 | const char *paths[1] = { "/default" }; 70 | unsetenv("XDG_DATA_DIRS"); 71 | 72 | const char *path; 73 | struct xdg_paths state = {0}; 74 | while ((path = xdg_get_paths("XDG_DATA_DIRS", "/default:relative:skip/path", &state, 1))) { 75 | assert(i < 1 && !strcmp(path, paths[i])); 76 | ++i; 77 | } 78 | } 79 | 80 | return EXIT_SUCCESS; 81 | } 82 | -------------------------------------------------------------------------------- /chck/xdg/xdg.c: -------------------------------------------------------------------------------- 1 | #include "xdg.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static inline char* 11 | ccopy(const char *str) 12 | { 13 | assert(str); 14 | const size_t size = strlen(str); 15 | char *cpy = chck_calloc_add_of(size, 1); 16 | return (cpy ? memcpy(cpy, str, size) : NULL); 17 | } 18 | 19 | static inline char* 20 | strip_slash(char *str) 21 | { 22 | // if you get invalid read of size 4 here with valgrind 23 | // it means that you compiled with O2 or higher optimizations on gcc 24 | // gcc has builtin optimized strlen that reads bytes in aligned chunks 25 | 26 | size_t size; 27 | if (!str || !(size = strlen(str))) 28 | return NULL; 29 | 30 | for (char *s = str + size - 1; s >= str && *s == '/'; --s) 31 | *s = 0; 32 | 33 | return str; 34 | } 35 | 36 | static inline char* 37 | get_home(void) 38 | { 39 | const char *env; 40 | if ((env = getenv("HOME")) && env[0] == '/') 41 | return ccopy(env); 42 | 43 | struct passwd *pwd; 44 | if (!(pwd = getpwuid(getuid())) || !pwd->pw_dir || pwd->pw_dir[0] != '/') 45 | return NULL; 46 | 47 | return ccopy(pwd->pw_dir); 48 | } 49 | 50 | char* 51 | xdg_get_path(const char *xdg_env, const char *default_path) 52 | { 53 | assert(xdg_env && default_path && default_path[0] != '/'); 54 | 55 | if (!xdg_env || !default_path || default_path[0] == '/') 56 | return NULL; 57 | 58 | const char *xdg_dir; 59 | if ((xdg_dir = getenv(xdg_env)) && xdg_dir[0] == '/') 60 | return strip_slash(ccopy(xdg_dir)); 61 | 62 | char *home; 63 | if (!(home = get_home())) 64 | return NULL; /** fatal! */ 65 | 66 | const size_t len = snprintf(NULL, 0, "%s/%s", home, default_path) + 1; 67 | 68 | char *path; 69 | if (!(path = calloc(1, len))) { 70 | free(home); 71 | return NULL; /** fatal! */ 72 | } 73 | 74 | snprintf(path, len, "%s/%s", home, default_path); 75 | free(home); 76 | return path; 77 | } 78 | 79 | const char* 80 | xdg_get_paths(const char *xdg_env, const char *default_paths, struct xdg_paths *state, uint32_t max_iter) 81 | { 82 | assert(xdg_env && default_paths && state); 83 | 84 | if ((state->path && !*state->path) || state->iter == max_iter) { 85 | free((char*)state->paths); 86 | return NULL; 87 | } 88 | 89 | ++state->iter; 90 | 91 | if (!state->paths) { 92 | if (!xdg_env || !default_paths) 93 | return NULL; 94 | 95 | const char *paths; 96 | if (!(paths = getenv(xdg_env)) || !paths[0]) 97 | paths = default_paths; 98 | 99 | state->path = state->paths = ccopy(paths); 100 | } 101 | 102 | if (!state->path || !state->paths) 103 | return NULL; 104 | 105 | char *path; 106 | do { 107 | size_t f; 108 | path = (char*)state->path; 109 | if ((f = strcspn(state->path, ":")) > 0) { 110 | state->path += f + (path[f] ? 1 : 0); 111 | path[f] = 0; 112 | } 113 | 114 | if (!*path) { 115 | free((char*)state->paths); 116 | return NULL; 117 | } 118 | } while (path[0] != '/'); 119 | 120 | return strip_slash(path); 121 | } 122 | -------------------------------------------------------------------------------- /chck/xdg/xdg.h: -------------------------------------------------------------------------------- 1 | #ifndef __chck_xdg__ 2 | #define __chck_xdg__ 3 | 4 | #include 5 | #include 6 | 7 | struct xdg_paths { 8 | const char *path; 9 | const char *paths; 10 | uint32_t iter; 11 | }; 12 | 13 | char* xdg_get_path(const char *xdg_env, const char *default_path); 14 | const char* xdg_get_paths(const char *xdg_env, const char *default_paths, struct xdg_paths *state, uint32_t max_iter); 15 | 16 | #endif /* __chck_xdg__ */ 17 | -------------------------------------------------------------------------------- /doxygen/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Doxygen) 2 | set_package_properties(Doxygen PROPERTIES TYPE PURPOSE "Enables documentation") 3 | 4 | if (DOXYGEN_FOUND) 5 | configure_file(Doxyfile.in Doxyfile @ONLY) 6 | add_custom_target(doxygen 7 | ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile 8 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 9 | COMMENT "Generating chck documentation with Doxygen" VERBATIM) 10 | message("-!- Use 'make doxygen' to generate documentation") 11 | endif (DOXYGEN_FOUND) 12 | --------------------------------------------------------------------------------