├── .github └── workflows │ └── cmake.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── conanfile.txt ├── perf_tests ├── CMakeLists.txt └── perf_test_ghashtable.c ├── src ├── garray.h ├── ghashtable.h ├── glist.h └── gstring.h └── tests ├── CMakeLists.txt ├── test_garray.c ├── test_ghashtable.c ├── test_glist.c ├── test_gstring.c └── test_integration.c /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | name: ${{ matrix.config.name }} 12 | runs-on: ${{ matrix.config.os }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | config: 17 | - { 18 | name: "Ubuntu", 19 | os: ubuntu-latest, 20 | build_type: asan 21 | } 22 | - { 23 | name: "macOS", 24 | os: macos-latest, 25 | build_type: asan 26 | } 27 | - { 28 | name: "Windows", 29 | os: windows-latest, 30 | build_type: Debug 31 | } 32 | 33 | steps: 34 | - uses: actions/checkout@v2 35 | 36 | - name: Print env 37 | run: | 38 | echo github.event.action: ${{ github.event.action }} 39 | echo github.event_name: ${{ github.event_name }} 40 | 41 | - name: Install dependencies 42 | run: pip3 install conan==1.59 43 | 44 | - name: Create build directory on Unix 45 | if: ${{ !startsWith(matrix.config.name, 'Windows') }} 46 | run: mkdir build 47 | 48 | - name: Create build directory on Windows 49 | if: startsWith(matrix.config.name, 'Windows') 50 | run: md build 51 | 52 | - name: Install Conan dependencies 53 | working-directory: ./build 54 | run: conan install .. --build=missing 55 | 56 | - name: Configure CMake Unix 57 | if: ${{ !startsWith(matrix.config.name, 'Windows') }} 58 | working-directory: ./build 59 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 60 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 61 | run: cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} 62 | 63 | - name: Configure CMake Windows 64 | if: startsWith(matrix.config.name, 'Windows') 65 | working-directory: ./build 66 | run: cmake .. -G "Visual Studio 17" -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} 67 | 68 | - name: Build on Unix 69 | if: ${{ !startsWith(matrix.config.name, 'Windows') }} 70 | working-directory: ./build 71 | run: make 72 | 73 | - name: Build on Windows 74 | if: startsWith(matrix.config.name, 'Windows') 75 | working-directory: ./build 76 | run: cmake --build . --config Debug 77 | 78 | - name: Test 79 | working-directory: ./build 80 | # Execute tests defined by the CMake configuration. 81 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 82 | run: ctest -C ${{ matrix.config.build_type }} --output-on-failure 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | build/ 3 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | if ("${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") 4 | message(FATAL_ERROR "In-source builds are disabled. 5 | Please create a subfolder and use `cmake ..` inside it. 6 | NOTE: cmake will now create CMakeCache.txt and CMakeFiles/*. 7 | You must delete them, or cmake will refuse to work.") 8 | endif() 9 | 10 | project(clib C) 11 | 12 | set(C_STANDARD 99) 13 | set(C_STANDARD_REQUIRED ON) 14 | 15 | if(MSVC) 16 | add_compile_options(/W3) 17 | else() 18 | add_compile_options(-Wall) 19 | endif() 20 | 21 | message(STATUS "Binary dir: ${CMAKE_BINARY_DIR}") 22 | set(CMAKE_MODULE_PATH ${CMAKE_BINARY_DIR}) 23 | 24 | if(NOT CMAKE_BUILD_TYPE) 25 | set(CMAKE_BUILD_TYPE "Debug") 26 | endif() 27 | 28 | if(MSVC) 29 | if(NOT "Asan" IN_LIST CMAKE_CONFIGURATION_TYPES) 30 | list(APPEND CMAKE_CONFIGURATION_TYPES Asan) 31 | endif() 32 | 33 | set(CMAKE_EXE_LINKER_FLAGS_ASAN "/DEBUG") 34 | add_compile_options("$<$:/DEBUG /fsanitize=address>") 35 | add_link_options("$<$:/NODEFAULTLIB:MSVCRT>") 36 | else() 37 | # Support for sanitizer build types 38 | set(CMAKE_C_FLAGS_ASAN 39 | "-fsanitize=address -fno-optimize-sibling-calls -fsanitize-address-use-after-scope -fno-omit-frame-pointer -g -O1" 40 | CACHE STRING "Flags used by the C compiler during AddressSanitizer builds." 41 | FORCE) 42 | 43 | set(CMAKE_C_FLAGS_LSAN 44 | "-fsanitize=leak -fno-omit-frame-pointer -g -O1" 45 | CACHE STRING "Flags used by the C compiler during LeakSanitizer builds." 46 | FORCE) 47 | 48 | set(CMAKE_C_FLAGS_MSAN 49 | "-fsanitize=memory -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer -g -O2" 50 | CACHE STRING "Flags used by the C compiler during MemorySanitizer builds." 51 | FORCE) 52 | 53 | set(CMAKE_C_FLAGS_UBSAN 54 | "-fsanitize=undefined" 55 | CACHE STRING "Flags used by the C compiler during UndefinedBehaviourSanitizer builds." 56 | FORCE) 57 | endif() 58 | 59 | find_package(check REQUIRED) 60 | enable_testing() 61 | 62 | add_subdirectory(tests) 63 | add_subdirectory(perf_tests) 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Andreas Heck 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files 5 | (the "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included 12 | in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CLib 2 | 3 | CLib is a header-only library for C99 that implements the most important classes 4 | from GLib: **GArray**, **GHashTable**, **GList** and **GString**. 5 | 6 | GLib is a great library that provides some classes that are so useful that you 7 | want to use them in every C project. For some of them you might even wish they 8 | were part of the C standard libraray. 9 | 10 | Unfortunately, GLib is a bit too heavy for many projects. An additional problem 11 | that prevents its wide-spread use is its license: Many people don't want to use 12 | LGPL licensed code or want to link everything statically which is in many cases 13 | prevented by the LGPL. 14 | 15 | Therefore CLib implements the APIs of the most important classes as header-only 16 | libs under the MIT license. 17 | 18 | ## Usage 19 | 20 | Pick the header files of the classes you want to use from *src/*, copy them to 21 | a location in your project where header files are found by the compiler, and 22 | include them in your C project files. 23 | 24 | When you include a class for the first time in a compilation unit you need to 25 | define *_CLIB_IMPL* to tell the preprocessor that you want the declarations 26 | AND the implementations of the functions: 27 | 28 | ```c 29 | #define _CLIB_IMPL 1 30 | #include 31 | ``` 32 | 33 | Whenever you use the same library again in the same compilation unit you only 34 | need to include the header file: 35 | 36 | ```c 37 | #include 38 | ``` 39 | 40 | ## Documentation 41 | 42 | Since clib aims to be as API compatible as possible with GLib it is best to look 43 | up the respective class in the GLib documentation: 44 | 45 | [GArray](https://libsoup.org/glib/glib-Arrays.html) 46 | 47 | [GHashTable](https://libsoup.org/glib/glib-Hash-Tables.html) 48 | 49 | [GList](https://libsoup.org/glib/glib-Doubly-Linked-Lists.html) 50 | 51 | [GString](https://libsoup.org/glib/glib-Strings.html) 52 | 53 | Most methods are implemented. Missing methods and different behaviour are 54 | considered bugs. 55 | 56 | Pull requests are welcome! 57 | 58 | ## Out of Memory Errors 59 | 60 | This library handles out of memory errors by printing an error message to 61 | *stderr* and terminating the program via a call to *exit(1)*. This behaviour is 62 | controversial and depending on your use case it might prevent you from using 63 | clib. Unfortunately, this is how GLib handles out of memory errors and since 64 | a goal of this lib is to be compatible with GLib we have to handle it the same 65 | way. 66 | 67 | For most use cases (like the ones for which people use GLib) this shouldn't be a 68 | big problem, though. 69 | 70 | ## Building the Tests 71 | 72 | Since this library is header-only the only purpose of the build system is to 73 | build the tests. 74 | 75 | The tests can be built with the following commands on Linux and macOS: 76 | 77 | ```bash 78 | mkdir build 79 | cd build 80 | conan install .. --build=missing 81 | cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Debug 82 | make 83 | ``` 84 | 85 | And on Windows with Visual Studio 2022: 86 | 87 | ```bash 88 | md build 89 | cd build 90 | conan install .. --build=missing 91 | cmake .. -G "Visual Studio 17" 92 | cmake --build . --config Debug 93 | ``` 94 | 95 | You can create a release build on Linux and macOS with: 96 | 97 | ```bash 98 | cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release 99 | make 100 | ``` 101 | 102 | And on Windows with: 103 | 104 | ```bash 105 | cmake --build . --config Release 106 | ``` 107 | 108 | The following build types are supported (only on UNIX): 109 | 110 | |Name | Description | 111 | |---|---| 112 | | Debug | Build with debug info but without a sanitizer | 113 | | Release | Build with full optimization and without debug support | 114 | | asan | Build with debug and address sanitizer | 115 | | lsan | Build with debug and leak sanitizer | 116 | | msan | Build with debug and memory sanitizer | 117 | | ubsan | Build with debug and undefined behaviour sanitizer | 118 | 119 | For most development purposes it is best to create an address sanitizer build: 120 | 121 | ```bash 122 | cmake .. -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=asan 123 | ``` 124 | 125 | ### Asan on Windows 126 | 127 | MSVC supports only Asan at the moment. To build with it use the following command: 128 | 129 | ```bash 130 | cmake --build . --config Asan 131 | ``` 132 | -------------------------------------------------------------------------------- /conanfile.txt: -------------------------------------------------------------------------------- 1 | [requires] 2 | libcheck/0.15.2 3 | 4 | [generators] 5 | cmake_find_package 6 | cmake_paths 7 | 8 | [options] 9 | libcheck:with_subunit=False -------------------------------------------------------------------------------- /perf_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(CLIB_SRC_DIR "../src") 4 | 5 | add_executable(perf_test_ghashtable perf_test_ghashtable.c) 6 | target_include_directories(perf_test_ghashtable PRIVATE ${CLIB_SRC_DIR}) 7 | -------------------------------------------------------------------------------- /perf_tests/perf_test_ghashtable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define _CLIB_IMPL 1 5 | #include "ghashtable.h" 6 | 7 | #define CALC_SECONDS(start, end) (double) (end - start) / CLOCKS_PER_SEC 8 | 9 | void measure_insert(uint32_t num_inserts) 10 | { 11 | GHashTable *htable = g_hash_table_new(g_int_hash, g_int_equal); 12 | 13 | int start_time = clock(); 14 | 15 | for (uint32_t i = 0; i < num_inserts; i++) { 16 | g_hash_table_insert(htable, (void*) (uint64_t) i, (void*) "Hello World"); 17 | } 18 | 19 | int end_time = clock(); 20 | g_hash_table_destroy(htable); 21 | 22 | printf("Inserting %-7d elements: %fs\n", num_inserts, CALC_SECONDS(start_time, end_time)); 23 | } 24 | 25 | void measure_lookup(uint32_t num_lookups) 26 | { 27 | GHashTable *htable = g_hash_table_new(g_int_hash, g_int_equal); 28 | 29 | for (uint32_t i = 0; i < num_lookups; i++) { 30 | g_hash_table_insert(htable, (void*) (uint64_t) i, (void*) "Hello World"); 31 | } 32 | 33 | int start_time = clock(); 34 | 35 | for (uint32_t i = 0; i < num_lookups; i++) { 36 | g_hash_table_lookup(htable, (void*) (uint64_t) i); 37 | } 38 | 39 | int end_time = clock(); 40 | g_hash_table_destroy(htable); 41 | 42 | printf("Looking up %-7d elements: %fs\n", num_lookups, CALC_SECONDS(start_time, end_time)); 43 | } 44 | 45 | void perf_test_insert() 46 | { 47 | printf("= perf_test_insert =\n\n"); 48 | 49 | measure_insert(10); 50 | measure_insert(100); 51 | measure_insert(1000); 52 | measure_insert(10000); 53 | measure_insert(100000); 54 | measure_insert(1000000); 55 | } 56 | 57 | void perf_test_lookup() 58 | { 59 | printf("= perf_test_lookup =\n\n"); 60 | 61 | measure_lookup(10); 62 | measure_lookup(100); 63 | measure_lookup(1000); 64 | measure_lookup(10000); 65 | measure_lookup(100000); 66 | measure_lookup(1000000); 67 | } 68 | 69 | int main(int argc, char **argv) 70 | { 71 | perf_test_insert(); 72 | printf("\n\n"); 73 | perf_test_lookup(); 74 | 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /src/garray.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GArray 3 | * 4 | * Copyright (c) 2023 Andreas Heck 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef _GARRAY_H 27 | #define _GARRAY_H 28 | 29 | // We would need to define _GNU_SOURCE for stdlib.h to declare qsort_r. Since we 30 | // are header-only we better declare it ourselves to make sure we don't define 31 | // _GNU_SOURCE when we shouldn't 32 | #ifdef __linux__ 33 | void qsort_r(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *arg); 34 | #endif 35 | 36 | // Needed for qsort_s 37 | #if (defined _WIN32 || defined _WIN64 || defined __WINDOWS__) 38 | #include 39 | #endif 40 | 41 | #include 42 | #include 43 | #include 44 | #include 45 | 46 | typedef int(*GCompareFunc) (const void *a, const void *b); 47 | typedef int(*GCompareDataFunc) (const void *a, const void *b, void *user_data); 48 | typedef void (*GDestroyNotify)(void *data); 49 | 50 | typedef struct GArray { 51 | char *data; 52 | unsigned int len; 53 | unsigned int _allocated_elements; 54 | bool _zero_terminated; 55 | bool _clear; 56 | unsigned int _element_size; 57 | GDestroyNotify _clear_func; 58 | } GArray; 59 | 60 | GArray* g_array_new(bool zero_terminated, bool clear, unsigned int element_size); 61 | void* g_array_steal(GArray *array, size_t *len); 62 | GArray* g_array_sized_new(bool zero_terminated, bool clear, unsigned int element_size, unsigned int reserved_size); 63 | GArray* g_array_copy(GArray *array); 64 | unsigned int g_array_get_element_size(GArray *array); 65 | #define g_array_append_val(a, v) g_array_append_vals(a, &v, 1); 66 | GArray* g_array_append_vals (GArray *array, const void *data, unsigned int len); 67 | #define g_array_prepend_val(a, v) g_array_prepend_vals(a, &v, 1); 68 | GArray* g_array_prepend_vals(GArray *array, const void *data, unsigned int len); 69 | #define g_array_insert_val(a, i, v) g_array_insert_vals(a, i, &v, 1); 70 | GArray* g_array_insert_vals (GArray *array, unsigned int index, const void *data, unsigned int len); 71 | GArray* g_array_remove_index(GArray *array, unsigned int index); 72 | GArray* g_array_remove_index_fast(GArray *array, unsigned int index); 73 | GArray* g_array_remove_range(GArray *array, unsigned int index, unsigned int length); 74 | void g_array_sort(GArray *array, GCompareFunc compare_func); 75 | void g_array_sort_with_data(GArray *array, GCompareDataFunc compare_func, void *user_data); 76 | bool g_array_binary_search(GArray *array, const void *target, GCompareFunc compare_func, unsigned int *out_match_index); 77 | #define g_array_index(a, t, i) (t) a->data[i * a->_element_size] 78 | GArray* g_array_set_size(GArray *array, unsigned int length); 79 | void g_array_set_clear_func(GArray *array, GDestroyNotify clear_func); 80 | 81 | char* g_array_free(GArray *array, bool free_segment); 82 | 83 | #ifdef _CLIB_IMPL 84 | struct _garray_qsort_r_data 85 | { 86 | void *user_data; 87 | int (*compare_func)(const void *a, const void *b, void *user_data); 88 | }; 89 | 90 | int _garray_qsort_r_arg_swap(void *wrapper_arg, const void *a, const void *b) 91 | { 92 | struct _garray_qsort_r_data *wrapper = (struct _garray_qsort_r_data*) wrapper_arg; 93 | return (wrapper->compare_func)(a, b, wrapper->user_data); 94 | } 95 | 96 | void _g_array_zero_terminate(GArray *array) 97 | { 98 | memset(&array->data[array->len * array->_element_size], 0, array->_element_size); 99 | } 100 | 101 | void _g_array_resize_if_needed(GArray *array, unsigned int new_elements) 102 | { 103 | unsigned int new_len = array->len + new_elements; 104 | unsigned int needed = new_len; 105 | 106 | if (array->_zero_terminated) { 107 | needed++; 108 | } 109 | 110 | if (needed <= array->_allocated_elements) { 111 | return; 112 | } 113 | 114 | array->data = realloc(array->data, needed * array->_element_size); 115 | if (array->data == NULL) { 116 | fprintf(stderr, "FATAL ERROR: _g_array_resize_if_needed: Out of memory"); 117 | exit(1); 118 | } 119 | 120 | if (array->_clear) { 121 | memset(&array->data[array->_allocated_elements * array->_element_size], 0, 122 | (needed - array->_allocated_elements) * array->_element_size); 123 | } 124 | 125 | array->_allocated_elements = needed; 126 | } 127 | 128 | GArray* g_array_new(bool zero_terminated, bool clear, unsigned int element_size) 129 | { 130 | return g_array_sized_new(zero_terminated, clear, element_size, 0); 131 | } 132 | 133 | void* g_array_steal(GArray *array, size_t *len) 134 | { 135 | void *data; 136 | 137 | *len = array->len; 138 | 139 | data = array->data; 140 | array->data = NULL; 141 | array->len = 0; 142 | array->_allocated_elements = 0; 143 | 144 | if (array->_zero_terminated) { 145 | if (array->_clear) { 146 | array->data = calloc(array->_element_size, 1); 147 | } else { 148 | array->data = malloc(array->_element_size); 149 | } 150 | 151 | if (array->data == NULL) { 152 | fprintf(stderr, "FATAL ERROR: g_array_steal: Out of memory"); 153 | exit(1); 154 | } 155 | 156 | array->_allocated_elements = 1; 157 | 158 | _g_array_zero_terminate(array); 159 | } 160 | 161 | return data; 162 | } 163 | 164 | GArray* g_array_sized_new(bool zero_terminated, bool clear, unsigned int element_size, unsigned int reserved_size) 165 | { 166 | GArray *array; 167 | 168 | array = malloc(sizeof(GArray)); 169 | if (array == NULL) { 170 | fprintf(stderr, "FATAL ERROR: g_array_sized_new: Out of memory"); 171 | exit(1); 172 | } 173 | 174 | array->data = NULL; 175 | array->len = 0; 176 | array->_allocated_elements = reserved_size; 177 | array->_zero_terminated = zero_terminated; 178 | array->_clear = clear; 179 | array->_element_size = element_size; 180 | array->_clear_func = NULL; 181 | 182 | if (zero_terminated) { 183 | array->_allocated_elements++; 184 | } 185 | 186 | if (array->_allocated_elements == 0) { 187 | return array; 188 | } 189 | 190 | if (clear) { 191 | array->data = calloc(array->_allocated_elements * array->_element_size, 1); 192 | } else { 193 | array->data = malloc(array->_allocated_elements * array->_element_size); 194 | } 195 | 196 | if (array->data == NULL) { 197 | fprintf(stderr, "FATAL ERROR: g_array_sized_new: Out of memory"); 198 | exit(1); 199 | } 200 | 201 | if (!clear && zero_terminated) { 202 | _g_array_zero_terminate(array); 203 | } 204 | 205 | return array; 206 | } 207 | 208 | GArray* g_array_copy(GArray *array) 209 | { 210 | GArray *copy; 211 | 212 | if (array == NULL) { 213 | return NULL; 214 | } 215 | 216 | copy = malloc(sizeof(GArray)); 217 | if (copy == NULL) { 218 | fprintf(stderr, "FATAL ERROR: g_array_copy: Out of memory"); 219 | exit(1); 220 | } 221 | 222 | memcpy(copy, array, sizeof(GArray)); 223 | 224 | copy->data = malloc(copy->_allocated_elements * copy->_element_size); 225 | if (copy->data == NULL) { 226 | fprintf(stderr, "FATAL ERROR: g_array_copy: Out of memory"); 227 | exit(1); 228 | } 229 | 230 | memcpy(copy->data, array->data, copy->_allocated_elements); 231 | 232 | return copy; 233 | } 234 | 235 | unsigned int g_array_get_element_size(GArray *array) 236 | { 237 | return array->_element_size; 238 | } 239 | 240 | GArray* g_array_append_vals(GArray *array, const void *data, unsigned int len) 241 | { 242 | _g_array_resize_if_needed(array, len); 243 | 244 | memcpy(&array->data[array->len * array->_element_size], data, len * array->_element_size); 245 | array->len += len; 246 | if (array->_zero_terminated) { 247 | _g_array_zero_terminate(array); 248 | } 249 | 250 | return array; 251 | } 252 | 253 | GArray* g_array_prepend_vals(GArray *array, const void *data, unsigned int len) 254 | { 255 | _g_array_resize_if_needed(array, len); 256 | 257 | memmove(&array->data[len * array->_element_size], &array->data[0], array->len * array->_element_size); 258 | memcpy(array->data, data, len * array->_element_size); 259 | array->len += len; 260 | if (array->_zero_terminated) { 261 | _g_array_zero_terminate(array); 262 | } 263 | 264 | return array; 265 | } 266 | 267 | GArray* g_array_insert_vals(GArray *array, unsigned int index, const void *data, unsigned int len) 268 | { 269 | unsigned int needed = len; 270 | 271 | // we need to allocate extra bytes if the index to insert at is outside of 272 | // the array 273 | if (index > (array->len - 1)) { 274 | needed += index - array->len; 275 | } 276 | 277 | _g_array_resize_if_needed(array, needed); 278 | 279 | if (index < array->len) { 280 | memmove(&array->data[(index + len) * array->_element_size], &array->data[index * array->_element_size], (array->len - index) * array->_element_size); 281 | } 282 | memcpy(&array->data[index * array->_element_size], data, len * array->_element_size); 283 | array->len += needed; 284 | 285 | if (array->_zero_terminated) { 286 | _g_array_zero_terminate(array); 287 | } 288 | 289 | return array; 290 | } 291 | 292 | GArray* g_array_remove_index(GArray *array, unsigned int index) 293 | { 294 | if (array->_clear_func) { 295 | array->_clear_func(&array->data[index]); 296 | } 297 | 298 | // do we need to move other elements back? 299 | if (array->len > 1 && index < (array->len - 1)) { 300 | memmove(&array->data[index * array->_element_size], &array->data[(index + 1) * array->_element_size], (array->len - index - 1) * array->_element_size); 301 | } 302 | 303 | array->len--; 304 | 305 | if (array->_zero_terminated) { 306 | _g_array_zero_terminate(array); 307 | } 308 | 309 | return array; 310 | } 311 | 312 | GArray* g_array_remove_index_fast(GArray *array, unsigned int index) 313 | { 314 | if (array->_clear_func) { 315 | array->_clear_func(&array->data[index]); 316 | } 317 | 318 | if (array->len > 1 && index < (array->len - 1)) { 319 | memcpy(&array->data[index * array->_element_size], &array->data[(array->len - 1) * array->_element_size], array->_element_size); 320 | } 321 | 322 | array->len--; 323 | 324 | if (array->_zero_terminated) { 325 | _g_array_zero_terminate(array); 326 | } 327 | 328 | return array; 329 | } 330 | 331 | GArray* g_array_remove_range(GArray *array, unsigned int index, unsigned int length) 332 | { 333 | if (array->_clear_func) { 334 | for (int i = index; i < (index + length - 1); i++) { 335 | array->_clear_func(&array->data[i]); 336 | } 337 | } 338 | 339 | // do we need to move other elements back? 340 | if (array->len > 1 && index < (array->len - length)) { 341 | memmove(&array->data[index * array->_element_size], &array->data[(index + length) * array->_element_size], (array->len - index - length) * array->_element_size); 342 | } 343 | 344 | array->len -= length; 345 | 346 | if (array->_zero_terminated) { 347 | _g_array_zero_terminate(array); 348 | } 349 | 350 | return array; 351 | } 352 | 353 | void g_array_sort(GArray *array, GCompareFunc compare_func) 354 | { 355 | qsort(array->data, array->len, array->_element_size, compare_func); 356 | } 357 | 358 | void g_array_sort_with_data(GArray *array, GCompareDataFunc compare_func, void *user_data) 359 | { 360 | #if (defined __linux__) 361 | qsort_r(array->data, array->len, array->_element_size, compare_func, user_data); 362 | #elif (defined _WIN32 || defined _WIN64 || defined __WINDOWS__) 363 | struct _garray_qsort_r_data tmp; 364 | tmp.user_data = user_data; 365 | tmp.compare_func = compare_func; 366 | qsort_s(array->data, array->len, array->_element_size, &_garray_qsort_r_arg_swap, &tmp); 367 | #else 368 | // BSD / macOS 369 | struct _garray_qsort_r_data tmp; 370 | tmp.user_data = user_data; 371 | tmp.compare_func = compare_func; 372 | qsort_r(array->data, array->len, array->_element_size, &tmp, &_garray_qsort_r_arg_swap); 373 | #endif 374 | } 375 | 376 | bool g_array_binary_search(GArray *array, const void *target, GCompareFunc compare_func, unsigned int *out_match_index) 377 | { 378 | if (array == NULL) { 379 | return false; 380 | } 381 | 382 | if (target == NULL) { 383 | return false; 384 | } 385 | 386 | if (array->len <= 0) { 387 | return false; 388 | } 389 | 390 | int l = 0; 391 | int r = array->len - 1; 392 | unsigned int m = 0; 393 | 394 | while (l <= r) { 395 | m = (r - l) / 2 + l; // (l + r) / 2 but prevent overflow 396 | 397 | if (compare_func(&array->data[m * array->_element_size], target) < 0) { 398 | l = m + 1; 399 | } else if (compare_func(&array->data[m * array->_element_size], target) > 0) { 400 | r = m - 1; 401 | } else { 402 | // ensure we always return the left-most element 403 | while (m > 0) { 404 | if (compare_func(&array->data[m * array->_element_size], &array->data[(m - 1) * array->_element_size]) != 0) { 405 | break; 406 | } 407 | m--; 408 | } 409 | 410 | if (out_match_index) { 411 | *out_match_index = m; 412 | } 413 | 414 | return true; 415 | } 416 | } 417 | 418 | return false; 419 | } 420 | 421 | GArray* g_array_set_size(GArray *array, unsigned int length) 422 | { 423 | if (length <= array->len) { 424 | if (array->_clear_func && array->len > length) { 425 | // call clear func on all elements that are going to be removed 426 | int start_index = (array->len - length) - 1; 427 | for (int i = start_index; i < array->len; i++) { 428 | array->_clear_func(&array->data[i]); 429 | } 430 | } 431 | 432 | array->len = length; 433 | 434 | if (array->_zero_terminated) { 435 | _g_array_zero_terminate(array); 436 | } 437 | 438 | return array; 439 | } 440 | 441 | _g_array_resize_if_needed(array, length - array->len); 442 | array->len = length; 443 | if (array->_zero_terminated) { 444 | _g_array_zero_terminate(array); 445 | } 446 | 447 | return array; 448 | } 449 | 450 | void g_array_set_clear_func(GArray *array, GDestroyNotify clear_func) 451 | { 452 | array->_clear_func = clear_func; 453 | } 454 | 455 | char* g_array_free(GArray *array, bool free_segment) 456 | { 457 | char *data; 458 | 459 | if (array == NULL) { 460 | return NULL; 461 | } 462 | 463 | if (free_segment == false) { 464 | data = array->data; 465 | free(array); 466 | return data; 467 | } 468 | 469 | if (array->_clear_func != NULL) { 470 | for (int i = 0; i < array->len; i++) { 471 | array->_clear_func(&array->data[i]); 472 | } 473 | } 474 | 475 | free(array->data); 476 | free(array); 477 | 478 | return NULL; 479 | } 480 | 481 | #endif 482 | #endif 483 | -------------------------------------------------------------------------------- /src/ghashtable.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GHashTable 3 | * 4 | * Copyright (c) 2022 Andreas Heck 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef _GHASHTABLE_H 27 | #define _GHASHTABLE_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #define GHASHTABLE_MIN_SLOTS 64 35 | #define GHASHTABLE_MAX_LOAD 0.5 36 | 37 | typedef uint32_t (*GHashFunc)(void *key); 38 | typedef bool (*GEqualFunc)(void *a, void *b); 39 | typedef void (*GDestroyNotify)(void *data); 40 | typedef void (*GHFunc) (void *key, void *value, void *user_data); 41 | typedef bool (*GHRFunc) (void *key, void *value, void *user_data); 42 | 43 | struct GHashTableSlot { 44 | void *key; 45 | void *value; 46 | bool used : 1; 47 | bool deleted : 1; 48 | }; 49 | 50 | typedef struct GHashTable { 51 | uint32_t num_slots; 52 | uint32_t num_used; 53 | uint32_t resize_threshold; 54 | GHashFunc hash_func; 55 | GEqualFunc key_equal_func; 56 | GDestroyNotify key_destroy_func; 57 | GDestroyNotify value_destroy_func; 58 | struct GHashTableSlot *slots; 59 | } GHashTable; 60 | 61 | uint32_t g_int_hash(void *v); 62 | bool g_int_equal(void *v1, void *v2); 63 | uint32_t g_str_hash(void *v); 64 | bool g_str_equal(void *v1, void *v2); 65 | GHashTable *g_hash_table_new(GHashFunc hash_func, GEqualFunc key_equal_func); 66 | GHashTable *g_hash_table_new_full(GHashFunc hash_func, GEqualFunc key_equal_func, GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func); 67 | void g_hash_table_insert(GHashTable *hash_table, void *key, void *value); 68 | uint32_t g_hash_table_size(GHashTable *hash_table); 69 | void* g_hash_table_lookup(GHashTable *hash_table, void *key); 70 | void g_hash_table_foreach(GHashTable *hash_table, GHFunc func, void *user_data); 71 | bool g_hash_table_remove(GHashTable *hash_table, void *key); 72 | void g_hash_table_destroy(GHashTable *hash_table); 73 | 74 | 75 | #ifdef _CLIB_IMPL 76 | 77 | uint32_t g_int_hash(void *v) 78 | { 79 | uint32_t x = (uint32_t) (uint64_t) v; // cast to uint64_t to omit warning 80 | 81 | x = ((x >> 16) ^ x) * 0x45d9f3b; 82 | x = ((x >> 16) ^ x) * 0x45d9f3b; 83 | x = (x >> 16) ^ x; 84 | 85 | return x; 86 | } 87 | 88 | bool g_int_equal(void *v1, void *v2) 89 | { 90 | return (uint32_t*) v1 == (uint32_t*) v2; 91 | } 92 | 93 | uint32_t g_str_hash(void *v) 94 | { 95 | // djb2 hash function 96 | uint32_t hash = 5381; 97 | int c; 98 | unsigned char *str = (unsigned char*) v; 99 | 100 | while ((c = *str++)) { 101 | hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ 102 | } 103 | 104 | return hash; 105 | } 106 | 107 | bool g_str_equal(void *v1, void *v2) 108 | { 109 | return strcmp((char*) v1, (char*) v2) == 0; 110 | } 111 | 112 | GHashTable *g_hash_table_new(GHashFunc hash_func, GEqualFunc key_equal_func) 113 | { 114 | if (hash_func == NULL) { 115 | return NULL; 116 | } 117 | 118 | if (key_equal_func == NULL) { 119 | return NULL; 120 | } 121 | 122 | GHashTable *hash_table = (GHashTable*) malloc(sizeof(GHashTable)); 123 | if (hash_table == NULL) { 124 | fprintf(stderr, "FATAL ERROR: g_hash_table_new: Out of memory"); 125 | exit(1); 126 | } 127 | 128 | hash_table->num_slots = GHASHTABLE_MIN_SLOTS; 129 | hash_table->num_used = 0; 130 | hash_table->resize_threshold = (uint32_t) (hash_table->num_slots * GHASHTABLE_MAX_LOAD); 131 | hash_table->hash_func = hash_func; 132 | hash_table->key_equal_func = key_equal_func; 133 | hash_table->key_destroy_func = NULL; 134 | hash_table->value_destroy_func = NULL; 135 | 136 | size_t buf_size = hash_table->num_slots * sizeof(struct GHashTableSlot); 137 | hash_table->slots = malloc(buf_size); 138 | if (hash_table->slots == NULL) { 139 | fprintf(stderr, "FATAL ERROR: g_hash_table_new: Out of memory"); 140 | exit(1); 141 | } 142 | 143 | memset(hash_table->slots, 0, buf_size); 144 | 145 | return hash_table; 146 | } 147 | 148 | GHashTable *g_hash_table_new_full(GHashFunc hash_func, GEqualFunc key_equal_func, GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func) 149 | { 150 | GHashTable *hash_table = g_hash_table_new(hash_func, key_equal_func); 151 | 152 | if (hash_table == NULL) { 153 | return NULL; 154 | } 155 | 156 | hash_table->key_destroy_func = key_destroy_func; 157 | hash_table->value_destroy_func = value_destroy_func; 158 | 159 | return hash_table; 160 | } 161 | 162 | uint32_t _g_hash_table_find_free_slot(GHashTable *hash_table, uint32_t start_slot, void *key) 163 | { 164 | for (uint32_t i = start_slot; i < hash_table->num_slots; i++) { 165 | if (hash_table->slots[i].used == false) { 166 | return i; 167 | } 168 | 169 | if (key && hash_table->key_equal_func(hash_table->slots[i].key, key)) { 170 | return i; 171 | } 172 | } 173 | 174 | for (uint32_t i = 0; i < start_slot; i++) { 175 | if (hash_table->slots[i].used == false) { 176 | return i; 177 | } 178 | 179 | if (key && hash_table->key_equal_func(hash_table->slots[i].key, key)) { 180 | return i; 181 | } 182 | } 183 | 184 | // this should never happen 185 | fprintf(stderr, "BUG: _g_hash_table_find_free_slot: Failed to find an empty slot."); 186 | abort(); 187 | 188 | return 0; 189 | } 190 | 191 | uint32_t _g_hash_table_calc_start_slot(GHashTable *hash_table, void *key) 192 | { 193 | return hash_table->hash_func(key) % hash_table->num_slots; 194 | } 195 | 196 | uint32_t _g_hash_table_find_slot_by_key(GHashTable *hash_table, void *key, uint32_t start_slot, bool *ret_found) 197 | { 198 | for (uint32_t i = start_slot; i < hash_table->num_slots; i++) { 199 | if (hash_table->slots[i].used == false && hash_table->slots[i].deleted == false) { 200 | *ret_found = false; 201 | return 0; 202 | } 203 | 204 | if (hash_table->key_equal_func(key, hash_table->slots[i].key)) { 205 | *ret_found = true; 206 | return i; 207 | } 208 | } 209 | 210 | for (uint32_t i = 0; i < start_slot; i++) { 211 | if (hash_table->slots[i].used == false) { 212 | *ret_found = false; 213 | return 0; 214 | } 215 | 216 | if (hash_table->key_equal_func(key, hash_table->slots[i].key)) { 217 | *ret_found = true; 218 | return i; 219 | } 220 | } 221 | 222 | *ret_found = false; 223 | return 0; 224 | } 225 | 226 | void _g_hash_table_resize(GHashTable *hash_table, uint32_t new_num_slots) 227 | { 228 | uint32_t old_num_slots = hash_table->num_slots; 229 | struct GHashTableSlot *old_slots = hash_table->slots; 230 | struct GHashTableSlot *new_slots; 231 | 232 | size_t buf_size = new_num_slots * sizeof(struct GHashTableSlot); 233 | new_slots = malloc(buf_size); 234 | if (new_slots == NULL) { 235 | fprintf(stderr, "FATAL ERROR: _g_hash_table_resize: Out of memory"); 236 | exit(1); 237 | } 238 | 239 | hash_table->slots = new_slots; 240 | hash_table->num_used = 0; 241 | hash_table->num_slots = new_num_slots; 242 | hash_table->resize_threshold = (uint32_t) (hash_table->num_slots * GHASHTABLE_MAX_LOAD); 243 | 244 | memset(hash_table->slots, 0, buf_size); 245 | 246 | for (uint32_t i = 0; i < old_num_slots; i++) { 247 | if (old_slots[i].used) { 248 | g_hash_table_insert(hash_table, old_slots[i].key, old_slots[i].value); 249 | } 250 | } 251 | 252 | free(old_slots); 253 | } 254 | 255 | void g_hash_table_insert(GHashTable *hash_table, void *key, void *value) 256 | { 257 | if (hash_table->num_used >= hash_table->resize_threshold) { 258 | _g_hash_table_resize(hash_table, hash_table->num_slots * 2); 259 | } 260 | 261 | uint32_t start_slot = _g_hash_table_calc_start_slot(hash_table, key); 262 | uint32_t slot = 0; 263 | 264 | if (hash_table->slots[start_slot].used == false) { 265 | slot = start_slot; 266 | } else { 267 | // hashed slot is already used 268 | // search for next free one 269 | slot = _g_hash_table_find_free_slot(hash_table, start_slot, key); 270 | } 271 | 272 | if (hash_table->slots[slot].used) { 273 | // key already exists in the hash table 274 | if (hash_table->key_destroy_func && key != hash_table->slots[slot].key) { 275 | hash_table->key_destroy_func(hash_table->slots[slot].key); 276 | } 277 | 278 | if (hash_table->value_destroy_func && value != hash_table->slots[slot].value) { 279 | hash_table->value_destroy_func(hash_table->slots[slot].value); 280 | } 281 | 282 | hash_table->slots[slot].value = value; 283 | } else { 284 | hash_table->slots[slot].key = key; 285 | hash_table->slots[slot].value = value; 286 | hash_table->slots[slot].used = true; 287 | hash_table->slots[slot].deleted = false; 288 | hash_table->num_used++; 289 | } 290 | } 291 | 292 | uint32_t g_hash_table_size(GHashTable *hash_table) 293 | { 294 | return hash_table->num_used; 295 | } 296 | 297 | void* g_hash_table_lookup(GHashTable *hash_table, void *key) 298 | { 299 | bool found = false; 300 | uint32_t start_slot = _g_hash_table_calc_start_slot(hash_table, key); 301 | uint32_t slot = _g_hash_table_find_slot_by_key(hash_table, key, start_slot, &found); 302 | 303 | if (!found) { 304 | return NULL; 305 | } 306 | 307 | return hash_table->slots[slot].value; 308 | } 309 | 310 | void g_hash_table_foreach(GHashTable *hash_table, GHFunc func, void *user_data) 311 | { 312 | for (uint32_t i = 0; i < hash_table->num_slots; i++) { 313 | if (hash_table->slots[i].used == true) { 314 | func(hash_table->slots[i].key, hash_table->slots[i].value, user_data); 315 | } 316 | } 317 | } 318 | 319 | bool g_hash_table_remove(GHashTable *hash_table, void *key) 320 | { 321 | bool found = false; 322 | uint32_t start_slot = _g_hash_table_calc_start_slot(hash_table, key); 323 | uint32_t slot = _g_hash_table_find_slot_by_key(hash_table, key, start_slot, &found); 324 | 325 | if (!found) { 326 | return false; 327 | } 328 | 329 | if (hash_table->key_destroy_func) { 330 | hash_table->key_destroy_func(hash_table->slots[slot].key); 331 | } 332 | 333 | if (hash_table->value_destroy_func) { 334 | hash_table->value_destroy_func(hash_table->slots[slot].value); 335 | } 336 | 337 | hash_table->slots[slot].key = 0; 338 | hash_table->slots[slot].value = 0; 339 | hash_table->slots[slot].used = false; 340 | hash_table->slots[slot].deleted = true; 341 | hash_table->num_used--; 342 | 343 | return true; 344 | } 345 | 346 | void g_hash_table_destroy(GHashTable *hash_table) 347 | { 348 | if (hash_table) { 349 | if (hash_table->key_destroy_func || hash_table->value_destroy_func) { 350 | for (uint32_t i = 0; i < hash_table->num_slots; i++) { 351 | if (hash_table->slots[i].used == false) { 352 | continue; 353 | } 354 | 355 | if (hash_table->key_destroy_func) { 356 | hash_table->key_destroy_func(hash_table->slots[i].key); 357 | } 358 | 359 | if (hash_table->value_destroy_func) { 360 | hash_table->value_destroy_func(hash_table->slots[i].value); 361 | } 362 | } 363 | } 364 | 365 | if (hash_table->slots) { 366 | free(hash_table->slots); 367 | } 368 | free(hash_table); 369 | } 370 | } 371 | 372 | #endif 373 | #endif 374 | -------------------------------------------------------------------------------- /src/glist.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GList 3 | * 4 | * Copyright (c) 2022 Andreas Heck 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef _GLIST_H 27 | #define _GLIST_H 28 | #include 29 | 30 | typedef int(*GCompareFunc) (const void *a, const void *b); 31 | typedef int(*GCompareDataFunc) (const void *a, const void *b, void *user_data); 32 | typedef void(*GFunc) (void *data, void *user_data); 33 | 34 | typedef struct GList { 35 | void *data; 36 | struct GList *next; 37 | struct GList *prev; 38 | } GList; 39 | 40 | GList* g_list_append(GList *list, void *data); 41 | GList* g_list_prepend(GList *list, void *data); 42 | GList* g_list_insert(GList *list, void *data, int position); 43 | GList* g_list_insert_before(GList *list, GList *sibling, void *data); 44 | GList* g_list_insert_sorted(GList *list, void *data, GCompareFunc func); 45 | GList* g_list_insert_sorted_with_data(GList *list, void *data, GCompareDataFunc func, void *user_data); 46 | GList* g_list_remove(GList *list, const void *data); 47 | GList* g_list_remove_link(GList *list, GList *llink); 48 | GList* g_list_delete_link(GList *list, GList *link_); 49 | GList* g_list_remove_all(GList *list, const void *data); 50 | void g_list_free(GList *list); 51 | GList* g_list_alloc(); 52 | void g_list_free_1(GList *list); 53 | uint32_t g_list_length(GList *list); 54 | GList* g_list_copy(GList *list); 55 | GList* g_list_reverse(GList *list); 56 | GList* g_list_sort(GList *list, GCompareFunc compare_func); 57 | GList* g_list_sort_with_data(GList *list, GCompareDataFunc compare_func, void *user_data); 58 | GList* g_list_concat(GList *list1, GList *list2); 59 | void g_list_foreach(GList *list, GFunc func, void *user_data); 60 | GList* g_list_first(GList *list); 61 | GList* g_list_last(GList *list); 62 | GList* g_list_nth(GList *list, uint32_t n); 63 | void* g_list_nth_data(GList *list, uint32_t n); 64 | GList* g_list_nth_prev(GList *list, uint32_t n); 65 | GList* g_list_find(GList *list, const void *data); 66 | GList* g_list_find_custom(GList *list, const void *data, GCompareFunc func); 67 | int g_list_position(GList *list, GList *llink); 68 | int g_list_index(GList *list, const void *data); 69 | 70 | 71 | #ifdef _CLIB_IMPL 72 | 73 | GList* g_list_append(GList *list, void *data) 74 | { 75 | GList *last = g_list_last(list); 76 | 77 | GList *new_entry = g_list_alloc(); 78 | 79 | new_entry->data = data; 80 | new_entry->prev = last; 81 | new_entry->next = NULL; 82 | 83 | if (last == NULL) { 84 | return new_entry; 85 | } 86 | 87 | last->next = new_entry; 88 | 89 | return list; 90 | } 91 | 92 | GList* g_list_prepend(GList *list, void *data) 93 | { 94 | GList *first = g_list_first(list); 95 | 96 | GList *new_entry = g_list_alloc(); 97 | 98 | new_entry->data = data; 99 | new_entry->prev = NULL; 100 | new_entry->next = first; 101 | 102 | if (first != NULL) { 103 | first->prev = new_entry; 104 | } 105 | 106 | return new_entry; 107 | } 108 | 109 | GList* g_list_insert(GList *list, void *data, int position) 110 | { 111 | if (list == NULL) { 112 | return g_list_append(list, data); 113 | } 114 | 115 | if (position < 0) { 116 | return g_list_append(list, data); 117 | } 118 | 119 | GList *last; 120 | GList *cur = list; 121 | for (int i = 0; cur; last = cur, cur = cur->next, i++) { 122 | if (position != i) { 123 | continue; 124 | } 125 | 126 | GList *new_elem = g_list_alloc(); 127 | 128 | if (cur->prev) { 129 | cur->prev->next = new_elem; 130 | } 131 | new_elem->data = data; 132 | new_elem->prev = cur->prev; 133 | new_elem->next = cur; 134 | cur->prev = new_elem; 135 | 136 | // new list start? 137 | if (new_elem->prev == NULL) { 138 | return new_elem; 139 | } 140 | 141 | return list; 142 | } 143 | 144 | // we reached the end of the list but didn't find position 145 | // so we append to the end of the list 146 | GList *new_elem = g_list_alloc(); 147 | 148 | last->next = new_elem; 149 | new_elem->data = data; 150 | new_elem->prev = last; 151 | new_elem->next = NULL; 152 | 153 | return list; 154 | } 155 | 156 | GList* g_list_insert_before(GList *list, GList *sibling, void *data) 157 | { 158 | if (sibling == NULL) { 159 | return g_list_append(list, data); 160 | } 161 | 162 | GList *new_elem = g_list_alloc(); 163 | 164 | new_elem->data = data; 165 | 166 | if (sibling->prev) { 167 | sibling->prev->next = new_elem; 168 | } 169 | 170 | new_elem->prev = sibling->prev; 171 | new_elem->next = sibling; 172 | 173 | sibling->prev = new_elem; 174 | 175 | // new list start? 176 | if (new_elem->prev == NULL) { 177 | return new_elem; 178 | } 179 | 180 | return list; 181 | } 182 | 183 | #define _G_LIST_INSERT_SORTED(call_compare)\ 184 | if (list == NULL) {\ 185 | return g_list_append(list, data);\ 186 | }\ 187 | \ 188 | GList *cur = list;\ 189 | while (cur) {\ 190 | int comp = call_compare;\ 191 | \ 192 | if (comp >= 0) {\ 193 | GList * new_elem = g_list_alloc();\ 194 | \ 195 | new_elem->data = data;\ 196 | new_elem->next = cur;\ 197 | new_elem->prev = cur->prev;\ 198 | if (cur->prev) {\ 199 | cur->prev->next = new_elem;\ 200 | } else {\ 201 | list = new_elem;\ 202 | }\ 203 | cur->prev = new_elem;\ 204 | \ 205 | return list;\ 206 | }\ 207 | \ 208 | if (cur->next == NULL) {\ 209 | GList * new_elem = g_list_alloc();\ 210 | \ 211 | new_elem->data = data;\ 212 | new_elem->next = NULL;\ 213 | new_elem->prev = cur;\ 214 | cur->next = new_elem;\ 215 | \ 216 | return list;\ 217 | }\ 218 | \ 219 | cur = cur->next;\ 220 | }\ 221 | \ 222 | return list; 223 | 224 | GList* g_list_insert_sorted(GList *list, void *data, GCompareFunc func) 225 | { 226 | _G_LIST_INSERT_SORTED(func(cur->data, data)); 227 | } 228 | 229 | GList* g_list_insert_sorted_with_data(GList *list, void *data, GCompareDataFunc func, void *user_data) 230 | { 231 | _G_LIST_INSERT_SORTED(func(cur->data, data, user_data)); 232 | } 233 | 234 | GList* g_list_remove(GList *list, const void *data) 235 | { 236 | GList *cur = list; 237 | 238 | while (cur != NULL) { 239 | if (cur->data == data) { 240 | if (cur->prev) { 241 | cur->prev->next = cur->next; 242 | } 243 | 244 | if (cur->next) { 245 | cur->next->prev = cur->prev; 246 | } 247 | 248 | if (cur == list) { 249 | if (cur->prev) { 250 | list = cur->prev; 251 | } else { 252 | list = cur->next; 253 | } 254 | } 255 | 256 | free(cur); 257 | return list; 258 | } 259 | 260 | cur = cur->next; 261 | } 262 | 263 | return list; 264 | } 265 | 266 | GList* g_list_remove_link(GList *list, GList *llink) 267 | { 268 | if (llink == NULL) { 269 | return list; 270 | } 271 | 272 | if (llink->prev) { 273 | llink->prev->next = llink->next; 274 | } 275 | 276 | if (llink->next) { 277 | llink->next->prev = llink->prev; 278 | } 279 | 280 | if (list == llink) { 281 | if (llink->prev) { 282 | list = llink->prev; 283 | } else { 284 | list = llink->next; 285 | } 286 | } 287 | 288 | llink->prev = llink->next = NULL; 289 | 290 | return list; 291 | } 292 | 293 | GList* g_list_delete_link(GList *list, GList *link_) 294 | { 295 | list = g_list_remove_link(list, link_); 296 | free(link_); 297 | return list; 298 | } 299 | 300 | GList* g_list_remove_all(GList *list, const void *data) 301 | { 302 | GList *next; 303 | GList *cur = list; 304 | 305 | while (cur != NULL) { 306 | if (cur->data == data) { 307 | if (cur->prev) { 308 | cur->prev->next = cur->next; 309 | } 310 | 311 | if (cur->next) { 312 | cur->next->prev = cur->prev; 313 | } 314 | 315 | if (cur == list) { 316 | if (cur->prev) { 317 | list = cur->prev; 318 | } else { 319 | list = cur->next; 320 | } 321 | } 322 | 323 | next = cur->next; 324 | free(cur); 325 | cur = next; 326 | continue; 327 | } 328 | 329 | cur = cur->next; 330 | } 331 | 332 | return list; 333 | } 334 | 335 | void g_list_free(GList *list) 336 | { 337 | list = g_list_first(list); 338 | 339 | if (list == NULL) { 340 | return; 341 | } 342 | 343 | GList *next; 344 | 345 | while (list != NULL) { 346 | next = list->next; 347 | free(list); 348 | list = next; 349 | } 350 | } 351 | 352 | GList* g_list_alloc() 353 | { 354 | GList *list = (GList*) malloc(sizeof(GList)); 355 | 356 | if (list == NULL) { 357 | fprintf(stderr, "FATAL ERROR: g_list_alloc: Out of memory"); 358 | exit(1); 359 | } 360 | 361 | return list; 362 | } 363 | 364 | #define g_list_free1 g_list_free_1 365 | 366 | void g_list_free_1(GList *list) 367 | { 368 | free(list); 369 | } 370 | 371 | uint32_t g_list_length(GList *list) 372 | { 373 | uint32_t len; 374 | 375 | if (list == NULL) { 376 | return 0; 377 | } 378 | 379 | for (len = 1; list->next != NULL; list = list->next) { 380 | len++; 381 | } 382 | 383 | return len; 384 | } 385 | 386 | GList* g_list_copy(GList *list) 387 | { 388 | if (list == NULL) { 389 | return NULL; 390 | } 391 | 392 | GList *new_list = NULL; 393 | GList *new_prev = NULL; 394 | 395 | while (list) { 396 | GList *new_elem = g_list_alloc(); 397 | 398 | if (new_list == NULL) { 399 | new_list = new_elem; 400 | } 401 | 402 | new_elem->data = list->data; 403 | new_elem->prev = new_prev; 404 | new_elem->next = NULL; 405 | if (new_prev) { 406 | new_prev->next = new_elem; 407 | } 408 | 409 | new_prev = new_elem; 410 | list = list->next; 411 | } 412 | 413 | return new_list; 414 | } 415 | 416 | GList* g_list_reverse(GList *list) 417 | { 418 | if (list == NULL) { 419 | return NULL; 420 | } 421 | 422 | GList *cur = list; 423 | GList *old_next; 424 | 425 | while (cur) { 426 | old_next = cur->next; 427 | 428 | cur->next = cur->prev; 429 | cur->prev = old_next; 430 | 431 | list = cur; 432 | cur = old_next; 433 | } 434 | 435 | return list; 436 | } 437 | 438 | #define _G_LIST_MERGE_SORTED(list1, list2, compare_func, user_data, call_compare)\ 439 | GList *new_list;\ 440 | \ 441 | if (list1 == list2) {\ 442 | return list1;\ 443 | }\ 444 | \ 445 | if (list1 == NULL) {\ 446 | return list2;\ 447 | }\ 448 | \ 449 | if (list2 == NULL) {\ 450 | return list1;\ 451 | }\ 452 | \ 453 | GList *new_cur = NULL;\ 454 | \ 455 | /* add first element to new list*/\ 456 | if (call_compare < 0) {\ 457 | new_list = list1;\ 458 | list1 = list1->next;\ 459 | } else {\ 460 | new_list = list2;\ 461 | list2 = list2->next;\ 462 | }\ 463 | \ 464 | new_cur = new_list;\ 465 | int comp;\ 466 | \ 467 | while (list1 || list2) {\ 468 | if (list1 == NULL) {\ 469 | comp = 1;\ 470 | } else if (list2 == NULL) {\ 471 | comp = -1;\ 472 | } else {\ 473 | comp = call_compare;\ 474 | }\ 475 | \ 476 | if (comp < 0) {\ 477 | new_cur->next = list1;\ 478 | list1->prev = new_cur;\ 479 | new_cur = list1;\ 480 | list1 = list1->next;\ 481 | new_cur->next = NULL;\ 482 | } else {\ 483 | new_cur->next = list2;\ 484 | list2->prev = new_cur;\ 485 | new_cur = list2;\ 486 | list2 = list2->next;\ 487 | new_cur->next = NULL;\ 488 | }\ 489 | }\ 490 | \ 491 | return new_list;\ 492 | 493 | GList* _g_list_merge_sorted(GList *list1, GList *list2, GCompareFunc compare_func) 494 | { 495 | _G_LIST_MERGE_SORTED(list1, list2, compare_func, NULL, compare_func(list1->data, list2->data)); 496 | } 497 | 498 | GList* _g_list_merge_sorted_with_data(GList *list1, GList *list2, GCompareDataFunc compare_func, void *user_data) 499 | { 500 | _G_LIST_MERGE_SORTED(list1, list2, compare_func, NULL, compare_func(list1->data, list2->data, user_data)); 501 | } 502 | 503 | #define _G_LIST_SORT(list, compare_func, user_data, call_merge)\ 504 | /* \ 505 | * Bottom-Up Mergesort\ 506 | */\ 507 | \ 508 | if (list == NULL) {\ 509 | return NULL;\ 510 | }\ 511 | \ 512 | if (compare_func == NULL) {\ 513 | return list;\ 514 | }\ 515 | \ 516 | uint32_t total_len = g_list_length(list);\ 517 | \ 518 | if (total_len == 1) {\ 519 | return list;\ 520 | }\ 521 | \ 522 | uint32_t max_lists = total_len;\ 523 | max_lists = (uint32_t) ceil(max_lists / 2.0);\ 524 | \ 525 | if (max_lists % 2 != 0) {\ 526 | max_lists += 1;\ 527 | }\ 528 | \ 529 | uint32_t buf_size = sizeof(GList*) * max_lists;\ 530 | GList **sorted_lists = (GList**) malloc(buf_size);\ 531 | \ 532 | if (sorted_lists == NULL) {\ 533 | return list;\ 534 | }\ 535 | \ 536 | /* \ 537 | * Set last element to NULL to "normalize" lists with an uneven number of\ 538 | * elements\ 539 | */\ 540 | sorted_lists[max_lists - 1] = NULL;\ 541 | \ 542 | /*\ 543 | * Break list into single element lists and merge them pair-wise.\ 544 | * The pointers to the merged lists are stored in sorted_lists.\ 545 | */\ 546 | GList *cur = list;\ 547 | uint32_t i;\ 548 | for (i = 0; cur; i++) {\ 549 | GList *list1 = cur;\ 550 | GList *list2 = cur->next;\ 551 | \ 552 | if (list2) {\ 553 | cur = list2->next;\ 554 | } else {\ 555 | cur = NULL;\ 556 | }\ 557 | \ 558 | list1->prev = NULL;\ 559 | list1->next = NULL;\ 560 | if (list2) {\ 561 | list2->prev = NULL;\ 562 | list2->next = NULL;\ 563 | }\ 564 | \ 565 | sorted_lists[i] = call_merge;\ 566 | }\ 567 | \ 568 | uint32_t num_sublists = max_lists;\ 569 | while (num_sublists > 1) {\ 570 | uint32_t j = 0;\ 571 | for (i = 0; i < num_sublists; i += 2, j++) {\ 572 | GList *list1 = sorted_lists[i];\ 573 | GList *list2 = sorted_lists[i + 1];\ 574 | sorted_lists[j] = call_merge;\ 575 | }\ 576 | num_sublists = j;\ 577 | \ 578 | if (num_sublists > 1 && num_sublists % 2 != 0) {\ 579 | num_sublists += 1;\ 580 | sorted_lists[num_sublists - 1] = NULL;\ 581 | }\ 582 | }\ 583 | \ 584 | GList *result = sorted_lists[0];\ 585 | free(sorted_lists);\ 586 | \ 587 | return result; 588 | 589 | GList* g_list_sort(GList *list, GCompareFunc compare_func) 590 | { 591 | _G_LIST_SORT(list, compare_func, NULL, _g_list_merge_sorted(list1, list2, compare_func)); 592 | } 593 | 594 | GList* g_list_sort_with_data(GList *list, GCompareDataFunc compare_func, void *user_data) 595 | { 596 | _G_LIST_SORT(list, compare_func, NULL, _g_list_merge_sorted_with_data(list1, list2, compare_func, user_data)); 597 | } 598 | 599 | GList* g_list_concat(GList *list1, GList *list2) 600 | { 601 | if (list1 == NULL || list2 == NULL) { 602 | return list1; 603 | } 604 | 605 | GList *last1 = g_list_last(list1); 606 | 607 | last1->next = list2; 608 | list2->prev = last1; 609 | 610 | return list1; 611 | } 612 | 613 | void g_list_foreach(GList *list, GFunc func, void *user_data) 614 | { 615 | while (list) { 616 | func(list->data, user_data); 617 | list = list->next; 618 | } 619 | } 620 | 621 | GList* g_list_first(GList *list) 622 | { 623 | if (list == NULL) { 624 | return NULL; 625 | } 626 | 627 | for(; list->prev != NULL; list = list->prev) { 628 | } 629 | 630 | return list; 631 | } 632 | 633 | GList* g_list_last(GList *list) 634 | { 635 | if (list == NULL) { 636 | return NULL; 637 | } 638 | 639 | for(; list->next != NULL; list = list->next) { 640 | } 641 | 642 | return list; 643 | } 644 | 645 | #define g_list_previous(list) list->prev 646 | 647 | #define g_list_next(list) list->next 648 | 649 | GList* g_list_nth(GList *list, uint32_t n) 650 | { 651 | uint32_t i; 652 | 653 | for (i = 0; list != NULL; list = list->next, i++) { 654 | if (i == n) { 655 | return list; 656 | } 657 | } 658 | 659 | return NULL; 660 | } 661 | 662 | void* g_list_nth_data(GList *list, uint32_t n) 663 | { 664 | uint32_t i; 665 | 666 | for (i = 0; list != NULL; list = list->next, i++) { 667 | if (i == n) { 668 | return list->data; 669 | } 670 | } 671 | 672 | return NULL; 673 | } 674 | 675 | GList* g_list_nth_prev(GList *list, uint32_t n) 676 | { 677 | uint32_t i; 678 | 679 | for (i = 0; list != NULL; list = list->prev, i++) { 680 | if (i == n) { 681 | return list; 682 | } 683 | } 684 | 685 | return NULL; 686 | } 687 | 688 | GList* g_list_find(GList *list, const void *data) 689 | { 690 | while (list) { 691 | if (list->data == data) { 692 | return list; 693 | } 694 | 695 | list = list->next; 696 | } 697 | 698 | return NULL; 699 | } 700 | 701 | GList* g_list_find_custom(GList *list, const void *data, GCompareFunc func) 702 | { 703 | while (list) { 704 | if (func(list->data, data) == 0) { 705 | return list; 706 | } 707 | 708 | list = list->next; 709 | } 710 | 711 | return NULL; 712 | } 713 | 714 | int g_list_position(GList *list, GList *llink) 715 | { 716 | uint32_t i; 717 | 718 | for (i = 0; list != NULL; list = list->next, i++) { 719 | if (list == llink) { 720 | return i; 721 | } 722 | } 723 | 724 | return -1; 725 | } 726 | 727 | int g_list_index(GList *list, const void *data) 728 | { 729 | uint32_t i; 730 | 731 | for (i = 0; list != NULL; list = list->next, i++) { 732 | if (list->data == data) { 733 | return i; 734 | } 735 | } 736 | 737 | return -1; 738 | } 739 | 740 | #endif 741 | #endif 742 | -------------------------------------------------------------------------------- /src/gstring.h: -------------------------------------------------------------------------------- 1 | /* 2 | * GString 3 | * 4 | * Copyright (c) 2022 Andreas Heck 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef _GSTRING_H 27 | #define _GSTRING_H 28 | 29 | #ifdef _MSC_VER 30 | #include 31 | typedef SSIZE_T ssize_t; 32 | #endif 33 | 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #define GSTRING_MIN_BUF_SIZE 32 43 | 44 | #define TRUE true 45 | #define FALSE false 46 | 47 | typedef struct GString { 48 | char *str; 49 | size_t len; 50 | size_t allocated_len; 51 | } GString; 52 | 53 | GString* g_string_new(const char *init); 54 | GString* g_string_new_len(const char *init, size_t len); 55 | GString* g_string_sized_new(ssize_t dfl_size); 56 | GString* g_string_assign(GString *string, const char *rval); 57 | GString* g_string_append(GString *string, const char *val); 58 | GString* g_string_append_c(GString *string, char c); 59 | GString* g_string_append_len(GString *string, const char *val, ssize_t len); 60 | GString* g_string_prepend(GString *string, const char *val); 61 | GString* g_string_prepend_c(GString *string, char c); 62 | GString* g_string_prepend_len(GString *string, const char *val, ssize_t len); 63 | GString* g_string_insert(GString *string, ssize_t pos, const char *val); 64 | GString* g_string_insert_c(GString *string, ssize_t pos, char c); 65 | GString* g_string_insert_len(GString *string, ssize_t pos, const char *val, ssize_t len); 66 | GString* g_string_overwrite(GString *string, size_t pos, const char *val); 67 | GString* g_string_overwrite_len(GString *string, size_t pos, const char *val, ssize_t len); 68 | unsigned int g_string_replace(GString *string, const char *find, const char *replace, unsigned int limit); 69 | GString* g_string_erase(GString *string, ssize_t pos, ssize_t len); 70 | GString* g_string_truncate(GString *string, size_t len); 71 | void g_string_vprintf(GString *string, const char *format, va_list args); 72 | void g_string_append_vprintf(GString *string, const char *format, va_list args); 73 | void g_string_printf(GString *string, const char *format, ...); 74 | void g_string_append_printf(GString *string, const char *format, ...); 75 | bool g_string_equal(GString *v, GString *v2); 76 | char* g_string_free(GString *string, bool free_segment); 77 | 78 | 79 | #ifdef _CLIB_IMPL 80 | 81 | #define _GSTRING_LTOA_BUF_SIZE (sizeof(long) * 8 + 1) 82 | 83 | /* 84 | ** Converts a long integer to a string. 85 | ** 86 | ** Copyright 1988-90 by Robert B. Stout dba MicroFirm 87 | ** 88 | ** Released to public domain, 1991 89 | ** 90 | ** Parameters: 1 - number to be converted 91 | ** 2 - buffer in which to build the converted string 92 | ** 3 - number base to use for conversion 93 | ** 94 | ** Returns: A character pointer to the converted string if 95 | ** successful, a NULL pointer if the number base specified 96 | ** is out of range. 97 | */ 98 | char* _gstring_ltoa(long N, char *str, int base) 99 | { 100 | register int i = 2; 101 | long uarg; 102 | char *tail, *head = str, buf[_GSTRING_LTOA_BUF_SIZE]; 103 | 104 | if (36 < base || 2 > base) 105 | base = 10; /* can only use 0-9, A-Z */ 106 | tail = &buf[_GSTRING_LTOA_BUF_SIZE - 1]; /* last character position */ 107 | *tail-- = '\0'; 108 | 109 | if (10 == base && N < 0L) 110 | { 111 | *head++ = '-'; 112 | uarg = -N; 113 | } 114 | else uarg = N; 115 | 116 | if (uarg) 117 | { 118 | for (i = 1; uarg; ++i) 119 | { 120 | register ldiv_t r; 121 | 122 | r = ldiv(uarg, base); 123 | *tail-- = (char)(r.rem + ((9L < r.rem) ? 124 | ('A' - 10L) : '0')); 125 | uarg = r.quot; 126 | } 127 | } 128 | else *tail-- = '0'; 129 | 130 | memcpy(head, ++tail, i); 131 | return str; 132 | } 133 | 134 | GString* g_string_new(const char *init) 135 | { 136 | size_t init_len = 0; 137 | size_t buf_size = GSTRING_MIN_BUF_SIZE; 138 | GString *string; 139 | 140 | if (init != NULL) { 141 | init_len = strlen(init); 142 | } 143 | 144 | if (init_len + 1 > buf_size) { 145 | buf_size = init_len + 1; 146 | } 147 | 148 | string = malloc(sizeof(GString)); 149 | if (string == NULL) { 150 | fprintf(stderr, "FATAL ERROR: g_string_new: Out of memory"); 151 | exit(1); 152 | } 153 | 154 | string->str = malloc(buf_size); 155 | if (string->str == NULL) { 156 | fprintf(stderr, "FATAL ERROR: g_string_new: Out of memory"); 157 | exit(1); 158 | } 159 | 160 | // fill GString attributes 161 | if (init_len > 0) { 162 | memcpy(string->str, init, init_len + 1); 163 | } else { 164 | string->str[0] = '\0'; 165 | } 166 | 167 | string->len = init_len; 168 | string->allocated_len = buf_size; 169 | 170 | return string; 171 | } 172 | 173 | GString* g_string_new_len(const char *init, size_t len) 174 | { 175 | GString *string; 176 | 177 | string = malloc(sizeof(GString)); 178 | if (string == NULL) { 179 | fprintf(stderr, "FATAL ERROR: g_string_new_len: Out of memory"); 180 | exit(1); 181 | } 182 | 183 | size_t buf_size = len + 1; 184 | 185 | string->str = malloc(buf_size); 186 | if (string->str == NULL) { 187 | fprintf(stderr, "FATAL ERROR: g_string_new_len: Out of memory"); 188 | exit(1); 189 | } 190 | 191 | memcpy(string->str, init, len); 192 | 193 | string->str[buf_size - 1] = '\0'; 194 | string->len = len; 195 | string->allocated_len = buf_size; 196 | 197 | return string; 198 | } 199 | 200 | GString* g_string_sized_new(ssize_t dfl_size) 201 | { 202 | GString *string; 203 | 204 | string = malloc(sizeof(GString)); 205 | if (string == NULL) { 206 | fprintf(stderr, "FATAL ERROR: g_string_sized_new: Out of memory"); 207 | exit(1); 208 | } 209 | 210 | string->str = malloc(dfl_size); 211 | if (string->str == NULL) { 212 | fprintf(stderr, "FATAL ERROR: g_string_sized_new: Out of memory"); 213 | exit(1); 214 | } 215 | 216 | string->len = 0; 217 | string->allocated_len = dfl_size; 218 | 219 | return string; 220 | } 221 | 222 | void _g_string_resize(GString *string, size_t requested_size) 223 | { 224 | char *new_buf; 225 | size_t buf_size; 226 | 227 | if (requested_size <= string->allocated_len) { 228 | return; 229 | } 230 | 231 | buf_size = requested_size * 2; 232 | 233 | new_buf = realloc(string->str, buf_size); 234 | if (new_buf == NULL) { 235 | fprintf(stderr, "FATAL ERROR: g_string_new: Out of memory"); 236 | exit(1); 237 | } 238 | 239 | string->str = new_buf; 240 | string->allocated_len = buf_size; 241 | } 242 | 243 | GString* g_string_assign(GString *string, const char *rval) 244 | { 245 | size_t new_len; 246 | 247 | if (rval == NULL) { 248 | return string; 249 | } 250 | 251 | new_len = strlen(rval); 252 | 253 | if (new_len + 1 > string->allocated_len) { 254 | _g_string_resize(string, new_len + 1); 255 | } 256 | 257 | memcpy(string->str, rval, new_len + 1); 258 | string->len = new_len; 259 | 260 | return string; 261 | } 262 | 263 | GString* g_string_append(GString *string, const char *val) 264 | { 265 | if (val == NULL) { 266 | return NULL; 267 | } 268 | 269 | return g_string_append_len(string, val, strlen(val)); 270 | } 271 | 272 | GString* g_string_append_c(GString *string, char c) 273 | { 274 | return g_string_append_len(string, &c, 1); 275 | } 276 | 277 | GString* g_string_append_len(GString *string, const char *val, ssize_t len) 278 | { 279 | if (len <= 0) { 280 | return string; 281 | } 282 | 283 | if (val == NULL) { 284 | return string; 285 | } 286 | 287 | size_t new_len = len + string->len; 288 | 289 | if (new_len + 1 > string->allocated_len) { 290 | _g_string_resize(string, new_len + 1); 291 | } 292 | 293 | memcpy(&string->str[string->len], val, len); 294 | string->len = new_len; 295 | string->str[new_len] = '\0'; 296 | 297 | return string; 298 | } 299 | 300 | GString* g_string_prepend(GString *string, const char *val) 301 | { 302 | if (val == NULL) { 303 | return string; 304 | } 305 | 306 | return g_string_prepend_len(string, val, strlen(val)); 307 | } 308 | 309 | GString* g_string_prepend_c(GString *string, char c) 310 | { 311 | return g_string_prepend_len(string, &c, 1); 312 | } 313 | 314 | GString* g_string_prepend_len(GString *string, const char *val, ssize_t len) 315 | { 316 | if (val == NULL) { 317 | return string; 318 | } 319 | 320 | size_t new_len = len + string->len; 321 | 322 | if (new_len + 1 > string->allocated_len) { 323 | _g_string_resize(string, new_len + 1); 324 | } 325 | 326 | // move current string to end 327 | memmove(&string->str[len], string->str, string->len + 1); 328 | 329 | // copy string that shall be prepended 330 | memcpy(string->str, val, len); 331 | 332 | string->len = new_len; 333 | 334 | return string; 335 | } 336 | 337 | GString* g_string_insert(GString *string, ssize_t pos, const char *val) 338 | { 339 | if (val == NULL) { 340 | return string; 341 | } 342 | 343 | return g_string_insert_len(string, pos, val, strlen(val)); 344 | } 345 | 346 | GString* g_string_insert_c(GString *string, ssize_t pos, char c) 347 | { 348 | return g_string_insert_len(string, pos, &c, 1); 349 | } 350 | 351 | GString* g_string_insert_len(GString *string, ssize_t pos, const char *val, ssize_t len) 352 | { 353 | if (pos < 0) { 354 | return string; 355 | } 356 | 357 | // pos beyond last char of string? 358 | if (pos > (ssize_t) string->len) { 359 | fprintf(stderr, "Critical: g_string_insert_len: pos (%zd) > string->len (%zu)\n", pos, string->len); 360 | return string; 361 | } 362 | 363 | if (len == 0) { 364 | return string; 365 | } 366 | 367 | ssize_t new_len = string->len + len; 368 | 369 | // enlarge buffer? 370 | if (new_len + 1 > (ssize_t) string->allocated_len) { 371 | _g_string_resize(string, new_len + 1); 372 | } 373 | 374 | memmove(&string->str[pos + len], &string->str[pos], string->len - pos + 1); 375 | memcpy(&string->str[pos], val, len); 376 | string->len += len; 377 | 378 | return string; 379 | } 380 | 381 | GString* g_string_overwrite(GString *string, size_t pos, const char *val) 382 | { 383 | if (val == NULL) { 384 | return string; 385 | } 386 | 387 | return g_string_overwrite_len(string, pos, val, strlen(val)); 388 | } 389 | 390 | GString* g_string_overwrite_len(GString *string, size_t pos, const char *val, ssize_t len) 391 | { 392 | if (pos < 0) { 393 | return string; 394 | } 395 | 396 | // pos beyond last char of string? 397 | if (pos > (ssize_t) string->len) { 398 | fprintf(stderr, "Critical: g_string_overwrite_len: pos (%zd) > string->len (%zu)\n", pos, string->len); 399 | return string; 400 | } 401 | 402 | size_t overwrite_len = pos + len; 403 | 404 | // enlarge buffer? 405 | if (overwrite_len >= string->allocated_len) { 406 | _g_string_resize(string, overwrite_len + 1); 407 | } 408 | 409 | memcpy(&string->str[pos], val, len); 410 | 411 | if (overwrite_len > string->len) { 412 | string->len += len - pos; 413 | string->str[string->len] = '\0'; 414 | } 415 | 416 | return string; 417 | } 418 | 419 | unsigned int g_string_replace(GString *string, const char *find, const char *replace, unsigned int limit) 420 | { 421 | unsigned int replacements = 0; 422 | 423 | if (find == NULL || replace == NULL) { 424 | return 0; 425 | } 426 | 427 | if (replace[0] == '\0') { 428 | return 0; 429 | } 430 | 431 | size_t find_len = strlen(find); 432 | size_t replace_len = strlen(replace); 433 | 434 | // if the find string is empty we insert the replace string at the beginning 435 | // and end of the string as well as after each character 436 | if (find[0] == '\0') { 437 | for (ssize_t i = 0; i < string->len; i++) { 438 | g_string_insert(string, i, replace); 439 | i += replace_len; 440 | replacements++; 441 | 442 | if (limit != 0) { 443 | if (replacements == limit) { 444 | break; 445 | } 446 | } 447 | } 448 | 449 | if (limit == 0 || limit < replacements) { 450 | g_string_append(string, "_"); 451 | replacements++; 452 | } 453 | 454 | return replacements; 455 | } 456 | 457 | // calculate how many chars of the find and the replace string overlap 458 | size_t overlap = find_len; 459 | if (replace_len > find_len) { 460 | overlap = replace_len - find_len; 461 | } else if (find_len > replace_len) { 462 | overlap = find_len - replace_len; 463 | } 464 | 465 | char *pos = string->str; 466 | while ((pos = strstr(pos, find))) { 467 | // overwrite overlapping part of the string 468 | memcpy(pos, replace, overlap); 469 | 470 | if (replace_len > find_len) { 471 | // insert the non-overlapping part of the replace string 472 | g_string_insert(string, (pos + overlap) - string->str, &replace[overlap]); 473 | } else if (find_len > replace_len) { 474 | // remove the non-overlapping part of the find string 475 | g_string_erase(string, (pos + overlap) - string->str, find_len - replace_len); 476 | } 477 | 478 | pos += replace_len; 479 | replacements++; 480 | 481 | // enforce limit 482 | if (limit != 0) { 483 | if (replacements == limit) { 484 | return replacements; 485 | } 486 | } 487 | } 488 | 489 | return replacements; 490 | } 491 | 492 | GString* g_string_erase(GString *string, ssize_t pos, ssize_t len) 493 | { 494 | if (pos < 0) { 495 | return string; 496 | } 497 | 498 | if (len == 0) { 499 | return string; 500 | } 501 | 502 | // pos beyond last char of string? 503 | if (pos > (ssize_t) (string->len - 1)) { 504 | fprintf(stderr, "Critical: g_string_erase: pos (%zd) > string->len - 1 (%zu)\n", pos, string->len - 1); 505 | return string; 506 | } 507 | 508 | // do we erase to the end of the string? 509 | if (len < 0 || (pos + len) >= (ssize_t) string->len) { 510 | string->str[pos] = '\0'; 511 | string->len = pos; 512 | return string; 513 | } 514 | 515 | memmove(&string->str[pos], &string->str[pos + len], string->len - pos - len + 1); 516 | string->len = string->len - len; 517 | 518 | return string; 519 | } 520 | 521 | GString* g_string_truncate(GString *string, size_t len) 522 | { 523 | if (len >= string->len) { 524 | return string; 525 | } 526 | 527 | string->str[len] = '\0'; 528 | string->len = len; 529 | 530 | return string; 531 | } 532 | 533 | void g_string_vprintf(GString *string, const char *format, va_list args) 534 | { 535 | g_string_truncate(string, 0); 536 | 537 | g_string_append_vprintf(string, format, args); 538 | } 539 | 540 | void g_string_append_vprintf(GString *string, const char *format, va_list args) 541 | { 542 | const char *c; 543 | bool format_mode = false; 544 | bool precision_mode = false; 545 | int precision = -1; 546 | int int_arg; 547 | double double_arg; 548 | char buf[20]; 549 | char format_buf[20]; 550 | char *cur; 551 | 552 | if (format == NULL) { 553 | return; 554 | } 555 | 556 | for (c = format; *c; c++) { 557 | if (format_mode) { 558 | if (*c == '%') { 559 | g_string_append_c(string, '%'); 560 | } else if (*c == 'c') { 561 | g_string_append_c(string, va_arg(args, int)); 562 | } else if (*c == 's') { 563 | g_string_append(string, va_arg(args, char*)); 564 | } else if (*c == 'd') { 565 | int_arg = va_arg(args, int); 566 | _gstring_ltoa(int_arg, (char*) &buf, 10); 567 | g_string_append(string, buf); 568 | } else if (*c == 'f') { 569 | double_arg = va_arg(args, double); 570 | if (precision_mode) { 571 | snprintf(format_buf, sizeof(format_buf), "%%.%df", precision); 572 | snprintf(buf, sizeof(buf), format_buf, double_arg); 573 | } else { 574 | snprintf(buf, sizeof(buf), "%f", double_arg); 575 | } 576 | g_string_append(string, buf); 577 | } else if (*c == 'x' || *c == 'X') { 578 | int_arg = va_arg(args, int); 579 | _gstring_ltoa(int_arg, (char*) &buf, 16); 580 | if (*c == 'x') { 581 | for (cur = (char*) &buf; *cur; cur++) { 582 | *cur = tolower(*cur); 583 | } 584 | } 585 | g_string_append(string, buf); 586 | } else if (*c == '.') { 587 | precision_mode = true; 588 | continue; 589 | } else if (precision_mode && isdigit(*c)) { 590 | precision = *c - '0'; 591 | continue; 592 | } 593 | 594 | format_mode = false; 595 | precision_mode = false; 596 | } else if (*c != '%') { 597 | g_string_append_c(string, *c); 598 | } else { 599 | format_mode = true; 600 | } 601 | } 602 | } 603 | 604 | void g_string_printf(GString *string, const char *format, ...) 605 | { 606 | va_list args; 607 | 608 | g_string_truncate(string, 0); 609 | 610 | va_start(args, format); 611 | g_string_vprintf(string, format, args); 612 | va_end(args); 613 | } 614 | 615 | void g_string_append_printf(GString *string, const char *format, ...) 616 | { 617 | va_list args; 618 | 619 | va_start(args, format); 620 | g_string_append_vprintf(string, format, args); 621 | va_end(args); 622 | } 623 | 624 | bool g_string_equal(GString *v, GString *v2) 625 | { 626 | if (v->len != v2->len) { 627 | return false; 628 | } 629 | 630 | return memcmp(v->str, v2->str, v->len) == 0; 631 | } 632 | 633 | char* g_string_free(GString *string, bool free_segment) 634 | { 635 | char *segment; 636 | 637 | if (free_segment) { 638 | free(string->str); 639 | free(string); 640 | return NULL; 641 | } 642 | 643 | segment = string->str; 644 | 645 | free(string); 646 | 647 | return segment; 648 | } 649 | 650 | #endif 651 | #endif 652 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(CLIB_SRC_DIR "../src") 4 | 5 | # 6 | # GArray 7 | # 8 | add_executable(test_garray test_garray.c) 9 | target_include_directories(test_garray PRIVATE ${CLIB_SRC_DIR}) 10 | target_link_libraries(test_garray PRIVATE Check::check) 11 | add_test(NAME test_garray COMMAND test_garray) 12 | 13 | # 14 | # GString 15 | # 16 | add_executable(test_gstring test_gstring.c) 17 | target_include_directories(test_gstring PRIVATE ${CLIB_SRC_DIR}) 18 | target_link_libraries(test_gstring PRIVATE Check::check) 19 | add_test(NAME test_gstring COMMAND test_gstring) 20 | 21 | # 22 | # GHashTable 23 | # 24 | add_executable(test_ghashtable test_ghashtable.c) 25 | target_include_directories(test_ghashtable PRIVATE ${CLIB_SRC_DIR}) 26 | target_link_libraries(test_ghashtable PRIVATE Check::check) 27 | if(LINUX) 28 | target_link_libraries(test_ghashtable PRIVATE -lm) 29 | endif() 30 | add_test(NAME test_ghashtable COMMAND test_ghashtable) 31 | 32 | # 33 | # GList 34 | # 35 | add_executable(test_glist test_glist.c) 36 | target_include_directories(test_glist PRIVATE ${CLIB_SRC_DIR}) 37 | target_link_libraries(test_glist PRIVATE Check::check) 38 | if(LINUX) 39 | target_link_libraries(test_glist PRIVATE -lm) 40 | endif() 41 | add_test(NAME test_glist COMMAND test_glist) 42 | 43 | # 44 | # Integration 45 | # 46 | add_executable(test_integration test_integration.c) 47 | target_include_directories(test_integration PRIVATE ${CLIB_SRC_DIR}) 48 | target_link_libraries(test_integration PRIVATE Check::check) 49 | add_test(NAME test_integration COMMAND test_integration) 50 | -------------------------------------------------------------------------------- /tests/test_garray.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define _CLIB_IMPL 1 6 | #include "garray.h" 7 | 8 | int compare_int(const void *a, const void *b) 9 | { 10 | return *((int*) a) - *((int*) b); 11 | } 12 | 13 | int compare_int_with_data(const void *a, const void *b, void *user_data) 14 | { 15 | ck_assert_int_eq(*((int*) user_data), 42); 16 | return *((int*) a) - *((int*) b); 17 | } 18 | 19 | START_TEST(test_garray_new) 20 | { 21 | GArray *array = NULL; 22 | array = g_array_new(false, false, sizeof(int)); 23 | 24 | ck_assert_ptr_nonnull(array); 25 | ck_assert_ptr_null(array->data); 26 | ck_assert_int_eq(array->len, 0); 27 | ck_assert_int_eq(array->_allocated_elements, 0); 28 | ck_assert_int_eq(array->_zero_terminated, false); 29 | ck_assert_int_eq(array->_clear, false); 30 | ck_assert_int_eq(array->_element_size, 4); 31 | ck_assert_ptr_null(array->_clear_func); 32 | 33 | g_array_free(array, true); 34 | } 35 | END_TEST 36 | 37 | START_TEST(test_garray_sized_new) 38 | { 39 | GArray *array = NULL; 40 | array = g_array_sized_new(false, false, sizeof(int), 10); 41 | 42 | ck_assert_ptr_nonnull(array); 43 | ck_assert_ptr_nonnull(array->data); 44 | ck_assert_int_eq(array->len, 0); 45 | ck_assert_int_eq(array->_allocated_elements, 10); 46 | ck_assert_int_eq(array->_zero_terminated, false); 47 | ck_assert_int_eq(array->_clear, false); 48 | ck_assert_int_eq(array->_element_size, 4); 49 | ck_assert_ptr_null(array->_clear_func); 50 | 51 | g_array_free(array, true); 52 | } 53 | END_TEST 54 | 55 | START_TEST(test_garray_steal) 56 | { 57 | GArray *array = NULL; 58 | size_t len = 0; 59 | 60 | array = g_array_new(false, false, sizeof(int)); 61 | 62 | int vals[] = {34, 82, 43, 12, 71}; 63 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 64 | 65 | ck_assert_int_eq(array->len, 5); 66 | ck_assert_int_eq(array->_allocated_elements, 5); 67 | 68 | void *array_data = g_array_steal(array, &len); 69 | 70 | ck_assert_int_eq(len, 5); 71 | ck_assert_ptr_nonnull(array_data); 72 | ck_assert_int_eq(array->len, 0); 73 | ck_assert_ptr_null(array->data); 74 | ck_assert_int_eq(array->_allocated_elements, 0); 75 | 76 | free(array_data); 77 | g_array_free(array, true); 78 | } 79 | END_TEST 80 | 81 | START_TEST(test_garray_steal_zero_terminated) 82 | { 83 | GArray *array = NULL; 84 | size_t len = 0; 85 | 86 | array = g_array_new(true, false, sizeof(int)); 87 | 88 | int vals[] = {34, 82, 43, 12, 71}; 89 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 90 | 91 | ck_assert_int_eq(array->len, 5); 92 | ck_assert_int_eq(array->_allocated_elements, 6); 93 | 94 | void *array_data = g_array_steal(array, &len); 95 | 96 | ck_assert_int_eq(len, 5); 97 | ck_assert_ptr_nonnull(array_data); 98 | ck_assert_int_eq(array->len, 0); 99 | ck_assert_int_eq(array->_allocated_elements, 1); 100 | 101 | // zero terminated? 102 | ck_assert_int_eq(g_array_index(array, int, array->len), 0); 103 | 104 | free(array_data); 105 | g_array_free(array, true); 106 | } 107 | END_TEST 108 | 109 | START_TEST(test_garray_append_val) 110 | { 111 | GArray *array = NULL; 112 | GArray *result = NULL; 113 | 114 | int val = 5; 115 | 116 | array = g_array_new(false, false, sizeof(int)); 117 | result = g_array_append_val(array, val); 118 | 119 | ck_assert_ptr_eq(array, result); 120 | ck_assert_int_eq(array->len, 1); 121 | 122 | ck_assert_int_eq(g_array_index(array, int, 0), 5); 123 | 124 | g_array_free(array, true); 125 | } 126 | END_TEST 127 | 128 | START_TEST(test_garray_append_val_many) 129 | { 130 | GArray *array = NULL; 131 | GArray *result = NULL; 132 | 133 | int vals[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 134 | 135 | array = g_array_new(false, false, sizeof(int)); 136 | for (int i = 0; i < (sizeof(vals) / sizeof(int)); i++) { 137 | result = g_array_append_val(array, vals[i]); 138 | } 139 | 140 | ck_assert_ptr_eq(array, result); 141 | ck_assert_int_eq(array->len, 10); 142 | 143 | ck_assert_int_eq(g_array_index(array, int, 0), 0); 144 | ck_assert_int_eq(g_array_index(array, int, 1), 1); 145 | ck_assert_int_eq(g_array_index(array, int, 2), 2); 146 | ck_assert_int_eq(g_array_index(array, int, 3), 3); 147 | ck_assert_int_eq(g_array_index(array, int, 4), 4); 148 | ck_assert_int_eq(g_array_index(array, int, 5), 5); 149 | ck_assert_int_eq(g_array_index(array, int, 6), 6); 150 | ck_assert_int_eq(g_array_index(array, int, 7), 7); 151 | ck_assert_int_eq(g_array_index(array, int, 8), 8); 152 | ck_assert_int_eq(g_array_index(array, int, 9), 9); 153 | 154 | g_array_free(array, true); 155 | } 156 | END_TEST 157 | 158 | START_TEST(test_garray_append_vals) 159 | { 160 | GArray *array = NULL; 161 | GArray *result = NULL; 162 | 163 | int vals[] = {4, 8, 2, 7, 1, 3, 6}; 164 | 165 | array = g_array_new(false, false, sizeof(int)); 166 | result = g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 167 | 168 | ck_assert_ptr_eq(array, result); 169 | ck_assert_int_eq(array->len, 7); 170 | 171 | ck_assert_int_eq(g_array_index(array, int, 0), 4); 172 | ck_assert_int_eq(g_array_index(array, int, 1), 8); 173 | ck_assert_int_eq(g_array_index(array, int, 2), 2); 174 | ck_assert_int_eq(g_array_index(array, int, 3), 7); 175 | ck_assert_int_eq(g_array_index(array, int, 4), 1); 176 | ck_assert_int_eq(g_array_index(array, int, 5), 3); 177 | ck_assert_int_eq(g_array_index(array, int, 6), 6); 178 | 179 | g_array_free(array, true); 180 | } 181 | END_TEST 182 | 183 | START_TEST(test_garray_prepend_val) 184 | { 185 | GArray *array = NULL; 186 | GArray *result = NULL; 187 | 188 | int val1 = 5; 189 | int val2 = 8; 190 | 191 | array = g_array_new(false, false, sizeof(int)); 192 | result = g_array_append_val(array, val1); 193 | result = g_array_prepend_val(array, val2); 194 | 195 | ck_assert_ptr_eq(array, result); 196 | ck_assert_int_eq(array->len, 2); 197 | 198 | ck_assert_int_eq(g_array_index(array, int, 0), 8); 199 | ck_assert_int_eq(g_array_index(array, int, 1), 5); 200 | 201 | g_array_free(array, true); 202 | } 203 | END_TEST 204 | 205 | START_TEST(test_garray_prepend_val_many) 206 | { 207 | GArray *array = NULL; 208 | GArray *result = NULL; 209 | 210 | int vals[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 211 | 212 | array = g_array_new(false, false, sizeof(int)); 213 | for (int i = 0; i < (sizeof(vals) / sizeof(int)); i++) { 214 | result = g_array_prepend_val(array, vals[i]); 215 | } 216 | 217 | ck_assert_ptr_eq(array, result); 218 | ck_assert_int_eq(array->len, 10); 219 | 220 | ck_assert_int_eq(g_array_index(array, int, 0), 9); 221 | ck_assert_int_eq(g_array_index(array, int, 1), 8); 222 | ck_assert_int_eq(g_array_index(array, int, 2), 7); 223 | ck_assert_int_eq(g_array_index(array, int, 3), 6); 224 | ck_assert_int_eq(g_array_index(array, int, 4), 5); 225 | ck_assert_int_eq(g_array_index(array, int, 5), 4); 226 | ck_assert_int_eq(g_array_index(array, int, 6), 3); 227 | ck_assert_int_eq(g_array_index(array, int, 7), 2); 228 | ck_assert_int_eq(g_array_index(array, int, 8), 1); 229 | ck_assert_int_eq(g_array_index(array, int, 9), 0); 230 | 231 | g_array_free(array, true); 232 | } 233 | END_TEST 234 | 235 | START_TEST(test_garray_prepend_vals) 236 | { 237 | GArray *array = NULL; 238 | GArray *result = NULL; 239 | 240 | int vals1[] = {4, 8, 2, 7, 1, 3, 6}; 241 | int vals2[] = {12, 67, 89}; 242 | 243 | array = g_array_new(false, false, sizeof(int)); 244 | result = g_array_append_vals(array, vals1, sizeof(vals1) / sizeof(int)); 245 | result = g_array_prepend_vals(array, vals2, sizeof(vals2) / sizeof(int)); 246 | 247 | ck_assert_ptr_eq(array, result); 248 | ck_assert_int_eq(array->len, 10); 249 | 250 | ck_assert_int_eq(g_array_index(array, int, 0), 12); 251 | ck_assert_int_eq(g_array_index(array, int, 1), 67); 252 | ck_assert_int_eq(g_array_index(array, int, 2), 89); 253 | 254 | ck_assert_int_eq(g_array_index(array, int, 3), 4); 255 | ck_assert_int_eq(g_array_index(array, int, 4), 8); 256 | ck_assert_int_eq(g_array_index(array, int, 5), 2); 257 | ck_assert_int_eq(g_array_index(array, int, 6), 7); 258 | ck_assert_int_eq(g_array_index(array, int, 7), 1); 259 | ck_assert_int_eq(g_array_index(array, int, 8), 3); 260 | ck_assert_int_eq(g_array_index(array, int, 9), 6); 261 | 262 | g_array_free(array, true); 263 | } 264 | END_TEST 265 | 266 | START_TEST(test_garray_insert_val) 267 | { 268 | GArray *array = NULL; 269 | GArray *result = NULL; 270 | 271 | int val1 = 5; 272 | int val2 = 8; 273 | int val3 = 13; 274 | 275 | array = g_array_new(false, false, sizeof(int)); 276 | result = g_array_append_val(array, val1); 277 | result = g_array_append_val(array, val3); 278 | result = g_array_insert_val(array, 1, val2); 279 | 280 | ck_assert_ptr_eq(array, result); 281 | ck_assert_int_eq(array->len, 3); 282 | 283 | ck_assert_int_eq(g_array_index(array, int, 0), 5); 284 | ck_assert_int_eq(g_array_index(array, int, 1), 8); 285 | ck_assert_int_eq(g_array_index(array, int, 2), 13); 286 | 287 | g_array_free(array, true); 288 | } 289 | END_TEST 290 | 291 | START_TEST(test_garray_insert_val_beginning) 292 | { 293 | GArray *array = NULL; 294 | GArray *result = NULL; 295 | 296 | int val1 = 5; 297 | int val2 = 8; 298 | int val3 = 13; 299 | 300 | array = g_array_new(false, false, sizeof(int)); 301 | result = g_array_append_val(array, val1); 302 | result = g_array_append_val(array, val2); 303 | result = g_array_insert_val(array, 0, val3); 304 | 305 | ck_assert_ptr_eq(array, result); 306 | ck_assert_int_eq(array->len, 3); 307 | 308 | ck_assert_int_eq(g_array_index(array, int, 0), 13); 309 | ck_assert_int_eq(g_array_index(array, int, 1), 5); 310 | ck_assert_int_eq(g_array_index(array, int, 2), 8); 311 | 312 | g_array_free(array, true); 313 | } 314 | END_TEST 315 | 316 | START_TEST(test_garray_insert_val_end) 317 | { 318 | GArray *array = NULL; 319 | GArray *result = NULL; 320 | 321 | int val1 = 5; 322 | int val2 = 8; 323 | int val3 = 13; 324 | 325 | array = g_array_new(false, false, sizeof(int)); 326 | result = g_array_append_val(array, val1); 327 | result = g_array_append_val(array, val2); 328 | result = g_array_insert_val(array, 2, val3); 329 | 330 | ck_assert_ptr_eq(array, result); 331 | ck_assert_int_eq(array->len, 3); 332 | 333 | ck_assert_int_eq(g_array_index(array, int, 0), 5); 334 | ck_assert_int_eq(g_array_index(array, int, 1), 8); 335 | ck_assert_int_eq(g_array_index(array, int, 2), 13); 336 | 337 | g_array_free(array, true); 338 | } 339 | END_TEST 340 | 341 | 342 | START_TEST(test_garray_insert_vals) 343 | { 344 | GArray *array = NULL; 345 | GArray *result = NULL; 346 | 347 | int vals1[] = {4, 8, 2, 7, 1, 3, 6}; 348 | int vals2[] = {12, 67, 89}; 349 | 350 | array = g_array_new(false, false, sizeof(int)); 351 | result = g_array_append_vals(array, vals1, sizeof(vals1) / sizeof(int)); 352 | result = g_array_insert_vals(array, 3, vals2, sizeof(vals2) / sizeof(int)); 353 | 354 | ck_assert_ptr_eq(array, result); 355 | ck_assert_int_eq(array->len, 10); 356 | 357 | ck_assert_int_eq(g_array_index(array, int, 0), 4); 358 | ck_assert_int_eq(g_array_index(array, int, 1), 8); 359 | ck_assert_int_eq(g_array_index(array, int, 2), 2); 360 | 361 | ck_assert_int_eq(g_array_index(array, int, 3), 12); 362 | ck_assert_int_eq(g_array_index(array, int, 4), 67); 363 | ck_assert_int_eq(g_array_index(array, int, 5), 89); 364 | 365 | ck_assert_int_eq(g_array_index(array, int, 6), 7); 366 | ck_assert_int_eq(g_array_index(array, int, 7), 1); 367 | ck_assert_int_eq(g_array_index(array, int, 8), 3); 368 | ck_assert_int_eq(g_array_index(array, int, 9), 6); 369 | 370 | g_array_free(array, true); 371 | } 372 | END_TEST 373 | 374 | START_TEST(test_garray_insert_vals_non_existing_index) 375 | { 376 | GArray *array = NULL; 377 | GArray *result = NULL; 378 | 379 | int vals1[] = {4, 8, 2, 7, 1, 3, 6}; 380 | int vals2[] = {12, 67, 89}; 381 | 382 | array = g_array_new(false, false, sizeof(int)); 383 | result = g_array_append_vals(array, vals1, sizeof(vals1) / sizeof(int)); 384 | result = g_array_insert_vals(array, 10, vals2, sizeof(vals2) / sizeof(int)); 385 | 386 | ck_assert_ptr_eq(array, result); 387 | ck_assert_int_eq(array->len, 13); 388 | 389 | ck_assert_int_eq(g_array_index(array, int, 0), 4); 390 | ck_assert_int_eq(g_array_index(array, int, 1), 8); 391 | ck_assert_int_eq(g_array_index(array, int, 2), 2); 392 | ck_assert_int_eq(g_array_index(array, int, 3), 7); 393 | ck_assert_int_eq(g_array_index(array, int, 4), 1); 394 | ck_assert_int_eq(g_array_index(array, int, 5), 3); 395 | ck_assert_int_eq(g_array_index(array, int, 6), 6); 396 | 397 | ck_assert_int_eq(g_array_index(array, int, 10), 12); 398 | ck_assert_int_eq(g_array_index(array, int, 11), 67); 399 | ck_assert_int_eq(g_array_index(array, int, 12), 89); 400 | 401 | g_array_free(array, true); 402 | } 403 | END_TEST 404 | 405 | START_TEST(test_garray_insert_vals_non_existing_index_clear) 406 | { 407 | GArray *array = NULL; 408 | GArray *result = NULL; 409 | 410 | int vals1[] = {4, 8, 2, 7, 1, 3, 6}; 411 | int vals2[] = {12, 67, 89}; 412 | 413 | array = g_array_new(false, true, sizeof(int)); 414 | result = g_array_append_vals(array, vals1, sizeof(vals1) / sizeof(int)); 415 | result = g_array_insert_vals(array, 10, vals2, sizeof(vals2) / sizeof(int)); 416 | 417 | ck_assert_ptr_eq(array, result); 418 | ck_assert_int_eq(array->len, 13); 419 | 420 | ck_assert_int_eq(g_array_index(array, int, 0), 4); 421 | ck_assert_int_eq(g_array_index(array, int, 1), 8); 422 | ck_assert_int_eq(g_array_index(array, int, 2), 2); 423 | ck_assert_int_eq(g_array_index(array, int, 3), 7); 424 | ck_assert_int_eq(g_array_index(array, int, 4), 1); 425 | ck_assert_int_eq(g_array_index(array, int, 5), 3); 426 | ck_assert_int_eq(g_array_index(array, int, 6), 6); 427 | 428 | ck_assert_int_eq(g_array_index(array, int, 7), 0); 429 | ck_assert_int_eq(g_array_index(array, int, 8), 0); 430 | ck_assert_int_eq(g_array_index(array, int, 9), 0); 431 | 432 | ck_assert_int_eq(g_array_index(array, int, 10), 12); 433 | ck_assert_int_eq(g_array_index(array, int, 11), 67); 434 | ck_assert_int_eq(g_array_index(array, int, 12), 89); 435 | 436 | g_array_free(array, true); 437 | } 438 | END_TEST 439 | 440 | START_TEST(test_garray_remove_index) 441 | { 442 | GArray *array = NULL; 443 | GArray *result = NULL; 444 | 445 | int vals[] = {4, 8, 2, 7, 1, 3, 6}; 446 | 447 | array = g_array_new(false, false, sizeof(int)); 448 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 449 | result = g_array_remove_index(array, 4); 450 | 451 | ck_assert_ptr_eq(array, result); 452 | ck_assert_int_eq(array->len, 6); 453 | 454 | ck_assert_int_eq(g_array_index(array, int, 0), 4); 455 | ck_assert_int_eq(g_array_index(array, int, 1), 8); 456 | ck_assert_int_eq(g_array_index(array, int, 2), 2); 457 | ck_assert_int_eq(g_array_index(array, int, 3), 7); 458 | ck_assert_int_eq(g_array_index(array, int, 4), 3); 459 | ck_assert_int_eq(g_array_index(array, int, 5), 6); 460 | 461 | g_array_free(array, true); 462 | } 463 | END_TEST 464 | 465 | START_TEST(test_garray_remove_index_beginning) 466 | { 467 | GArray *array = NULL; 468 | GArray *result = NULL; 469 | 470 | int vals[] = {4, 8, 2, 7, 1, 3, 6}; 471 | 472 | array = g_array_new(false, false, sizeof(int)); 473 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 474 | result = g_array_remove_index(array, 0); 475 | 476 | ck_assert_ptr_eq(array, result); 477 | ck_assert_int_eq(array->len, 6); 478 | 479 | ck_assert_int_eq(g_array_index(array, int, 0), 8); 480 | ck_assert_int_eq(g_array_index(array, int, 1), 2); 481 | ck_assert_int_eq(g_array_index(array, int, 2), 7); 482 | ck_assert_int_eq(g_array_index(array, int, 3), 1); 483 | ck_assert_int_eq(g_array_index(array, int, 4), 3); 484 | ck_assert_int_eq(g_array_index(array, int, 5), 6); 485 | 486 | g_array_free(array, true); 487 | } 488 | END_TEST 489 | 490 | START_TEST(test_garray_remove_index_end) 491 | { 492 | GArray *array = NULL; 493 | GArray *result = NULL; 494 | 495 | int vals[] = {4, 8, 2, 7, 1, 3, 6}; 496 | 497 | array = g_array_new(false, false, sizeof(int)); 498 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 499 | result = g_array_remove_index(array, 6); 500 | 501 | ck_assert_ptr_eq(array, result); 502 | ck_assert_int_eq(array->len, 6); 503 | 504 | ck_assert_int_eq(g_array_index(array, int, 0), 4); 505 | ck_assert_int_eq(g_array_index(array, int, 1), 8); 506 | ck_assert_int_eq(g_array_index(array, int, 2), 2); 507 | ck_assert_int_eq(g_array_index(array, int, 3), 7); 508 | ck_assert_int_eq(g_array_index(array, int, 4), 1); 509 | ck_assert_int_eq(g_array_index(array, int, 5), 3); 510 | 511 | g_array_free(array, true); 512 | } 513 | END_TEST 514 | 515 | START_TEST(test_garray_remove_index_fast) 516 | { 517 | GArray *array = NULL; 518 | GArray *result = NULL; 519 | 520 | int vals[] = {4, 8, 2, 7, 1, 3, 6}; 521 | 522 | array = g_array_new(false, false, sizeof(int)); 523 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 524 | result = g_array_remove_index_fast(array, 2); 525 | 526 | ck_assert_ptr_eq(array, result); 527 | ck_assert_int_eq(array->len, 6); 528 | 529 | ck_assert_int_eq(g_array_index(array, int, 0), 4); 530 | ck_assert_int_eq(g_array_index(array, int, 1), 8); 531 | ck_assert_int_eq(g_array_index(array, int, 2), 6); 532 | ck_assert_int_eq(g_array_index(array, int, 3), 7); 533 | ck_assert_int_eq(g_array_index(array, int, 4), 1); 534 | ck_assert_int_eq(g_array_index(array, int, 5), 3); 535 | 536 | g_array_free(array, true); 537 | } 538 | END_TEST 539 | 540 | START_TEST(test_garray_remove_range) 541 | { 542 | GArray *array = NULL; 543 | GArray *result = NULL; 544 | 545 | int vals[] = {4, 8, 2, 7, 1, 3, 6}; 546 | 547 | array = g_array_new(false, false, sizeof(int)); 548 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 549 | result = g_array_remove_range(array, 1, 3); 550 | 551 | ck_assert_ptr_eq(array, result); 552 | ck_assert_int_eq(array->len, 4); 553 | 554 | ck_assert_int_eq(g_array_index(array, int, 0), 4); 555 | ck_assert_int_eq(g_array_index(array, int, 1), 1); 556 | ck_assert_int_eq(g_array_index(array, int, 2), 3); 557 | ck_assert_int_eq(g_array_index(array, int, 3), 6); 558 | 559 | g_array_free(array, true); 560 | } 561 | END_TEST 562 | 563 | START_TEST(test_garray_sort) 564 | { 565 | GArray *array = NULL; 566 | int vals[] = {4, 8, 5, 2, 7, 9, 1, 3, 0, 6}; 567 | 568 | array = g_array_new(false, false, sizeof(int)); 569 | 570 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 571 | g_array_sort(array, compare_int); 572 | 573 | ck_assert_int_eq(array->len, 10); 574 | 575 | ck_assert_int_eq(g_array_index(array, int, 0), 0); 576 | ck_assert_int_eq(g_array_index(array, int, 1), 1); 577 | ck_assert_int_eq(g_array_index(array, int, 2), 2); 578 | ck_assert_int_eq(g_array_index(array, int, 3), 3); 579 | ck_assert_int_eq(g_array_index(array, int, 4), 4); 580 | ck_assert_int_eq(g_array_index(array, int, 5), 5); 581 | ck_assert_int_eq(g_array_index(array, int, 6), 6); 582 | ck_assert_int_eq(g_array_index(array, int, 7), 7); 583 | ck_assert_int_eq(g_array_index(array, int, 8), 8); 584 | ck_assert_int_eq(g_array_index(array, int, 9), 9); 585 | 586 | g_array_free(array, true); 587 | } 588 | END_TEST 589 | 590 | START_TEST(test_garray_sort_with_data) 591 | { 592 | GArray *array = NULL; 593 | int vals[] = {4, 8, 5, 2, 7, 9, 1, 3, 0, 6}; 594 | int user_data = 42; 595 | 596 | array = g_array_new(false, false, sizeof(int)); 597 | 598 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 599 | g_array_sort_with_data(array, compare_int_with_data, &user_data); 600 | 601 | ck_assert_int_eq(array->len, 10); 602 | 603 | ck_assert_int_eq(g_array_index(array, int, 0), 0); 604 | ck_assert_int_eq(g_array_index(array, int, 1), 1); 605 | ck_assert_int_eq(g_array_index(array, int, 2), 2); 606 | ck_assert_int_eq(g_array_index(array, int, 3), 3); 607 | ck_assert_int_eq(g_array_index(array, int, 4), 4); 608 | ck_assert_int_eq(g_array_index(array, int, 5), 5); 609 | ck_assert_int_eq(g_array_index(array, int, 6), 6); 610 | ck_assert_int_eq(g_array_index(array, int, 7), 7); 611 | ck_assert_int_eq(g_array_index(array, int, 8), 8); 612 | ck_assert_int_eq(g_array_index(array, int, 9), 9); 613 | 614 | g_array_free(array, true); 615 | } 616 | END_TEST 617 | 618 | START_TEST(test_garray_binary_search_null) 619 | { 620 | GArray *array = NULL; 621 | bool result; 622 | int target = 5; 623 | unsigned int match_index = 0; 624 | 625 | array = g_array_new(false, false, sizeof(int)); 626 | 627 | result = g_array_binary_search(NULL, &target, compare_int, &match_index); 628 | ck_assert_int_eq(result, false); 629 | 630 | result = g_array_binary_search(NULL, &target, compare_int, &match_index); 631 | ck_assert_int_eq(result, false); 632 | 633 | g_array_free(array, true); 634 | } 635 | END_TEST 636 | 637 | START_TEST(test_garray_binary_search_empty) 638 | { 639 | GArray *array = NULL; 640 | bool result; 641 | 642 | array = g_array_new(false, false, sizeof(int)); 643 | 644 | unsigned int match_index = 0; 645 | int target = 5; 646 | 647 | result = g_array_binary_search(array, &target, compare_int, &match_index); 648 | 649 | ck_assert_int_eq(result, false); 650 | 651 | g_array_free(array, true); 652 | } 653 | END_TEST 654 | 655 | START_TEST(test_garray_binary_search_one) 656 | { 657 | GArray *array = NULL; 658 | int target; 659 | bool result; 660 | 661 | array = g_array_new(false, false, sizeof(int)); 662 | 663 | int val = 5; 664 | g_array_append_val(array, val); 665 | 666 | unsigned int match_index = 42; 667 | 668 | // smaller 669 | target = 4; 670 | result = g_array_binary_search(array, &target, compare_int, &match_index); 671 | ck_assert_int_eq(result, false); 672 | 673 | // bigger 674 | target = 6; 675 | result = g_array_binary_search(array, &target, compare_int, &match_index); 676 | ck_assert_int_eq(result, false); 677 | 678 | // match 679 | target = 5; 680 | result = g_array_binary_search(array, &target, compare_int, &match_index); 681 | ck_assert_int_eq(result, true); 682 | ck_assert_int_eq(match_index, 0); 683 | 684 | g_array_free(array, true); 685 | } 686 | END_TEST 687 | 688 | START_TEST(test_garray_binary_search_two) 689 | { 690 | GArray *array = NULL; 691 | int target; 692 | bool result; 693 | int vals[] = {4, 8}; 694 | 695 | array = g_array_new(false, false, sizeof(int)); 696 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 697 | 698 | unsigned int match_index = 42; 699 | 700 | // smaller 701 | target = 3; 702 | result = g_array_binary_search(array, &target, compare_int, &match_index); 703 | ck_assert_int_eq(result, false); 704 | 705 | // in between 706 | target = 5; 707 | result = g_array_binary_search(array, &target, compare_int, &match_index); 708 | ck_assert_int_eq(result, false); 709 | 710 | target = 6; 711 | result = g_array_binary_search(array, &target, compare_int, &match_index); 712 | ck_assert_int_eq(result, false); 713 | 714 | target = 7; 715 | result = g_array_binary_search(array, &target, compare_int, &match_index); 716 | ck_assert_int_eq(result, false); 717 | 718 | // bigger 719 | target = 9; 720 | result = g_array_binary_search(array, &target, compare_int, &match_index); 721 | ck_assert_int_eq(result, false); 722 | 723 | // match 1 724 | target = 4; 725 | result = g_array_binary_search(array, &target, compare_int, &match_index); 726 | ck_assert_int_eq(result, true); 727 | ck_assert_int_eq(match_index, 0); 728 | 729 | // match 2 730 | target = 8; 731 | result = g_array_binary_search(array, &target, compare_int, &match_index); 732 | ck_assert_int_eq(result, true); 733 | ck_assert_int_eq(match_index, 1); 734 | 735 | g_array_free(array, true); 736 | } 737 | END_TEST 738 | 739 | START_TEST(test_garray_binary_search_three) 740 | { 741 | GArray *array = NULL; 742 | int target; 743 | bool result; 744 | int vals[] = {4, 6, 8}; 745 | 746 | array = g_array_new(false, false, sizeof(int)); 747 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 748 | 749 | unsigned int match_index = 42; 750 | 751 | // smaller 752 | target = 3; 753 | result = g_array_binary_search(array, &target, compare_int, &match_index); 754 | ck_assert_int_eq(result, false); 755 | 756 | // in between 757 | target = 5; 758 | result = g_array_binary_search(array, &target, compare_int, &match_index); 759 | ck_assert_int_eq(result, false); 760 | 761 | target = 7; 762 | result = g_array_binary_search(array, &target, compare_int, &match_index); 763 | ck_assert_int_eq(result, false); 764 | 765 | // bigger 766 | target = 9; 767 | result = g_array_binary_search(array, &target, compare_int, &match_index); 768 | ck_assert_int_eq(result, false); 769 | 770 | // match 1 771 | target = 4; 772 | result = g_array_binary_search(array, &target, compare_int, &match_index); 773 | ck_assert_int_eq(result, true); 774 | ck_assert_int_eq(match_index, 0); 775 | 776 | // match 2 777 | target = 6; 778 | result = g_array_binary_search(array, &target, compare_int, &match_index); 779 | ck_assert_int_eq(result, true); 780 | ck_assert_int_eq(match_index, 1); 781 | 782 | // match 3 783 | target = 8; 784 | result = g_array_binary_search(array, &target, compare_int, &match_index); 785 | ck_assert_int_eq(result, true); 786 | ck_assert_int_eq(match_index, 2); 787 | 788 | g_array_free(array, true); 789 | } 790 | END_TEST 791 | 792 | START_TEST(test_garray_binary_search_same) 793 | { 794 | GArray *array = NULL; 795 | int target; 796 | bool result; 797 | int vals[] = {10, 10, 10, 10, 10, 10, 10, 10, 10, 10}; 798 | 799 | array = g_array_new(false, false, sizeof(int)); 800 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 801 | 802 | unsigned int match_index = 42; 803 | 804 | // searching for 10 should give us the left-most element (index 0) 805 | target = 10; 806 | result = g_array_binary_search(array, &target, compare_int, &match_index); 807 | ck_assert_int_eq(result, true); 808 | ck_assert_int_eq(match_index, 0); 809 | 810 | g_array_free(array, true); 811 | } 812 | END_TEST 813 | 814 | START_TEST(test_garray_binary_search_extensive) 815 | { 816 | GArray *array = NULL; 817 | bool result; 818 | 819 | array = g_array_new(false, false, sizeof(int)); 820 | 821 | const int max = 100; 822 | unsigned int match_index = 42; 823 | 824 | for (int i = 0; i < max; i++) { 825 | g_array_append_val(array, i); 826 | 827 | int j; 828 | for (j = 0; j <= i; j++) { 829 | result = g_array_binary_search(array, &j, compare_int, &match_index); 830 | ck_assert_int_eq(result, true); 831 | ck_assert_int_eq(match_index, j); 832 | } 833 | 834 | // the next element that hasn't been appended yet should not be found 835 | j++; 836 | result = g_array_binary_search(array, &j, compare_int, &match_index); 837 | ck_assert_int_eq(result, false); 838 | } 839 | 840 | g_array_free(array, true); 841 | } 842 | END_TEST 843 | 844 | START_TEST(test_garray_set_size) 845 | { 846 | GArray *array = NULL; 847 | GArray *result = NULL; 848 | 849 | int vals[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; 850 | array = g_array_new(false, false, sizeof(int)); 851 | 852 | result = g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 853 | 854 | ck_assert_ptr_eq(array, result); 855 | ck_assert_int_eq(array->len, 10); 856 | 857 | result = g_array_set_size(array, 20); 858 | ck_assert_ptr_eq(array, result); 859 | ck_assert_int_eq(array->len, 20); 860 | ck_assert_int_eq(array->_allocated_elements, 20); 861 | 862 | ck_assert_int_eq(g_array_index(array, int, 0), 0); 863 | ck_assert_int_eq(g_array_index(array, int, 1), 1); 864 | ck_assert_int_eq(g_array_index(array, int, 2), 2); 865 | ck_assert_int_eq(g_array_index(array, int, 3), 3); 866 | ck_assert_int_eq(g_array_index(array, int, 4), 4); 867 | ck_assert_int_eq(g_array_index(array, int, 5), 5); 868 | ck_assert_int_eq(g_array_index(array, int, 6), 6); 869 | ck_assert_int_eq(g_array_index(array, int, 7), 7); 870 | ck_assert_int_eq(g_array_index(array, int, 8), 8); 871 | ck_assert_int_eq(g_array_index(array, int, 9), 9); 872 | 873 | result = g_array_set_size(array, 5); 874 | ck_assert_ptr_eq(array, result); 875 | ck_assert_int_eq(array->len, 5); 876 | ck_assert_int_eq(array->_allocated_elements, 20); 877 | 878 | ck_assert_int_eq(g_array_index(array, int, 0), 0); 879 | ck_assert_int_eq(g_array_index(array, int, 1), 1); 880 | ck_assert_int_eq(g_array_index(array, int, 2), 2); 881 | ck_assert_int_eq(g_array_index(array, int, 3), 3); 882 | ck_assert_int_eq(g_array_index(array, int, 4), 4); 883 | 884 | g_array_free(array, true); 885 | } 886 | END_TEST 887 | 888 | START_TEST(test_garray_zero_termination) 889 | { 890 | GArray *array = NULL; 891 | int val; 892 | 893 | // 894 | // directly after creation 895 | // 896 | array = g_array_new(true, false, sizeof(int)); 897 | 898 | ck_assert_int_eq(array->len, 0); 899 | ck_assert_int_eq(array->_allocated_elements, 1); 900 | 901 | // zero terminated? 902 | ck_assert_int_eq(g_array_index(array, int, array->len), 0); 903 | 904 | // 905 | // after appending one value 906 | // 907 | val = 5; 908 | g_array_append_val(array, val); 909 | 910 | ck_assert_int_eq(array->len, 1); 911 | ck_assert_int_eq(array->_allocated_elements, 2); 912 | ck_assert_int_eq(g_array_index(array, int, 0), 5); 913 | 914 | // zero terminated? 915 | ck_assert_int_eq(g_array_index(array, int, array->len), 0); 916 | 917 | // 918 | // after prepending one value 919 | // 920 | val = 4; 921 | g_array_prepend_val(array, val); 922 | 923 | ck_assert_int_eq(array->len, 2); 924 | ck_assert_int_eq(array->_allocated_elements, 3); 925 | ck_assert_int_eq(g_array_index(array, int, 0), 4); 926 | ck_assert_int_eq(g_array_index(array, int, 1), 5); 927 | 928 | // zero terminated? 929 | ck_assert_int_eq(g_array_index(array, int, array->len), 0); 930 | 931 | // 932 | // after appending multiple values 933 | // 934 | int vals1[] = {89, 23, 11, 9}; 935 | g_array_append_vals(array, vals1, sizeof(vals1) / sizeof(int)); 936 | 937 | ck_assert_int_eq(array->len, 6); 938 | ck_assert_int_eq(array->_allocated_elements, 7); 939 | 940 | // zero terminated? 941 | ck_assert_int_eq(g_array_index(array, int, array->len), 0); 942 | 943 | // 944 | // after prepending multiple values 945 | // 946 | int vals2[] = {45, 28, 77, 31}; 947 | g_array_prepend_vals(array, vals2, sizeof(vals2) / sizeof(int)); 948 | 949 | ck_assert_int_eq(array->len, 10); 950 | ck_assert_int_eq(array->_allocated_elements, 11); 951 | 952 | // zero terminated? 953 | ck_assert_int_eq(g_array_index(array, int, array->len), 0); 954 | 955 | // 956 | // after removing an element 957 | // 958 | g_array_remove_index(array, 5); 959 | 960 | ck_assert_int_eq(array->len, 9); 961 | ck_assert_int_eq(array->_allocated_elements, 11); 962 | 963 | ck_assert_int_eq(g_array_index(array, int, 0), 45); 964 | ck_assert_int_eq(g_array_index(array, int, 1), 28); 965 | ck_assert_int_eq(g_array_index(array, int, 2), 77); 966 | ck_assert_int_eq(g_array_index(array, int, 3), 31); 967 | ck_assert_int_eq(g_array_index(array, int, 4), 4); 968 | ck_assert_int_eq(g_array_index(array, int, 5), 89); 969 | ck_assert_int_eq(g_array_index(array, int, 6), 23); 970 | ck_assert_int_eq(g_array_index(array, int, 7), 11); 971 | ck_assert_int_eq(g_array_index(array, int, 8), 9); 972 | 973 | // zero terminated? 974 | ck_assert_int_eq(g_array_index(array, int, array->len), 0); 975 | 976 | // 977 | // after removing an element with g_array_remove_index_fast 978 | // 979 | g_array_remove_index_fast(array, 5); 980 | 981 | ck_assert_int_eq(array->len, 8); 982 | ck_assert_int_eq(array->_allocated_elements, 11); 983 | 984 | ck_assert_int_eq(g_array_index(array, int, 0), 45); 985 | ck_assert_int_eq(g_array_index(array, int, 1), 28); 986 | ck_assert_int_eq(g_array_index(array, int, 2), 77); 987 | ck_assert_int_eq(g_array_index(array, int, 3), 31); 988 | ck_assert_int_eq(g_array_index(array, int, 4), 4); 989 | ck_assert_int_eq(g_array_index(array, int, 5), 9); 990 | ck_assert_int_eq(g_array_index(array, int, 6), 23); 991 | ck_assert_int_eq(g_array_index(array, int, 7), 11); 992 | 993 | // zero terminated? 994 | ck_assert_int_eq(g_array_index(array, int, array->len), 0); 995 | 996 | // 997 | // after removing a range of elements 998 | // 999 | g_array_remove_range(array, 3, 3); 1000 | 1001 | ck_assert_int_eq(array->len, 5); 1002 | ck_assert_int_eq(array->_allocated_elements, 11); 1003 | 1004 | ck_assert_int_eq(g_array_index(array, int, 0), 45); 1005 | ck_assert_int_eq(g_array_index(array, int, 1), 28); 1006 | ck_assert_int_eq(g_array_index(array, int, 2), 77); 1007 | ck_assert_int_eq(g_array_index(array, int, 3), 23); 1008 | ck_assert_int_eq(g_array_index(array, int, 4), 11); 1009 | 1010 | // zero terminated? 1011 | ck_assert_int_eq(g_array_index(array, int, array->len), 0); 1012 | 1013 | // 1014 | // after appending by using insert 1015 | // 1016 | int insert_val = 76; 1017 | g_array_insert_val(array, 5, insert_val); 1018 | 1019 | ck_assert_int_eq(array->len, 6); 1020 | ck_assert_int_eq(array->_allocated_elements, 11); 1021 | 1022 | ck_assert_int_eq(g_array_index(array, int, 0), 45); 1023 | ck_assert_int_eq(g_array_index(array, int, 1), 28); 1024 | ck_assert_int_eq(g_array_index(array, int, 2), 77); 1025 | ck_assert_int_eq(g_array_index(array, int, 3), 23); 1026 | ck_assert_int_eq(g_array_index(array, int, 4), 11); 1027 | ck_assert_int_eq(g_array_index(array, int, 5), 76); 1028 | 1029 | // zero terminated? 1030 | ck_assert_int_eq(g_array_index(array, int, array->len), 0); 1031 | 1032 | // 1033 | // after changing the size of the array 1034 | // 1035 | g_array_set_size(array, 20); 1036 | 1037 | ck_assert_int_eq(array->len, 20); 1038 | ck_assert_int_eq(array->_allocated_elements, 21); 1039 | 1040 | // zero terminated? 1041 | ck_assert_int_eq(g_array_index(array, int, array->len), 0); 1042 | 1043 | g_array_set_size(array, 0); 1044 | 1045 | ck_assert_int_eq(array->len, 0); 1046 | ck_assert_int_eq(array->_allocated_elements, 21); 1047 | 1048 | // zero terminated? 1049 | ck_assert_int_eq(g_array_index(array, int, array->len), 0); 1050 | 1051 | g_array_free(array, true); 1052 | } 1053 | END_TEST 1054 | 1055 | START_TEST(test_garray_free) 1056 | { 1057 | GArray *array = NULL; 1058 | 1059 | array = g_array_new(false, false, sizeof(int)); 1060 | 1061 | int vals[] = {34, 82, 43, 12, 71}; 1062 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 1063 | 1064 | char *result = g_array_free(array, true); 1065 | ck_assert_ptr_null(result); 1066 | } 1067 | END_TEST 1068 | 1069 | START_TEST(test_garray_free_without_segment) 1070 | { 1071 | GArray *array = NULL; 1072 | 1073 | array = g_array_new(false, false, sizeof(int)); 1074 | 1075 | int vals[] = {34, 82, 43, 12, 71}; 1076 | g_array_append_vals(array, vals, sizeof(vals) / sizeof(int)); 1077 | 1078 | char *result = g_array_free(array, false); 1079 | ck_assert_ptr_nonnull(result); 1080 | 1081 | free(result); 1082 | } 1083 | END_TEST 1084 | 1085 | Suite* garray_suite(void) 1086 | { 1087 | Suite *s; 1088 | TCase *tc_core; 1089 | 1090 | s = suite_create("GArray"); 1091 | 1092 | /* Core test case */ 1093 | tc_core = tcase_create("Core"); 1094 | 1095 | tcase_add_test(tc_core, test_garray_new); 1096 | tcase_add_test(tc_core, test_garray_sized_new); 1097 | 1098 | tcase_add_test(tc_core, test_garray_steal); 1099 | tcase_add_test(tc_core, test_garray_steal_zero_terminated); 1100 | 1101 | tcase_add_test(tc_core, test_garray_append_val); 1102 | tcase_add_test(tc_core, test_garray_append_val_many); 1103 | tcase_add_test(tc_core, test_garray_append_vals); 1104 | 1105 | tcase_add_test(tc_core, test_garray_prepend_val); 1106 | tcase_add_test(tc_core, test_garray_prepend_val_many); 1107 | tcase_add_test(tc_core, test_garray_prepend_vals); 1108 | 1109 | tcase_add_test(tc_core, test_garray_insert_val); 1110 | tcase_add_test(tc_core, test_garray_insert_val_beginning); 1111 | tcase_add_test(tc_core, test_garray_insert_val_end); 1112 | tcase_add_test(tc_core, test_garray_insert_vals); 1113 | tcase_add_test(tc_core, test_garray_insert_vals_non_existing_index); 1114 | tcase_add_test(tc_core, test_garray_insert_vals_non_existing_index_clear); 1115 | 1116 | tcase_add_test(tc_core, test_garray_remove_index); 1117 | tcase_add_test(tc_core, test_garray_remove_index_beginning); 1118 | tcase_add_test(tc_core, test_garray_remove_index_end); 1119 | 1120 | tcase_add_test(tc_core, test_garray_remove_index_fast); 1121 | 1122 | tcase_add_test(tc_core, test_garray_remove_range); 1123 | 1124 | tcase_add_test(tc_core, test_garray_sort); 1125 | tcase_add_test(tc_core, test_garray_sort_with_data); 1126 | 1127 | tcase_add_test(tc_core, test_garray_binary_search_null); 1128 | tcase_add_test(tc_core, test_garray_binary_search_empty); 1129 | tcase_add_test(tc_core, test_garray_binary_search_one); 1130 | tcase_add_test(tc_core, test_garray_binary_search_two); 1131 | tcase_add_test(tc_core, test_garray_binary_search_three); 1132 | tcase_add_test(tc_core, test_garray_binary_search_same); 1133 | tcase_add_test(tc_core, test_garray_binary_search_extensive); 1134 | 1135 | tcase_add_test(tc_core, test_garray_set_size); 1136 | 1137 | tcase_add_test(tc_core, test_garray_zero_termination); 1138 | 1139 | tcase_add_test(tc_core, test_garray_free); 1140 | tcase_add_test(tc_core, test_garray_free_without_segment); 1141 | 1142 | suite_add_tcase(s, tc_core); 1143 | 1144 | return s; 1145 | } 1146 | 1147 | int main(int argc, char **argv) 1148 | { 1149 | int number_failed; 1150 | Suite *s; 1151 | SRunner *sr; 1152 | 1153 | s = garray_suite(); 1154 | sr = srunner_create(s); 1155 | 1156 | srunner_run_all(sr, CK_NORMAL); 1157 | number_failed = srunner_ntests_failed(sr); 1158 | srunner_free(sr); 1159 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 1160 | } 1161 | -------------------------------------------------------------------------------- /tests/test_ghashtable.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | #define _CLIB_IMPL 1 8 | #include "ghashtable.h" 9 | 10 | // declare internal functions of GHashTable here so we can test them 11 | uint32_t _g_hash_table_find_free_slot(GHashTable *hash_table, uint32_t start_slot, void *key); 12 | uint32_t _g_hash_table_calc_start_slot(GHashTable *hash_table, void *key); 13 | uint32_t _g_hash_table_find_slot_by_key(GHashTable *hash_table, void *key, uint32_t start_slot, bool *ret_found); 14 | 15 | // Fake hash function for testing 16 | // 17 | // The static hash value makes the location of the data in the hash table 18 | // predictable 19 | uint32_t fake_int_hash_0(void *v) 20 | { 21 | return 0; 22 | } 23 | 24 | int keys_freed = 0; 25 | void* last_freed_key = NULL; 26 | int values_freed = 0; 27 | void* last_freed_value = NULL; 28 | 29 | void free_key_dummy(void *data) 30 | { 31 | keys_freed++; 32 | last_freed_key = data; 33 | } 34 | 35 | void free_value_dummy(void *data) 36 | { 37 | values_freed++; 38 | last_freed_value = data; 39 | } 40 | 41 | START_TEST(test_gint_hash) 42 | { 43 | ck_assert_int_eq(g_int_hash((void*) 56), 1332371660); 44 | } 45 | END_TEST 46 | 47 | START_TEST(test_ghashtable_new) 48 | { 49 | GHashTable *htable = NULL; 50 | htable = g_hash_table_new(g_int_hash, g_int_equal); 51 | 52 | ck_assert_ptr_nonnull(htable); 53 | ck_assert_int_eq(htable->num_slots, GHASHTABLE_MIN_SLOTS); 54 | ck_assert_int_eq(htable->num_used, 0); 55 | ck_assert_ptr_eq(htable->hash_func, g_int_hash); 56 | ck_assert_ptr_eq(htable->key_equal_func, g_int_equal); 57 | ck_assert_ptr_eq(htable->key_destroy_func, NULL); 58 | ck_assert_ptr_eq(htable->value_destroy_func, NULL); 59 | ck_assert_ptr_nonnull(htable->slots); 60 | 61 | g_hash_table_destroy(htable); 62 | } 63 | END_TEST 64 | 65 | START_TEST(test_ghashtable_new_full) 66 | { 67 | GHashTable *htable = NULL; 68 | htable = g_hash_table_new_full(g_int_hash, g_int_equal, free_key_dummy, free_value_dummy); 69 | 70 | ck_assert_ptr_nonnull(htable); 71 | ck_assert_int_eq(htable->num_slots, GHASHTABLE_MIN_SLOTS); 72 | ck_assert_int_eq(htable->num_used, 0); 73 | ck_assert_ptr_eq(htable->hash_func, g_int_hash); 74 | ck_assert_ptr_eq(htable->key_equal_func, g_int_equal); 75 | ck_assert_ptr_eq(htable->key_destroy_func, free_key_dummy); 76 | ck_assert_ptr_eq(htable->value_destroy_func, free_value_dummy); 77 | ck_assert_ptr_nonnull(htable->slots); 78 | 79 | g_hash_table_destroy(htable); 80 | } 81 | END_TEST 82 | 83 | START_TEST(test_ghashtable_calc_start_slot) 84 | { 85 | GHashTable *htable = NULL; 86 | 87 | htable = g_hash_table_new(g_int_hash, g_int_equal); 88 | ck_assert_int_eq(_g_hash_table_calc_start_slot(htable, (void*) 56), 12); 89 | ck_assert_int_eq(_g_hash_table_calc_start_slot(htable, (void*) 67), 49); 90 | ck_assert_int_eq(_g_hash_table_calc_start_slot(htable, (void*) 23), 49); 91 | 92 | g_hash_table_destroy(htable); 93 | } 94 | END_TEST 95 | 96 | START_TEST(test_ghashtable_find_free_slot) 97 | { 98 | GHashTable *htable = NULL; 99 | uint32_t slot = 0; 100 | 101 | htable = g_hash_table_new(g_int_hash, g_int_equal); 102 | 103 | slot = _g_hash_table_find_free_slot(htable, 10, NULL); 104 | ck_assert_int_eq(slot, 10); 105 | 106 | // mark slot 10 as used 107 | htable->slots[10].used = true; 108 | 109 | slot = _g_hash_table_find_free_slot(htable, 10, NULL); 110 | ck_assert_int_eq(slot, 11); 111 | 112 | // mark slot 11 as used 113 | htable->slots[11].used = true; 114 | 115 | slot = _g_hash_table_find_free_slot(htable, 10, NULL); 116 | ck_assert_int_eq(slot, 12); 117 | 118 | // if we reach the end of the hash table the search should restart from the 119 | // beginning 120 | 121 | slot = _g_hash_table_find_free_slot(htable, htable->num_slots - 1, NULL); 122 | ck_assert_int_eq(slot, htable->num_slots - 1); 123 | 124 | // mark last slot as used 125 | htable->slots[htable->num_slots - 1].used = true; 126 | 127 | slot = _g_hash_table_find_free_slot(htable, htable->num_slots - 1, NULL); 128 | ck_assert_int_eq(slot, 0); 129 | 130 | htable->slots[0].used = true; 131 | 132 | slot = _g_hash_table_find_free_slot(htable, htable->num_slots - 1, NULL); 133 | ck_assert_int_eq(slot, 1); 134 | 135 | g_hash_table_destroy(htable); 136 | } 137 | END_TEST 138 | 139 | START_TEST(test_ghashtable_insert_lookup) 140 | { 141 | GHashTable *htable = NULL; 142 | htable = g_hash_table_new(g_int_hash, g_int_equal); 143 | 144 | ck_assert_int_eq(htable->num_used, 0); 145 | 146 | g_hash_table_insert(htable, (void*) 42, (void*) "Answer to everything"); 147 | ck_assert_int_eq(htable->num_used, 1); 148 | char *result = g_hash_table_lookup(htable, (void*) 42); 149 | ck_assert_ptr_nonnull(result); 150 | ck_assert_str_eq(result, "Answer to everything"); 151 | 152 | result = NULL; 153 | g_hash_table_insert(htable, (void*) 56, (void*) "Fifty Six"); 154 | g_hash_table_insert(htable, (void*) 67, (void*) "Sixty Seven"); 155 | g_hash_table_insert(htable, (void*) 23, (void*) "Twenty Three"); 156 | 157 | ck_assert_int_eq(htable->num_used, 4); 158 | 159 | result = g_hash_table_lookup(htable, (void*) 42); 160 | ck_assert_str_eq(result, "Answer to everything"); 161 | 162 | result = g_hash_table_lookup(htable, (void*) 23); 163 | ck_assert_str_eq(result, "Twenty Three"); 164 | 165 | result = g_hash_table_lookup(htable, (void*) 67); 166 | ck_assert_str_eq(result, "Sixty Seven"); 167 | 168 | result = g_hash_table_lookup(htable, (void*) 56); 169 | ck_assert_str_eq(result, "Fifty Six"); 170 | 171 | g_hash_table_destroy(htable); 172 | } 173 | END_TEST 174 | 175 | uint32_t foreach_num_values = 3; 176 | 177 | struct KeyValuePair { 178 | void *key; 179 | void *value; 180 | bool seen; 181 | }; 182 | 183 | #define FOREACH_NUM_VALUES 3 184 | 185 | void test_foreach(void *key, void *value, void *user_data) 186 | { 187 | static bool first = true; 188 | static struct KeyValuePair expected_values[FOREACH_NUM_VALUES]; 189 | 190 | if (first) { 191 | expected_values[0].key = (void*) 56; 192 | expected_values[0].value = (void*) "Fifty Six"; 193 | expected_values[0].seen = false; 194 | 195 | expected_values[1].key = (void*) 67; 196 | expected_values[1].value = (void*) "Sixty Seven"; 197 | expected_values[1].seen = false; 198 | 199 | expected_values[2].key = (void*) 23; 200 | expected_values[2].value = (void*) "Twenty Three"; 201 | expected_values[2].seen = false; 202 | 203 | first = false; 204 | } 205 | 206 | ck_assert_str_eq("USER DATA", user_data); 207 | for (int i = 0; i < FOREACH_NUM_VALUES; i++) { 208 | if (key != expected_values[i].key) { 209 | continue; 210 | } 211 | 212 | ck_assert_str_eq(value, expected_values[i].value); 213 | ck_assert_int_eq(expected_values[i].seen, false); 214 | expected_values[i].seen = true; 215 | foreach_num_values--; 216 | break; 217 | } 218 | } 219 | 220 | START_TEST(test_ghashtable_foreach) 221 | { 222 | GHashTable *htable = NULL; 223 | htable = g_hash_table_new(g_int_hash, g_int_equal); 224 | 225 | g_hash_table_insert(htable, (void*) 56, (void*) "Fifty Six"); 226 | g_hash_table_insert(htable, (void*) 67, (void*) "Sixty Seven"); 227 | g_hash_table_insert(htable, (void*) 23, (void*) "Twenty Three"); 228 | 229 | char *user_data = "USER DATA"; 230 | ck_assert_int_eq(foreach_num_values, 3); 231 | g_hash_table_foreach(htable, test_foreach, user_data); 232 | ck_assert_int_eq(foreach_num_values, 0); 233 | 234 | g_hash_table_destroy(htable); 235 | } 236 | END_TEST 237 | 238 | START_TEST(test_ghashtable_remove) 239 | { 240 | GHashTable *htable = NULL; 241 | htable = g_hash_table_new(g_int_hash, g_int_equal); 242 | 243 | ck_assert_int_eq(htable->num_used, 0); 244 | 245 | g_hash_table_insert(htable, (void*) 56, (void*) "Fifty Six"); 246 | g_hash_table_insert(htable, (void*) 67, (void*) "Sixty Seven"); 247 | g_hash_table_insert(htable, (void*) 23, (void*) "Twenty Three"); 248 | 249 | ck_assert_int_eq(htable->num_used, 3); 250 | 251 | g_hash_table_remove(htable, (void*) 56); 252 | ck_assert_int_eq(htable->num_used, 2); 253 | 254 | char *result = g_hash_table_lookup(htable, (void*) 56); 255 | ck_assert_ptr_null(result); 256 | 257 | result = g_hash_table_lookup(htable, (void*) 67); 258 | ck_assert_str_eq(result, "Sixty Seven"); 259 | 260 | result = g_hash_table_lookup(htable, (void*) 23); 261 | ck_assert_str_eq(result, "Twenty Three"); 262 | 263 | g_hash_table_destroy(htable); 264 | } 265 | END_TEST 266 | 267 | START_TEST(test_ghashtable_lookup_after_remove) 268 | { 269 | GHashTable *htable = NULL; 270 | htable = g_hash_table_new(fake_int_hash_0, g_int_equal); 271 | 272 | ck_assert_int_eq(htable->num_used, 0); 273 | g_hash_table_insert(htable, (void*) 5, (void*) "Five"); 274 | g_hash_table_insert(htable, (void*) 6, (void*) "Six"); 275 | g_hash_table_insert(htable, (void*) 7, (void*) "Seven"); 276 | ck_assert_int_eq(htable->num_used, 3); 277 | 278 | g_hash_table_remove(htable, (void*) 6); 279 | ck_assert_int_eq(htable->num_used, 2); 280 | 281 | char *result = g_hash_table_lookup(htable, (void*) 7); 282 | ck_assert_str_eq(result, "Seven"); 283 | 284 | g_hash_table_destroy(htable); 285 | } 286 | END_TEST 287 | 288 | START_TEST(test_ghashtable_insert_after_remove) 289 | { 290 | GHashTable *htable = NULL; 291 | htable = g_hash_table_new(fake_int_hash_0, g_int_equal); 292 | 293 | ck_assert_int_eq(htable->num_used, 0); 294 | g_hash_table_insert(htable, (void*) 5, (void*) "Five"); 295 | g_hash_table_insert(htable, (void*) 6, (void*) "Six"); 296 | g_hash_table_insert(htable, (void*) 7, (void*) "Seven"); 297 | ck_assert_int_eq(htable->num_used, 3); 298 | 299 | g_hash_table_remove(htable, (void*) 6); 300 | ck_assert_int_eq(htable->num_used, 2); 301 | 302 | g_hash_table_insert(htable, (void*) 8, (void*) "Eight"); 303 | ck_assert_int_eq(htable->num_used, 3); 304 | 305 | // old entry for 6 should be reused 306 | ck_assert_int_eq((uint32_t) (uint64_t) htable->slots[1].key, 8); 307 | ck_assert_int_eq(htable->slots[1].used, true); 308 | 309 | // entry after 7 should be unused 310 | ck_assert_int_eq((uint32_t) (uint64_t) htable->slots[3].key, 0); 311 | ck_assert_int_eq((uint32_t) (uint64_t) htable->slots[3].value, 0); 312 | ck_assert_int_eq(htable->slots[3].used, false); 313 | 314 | g_hash_table_destroy(htable); 315 | } 316 | END_TEST 317 | 318 | START_TEST(test_ghashtable_double_insert) 319 | { 320 | GHashTable *htable = NULL; 321 | htable = g_hash_table_new(fake_int_hash_0, g_int_equal); 322 | 323 | ck_assert_int_eq(htable->num_used, 0); 324 | 325 | // first insert 326 | g_hash_table_insert(htable, (void*) 42, (void*) "Answer to everything"); 327 | ck_assert_int_eq(htable->num_used, 1); 328 | ck_assert_int_eq((uint32_t) (uint64_t) htable->slots[0].key, 42); 329 | ck_assert_int_eq(htable->slots[0].used, true); 330 | ck_assert_int_eq((uint32_t) (uint64_t) htable->slots[1].key, 0); 331 | ck_assert_int_eq(htable->slots[1].used, false); 332 | 333 | // second insert 334 | g_hash_table_insert(htable, (void*) 42, (void*) "Answer to everything"); 335 | ck_assert_int_eq(htable->num_used, 1); 336 | ck_assert_int_eq((uint32_t) (uint64_t) htable->slots[0].key, 42); 337 | ck_assert_int_eq(htable->slots[0].used, true); 338 | ck_assert_int_eq((uint32_t) (uint64_t) htable->slots[1].key, 0); 339 | ck_assert_int_eq(htable->slots[1].used, false); 340 | 341 | g_hash_table_destroy(htable); 342 | } 343 | END_TEST 344 | 345 | START_TEST(test_ghashtable_resize) 346 | { 347 | GHashTable *htable = NULL; 348 | htable = g_hash_table_new(g_int_hash, g_int_equal); 349 | 350 | uint32_t init_num_slots = htable->num_slots; 351 | uint32_t tipping_point = (uint32_t) (ceil(GHASHTABLE_MAX_LOAD * htable->num_slots) + 1); 352 | 353 | for (uint32_t i = 0; i < tipping_point; i++) { 354 | g_hash_table_insert(htable, (void*) (uint64_t) i, (void*) "Dummy String"); 355 | } 356 | 357 | ck_assert_int_eq(htable->num_slots, init_num_slots * 2); 358 | ck_assert_int_eq(htable->num_used, tipping_point); 359 | 360 | for (uint32_t i = 0; i < tipping_point; i++) { 361 | void *result = g_hash_table_lookup(htable, (void*) (uint64_t) i); 362 | ck_assert_ptr_nonnull(result); 363 | } 364 | 365 | g_hash_table_destroy(htable); 366 | } 367 | END_TEST 368 | 369 | START_TEST(test_ghashtable_insert_extensive) 370 | { 371 | const int num_inserts = 10000; 372 | GHashTable *htable = NULL; 373 | htable = g_hash_table_new(g_int_hash, g_int_equal); 374 | 375 | // insert 10000 numbers with key == value into the hash table 376 | for (int i = 0; i < num_inserts; i++) { 377 | g_hash_table_insert(htable, (void*) (uint64_t) i, (void*) (uint64_t) i); 378 | } 379 | 380 | ck_assert_int_eq(htable->num_slots, 32768); 381 | ck_assert_int_eq(g_hash_table_size(htable), num_inserts); 382 | 383 | // check if all 10000 numbers exist in the hash table 384 | for (int i = 0; i < num_inserts; i++) { 385 | void *result = g_hash_table_lookup(htable, (void*) (uint64_t) i); 386 | ck_assert_int_eq((uint64_t) result, i); 387 | } 388 | 389 | // remove the last 5000 inserts 390 | for (int i = num_inserts / 2; i < num_inserts; i++) { 391 | g_hash_table_remove(htable, (void*) (uint64_t) i); 392 | } 393 | 394 | ck_assert_int_eq(htable->num_slots, 32768); 395 | ck_assert_int_eq(g_hash_table_size(htable), num_inserts / 2); 396 | 397 | // check if all 5000 remaining numbers still exist in the hash table 398 | for (int i = 0; i < num_inserts / 2; i++) { 399 | void *result = g_hash_table_lookup(htable, (void*) (uint64_t) i); 400 | ck_assert_int_eq((uint64_t) result, i); 401 | } 402 | 403 | // check if all 5000 deleted numbers are gone 404 | for (int i = num_inserts / 2; i < num_inserts; i++) { 405 | void *result = g_hash_table_lookup(htable, (void*) (uint64_t) i); 406 | ck_assert_ptr_null(result); 407 | } 408 | 409 | // insert the 5000 removed entries again 410 | for (int i = num_inserts / 2; i < num_inserts; i++) { 411 | g_hash_table_insert(htable, (void*) (uint64_t) i, (void*) (uint64_t) i); 412 | } 413 | 414 | ck_assert_int_eq(htable->num_slots, 32768); 415 | ck_assert_int_eq(g_hash_table_size(htable), num_inserts); 416 | 417 | // check if all 10000 numbers exist in the hash table 418 | for (int i = 0; i < num_inserts; i++) { 419 | void *result = g_hash_table_lookup(htable, (void*) (uint64_t) i); 420 | ck_assert_int_eq((uint64_t) result, i); 421 | } 422 | 423 | g_hash_table_destroy(htable); 424 | } 425 | END_TEST 426 | 427 | START_TEST(test_ghashtable_free_keys_and_values) 428 | { 429 | GHashTable *htable = NULL; 430 | htable = g_hash_table_new_full(g_int_hash, g_int_equal, free_key_dummy, free_value_dummy); 431 | 432 | char *value_one = "One"; 433 | char *value_two = "Two"; 434 | char *value_three = "Three"; 435 | 436 | g_hash_table_insert(htable, (void*) 1, value_one); 437 | g_hash_table_insert(htable, (void*) 2, value_two); 438 | g_hash_table_insert(htable, (void*) 3, value_three); 439 | 440 | keys_freed = 0; 441 | values_freed = 0; 442 | 443 | g_hash_table_remove(htable, (void*) 2); 444 | 445 | ck_assert_int_eq(keys_freed, 1); 446 | ck_assert_int_eq(values_freed, 1); 447 | ck_assert_ptr_eq(last_freed_key, (void*) 2); 448 | ck_assert_ptr_eq(last_freed_value, (void*) value_two); 449 | 450 | g_hash_table_destroy(htable); 451 | 452 | ck_assert_int_eq(keys_freed, 3); 453 | ck_assert_int_eq(values_freed, 3); 454 | 455 | if (last_freed_key == (void*) 1) { 456 | ck_assert_ptr_eq(last_freed_value, value_one); 457 | } else if (last_freed_key == (void*) 3) { 458 | ck_assert_ptr_eq(last_freed_value, value_three); 459 | } else { 460 | ck_abort_msg("Unexpected value for last_freed_value"); 461 | } 462 | } 463 | END_TEST 464 | 465 | Suite* ghashtable_suite(void) 466 | { 467 | Suite *s; 468 | TCase *tc_core; 469 | 470 | s = suite_create("GHashTable"); 471 | 472 | /* Core test case */ 473 | tc_core = tcase_create("Core"); 474 | 475 | tcase_add_test(tc_core, test_gint_hash); 476 | 477 | tcase_add_test(tc_core, test_ghashtable_new); 478 | tcase_add_test(tc_core, test_ghashtable_new_full); 479 | 480 | tcase_add_test(tc_core, test_ghashtable_calc_start_slot); 481 | 482 | tcase_add_test(tc_core, test_ghashtable_find_free_slot); 483 | 484 | tcase_add_test(tc_core, test_ghashtable_insert_lookup); 485 | 486 | tcase_add_test(tc_core, test_ghashtable_foreach); 487 | 488 | tcase_add_test(tc_core, test_ghashtable_remove); 489 | tcase_add_test(tc_core, test_ghashtable_lookup_after_remove); 490 | tcase_add_test(tc_core, test_ghashtable_insert_after_remove); 491 | 492 | tcase_add_test(tc_core, test_ghashtable_double_insert); 493 | 494 | tcase_add_test(tc_core, test_ghashtable_resize); 495 | 496 | tcase_add_test(tc_core, test_ghashtable_insert_extensive); 497 | 498 | tcase_add_test(tc_core, test_ghashtable_free_keys_and_values); 499 | 500 | suite_add_tcase(s, tc_core); 501 | 502 | return s; 503 | } 504 | 505 | int main(int argc, char **argv) 506 | { 507 | int number_failed; 508 | Suite *s; 509 | SRunner *sr; 510 | 511 | s = ghashtable_suite(); 512 | sr = srunner_create(s); 513 | 514 | srunner_run_all(sr, CK_NORMAL); 515 | number_failed = srunner_ntests_failed(sr); 516 | srunner_free(sr); 517 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 518 | } 519 | -------------------------------------------------------------------------------- /tests/test_glist.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #define _CLIB_IMPL 1 7 | #include "glist.h" 8 | 9 | int simple_comp_func(const void *a, const void *b) 10 | { 11 | return ((int) (int64_t) a) - ((int) (int64_t) b); 12 | } 13 | 14 | int simple_comp_data_func(const void *a, const void *b, void *user_data) 15 | { 16 | ck_assert_int_eq(*((int*) user_data), 42); 17 | return ((int) (int64_t) a) - ((int) (int64_t) b); 18 | } 19 | 20 | 21 | START_TEST(test_glist_append) 22 | { 23 | GList *list = NULL; 24 | list = g_list_append(list, "Element 1"); 25 | 26 | ck_assert_ptr_nonnull(list); 27 | ck_assert_int_eq(g_list_length(list), 1); 28 | 29 | list = g_list_append(list, "Element 2"); 30 | ck_assert_ptr_nonnull(list); 31 | ck_assert_int_eq(g_list_length(list), 2); 32 | 33 | list = g_list_append(list, "Element 3"); 34 | ck_assert_ptr_nonnull(list); 35 | ck_assert_int_eq(g_list_length(list), 3); 36 | 37 | ck_assert_str_eq(list->data, "Element 1"); 38 | ck_assert_str_eq(list->next->data, "Element 2"); 39 | ck_assert_str_eq(list->next->next->data, "Element 3"); 40 | 41 | GList *last = list->next->next; 42 | 43 | ck_assert_str_eq(last->data, "Element 3"); 44 | ck_assert_str_eq(last->prev->data, "Element 2"); 45 | ck_assert_str_eq(last->prev->prev->data, "Element 1"); 46 | 47 | g_list_free(list); 48 | } 49 | END_TEST 50 | 51 | START_TEST(test_glist_prepend) 52 | { 53 | GList *list = NULL; 54 | list = g_list_prepend(list, "Element 1"); 55 | 56 | ck_assert_ptr_nonnull(list); 57 | ck_assert_int_eq(g_list_length(list), 1); 58 | 59 | list = g_list_prepend(list, "Element 2"); 60 | ck_assert_ptr_nonnull(list); 61 | ck_assert_int_eq(g_list_length(list), 2); 62 | 63 | list = g_list_prepend(list, "Element 3"); 64 | ck_assert_ptr_nonnull(list); 65 | ck_assert_int_eq(g_list_length(list), 3); 66 | 67 | ck_assert_str_eq(list->data, "Element 3"); 68 | ck_assert_str_eq(list->next->data, "Element 2"); 69 | ck_assert_str_eq(list->next->next->data, "Element 1"); 70 | 71 | GList *last = list->next->next; 72 | 73 | ck_assert_str_eq(last->data, "Element 1"); 74 | ck_assert_str_eq(last->prev->data, "Element 2"); 75 | ck_assert_str_eq(last->prev->prev->data, "Element 3"); 76 | 77 | g_list_free(list); 78 | } 79 | END_TEST 80 | 81 | START_TEST(test_glist_insert) 82 | { 83 | GList *list = NULL; 84 | 85 | // test insert on empty list and with position that is way too large 86 | list = g_list_insert(list, "Element 1", 20); 87 | ck_assert_ptr_nonnull(list); 88 | ck_assert_ptr_null(list->prev); 89 | ck_assert_ptr_null(list->next); 90 | ck_assert_int_eq(g_list_length(list), 1); 91 | ck_assert_str_eq(list->data, "Element 1"); 92 | 93 | list = g_list_insert(list, "Element 2", 1); 94 | ck_assert_ptr_nonnull(list); 95 | ck_assert_int_eq(g_list_length(list), 2); 96 | ck_assert_str_eq(list->data, "Element 1"); 97 | ck_assert_str_eq(list->next->data, "Element 2"); 98 | 99 | list = g_list_insert(list, "Element 3", 120); 100 | ck_assert_ptr_nonnull(list); 101 | ck_assert_int_eq(g_list_length(list), 3); 102 | ck_assert_str_eq(list->data, "Element 1"); 103 | ck_assert_str_eq(list->next->data, "Element 2"); 104 | ck_assert_str_eq(list->next->next->data, "Element 3"); 105 | 106 | ck_assert_ptr_nonnull(list); 107 | ck_assert_int_eq(g_list_length(list), 3); 108 | 109 | // test insert in the middle of a list 110 | list = g_list_insert(list, "New Element", 1); 111 | 112 | ck_assert_ptr_nonnull(list); 113 | ck_assert_int_eq(g_list_length(list), 4); 114 | ck_assert_str_eq(list->data, "Element 1"); 115 | ck_assert_str_eq(list->next->data, "New Element"); 116 | ck_assert_str_eq(list->next->next->data, "Element 2"); 117 | ck_assert_str_eq(list->next->next->next->data, "Element 3"); 118 | 119 | // test insert at the beginning of the list 120 | GList *old_list = list; 121 | list = g_list_insert(list, "New First Element", 0); 122 | 123 | ck_assert_ptr_nonnull(list); 124 | ck_assert(list != old_list); 125 | ck_assert_int_eq(g_list_length(list), 5); 126 | ck_assert_str_eq(list->data, "New First Element"); 127 | ck_assert_str_eq(list->next->data, "Element 1"); 128 | ck_assert_str_eq(list->next->next->data, "New Element"); 129 | ck_assert_str_eq(list->next->next->next->data, "Element 2"); 130 | ck_assert_str_eq(list->next->next->next->next->data, "Element 3"); 131 | 132 | // test reversal of list 133 | list = g_list_reverse(list); 134 | 135 | ck_assert_ptr_nonnull(list); 136 | ck_assert_int_eq(g_list_length(list), 5); 137 | 138 | ck_assert_str_eq(list->data, "Element 3"); 139 | ck_assert_str_eq(list->next->data, "Element 2"); 140 | ck_assert_str_eq(list->next->next->data, "New Element"); 141 | ck_assert_str_eq(list->next->next->next->data, "Element 1"); 142 | ck_assert_str_eq(list->next->next->next->next->data, "New First Element"); 143 | 144 | g_list_free(list); 145 | } 146 | END_TEST 147 | 148 | START_TEST(test_glist_insert_before) 149 | { 150 | GList *list = NULL; 151 | list = g_list_insert_before(list, NULL, "Element 1"); 152 | list = g_list_append(list, "Element 2"); 153 | list = g_list_append(list, "Element 3"); 154 | 155 | ck_assert_ptr_nonnull(list); 156 | ck_assert_int_eq(g_list_length(list), 3); 157 | 158 | list = g_list_insert_before(list, list->next, "New Element"); 159 | 160 | ck_assert_ptr_nonnull(list); 161 | ck_assert_int_eq(g_list_length(list), 4); 162 | 163 | ck_assert_str_eq(list->data, "Element 1"); 164 | ck_assert_str_eq(list->next->data, "New Element"); 165 | ck_assert_str_eq(list->next->next->data, "Element 2"); 166 | ck_assert_str_eq(list->next->next->next->data, "Element 3"); 167 | 168 | // insert before first element 169 | GList *old_list = list; 170 | list = g_list_insert_before(list, list, "New First Element"); 171 | 172 | ck_assert_ptr_nonnull(list); 173 | ck_assert(list != old_list); 174 | ck_assert_int_eq(g_list_length(list), 5); 175 | 176 | ck_assert_str_eq(list->data, "New First Element"); 177 | ck_assert_str_eq(list->next->data, "Element 1"); 178 | ck_assert_str_eq(list->next->next->data, "New Element"); 179 | ck_assert_str_eq(list->next->next->next->data, "Element 2"); 180 | ck_assert_str_eq(list->next->next->next->next->data, "Element 3"); 181 | 182 | // test reversal of list 183 | list = g_list_reverse(list); 184 | 185 | ck_assert_ptr_nonnull(list); 186 | ck_assert_int_eq(g_list_length(list), 5); 187 | 188 | ck_assert_str_eq(list->data, "Element 3"); 189 | ck_assert_str_eq(list->next->data, "Element 2"); 190 | ck_assert_str_eq(list->next->next->data, "New Element"); 191 | ck_assert_str_eq(list->next->next->next->data, "Element 1"); 192 | ck_assert_str_eq(list->next->next->next->next->data, "New First Element"); 193 | 194 | g_list_free(list); 195 | } 196 | END_TEST 197 | 198 | START_TEST(test_glist_insert_sorted) 199 | { 200 | GList *list = NULL; 201 | list = g_list_insert_sorted(list, (void*) 1, simple_comp_func); 202 | list = g_list_insert_sorted(list, (void*) 4, simple_comp_func); 203 | list = g_list_insert_sorted(list, (void*) 9, simple_comp_func); 204 | 205 | ck_assert_ptr_nonnull(list); 206 | ck_assert_int_eq(g_list_length(list), 3); 207 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 1); 208 | ck_assert_int_eq((uint32_t) (uint64_t) list->next->data, 4); 209 | ck_assert_int_eq((uint32_t) (uint64_t) list->next->next->data, 9); 210 | 211 | list = g_list_insert_sorted(list, (void*) 7, simple_comp_func); 212 | list = g_list_insert_sorted(list, (void*) 8, simple_comp_func); 213 | list = g_list_insert_sorted(list, (void*) 3, simple_comp_func); 214 | list = g_list_insert_sorted(list, (void*) 2, simple_comp_func); 215 | 216 | // test insert at the beginning of the list 217 | GList *old_list = list; 218 | list = g_list_insert_sorted(list, (void*) 0, simple_comp_func); 219 | ck_assert(list != old_list); 220 | 221 | list = g_list_insert_sorted(list, (void*) 5, simple_comp_func); 222 | list = g_list_insert_sorted(list, (void*) 6, simple_comp_func); 223 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 0); 224 | 225 | ck_assert_ptr_nonnull(list); 226 | ck_assert_int_eq(g_list_length(list), 10); 227 | 228 | GList *cur = list; 229 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 0); 230 | cur = cur->next; 231 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 1); 232 | cur = cur->next; 233 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 2); 234 | cur = cur->next; 235 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 3); 236 | cur = cur->next; 237 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 4); 238 | cur = cur->next; 239 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 5); 240 | cur = cur->next; 241 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 6); 242 | cur = cur->next; 243 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 7); 244 | cur = cur->next; 245 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 8); 246 | cur = cur->next; 247 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 9); 248 | 249 | g_list_free(list); 250 | } 251 | END_TEST 252 | 253 | START_TEST(test_glist_insert_sorted_with_data) 254 | { 255 | GList *list = NULL; 256 | int user_data = 42; 257 | 258 | list = g_list_insert_sorted_with_data(list, (void*) 9, simple_comp_data_func, &user_data); 259 | list = g_list_insert_sorted_with_data(list, (void*) 1, simple_comp_data_func, &user_data); 260 | list = g_list_insert_sorted_with_data(list, (void*) 4, simple_comp_data_func, &user_data); 261 | 262 | ck_assert_ptr_nonnull(list); 263 | ck_assert_int_eq(g_list_length(list), 3); 264 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 1); 265 | ck_assert_int_eq((uint32_t) (uint64_t) list->next->data, 4); 266 | ck_assert_int_eq((uint32_t) (uint64_t) list->next->next->data, 9); 267 | 268 | g_list_free(list); 269 | } 270 | END_TEST 271 | 272 | START_TEST(test_glist_remove) 273 | { 274 | GList *list = NULL; 275 | char *element0 = "Element 0"; 276 | char *element1 = "Element 1"; 277 | char *element2 = "Element 2"; 278 | char *element3 = "Element 3"; 279 | 280 | list = g_list_append(list, element0); 281 | list = g_list_append(list, element1); 282 | list = g_list_append(list, element2); 283 | list = g_list_append(list, element3); 284 | 285 | ck_assert_ptr_nonnull(list); 286 | ck_assert_int_eq(g_list_length(list), 4); 287 | 288 | // test removal of the first element 289 | GList *old_list = list; 290 | list = g_list_remove(list, element0); 291 | ck_assert_ptr_nonnull(list); 292 | ck_assert(list != old_list); 293 | ck_assert_int_eq(g_list_length(list), 3); 294 | 295 | list = g_list_remove(list, element2); 296 | ck_assert_ptr_nonnull(list); 297 | ck_assert_int_eq(g_list_length(list), 2); 298 | 299 | ck_assert_str_eq(list->data, element1); 300 | ck_assert_str_eq(list->next->data, element3); 301 | 302 | list = g_list_remove(list, element3); 303 | ck_assert_ptr_nonnull(list); 304 | ck_assert_int_eq(g_list_length(list), 1); 305 | 306 | ck_assert_str_eq(list->data, element1); 307 | 308 | list = g_list_remove(list, element1); 309 | ck_assert_ptr_null(list); 310 | 311 | g_list_free(list); 312 | } 313 | END_TEST 314 | 315 | START_TEST(test_glist_remove_link) 316 | { 317 | GList *list = NULL; 318 | GList *removed_elem = NULL; 319 | 320 | list = g_list_append(list, "Element 0"); 321 | list = g_list_append(list, "Element 1"); 322 | list = g_list_append(list, "Element 2"); 323 | list = g_list_append(list, "Element 3"); 324 | 325 | // test removal of the first element 326 | ck_assert_ptr_nonnull(list); 327 | ck_assert_int_eq(g_list_length(list), 4); 328 | 329 | removed_elem = list; 330 | GList *old_list = list; 331 | list = g_list_remove_link(list, removed_elem); 332 | ck_assert_ptr_nonnull(list); 333 | ck_assert(list != old_list); 334 | ck_assert_int_eq(g_list_length(list), 3); 335 | ck_assert_str_eq(removed_elem->data, "Element 0"); 336 | ck_assert_str_eq(list->data, "Element 1"); 337 | g_list_free(removed_elem); 338 | 339 | removed_elem = list->next; 340 | list = g_list_remove_link(list, removed_elem); 341 | ck_assert_ptr_nonnull(list); 342 | ck_assert_int_eq(g_list_length(list), 2); 343 | g_list_free(removed_elem); 344 | 345 | ck_assert_str_eq(list->data, "Element 1"); 346 | ck_assert_str_eq(list->next->data, "Element 3"); 347 | 348 | removed_elem = list->next; 349 | list = g_list_remove_link(list, removed_elem); 350 | ck_assert_ptr_nonnull(list); 351 | ck_assert_int_eq(g_list_length(list), 1); 352 | g_list_free(removed_elem); 353 | 354 | ck_assert_str_eq(list->data, "Element 1"); 355 | 356 | removed_elem = list; 357 | list = g_list_remove_link(list, removed_elem); 358 | ck_assert_ptr_null(list); 359 | g_list_free(removed_elem); 360 | 361 | g_list_free(list); 362 | } 363 | END_TEST 364 | 365 | START_TEST(test_glist_delete_link) 366 | { 367 | GList *list = NULL; 368 | list = g_list_append(list, "Element 0"); 369 | list = g_list_append(list, "Element 1"); 370 | list = g_list_append(list, "Element 2"); 371 | list = g_list_append(list, "Element 3"); 372 | 373 | ck_assert_ptr_nonnull(list); 374 | ck_assert_int_eq(g_list_length(list), 4); 375 | 376 | // test removal of the first element 377 | GList *old_list = list; 378 | list = g_list_delete_link(list, list); 379 | ck_assert_ptr_nonnull(list); 380 | ck_assert(list != old_list); 381 | ck_assert_int_eq(g_list_length(list), 3); 382 | ck_assert_str_eq(list->data, "Element 1"); 383 | 384 | list = g_list_delete_link(list, list->next); 385 | ck_assert_ptr_nonnull(list); 386 | ck_assert_int_eq(g_list_length(list), 2); 387 | 388 | ck_assert_str_eq(list->data, "Element 1"); 389 | ck_assert_str_eq(list->next->data, "Element 3"); 390 | 391 | list = g_list_delete_link(list, list->next); 392 | ck_assert_ptr_nonnull(list); 393 | ck_assert_int_eq(g_list_length(list), 1); 394 | 395 | ck_assert_str_eq(list->data, "Element 1"); 396 | 397 | list = g_list_delete_link(list, list); 398 | ck_assert_ptr_null(list); 399 | 400 | g_list_free(list); 401 | } 402 | END_TEST 403 | 404 | START_TEST(test_glist_remove_all) 405 | { 406 | GList *list = NULL; 407 | char *delete_me = "Delete Me"; 408 | 409 | list = g_list_append(list, "Element 1"); 410 | list = g_list_append(list, delete_me); 411 | list = g_list_append(list, "Element 2"); 412 | list = g_list_append(list, "Element 3"); 413 | list = g_list_append(list, delete_me); 414 | 415 | ck_assert_ptr_nonnull(list); 416 | ck_assert_int_eq(g_list_length(list), 5); 417 | 418 | list = g_list_remove_all(list, delete_me); 419 | ck_assert_ptr_nonnull(list); 420 | ck_assert_int_eq(g_list_length(list), 3); 421 | 422 | ck_assert_str_eq(list->data, "Element 1"); 423 | ck_assert_str_eq(list->next->data, "Element 2"); 424 | ck_assert_str_eq(list->next->next->data, "Element 3"); 425 | 426 | g_list_free(list); 427 | } 428 | END_TEST 429 | 430 | START_TEST(test_glist_length) 431 | { 432 | GList *list = NULL; 433 | 434 | // check empty list 435 | ck_assert_int_eq(g_list_length(list), 0); 436 | 437 | // check with elements 438 | list = g_list_append(list, "Element 1"); 439 | ck_assert_int_eq(g_list_length(list), 1); 440 | 441 | list = g_list_append(list, "Element 2"); 442 | ck_assert_int_eq(g_list_length(list), 2); 443 | 444 | list = g_list_append(list, "Element 3"); 445 | ck_assert_int_eq(g_list_length(list), 3); 446 | 447 | // check with start from sublist 448 | ck_assert_int_eq(g_list_length(list->next), 2); 449 | ck_assert_int_eq(g_list_length(list->next->next), 1); 450 | 451 | g_list_free(list); 452 | } 453 | END_TEST 454 | 455 | START_TEST(test_glist_copy) 456 | { 457 | GList *list = NULL; 458 | list = g_list_append(list, "Element 1"); 459 | list = g_list_append(list, "Element 2"); 460 | list = g_list_append(list, "Element 3"); 461 | 462 | ck_assert_ptr_nonnull(list); 463 | ck_assert_int_eq(g_list_length(list), 3); 464 | 465 | GList *new_list = g_list_copy(list); 466 | ck_assert_ptr_nonnull(new_list); 467 | ck_assert_int_eq(g_list_length(new_list), 3); 468 | 469 | ck_assert_str_eq(new_list->data, "Element 1"); 470 | ck_assert_str_eq(new_list->next->data, "Element 2"); 471 | ck_assert_str_eq(new_list->next->next->data, "Element 3"); 472 | 473 | ck_assert_ptr_ne(new_list, list); 474 | ck_assert_ptr_ne(new_list->next, list->next); 475 | ck_assert_ptr_ne(new_list->next->next, list->next->next); 476 | 477 | g_list_free(list); 478 | g_list_free(new_list); 479 | } 480 | END_TEST 481 | 482 | START_TEST(test_glist_concat) 483 | { 484 | GList *list1 = NULL; 485 | list1 = g_list_append(list1, "Element 1"); 486 | list1 = g_list_append(list1, "Element 2"); 487 | list1 = g_list_append(list1, "Element 3"); 488 | 489 | ck_assert_ptr_nonnull(list1); 490 | ck_assert_int_eq(g_list_length(list1), 3); 491 | 492 | GList *list2 = NULL; 493 | list2 = g_list_append(list2, "List 2 Element 1"); 494 | list2 = g_list_append(list2, "List 2 Element 2"); 495 | 496 | ck_assert_ptr_nonnull(list2); 497 | ck_assert_int_eq(g_list_length(list2), 2); 498 | 499 | list1 = g_list_concat(list1, list2); 500 | 501 | ck_assert_str_eq(list1->data, "Element 1"); 502 | ck_assert_str_eq(list1->next->data, "Element 2"); 503 | ck_assert_str_eq(list1->next->next->data, "Element 3"); 504 | ck_assert_str_eq(list1->next->next->next->data, "List 2 Element 1"); 505 | ck_assert_str_eq(list1->next->next->next->next->data, "List 2 Element 2"); 506 | 507 | g_list_free(list1); 508 | } 509 | END_TEST 510 | 511 | START_TEST(test_glist_reverse) 512 | { 513 | GList *list = NULL; 514 | list = g_list_append(list, "Element 1"); 515 | list = g_list_append(list, "Element 2"); 516 | list = g_list_append(list, "Element 3"); 517 | 518 | ck_assert_ptr_nonnull(list); 519 | ck_assert_int_eq(g_list_length(list), 3); 520 | 521 | ck_assert_str_eq(list->data, "Element 1"); 522 | ck_assert_str_eq(list->next->data, "Element 2"); 523 | ck_assert_str_eq(list->next->next->data, "Element 3"); 524 | 525 | list = g_list_reverse(list); 526 | 527 | ck_assert_ptr_nonnull(list); 528 | ck_assert_int_eq(g_list_length(list), 3); 529 | 530 | ck_assert_str_eq(list->data, "Element 3"); 531 | ck_assert_str_eq(list->next->data, "Element 2"); 532 | ck_assert_str_eq(list->next->next->data, "Element 1"); 533 | 534 | g_list_free(list); 535 | } 536 | END_TEST 537 | 538 | START_TEST(test_glist_merge_sorted) 539 | { 540 | GList *list1 = NULL; 541 | list1 = g_list_append(list1, (void*) 1); 542 | list1 = g_list_append(list1, (void*) 3); 543 | list1 = g_list_append(list1, (void*) 6); 544 | 545 | GList *list2 = NULL; 546 | list2 = g_list_append(list2, (void*) 2); 547 | list2 = g_list_append(list2, (void*) 4); 548 | list2 = g_list_append(list2, (void*) 5); 549 | 550 | list1 = _g_list_merge_sorted(list1, list2, simple_comp_func); 551 | ck_assert_ptr_nonnull(list1); 552 | ck_assert_int_eq(g_list_length(list1), 6); 553 | 554 | GList *list = list1; 555 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 1); 556 | list = list->next; 557 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 2); 558 | list = list->next; 559 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 3); 560 | list = list->next; 561 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 4); 562 | list = list->next; 563 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 5); 564 | list = list->next; 565 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 6); 566 | 567 | g_list_free(list1); 568 | } 569 | END_TEST 570 | 571 | START_TEST(test_glist_sort) 572 | { 573 | GList *list = NULL; 574 | 575 | // sort the empty list 576 | list = g_list_sort(list, simple_comp_func); 577 | ck_assert_ptr_null(list); 578 | 579 | // sort a list with one element 580 | list = g_list_append(list, (void*) 1); 581 | 582 | list = g_list_sort(list, simple_comp_func); 583 | ck_assert_ptr_nonnull(list); 584 | ck_assert_ptr_null(list->prev); 585 | ck_assert_ptr_null(list->next); 586 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 1); 587 | 588 | // sort a list with two elements 589 | list = g_list_prepend(list, (void*) 2); 590 | ck_assert_int_eq(g_list_length(list), 2); 591 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 2); 592 | ck_assert_int_eq((uint32_t) (uint64_t) list->next->data, 1); 593 | 594 | list = g_list_sort(list, simple_comp_func); 595 | ck_assert_int_eq(g_list_length(list), 2); 596 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 1); 597 | ck_assert_int_eq((uint32_t) (uint64_t) list->next->data, 2); 598 | 599 | // sort a list with three elements 600 | list = g_list_prepend(list, (void*) 3); 601 | ck_assert_int_eq(g_list_length(list), 3); 602 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 3); 603 | ck_assert_int_eq((uint32_t) (uint64_t) list->next->data, 1); 604 | ck_assert_int_eq((uint32_t) (uint64_t) list->next->next->data, 2); 605 | 606 | list = g_list_sort(list, simple_comp_func); 607 | ck_assert_int_eq(g_list_length(list), 3); 608 | ck_assert_int_eq((uint32_t) (uint64_t) list->data, 1); 609 | ck_assert_int_eq((uint32_t) (uint64_t) list->next->data, 2); 610 | ck_assert_int_eq((uint32_t) (uint64_t) list->next->next->data, 3); 611 | 612 | g_list_free(list); 613 | } 614 | END_TEST 615 | 616 | START_TEST(test_glist_sort_even) 617 | { 618 | GList *list = NULL; 619 | list = g_list_append(list, (void*) 7); 620 | list = g_list_append(list, (void*) 1); 621 | list = g_list_append(list, (void*) 8); 622 | list = g_list_append(list, (void*) 2); 623 | list = g_list_append(list, (void*) 3); 624 | list = g_list_append(list, (void*) 5); 625 | list = g_list_append(list, (void*) 9); 626 | list = g_list_append(list, (void*) 4); 627 | list = g_list_append(list, (void*) 6); 628 | list = g_list_append(list, (void*) 0); 629 | 630 | list = g_list_sort(list, simple_comp_func); 631 | ck_assert_ptr_nonnull(list); 632 | ck_assert_int_eq(g_list_length(list), 10); 633 | 634 | GList *cur = list; 635 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 0); 636 | cur = cur->next; 637 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 1); 638 | cur = cur->next; 639 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 2); 640 | cur = cur->next; 641 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 3); 642 | cur = cur->next; 643 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 4); 644 | cur = cur->next; 645 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 5); 646 | cur = cur->next; 647 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 6); 648 | cur = cur->next; 649 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 7); 650 | cur = cur->next; 651 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 8); 652 | cur = cur->next; 653 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 9); 654 | 655 | g_list_free(list); 656 | } 657 | END_TEST 658 | 659 | START_TEST(test_glist_sort_uneven) 660 | { 661 | GList *list = NULL; 662 | list = g_list_append(list, (void*) 7); 663 | list = g_list_append(list, (void*) 1); 664 | list = g_list_append(list, (void*) 8); 665 | list = g_list_append(list, (void*) 2); 666 | list = g_list_append(list, (void*) 3); 667 | list = g_list_append(list, (void*) 5); 668 | list = g_list_append(list, (void*) 9); 669 | list = g_list_append(list, (void*) 4); 670 | list = g_list_append(list, (void*) 6); 671 | 672 | list = g_list_sort(list, simple_comp_func); 673 | ck_assert_ptr_nonnull(list); 674 | ck_assert_int_eq(g_list_length(list), 9); 675 | 676 | GList *cur = list; 677 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 1); 678 | cur = cur->next; 679 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 2); 680 | cur = cur->next; 681 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 3); 682 | cur = cur->next; 683 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 4); 684 | cur = cur->next; 685 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 5); 686 | cur = cur->next; 687 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 6); 688 | cur = cur->next; 689 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 7); 690 | cur = cur->next; 691 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 8); 692 | cur = cur->next; 693 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 9); 694 | 695 | g_list_free(list); 696 | } 697 | END_TEST 698 | 699 | START_TEST(test_glist_sort_extensive) 700 | { 701 | GList *list = NULL; 702 | 703 | // Reverse a list, prepend a new element, sort it, and check the order of 704 | // the elements. Do this 1000 times so that the final list will contain 705 | // 1000 elements. 706 | for (int i = 0; i < 1000; i++) { 707 | list = g_list_reverse(list); 708 | list = g_list_prepend(list, (void*) (uint64_t) i); 709 | list = g_list_sort(list, simple_comp_func); 710 | 711 | GList *cur = list; 712 | for (int j = 0; j <= i; cur = cur->next, j++) { 713 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, j); 714 | } 715 | } 716 | 717 | g_list_free(list); 718 | } 719 | END_TEST 720 | 721 | START_TEST(test_glist_sort_with_data) 722 | { 723 | GList *list = NULL; 724 | list = g_list_append(list, (void*) 7); 725 | list = g_list_append(list, (void*) 1); 726 | list = g_list_append(list, (void*) 8); 727 | list = g_list_append(list, (void*) 2); 728 | list = g_list_append(list, (void*) 3); 729 | list = g_list_append(list, (void*) 5); 730 | list = g_list_append(list, (void*) 9); 731 | list = g_list_append(list, (void*) 4); 732 | list = g_list_append(list, (void*) 6); 733 | list = g_list_append(list, (void*) 0); 734 | 735 | int user_data = 42; 736 | 737 | list = g_list_sort_with_data(list, simple_comp_data_func, &user_data); 738 | ck_assert_ptr_nonnull(list); 739 | ck_assert_int_eq(g_list_length(list), 10); 740 | 741 | GList *cur = list; 742 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 0); 743 | cur = cur->next; 744 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 1); 745 | cur = cur->next; 746 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 2); 747 | cur = cur->next; 748 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 3); 749 | cur = cur->next; 750 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 4); 751 | cur = cur->next; 752 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 5); 753 | cur = cur->next; 754 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 6); 755 | cur = cur->next; 756 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 7); 757 | cur = cur->next; 758 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 8); 759 | cur = cur->next; 760 | ck_assert_int_eq((uint32_t) (uint64_t) cur->data, 9); 761 | 762 | g_list_free(list); 763 | } 764 | END_TEST 765 | 766 | uint32_t foreach_num_values = 3; 767 | 768 | void test_foreach(void *data, void *user_data) 769 | { 770 | static GList *expected_values = NULL; 771 | 772 | if (expected_values == NULL) { 773 | expected_values = g_list_append(expected_values, "Element 1"); 774 | expected_values = g_list_append(expected_values, "Element 2"); 775 | expected_values = g_list_append(expected_values, "Element 3"); 776 | } 777 | 778 | ck_assert_str_eq("USER DATA", user_data); 779 | ck_assert_str_eq(data, expected_values->data); 780 | expected_values = g_list_delete_link(expected_values, expected_values); 781 | foreach_num_values--; 782 | } 783 | 784 | START_TEST(test_glist_foreach) 785 | { 786 | GList *list = NULL; 787 | list = g_list_append(list, "Element 1"); 788 | list = g_list_append(list, "Element 2"); 789 | list = g_list_append(list, "Element 3"); 790 | 791 | char *user_data = "USER DATA"; 792 | 793 | ck_assert_int_eq(foreach_num_values, 3); 794 | g_list_foreach(list, test_foreach, user_data); 795 | ck_assert_int_eq(foreach_num_values, 0); 796 | 797 | g_list_free(list); 798 | } 799 | END_TEST 800 | 801 | START_TEST(test_glist_first) 802 | { 803 | GList *list = NULL; 804 | list = g_list_append(list, "Element 1"); 805 | list = g_list_append(list, "Element 2"); 806 | list = g_list_append(list, "Element 3"); 807 | 808 | ck_assert_ptr_eq(g_list_first(list), list); 809 | ck_assert_ptr_eq(g_list_first(list->next), list); 810 | ck_assert_ptr_eq(g_list_first(list->next->next), list); 811 | 812 | g_list_free(list); 813 | } 814 | END_TEST 815 | 816 | START_TEST(test_glist_last) 817 | { 818 | GList *list = NULL; 819 | list = g_list_append(list, "Element 1"); 820 | list = g_list_append(list, "Element 2"); 821 | list = g_list_append(list, "Element 3"); 822 | 823 | GList *last = list->next->next; 824 | 825 | ck_assert_ptr_eq(g_list_last(list), last); 826 | ck_assert_ptr_eq(g_list_last(list->next), last); 827 | ck_assert_ptr_eq(g_list_last(list->next->next), last); 828 | 829 | g_list_free(list); 830 | } 831 | END_TEST 832 | 833 | START_TEST(test_glist_nth) 834 | { 835 | GList *list = NULL; 836 | list = g_list_append(list, "Element 1"); 837 | list = g_list_append(list, "Element 2"); 838 | list = g_list_append(list, "Element 3"); 839 | 840 | ck_assert_ptr_eq(g_list_nth(list, 0), list); 841 | ck_assert_ptr_eq(g_list_nth(list, 1), list->next); 842 | ck_assert_ptr_eq(g_list_nth(list, 2), list->next->next); 843 | ck_assert_ptr_eq(g_list_nth(list, 3), NULL); 844 | ck_assert_ptr_eq(g_list_nth(list, 4), NULL); 845 | 846 | g_list_free(list); 847 | } 848 | END_TEST 849 | 850 | START_TEST(test_glist_nth_data) 851 | { 852 | GList *list = NULL; 853 | 854 | char *element1 = "Element 1"; 855 | char *element2 = "Element 2"; 856 | char *element3 = "Element 3"; 857 | 858 | list = g_list_append(list, element1); 859 | list = g_list_append(list, element2); 860 | list = g_list_append(list, element3); 861 | 862 | ck_assert_ptr_eq(g_list_nth_data(list, 0), element1); 863 | ck_assert_ptr_eq(g_list_nth_data(list, 1), element2); 864 | ck_assert_ptr_eq(g_list_nth_data(list, 2), element3); 865 | ck_assert_ptr_eq(g_list_nth_data(list, 3), NULL); 866 | ck_assert_ptr_eq(g_list_nth_data(list, 4), NULL); 867 | 868 | g_list_free(list); 869 | } 870 | END_TEST 871 | 872 | START_TEST(test_glist_nth_prev) 873 | { 874 | GList *list = NULL; 875 | list = g_list_append(list, "Element 1"); 876 | list = g_list_append(list, "Element 2"); 877 | list = g_list_append(list, "Element 3"); 878 | 879 | GList *last = list->next->next; 880 | 881 | ck_assert_ptr_eq(g_list_nth_prev(last, 0), last); 882 | ck_assert_ptr_eq(g_list_nth_prev(last, 1), last->prev); 883 | ck_assert_ptr_eq(g_list_nth_prev(last, 2), last->prev->prev); 884 | ck_assert_ptr_eq(g_list_nth_prev(last, 3), NULL); 885 | ck_assert_ptr_eq(g_list_nth_prev(last, 4), NULL); 886 | 887 | g_list_free(list); 888 | } 889 | END_TEST 890 | 891 | START_TEST(test_glist_find) 892 | { 893 | GList *list = NULL; 894 | 895 | char *element1 = "Element 1"; 896 | char *element2 = "Element 2"; 897 | char *element3 = "Element 3"; 898 | 899 | list = g_list_append(list, element1); 900 | list = g_list_append(list, element2); 901 | list = g_list_append(list, element3); 902 | 903 | ck_assert_ptr_eq(g_list_find(list, element1), list); 904 | ck_assert_ptr_eq(g_list_find(list, element2), list->next); 905 | ck_assert_ptr_eq(g_list_find(list, element3), list->next->next); 906 | 907 | g_list_free(list); 908 | } 909 | END_TEST 910 | 911 | START_TEST(test_glist_find_custom) 912 | { 913 | GList *list = NULL; 914 | 915 | char *element1 = "Element 1"; 916 | char *element2 = "Element 2"; 917 | char *element3 = "Element 3"; 918 | 919 | list = g_list_append(list, element1); 920 | list = g_list_append(list, element2); 921 | list = g_list_append(list, element3); 922 | 923 | ck_assert_ptr_eq(g_list_find_custom(list, element1, simple_comp_func), list); 924 | ck_assert_ptr_eq(g_list_find_custom(list, element2, simple_comp_func), list->next); 925 | ck_assert_ptr_eq(g_list_find_custom(list, element3, simple_comp_func), list->next->next); 926 | 927 | g_list_free(list); 928 | } 929 | END_TEST 930 | 931 | START_TEST(test_glist_position) 932 | { 933 | GList *list = NULL; 934 | 935 | list = g_list_append(list, "Element 1"); 936 | list = g_list_append(list, "Element 2"); 937 | list = g_list_append(list, "Element 3"); 938 | 939 | ck_assert_int_eq(g_list_position(list, list), 0); 940 | ck_assert_int_eq(g_list_position(list, list->next), 1); 941 | ck_assert_int_eq(g_list_position(list, list->next->next), 2); 942 | ck_assert_int_eq(g_list_position(list, NULL), -1); 943 | 944 | g_list_free(list); 945 | } 946 | END_TEST 947 | 948 | START_TEST(test_glist_index) 949 | { 950 | GList *list = NULL; 951 | 952 | char *element1 = "Element 1"; 953 | char *element2 = "Element 2"; 954 | char *element3 = "Element 3"; 955 | 956 | list = g_list_append(list, element1); 957 | list = g_list_append(list, element2); 958 | list = g_list_append(list, element3); 959 | 960 | ck_assert_int_eq(g_list_index(list, element1), 0); 961 | ck_assert_int_eq(g_list_index(list, element2), 1); 962 | ck_assert_int_eq(g_list_index(list, element3), 2); 963 | ck_assert_int_eq(g_list_index(list, NULL), -1); 964 | 965 | g_list_free(list); 966 | } 967 | END_TEST 968 | 969 | Suite* glist_suite(void) 970 | { 971 | Suite *s; 972 | TCase *tc_core; 973 | 974 | s = suite_create("GList"); 975 | 976 | /* Core test case */ 977 | tc_core = tcase_create("Core"); 978 | tcase_set_timeout(tc_core, 10); 979 | 980 | tcase_add_test(tc_core, test_glist_append); 981 | 982 | tcase_add_test(tc_core, test_glist_prepend); 983 | 984 | tcase_add_test(tc_core, test_glist_insert); 985 | tcase_add_test(tc_core, test_glist_insert_before); 986 | tcase_add_test(tc_core, test_glist_insert_sorted); 987 | 988 | tcase_add_test(tc_core, test_glist_insert_sorted_with_data); 989 | 990 | tcase_add_test(tc_core, test_glist_remove); 991 | 992 | tcase_add_test(tc_core, test_glist_remove_link); 993 | 994 | tcase_add_test(tc_core, test_glist_delete_link); 995 | 996 | tcase_add_test(tc_core, test_glist_remove_all); 997 | 998 | tcase_add_test(tc_core, test_glist_length); 999 | 1000 | tcase_add_test(tc_core, test_glist_copy); 1001 | 1002 | tcase_add_test(tc_core, test_glist_reverse); 1003 | 1004 | tcase_add_test(tc_core, test_glist_merge_sorted); 1005 | 1006 | tcase_add_test(tc_core, test_glist_sort); 1007 | tcase_add_test(tc_core, test_glist_sort_even); 1008 | tcase_add_test(tc_core, test_glist_sort_uneven); 1009 | tcase_add_test(tc_core, test_glist_sort_extensive); 1010 | 1011 | tcase_add_test(tc_core, test_glist_sort_with_data); 1012 | 1013 | tcase_add_test(tc_core, test_glist_concat); 1014 | 1015 | tcase_add_test(tc_core, test_glist_foreach); 1016 | 1017 | tcase_add_test(tc_core, test_glist_first); 1018 | 1019 | tcase_add_test(tc_core, test_glist_last); 1020 | 1021 | tcase_add_test(tc_core, test_glist_nth); 1022 | 1023 | tcase_add_test(tc_core, test_glist_nth_data); 1024 | 1025 | tcase_add_test(tc_core, test_glist_nth_prev); 1026 | 1027 | tcase_add_test(tc_core, test_glist_find); 1028 | 1029 | tcase_add_test(tc_core, test_glist_find_custom); 1030 | 1031 | tcase_add_test(tc_core, test_glist_position); 1032 | 1033 | tcase_add_test(tc_core, test_glist_index); 1034 | 1035 | suite_add_tcase(s, tc_core); 1036 | 1037 | return s; 1038 | } 1039 | 1040 | int main(int argc, char **argv) 1041 | { 1042 | int number_failed; 1043 | Suite *s; 1044 | SRunner *sr; 1045 | 1046 | s = glist_suite(); 1047 | sr = srunner_create(s); 1048 | 1049 | srunner_run_all(sr, CK_NORMAL); 1050 | number_failed = srunner_ntests_failed(sr); 1051 | srunner_free(sr); 1052 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 1053 | } 1054 | -------------------------------------------------------------------------------- /tests/test_gstring.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define _CLIB_IMPL 1 6 | #include "gstring.h" 7 | 8 | START_TEST(test_gstring_new_null) 9 | { 10 | GString *string = NULL; 11 | string = g_string_new(NULL); 12 | 13 | ck_assert_ptr_nonnull(string); 14 | ck_assert_ptr_nonnull(string->str); 15 | ck_assert_int_eq(string->len, 0); 16 | ck_assert_int_eq(string->allocated_len, GSTRING_MIN_BUF_SIZE); 17 | 18 | g_string_free(string, true); 19 | } 20 | END_TEST 21 | 22 | START_TEST(test_gstring_new_empty) 23 | { 24 | GString *string = NULL; 25 | string = g_string_new(""); 26 | 27 | ck_assert_ptr_nonnull(string); 28 | ck_assert_ptr_nonnull(string->str); 29 | 30 | ck_assert_int_eq(string->len, 0); 31 | ck_assert_int_eq(string->allocated_len, GSTRING_MIN_BUF_SIZE); 32 | 33 | g_string_free(string, true); 34 | } 35 | END_TEST 36 | 37 | START_TEST(test_gstring_new_small_string) 38 | { 39 | GString *string = NULL; 40 | string = g_string_new("Hello World"); 41 | 42 | ck_assert_ptr_nonnull(string); 43 | ck_assert_ptr_nonnull(string->str); 44 | ck_assert_str_eq("Hello World", string->str); 45 | 46 | ck_assert_int_eq(string->len, 11); 47 | ck_assert_int_eq(string->allocated_len, GSTRING_MIN_BUF_SIZE); 48 | 49 | g_string_free(string, true); 50 | } 51 | END_TEST 52 | 53 | START_TEST(test_gstring_new_big_string) 54 | { 55 | GString *string = NULL; 56 | 57 | // alphabet * 2 = 52 characters 58 | string = g_string_new("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); 59 | 60 | ck_assert_ptr_nonnull(string); 61 | ck_assert_ptr_nonnull(string->str); 62 | ck_assert_str_eq("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", string->str); 63 | 64 | ck_assert_int_eq(string->len, 52); 65 | ck_assert_int_eq(string->allocated_len, 53); 66 | 67 | g_string_free(string, true); 68 | } 69 | END_TEST 70 | 71 | START_TEST(test_gstring_new_len) 72 | { 73 | GString *string = NULL; 74 | const char *data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\0abcdefghijklmnopqrstuvwxyz"; 75 | 76 | string = g_string_new_len(data, 52 + 1); // 2 * alphabet + internal null byte 77 | ck_assert_ptr_nonnull(string); 78 | ck_assert_ptr_nonnull(string->str); 79 | ck_assert_int_eq(string->len, 52 + 1); 80 | ck_assert_int_eq(string->allocated_len, 53 + 1); 81 | ck_assert_int_eq(string->str[string->len], '\0'); // terminating null byte 82 | 83 | g_string_free(string, true); 84 | } 85 | END_TEST 86 | 87 | START_TEST(test_gstring_sized_new) 88 | { 89 | GString *string = NULL; 90 | 91 | string = g_string_sized_new(4096); 92 | ck_assert_ptr_nonnull(string); 93 | ck_assert_ptr_nonnull(string->str); 94 | ck_assert_int_eq(string->len, 0); 95 | ck_assert_int_eq(string->allocated_len, 4096); 96 | 97 | g_string_free(string, true); 98 | } 99 | END_TEST 100 | 101 | START_TEST(test_gstring_free) 102 | { 103 | GString *string = NULL; 104 | char *result = NULL; 105 | 106 | string = g_string_new("Hello World"); 107 | result = g_string_free(string, true); 108 | ck_assert_ptr_null(result); 109 | 110 | string = g_string_new("Hello World"); 111 | result = g_string_free(string, false); 112 | ck_assert_ptr_nonnull(result); 113 | ck_assert_str_eq(result, "Hello World"); 114 | free(result); 115 | } 116 | END_TEST 117 | 118 | START_TEST(test_gstring_assign_empty) 119 | { 120 | GString *string = NULL; 121 | 122 | string = g_string_new("Hello World"); 123 | 124 | g_string_assign(string, ""); 125 | 126 | ck_assert_int_eq(string->len, 0); 127 | ck_assert_int_eq(string->allocated_len, GSTRING_MIN_BUF_SIZE); 128 | ck_assert_str_eq("", string->str); 129 | 130 | g_string_free(string, true); 131 | } 132 | END_TEST 133 | 134 | START_TEST(test_gstring_assign) 135 | { 136 | GString *string = NULL; 137 | 138 | string = g_string_new(""); 139 | 140 | g_string_assign(string, "Hello World"); 141 | 142 | ck_assert_int_eq(string->len, 11); 143 | ck_assert_int_eq(string->allocated_len, GSTRING_MIN_BUF_SIZE); 144 | ck_assert_str_eq(string->str, "Hello World"); 145 | 146 | g_string_free(string, true); 147 | } 148 | END_TEST 149 | 150 | START_TEST(test_gstring_append) 151 | { 152 | GString *string = NULL; 153 | 154 | string = g_string_new("Hello World"); 155 | 156 | g_string_append(string, "!!!"); 157 | ck_assert_int_eq(string->len, 14); 158 | ck_assert_int_eq(string->allocated_len, 32); 159 | ck_assert_str_eq("Hello World!!!", string->str); 160 | 161 | g_string_free(string, true); 162 | } 163 | END_TEST 164 | 165 | START_TEST(test_gstring_append_realloc) 166 | { 167 | GString *string = NULL; 168 | 169 | string = g_string_new("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); 170 | 171 | ck_assert_int_eq(string->len, 26); 172 | ck_assert_int_eq(string->allocated_len, 32); 173 | ck_assert_str_eq("ABCDEFGHIJKLMNOPQRSTUVWXYZ", string->str); 174 | 175 | g_string_append(string, "abcdefghijklmnopqrstuvwxyz"); 176 | 177 | ck_assert_int_eq(string->len, 52); 178 | // required would be 53 bytes but GString reallocs double the requested space 179 | ck_assert_int_eq(string->allocated_len, 53 * 2); 180 | 181 | ck_assert_str_eq("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", string->str); 182 | 183 | g_string_free(string, true); 184 | } 185 | END_TEST 186 | 187 | START_TEST(test_gstring_append_c) 188 | { 189 | GString *string = NULL; 190 | 191 | string = g_string_new("Hello World"); 192 | 193 | g_string_append_c(string, '!'); 194 | ck_assert_int_eq(string->len, 12); 195 | ck_assert_int_eq(string->allocated_len, 32); 196 | ck_assert_str_eq("Hello World!", string->str); 197 | 198 | g_string_free(string, true); 199 | } 200 | END_TEST 201 | 202 | START_TEST(test_gstring_append_c_realloc) 203 | { 204 | GString *string = NULL; 205 | size_t len; 206 | 207 | string = g_string_new("Hello World"); 208 | len = string->len; 209 | 210 | for (int i = 0; i < GSTRING_MIN_BUF_SIZE - len; i++) { 211 | g_string_append_c(string, '!'); 212 | } 213 | 214 | ck_assert_int_eq(string->len, 32); 215 | ck_assert_int_eq(string->allocated_len, 66); 216 | ck_assert_str_eq("Hello World!!!!!!!!!!!!!!!!!!!!!", string->str); 217 | 218 | g_string_free(string, true); 219 | } 220 | END_TEST 221 | 222 | START_TEST(test_gstring_append_len) 223 | { 224 | GString *string = NULL; 225 | 226 | string = g_string_new("Hello World"); 227 | 228 | g_string_append_len(string, "!!!X", 3); 229 | ck_assert_int_eq(string->len, 14); 230 | ck_assert_str_eq("Hello World!!!", string->str); 231 | 232 | g_string_free(string, true); 233 | } 234 | END_TEST 235 | 236 | START_TEST(test_gstring_append_len_realloc) 237 | { 238 | GString *string = NULL; 239 | 240 | string = g_string_new("Hello World"); 241 | const char *data = "!!!!!!!!!!!!!!!!!!!!!"; // 21 ! chars 242 | 243 | g_string_append_len(string, data, 21); 244 | ck_assert_int_eq(string->len, 32); 245 | ck_assert_int_eq(string->allocated_len, 66); 246 | ck_assert_str_eq("Hello World!!!!!!!!!!!!!!!!!!!!!", string->str); 247 | 248 | g_string_free(string, true); 249 | } 250 | END_TEST 251 | 252 | START_TEST(test_gstring_prepend) 253 | { 254 | GString *string = NULL; 255 | 256 | string = g_string_new("Hello World"); 257 | 258 | g_string_prepend(string, "*=-"); 259 | ck_assert_int_eq(string->len, 14); 260 | ck_assert_str_eq(string->str, "*=-Hello World"); 261 | 262 | g_string_free(string, true); 263 | } 264 | END_TEST 265 | 266 | START_TEST(test_gstring_prepend_realloc) 267 | { 268 | GString *string = NULL; 269 | 270 | string = g_string_new("Hello World"); 271 | ck_assert_int_eq(string->allocated_len, GSTRING_MIN_BUF_SIZE); 272 | 273 | g_string_prepend(string, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); 274 | ck_assert_int_eq(string->len, 63); 275 | ck_assert_str_eq(string->str, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzHello World"); 276 | ck_assert_int_eq(string->allocated_len, 64 * 2); 277 | 278 | g_string_free(string, true); 279 | } 280 | END_TEST 281 | 282 | START_TEST(test_gstring_prepend_c) 283 | { 284 | GString *string = NULL; 285 | size_t len; 286 | 287 | string = g_string_new("Hello World"); 288 | len = string->len; 289 | 290 | for (int i = 0; i < GSTRING_MIN_BUF_SIZE - len; i++) { 291 | g_string_prepend_c(string, '='); 292 | } 293 | 294 | ck_assert_int_eq(string->len, 32); 295 | ck_assert_int_eq(string->allocated_len, 66); 296 | ck_assert_str_eq(string->str, "=====================Hello World"); 297 | 298 | g_string_free(string, true); 299 | } 300 | END_TEST 301 | 302 | START_TEST(test_gstring_prepend_len) 303 | { 304 | GString *string = NULL; 305 | 306 | string = g_string_new("Hello World"); 307 | 308 | g_string_prepend_len(string, "*=-", 3); 309 | ck_assert_int_eq(string->len, 14); 310 | ck_assert_str_eq(string->str, "*=-Hello World"); 311 | 312 | g_string_free(string, true); 313 | } 314 | END_TEST 315 | 316 | START_TEST(test_gstring_prepend_len_realloc) 317 | { 318 | GString *string = NULL; 319 | 320 | string = g_string_new("Hello World"); 321 | ck_assert_int_eq(string->allocated_len, GSTRING_MIN_BUF_SIZE); 322 | 323 | g_string_prepend_len(string, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 52); 324 | ck_assert_int_eq(string->len, 63); 325 | ck_assert_str_eq(string->str, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzHello World"); 326 | ck_assert_int_eq(string->allocated_len, 64 * 2); 327 | 328 | g_string_free(string, true); 329 | } 330 | END_TEST 331 | 332 | START_TEST(test_gstring_prepend_c_realloc) 333 | { 334 | GString *string = NULL; 335 | 336 | string = g_string_new("Hello World"); 337 | 338 | 339 | g_string_free(string, true); 340 | } 341 | END_TEST 342 | 343 | START_TEST(test_gstring_insert) 344 | { 345 | GString *string = NULL; 346 | 347 | string = g_string_new("Hello World"); 348 | 349 | g_string_insert(string, 5, " you cruel"); 350 | ck_assert_int_eq(string->len, 21); 351 | ck_assert_str_eq(string->str, "Hello you cruel World"); 352 | 353 | g_string_insert(string, 21, "!!!"); 354 | ck_assert_int_eq(string->len, 24); 355 | ck_assert_str_eq(string->str, "Hello you cruel World!!!"); 356 | 357 | g_string_free(string, true); 358 | } 359 | END_TEST 360 | 361 | START_TEST(test_gstring_insert_realloc) 362 | { 363 | GString *string = NULL; 364 | 365 | string = g_string_new("Hello World"); 366 | 367 | g_string_insert(string, 5, " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"); 368 | ck_assert_int_eq(string->len, 64); 369 | ck_assert_int_eq(string->allocated_len, 130); 370 | ck_assert_str_eq(string->str, "Hello ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz World"); 371 | 372 | g_string_free(string, true); 373 | } 374 | END_TEST 375 | 376 | START_TEST(test_gstring_insert_len) 377 | { 378 | GString *string = NULL; 379 | 380 | string = g_string_new("Hello World"); 381 | 382 | g_string_insert_len(string, 5, " you cruel", 10); 383 | ck_assert_int_eq(string->len, 21); 384 | ck_assert_str_eq(string->str, "Hello you cruel World"); 385 | 386 | g_string_insert_len(string, 21, "!!!", 3); 387 | ck_assert_int_eq(string->len, 24); 388 | ck_assert_str_eq(string->str, "Hello you cruel World!!!"); 389 | 390 | g_string_free(string, true); 391 | } 392 | END_TEST 393 | 394 | START_TEST(test_gstring_insert_len_realloc) 395 | { 396 | GString *string = NULL; 397 | 398 | string = g_string_new("Hello World"); 399 | 400 | g_string_insert_len(string, 5, " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", 53); 401 | ck_assert_int_eq(string->len, 64); 402 | ck_assert_int_eq(string->allocated_len, 130); 403 | ck_assert_str_eq(string->str, "Hello ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz World"); 404 | 405 | g_string_free(string, true); 406 | } 407 | END_TEST 408 | 409 | START_TEST(test_gstring_insert_c) 410 | { 411 | GString *string = NULL; 412 | 413 | string = g_string_new("Hello World"); 414 | 415 | g_string_insert_c(string, 5, '*'); 416 | ck_assert_int_eq(string->len, 12); 417 | ck_assert_str_eq(string->str, "Hello* World"); 418 | 419 | g_string_free(string, true); 420 | } 421 | END_TEST 422 | 423 | START_TEST(test_gstring_insert_c_realloc) 424 | { 425 | GString *string = NULL; 426 | 427 | string = g_string_new("Hello World"); 428 | size_t len = string->len; 429 | 430 | for (int i = 0; i < GSTRING_MIN_BUF_SIZE - len; i++) { 431 | g_string_insert_c(string, 5, '*'); 432 | } 433 | 434 | ck_assert_int_eq(string->len, 32); 435 | ck_assert_int_eq(string->allocated_len, 66); 436 | ck_assert_str_eq(string->str, "Hello********************* World"); 437 | 438 | g_string_free(string, true); 439 | } 440 | END_TEST 441 | 442 | START_TEST(test_gstring_overwrite) 443 | { 444 | GString *string = NULL; 445 | GString *result; 446 | 447 | string = g_string_new("ABCDEFGHIJKLMN"); 448 | result = g_string_overwrite(string, 3, "def"); 449 | 450 | ck_assert_ptr_eq(result, string); 451 | ck_assert_int_eq(string->len, 14); 452 | ck_assert_str_eq(string->str, "ABCdefGHIJKLMN"); 453 | 454 | g_string_free(string, true); 455 | } 456 | END_TEST 457 | 458 | START_TEST(test_gstring_overwrite_extend) 459 | { 460 | GString *string = NULL; 461 | GString *result; 462 | 463 | string = g_string_new("ABCDEF"); 464 | result = g_string_overwrite(string, 3, "defghijklmn"); 465 | 466 | ck_assert_ptr_eq(result, string); 467 | ck_assert_int_eq(string->len, 14); 468 | ck_assert_str_eq(string->str, "ABCdefghijklmn"); 469 | 470 | g_string_free(string, true); 471 | } 472 | END_TEST 473 | 474 | START_TEST(test_gstring_replace_null) 475 | { 476 | GString *string = NULL; 477 | unsigned int result; 478 | 479 | string = g_string_new("A string is a string is a string"); 480 | 481 | result = g_string_replace(string, NULL, "car", 0); 482 | 483 | ck_assert_int_eq(result, 0); 484 | ck_assert_int_eq(string->len, 32); 485 | ck_assert_str_eq(string->str, "A string is a string is a string"); 486 | 487 | result = g_string_replace(string, "string", NULL, 0); 488 | 489 | ck_assert_int_eq(result, 0); 490 | ck_assert_int_eq(string->len, 32); 491 | ck_assert_str_eq(string->str, "A string is a string is a string"); 492 | 493 | g_string_free(string, true); 494 | } 495 | END_TEST 496 | 497 | START_TEST(test_gstring_replace_empty_find) 498 | { 499 | GString *string = NULL; 500 | unsigned int result; 501 | 502 | string = g_string_new(""); 503 | result = g_string_replace(string, "", "_", 0); 504 | 505 | ck_assert_int_eq(result, 1); 506 | ck_assert_int_eq(string->len, 1); 507 | ck_assert_str_eq(string->str, "_"); 508 | 509 | g_string_assign(string, "This is my string!"); 510 | result = g_string_replace(string, "", "_", 0); 511 | 512 | ck_assert_int_eq(result, 19); 513 | ck_assert_int_eq(string->len, 37); 514 | ck_assert_str_eq(string->str, "_T_h_i_s_ _i_s_ _m_y_ _s_t_r_i_n_g_!_"); 515 | 516 | g_string_free(string, true); 517 | } 518 | END_TEST 519 | 520 | START_TEST(test_gstring_replace) 521 | { 522 | GString *string = NULL; 523 | unsigned int result; 524 | 525 | string = g_string_new("A string is a string is a string"); 526 | 527 | // replace string with car 528 | result = g_string_replace(string, "string", "car", 0); 529 | 530 | ck_assert_int_eq(result, 3); 531 | ck_assert_int_eq(string->len, 23); 532 | ck_assert_str_eq(string->str, "A car is a car is a car"); 533 | 534 | // replace car with string to recreate the original string 535 | result = g_string_replace(string, "car", "string", 0); 536 | ck_assert_int_eq(result, 3); 537 | ck_assert_int_eq(string->len, 32); 538 | ck_assert_str_eq(string->str, "A string is a string is a string"); 539 | 540 | g_string_free(string, true); 541 | } 542 | END_TEST 543 | 544 | START_TEST(test_gstring_replace_same) 545 | { 546 | GString *string = NULL; 547 | unsigned int result; 548 | 549 | string = g_string_new("A string is a string is a string"); 550 | 551 | // replace string with string 552 | result = g_string_replace(string, "string", "string", 0); 553 | 554 | ck_assert_int_eq(result, 3); 555 | ck_assert_int_eq(string->len, 32); 556 | ck_assert_str_eq(string->str, "A string is a string is a string"); 557 | 558 | g_string_free(string, true); 559 | } 560 | END_TEST 561 | 562 | START_TEST(test_gstring_replace_limit) 563 | { 564 | GString *string = NULL; 565 | unsigned int result; 566 | 567 | string = g_string_new("A string is a string is a string"); 568 | 569 | // replace the first two occurences of string with car 570 | result = g_string_replace(string, "string", "car", 2); 571 | 572 | ck_assert_int_eq(result, 2); 573 | ck_assert_int_eq(string->len, 26); 574 | ck_assert_str_eq(string->str, "A car is a car is a string"); 575 | 576 | g_string_free(string, true); 577 | } 578 | END_TEST 579 | 580 | START_TEST(test_gstring_erase) 581 | { 582 | GString *string = NULL; 583 | 584 | string = g_string_new("Hello you cruel World"); 585 | // remove " you cruel " 586 | g_string_erase(string, 5, 10); 587 | 588 | ck_assert_int_eq(string->len, 11); 589 | ck_assert_str_eq("Hello World", string->str); 590 | 591 | // remove " World" 592 | g_string_erase(string, 5, 10); 593 | 594 | ck_assert_int_eq(string->len, 5); 595 | ck_assert_str_eq("Hello", string->str); 596 | 597 | // truncate via erase 598 | g_string_erase(string, 3, -1); 599 | ck_assert_int_eq(string->len, 3); 600 | ck_assert_str_eq("Hel", string->str); 601 | 602 | g_string_free(string, true); 603 | } 604 | END_TEST 605 | 606 | START_TEST(test_gstring_truncate) 607 | { 608 | GString *string = NULL; 609 | 610 | string = g_string_new("Hello World"); 611 | 612 | g_string_truncate(string, 5); 613 | ck_assert_int_eq(string->len, 5); 614 | ck_assert_str_eq("Hello", string->str); 615 | 616 | g_string_truncate(string, 0); 617 | ck_assert_int_eq(string->len, 0); 618 | ck_assert_str_eq("", string->str); 619 | 620 | g_string_free(string, true); 621 | } 622 | END_TEST 623 | 624 | START_TEST(test_gstring_printf_char) 625 | { 626 | GString *string = NULL; 627 | string = g_string_new(""); 628 | 629 | g_string_printf(string, "%c", 'A'); 630 | ck_assert_int_eq(string->len, 1); 631 | ck_assert_str_eq(string->str, "A"); 632 | 633 | g_string_printf(string, "%c", 'B'); 634 | ck_assert_int_eq(string->len, 1); 635 | ck_assert_str_eq(string->str, "B"); 636 | 637 | g_string_free(string, true); 638 | } 639 | END_TEST 640 | 641 | START_TEST(test_gstring_printf_int) 642 | { 643 | GString *string = NULL; 644 | string = g_string_new(""); 645 | 646 | g_string_printf(string, "%d", -1234); 647 | ck_assert_int_eq(string->len, 5); 648 | ck_assert_str_eq(string->str, "-1234"); 649 | 650 | g_string_printf(string, "%d", 1234); 651 | ck_assert_int_eq(string->len, 4); 652 | ck_assert_str_eq(string->str, "1234"); 653 | 654 | g_string_free(string, true); 655 | } 656 | END_TEST 657 | 658 | START_TEST(test_gstring_printf_hex) 659 | { 660 | GString *string = NULL; 661 | string = g_string_new(""); 662 | 663 | g_string_printf(string, "0x%x", 255); 664 | ck_assert_int_eq(string->len, 4); 665 | ck_assert_str_eq(string->str, "0xff"); 666 | 667 | g_string_printf(string, "0x%X", 255); 668 | ck_assert_int_eq(string->len, 4); 669 | ck_assert_str_eq(string->str, "0xFF"); 670 | 671 | g_string_free(string, true); 672 | } 673 | END_TEST 674 | 675 | START_TEST(test_gstring_printf_string) 676 | { 677 | GString *string = NULL; 678 | string = g_string_new(""); 679 | 680 | g_string_printf(string, "%s", "Hello World"); 681 | ck_assert_int_eq(string->len, 11); 682 | ck_assert_str_eq(string->str, "Hello World"); 683 | 684 | g_string_free(string, true); 685 | } 686 | END_TEST 687 | 688 | START_TEST(test_gstring_printf_float) 689 | { 690 | GString *string = NULL; 691 | string = g_string_new(""); 692 | 693 | g_string_printf(string, "%f", 17.38); 694 | ck_assert_int_eq(string->len, 9); 695 | ck_assert_str_eq(string->str, "17.380000"); 696 | 697 | g_string_free(string, true); 698 | } 699 | END_TEST 700 | 701 | START_TEST(test_gstring_printf_float_precision) 702 | { 703 | GString *string = NULL; 704 | string = g_string_new(""); 705 | 706 | g_string_printf(string, "%.2f", 17.38); 707 | ck_assert_int_eq(string->len, 5); 708 | ck_assert_str_eq(string->str, "17.38"); 709 | 710 | g_string_printf(string, "%.2f and %f", 17.38, 17.38); 711 | ck_assert_int_eq(string->len, 19); 712 | ck_assert_str_eq(string->str, "17.38 and 17.380000"); 713 | 714 | g_string_free(string, true); 715 | } 716 | END_TEST 717 | 718 | START_TEST(test_gstring_printf) 719 | { 720 | GString *string = NULL; 721 | string = g_string_new(""); 722 | 723 | g_string_printf(string, "Char: '%c'; String: '%s'; Number: %d; Hex: 0x%x", 'A', "STR1", 42, 0xa1); 724 | ck_assert_int_eq(string->len, 48); 725 | ck_assert_str_eq(string->str, "Char: 'A'; String: 'STR1'; Number: 42; Hex: 0xa1"); 726 | 727 | g_string_free(string, true); 728 | } 729 | END_TEST 730 | 731 | START_TEST(test_gstring_append_printf) 732 | { 733 | GString *string = NULL; 734 | string = g_string_new("TEST: "); 735 | 736 | g_string_append_printf(string, "String: '%s'; Number: %d; Hex: 0x%x", "STR1", 42, 0xa1); 737 | ck_assert_int_eq(string->len, 43); 738 | ck_assert_str_eq(string->str, "TEST: String: 'STR1'; Number: 42; Hex: 0xa1"); 739 | 740 | g_string_free(string, true); 741 | } 742 | END_TEST 743 | 744 | START_TEST(test_gstring_equal) 745 | { 746 | GString *string1 = NULL; 747 | GString *string2 = NULL; 748 | 749 | string1 = g_string_new("Hello World"); 750 | string2 = g_string_new(""); 751 | 752 | ck_assert(!g_string_equal(string1, string2)); 753 | 754 | g_string_assign(string2, "Hello World"); 755 | ck_assert(g_string_equal(string1, string2)); 756 | 757 | g_string_assign(string2, "Hello Worlt"); 758 | ck_assert(!g_string_equal(string1, string2)); 759 | 760 | g_string_free(string1, true); 761 | g_string_free(string2, true); 762 | } 763 | END_TEST 764 | 765 | Suite* gstring_suite(void) 766 | { 767 | Suite *s; 768 | TCase *tc_core; 769 | 770 | s = suite_create("GString"); 771 | 772 | /* Core test case */ 773 | tc_core = tcase_create("Core"); 774 | 775 | tcase_add_test(tc_core, test_gstring_new_null); 776 | tcase_add_test(tc_core, test_gstring_new_empty); 777 | tcase_add_test(tc_core, test_gstring_new_small_string); 778 | tcase_add_test(tc_core, test_gstring_new_big_string); 779 | tcase_add_test(tc_core, test_gstring_new_len); 780 | tcase_add_test(tc_core, test_gstring_sized_new); 781 | 782 | tcase_add_test(tc_core, test_gstring_assign_empty); 783 | tcase_add_test(tc_core, test_gstring_assign); 784 | 785 | tcase_add_test(tc_core, test_gstring_append); 786 | tcase_add_test(tc_core, test_gstring_append_realloc); 787 | tcase_add_test(tc_core, test_gstring_append_c); 788 | tcase_add_test(tc_core, test_gstring_append_c_realloc); 789 | tcase_add_test(tc_core, test_gstring_append_len); 790 | tcase_add_test(tc_core, test_gstring_append_len_realloc); 791 | 792 | tcase_add_test(tc_core, test_gstring_prepend); 793 | tcase_add_test(tc_core, test_gstring_prepend_realloc); 794 | tcase_add_test(tc_core, test_gstring_prepend_len); 795 | tcase_add_test(tc_core, test_gstring_prepend_len_realloc); 796 | tcase_add_test(tc_core, test_gstring_prepend_c); 797 | tcase_add_test(tc_core, test_gstring_prepend_c_realloc); 798 | 799 | tcase_add_test(tc_core, test_gstring_insert); 800 | tcase_add_test(tc_core, test_gstring_insert_realloc); 801 | tcase_add_test(tc_core, test_gstring_insert_c); 802 | tcase_add_test(tc_core, test_gstring_insert_c_realloc); 803 | tcase_add_test(tc_core, test_gstring_insert_len); 804 | tcase_add_test(tc_core, test_gstring_insert_len_realloc); 805 | 806 | tcase_add_test(tc_core, test_gstring_overwrite); 807 | tcase_add_test(tc_core, test_gstring_overwrite_extend); 808 | 809 | tcase_add_test(tc_core, test_gstring_replace_null); 810 | tcase_add_test(tc_core, test_gstring_replace_empty_find); 811 | tcase_add_test(tc_core, test_gstring_replace); 812 | tcase_add_test(tc_core, test_gstring_replace_same); 813 | tcase_add_test(tc_core, test_gstring_replace_limit); 814 | 815 | tcase_add_test(tc_core, test_gstring_erase); 816 | 817 | tcase_add_test(tc_core, test_gstring_truncate); 818 | 819 | tcase_add_test(tc_core, test_gstring_printf_char); 820 | tcase_add_test(tc_core, test_gstring_printf_int); 821 | tcase_add_test(tc_core, test_gstring_printf_hex); 822 | tcase_add_test(tc_core, test_gstring_printf_string); 823 | tcase_add_test(tc_core, test_gstring_printf_float); 824 | tcase_add_test(tc_core, test_gstring_printf_float_precision); 825 | tcase_add_test(tc_core, test_gstring_printf); 826 | 827 | tcase_add_test(tc_core, test_gstring_append_printf); 828 | 829 | tcase_add_test(tc_core, test_gstring_equal); 830 | 831 | tcase_add_test(tc_core, test_gstring_free); 832 | 833 | suite_add_tcase(s, tc_core); 834 | 835 | return s; 836 | } 837 | 838 | int main(int argc, char **argv) 839 | { 840 | int number_failed; 841 | Suite *s; 842 | SRunner *sr; 843 | 844 | s = gstring_suite(); 845 | sr = srunner_create(s); 846 | 847 | srunner_run_all(sr, CK_NORMAL); 848 | number_failed = srunner_ntests_failed(sr); 849 | srunner_free(sr); 850 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 851 | } 852 | -------------------------------------------------------------------------------- /tests/test_integration.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define _CLIB_IMPL 1 6 | #include "garray.h" 7 | #include "ghashtable.h" 8 | #include "glist.h" 9 | #include "gstring.h" 10 | 11 | START_TEST(test_integration) 12 | { 13 | // Dummy test 14 | // 15 | // The purpose of this test binary is to ensure that it is possible to 16 | // compile a binary containing all headers of CLib at the same time. 17 | } 18 | END_TEST 19 | 20 | Suite* all_suite(void) 21 | { 22 | Suite *s; 23 | TCase *tc_core; 24 | 25 | s = suite_create("All"); 26 | 27 | /* Core test case */ 28 | tc_core = tcase_create("Core"); 29 | 30 | tcase_add_test(tc_core, test_integration); 31 | 32 | suite_add_tcase(s, tc_core); 33 | 34 | return s; 35 | } 36 | 37 | int main(int argc, char **argv) 38 | { 39 | int number_failed; 40 | Suite *s; 41 | SRunner *sr; 42 | 43 | s = all_suite(); 44 | sr = srunner_create(s); 45 | 46 | srunner_run_all(sr, CK_NORMAL); 47 | number_failed = srunner_ntests_failed(sr); 48 | srunner_free(sr); 49 | return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 50 | } 51 | --------------------------------------------------------------------------------