├── .clang-format ├── .editorconfig ├── .gitattributes ├── .github └── workflows │ ├── cmake.yml │ └── codeql-analysis.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cvector.h ├── cvector_utils.h ├── example.c ├── test.c ├── unit-tests.c └── utest └── utest.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: true 6 | AlignConsecutiveDeclarations: false 7 | AlignEscapedNewlines: Left 8 | AlignOperands: true 9 | AlignTrailingComments: true 10 | AllowAllParametersOfDeclarationOnNextLine: true 11 | AllowShortBlocksOnASingleLine: false 12 | AllowShortCaseLabelsOnASingleLine: false 13 | AllowShortFunctionsOnASingleLine: InlineOnly 14 | AllowShortIfStatementsOnASingleLine: true 15 | AllowShortLoopsOnASingleLine: false 16 | AlwaysBreakAfterDefinitionReturnType: None 17 | AlwaysBreakAfterReturnType: None 18 | AlwaysBreakBeforeMultilineStrings: false 19 | AlwaysBreakTemplateDeclarations: Yes 20 | BinPackArguments: true 21 | BinPackParameters: true 22 | BraceWrapping: 23 | AfterClass: false 24 | AfterControlStatement: false 25 | AfterEnum: false 26 | AfterFunction: false 27 | AfterNamespace: false 28 | AfterObjCDeclaration: false 29 | AfterStruct: false 30 | AfterUnion: false 31 | AfterExternBlock: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | SplitEmptyFunction: true 36 | SplitEmptyRecord: true 37 | SplitEmptyNamespace: true 38 | BreakBeforeBinaryOperators: None 39 | BreakBeforeBraces: Attach 40 | BreakBeforeInheritanceComma: false 41 | BreakInheritanceList: BeforeColon 42 | BreakBeforeTernaryOperators: true 43 | BreakConstructorInitializersBeforeComma: false 44 | BreakConstructorInitializers: BeforeColon 45 | BreakAfterJavaFieldAnnotations: false 46 | BreakStringLiterals: true 47 | ColumnLimit: 0 48 | CommentPragmas: '^ IWYU pragma:' 49 | CompactNamespaces: false 50 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 51 | ConstructorInitializerIndentWidth: 4 52 | ContinuationIndentWidth: 4 53 | Cpp11BracedListStyle: true 54 | DerivePointerAlignment: false 55 | DisableFormat: false 56 | ExperimentalAutoDetectBinPacking: false 57 | FixNamespaceComments: false 58 | ForEachMacros: 59 | - foreach 60 | - Q_FOREACH 61 | - BOOST_FOREACH 62 | IncludeBlocks: Preserve 63 | IncludeCategories: 64 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 65 | Priority: 2 66 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 67 | Priority: 3 68 | - Regex: '.*' 69 | Priority: 1 70 | IncludeIsMainRegex: '(Test)?$' 71 | IndentCaseLabels: false 72 | IndentPPDirectives: None 73 | IndentWidth: 4 74 | IndentWrappedFunctionNames: false 75 | JavaScriptQuotes: Leave 76 | JavaScriptWrapImports: true 77 | KeepEmptyLinesAtTheStartOfBlocks: true 78 | MacroBlockBegin: '' 79 | MacroBlockEnd: '' 80 | MaxEmptyLinesToKeep: 1 81 | NamespaceIndentation: None 82 | ObjCBinPackProtocolList: Auto 83 | ObjCBlockIndentWidth: 2 84 | ObjCSpaceAfterProperty: false 85 | ObjCSpaceBeforeProtocolList: true 86 | PenaltyBreakAssignment: 2 87 | PenaltyBreakBeforeFirstCallParameter: 19 88 | PenaltyBreakComment: 300 89 | PenaltyBreakFirstLessLess: 120 90 | PenaltyBreakString: 1000 91 | PenaltyBreakTemplateDeclaration: 10 92 | PenaltyExcessCharacter: 1000000 93 | PenaltyReturnTypeOnItsOwnLine: 60 94 | PointerAlignment: Right 95 | ReflowComments: true 96 | SortIncludes: true 97 | SortUsingDeclarations: true 98 | SpaceAfterCStyleCast: false 99 | SpaceAfterTemplateKeyword: true 100 | SpaceBeforeAssignmentOperators: true 101 | SpaceBeforeCpp11BracedList: false 102 | SpaceBeforeCtorInitializerColon: true 103 | SpaceBeforeInheritanceColon: true 104 | SpaceBeforeParens: ControlStatements 105 | SpaceBeforeRangeBasedForLoopColon: true 106 | SpaceInEmptyParentheses: false 107 | SpacesBeforeTrailingComments: 1 108 | SpacesInAngles: false 109 | SpacesInContainerLiterals: true 110 | SpacesInCStyleCastParentheses: false 111 | SpacesInParentheses: false 112 | SpacesInSquareBrackets: false 113 | Standard: Cpp11 114 | StatementMacros: 115 | - Q_UNUSED 116 | - QT_REQUIRE_VERSION 117 | TabWidth: 4 118 | UseTab: Never 119 | ... 120 | 121 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | 12 | # Matches multiple files with brace expansion notation 13 | # Set default charset 14 | [*.{c,h}] 15 | charset = utf-8 16 | indent_style = space 17 | indent_size = 4 18 | trim_trailing_whitespace = true 19 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.h linguist-language=C 2 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | paths-ignore: 7 | - 'README.md' 8 | - '.github/workflows/codeql-analysis.yml' 9 | pull_request: 10 | branches: [ master ] 11 | paths-ignore: 12 | - 'README.md' 13 | - '.github/workflows/codeql-analysis.yml' 14 | 15 | env: 16 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 17 | BUILD_TYPE: Debug 18 | 19 | jobs: 20 | ubuntu-latest: 21 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 22 | # You can convert this to a matrix build if you need cross-platform coverage. 23 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 24 | runs-on: ubuntu-latest 25 | 26 | steps: 27 | - uses: actions/checkout@v2 28 | - name: Install dependencies 29 | run: sudo apt-get install -y valgrind 30 | 31 | - name: Configure CMake 32 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 33 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 34 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 35 | 36 | - name: Build 37 | # Build your program with the given configuration 38 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 39 | 40 | - name: Test 41 | working-directory: ${{github.workspace}}/build 42 | # Execute tests defined by the CMake configuration. 43 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 44 | run: | 45 | make test 46 | make memcheck 47 | 48 | macos-latest: 49 | runs-on: macos-latest 50 | steps: 51 | - uses: actions/checkout@v2 52 | - name: Configure CMake 53 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 54 | - name: Build 55 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 56 | - name: Test 57 | working-directory: ${{github.workspace}}/build 58 | run: | 59 | make test 60 | 61 | clang-format-checking: 62 | runs-on: ubuntu-latest 63 | steps: 64 | - uses: actions/checkout@v2 65 | - uses: RafikFarhad/clang-format-github-action@v1.0.1 66 | with: 67 | sources: "*.h,*.c" 68 | style: file 69 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | paths-ignore: 7 | - '**/*.md' 8 | - '.github/workflows/cmake.yml' 9 | pull_request: 10 | branches: [ master ] 11 | paths-ignore: 12 | - '**/*.md' 13 | - '.github/workflows/cmake.yml' 14 | schedule: 15 | - cron: '30 0 15 * *' 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | permissions: 22 | actions: read 23 | contents: read 24 | security-events: write 25 | 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | language: [ 'cpp' ] 30 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 31 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 32 | 33 | steps: 34 | - name: Checkout repository 35 | uses: actions/checkout@v2 36 | 37 | # Initializes the CodeQL tools for scanning. 38 | - name: Initialize CodeQL 39 | uses: github/codeql-action/init@v1 40 | with: 41 | languages: ${{ matrix.language }} 42 | # If you wish to specify custom queries, you can do so here or in a config file. 43 | # By default, queries listed here will override any specified in a config file. 44 | # Prefix the list here with "+" to use these queries and those in the config file. 45 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 46 | 47 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 48 | # If this step fails, then you should remove it and run the build manually (see below) 49 | - name: Autobuild 50 | uses: github/codeql-action/autobuild@v1 51 | 52 | # ℹ️ Command-line programs to run using the OS shell. 53 | # 📚 https://git.io/JvXDl 54 | 55 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 56 | # and modify them (or add more) to build your code if your project 57 | # uses a compiled language 58 | 59 | #- run: | 60 | # make bootstrap 61 | # make release 62 | 63 | - name: Perform CodeQL Analysis 64 | uses: github/codeql-action/analyze@v1 65 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | 34 | # toptal/gitignore/templates/CMake.gitignore 35 | 36 | CMakeLists.txt.user 37 | CMakeCache.txt 38 | CMakeFiles 39 | CMakeScripts 40 | Testing 41 | Makefile 42 | cmake_install.cmake 43 | install_manifest.txt 44 | compile_commands.json 45 | CTestTestfile.cmake 46 | _deps 47 | 48 | build 49 | 50 | # toptal/gitignore/templates/VisualStudioCode.gitignore 51 | 52 | .vscode/* 53 | !.vscode/settings.json 54 | !.vscode/tasks.json 55 | !.vscode/launch.json 56 | !.vscode/extensions.json 57 | 58 | !.vscode/*.code-snippets -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.7.0) 2 | project(c-vector LANGUAGES C VERSION 0.1.0) 3 | enable_testing() 4 | 5 | # ------ cvector library ------ 6 | add_library(${CMAKE_PROJECT_NAME} INTERFACE) 7 | 8 | target_sources(${CMAKE_PROJECT_NAME} INTERFACE 9 | ${CMAKE_CURRENT_SOURCE_DIR}/cvector.h 10 | ${CMAKE_CURRENT_SOURCE_DIR}/cvector_utils.h 11 | ) 12 | 13 | # ------------------------------ 14 | 15 | # ------ simple example ------ 16 | add_executable(c-vector-example 17 | example.c 18 | ) 19 | 20 | target_link_libraries(c-vector-example 21 | PUBLIC 22 | ${CMAKE_PROJECT_NAME} 23 | ) 24 | 25 | set_target_properties(c-vector-example PROPERTIES C_STANDARD 90) 26 | target_compile_options(c-vector-example PUBLIC -Wall -Werror -Wextra) 27 | 28 | # ---------------------------- 29 | 30 | # ------ test executable used for memory checks ------ 31 | add_executable(test-c-vector 32 | EXCLUDE_FROM_ALL 33 | ${CMAKE_CURRENT_SOURCE_DIR}/test.c 34 | ) 35 | 36 | add_test(NAME test-c-vector COMMAND test-c-vector) 37 | set_target_properties(test-c-vector PROPERTIES C_STANDARD 90) 38 | target_compile_options(test-c-vector PUBLIC -Wall -Werror -Wextra) 39 | 40 | add_test(test_build 41 | "${CMAKE_COMMAND}" 42 | --build "${CMAKE_BINARY_DIR}" 43 | --config "$" 44 | --target test-c-vector 45 | ) 46 | set_tests_properties(test_build PROPERTIES FIXTURES_SETUP test_fixture) 47 | set_tests_properties(test-c-vector PROPERTIES FIXTURES_REQUIRED test_fixture) 48 | 49 | find_program(VALGRIND "valgrind") 50 | if(VALGRIND) 51 | add_custom_target(memcheck 52 | COMMAND "${VALGRIND}" 53 | --tool=memcheck 54 | --leak-check=full 55 | --show-reachable=yes 56 | --error-exitcode=1 57 | $ 58 | ) 59 | endif() 60 | 61 | # ---------------------------------------------------- 62 | 63 | # ------ unit tests ------ 64 | 65 | add_executable(unit-tests 66 | EXCLUDE_FROM_ALL 67 | ${CMAKE_CURRENT_SOURCE_DIR}/unit-tests.c 68 | ) 69 | 70 | add_test(NAME unit-tests COMMAND $) 71 | set_target_properties(unit-tests PROPERTIES C_STANDARD 90) 72 | target_compile_options(unit-tests PUBLIC -Wall -Werror -Wextra) 73 | 74 | add_test(unit_test_build 75 | "${CMAKE_COMMAND}" 76 | --build "${CMAKE_BINARY_DIR}" 77 | --config "$" 78 | --target unit-tests 79 | ) 80 | set_tests_properties(unit_test_build PROPERTIES FIXTURES_SETUP test_fixture) 81 | set_tests_properties(unit-tests PROPERTIES FIXTURES_REQUIRED test_fixture) 82 | 83 | # ------------------------ 84 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Evan Teran 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![c-cpp-badge](https://github.com/eteran/c-vector/actions/workflows/cmake.yml/badge.svg)](https://github.com/eteran/c-vector/actions/workflows/cmake.yml) 2 | [![CodeQL](https://github.com/eteran/c-vector/actions/workflows/codeql-analysis.yml/badge.svg?branch=master)](https://github.com/eteran/c-vector/actions/workflows/codeql-analysis.yml) 3 | 4 | This is an implementation of a `std::vector` like growable array, but in plain 5 | C89 code. The result is a type safe, easy to use, dynamic array that has a 6 | familiar set of operations. 7 | 8 | It works by using the same trick as many allocators, which is to slightly 9 | allocate more data than requested, and using that extra padding in the front 10 | as storage for meta-data. Thus any non-null vector looks like this in memory: 11 | 12 | +------+----------+-----------------+---------+ 13 | | size | capacity | elem_destructor | data... | 14 | +------+----------+-----------------+---------+ 15 | ^ 16 | | user's pointer 17 | 18 | Where the user is given a pointer to first element of `data`. This way the 19 | code has trivial access to the necessary meta-data, but the user need not be 20 | concerned with these details. The total overhead is 21 | `2 * sizeof(size_t) + sizeof(void (*)(void *))` per vector. 22 | 23 | To allow the code to be maximally generic, it is implemented as all macros, and 24 | is thus header only. Usage is simple: 25 | ```c 26 | /* if this is defined, then the vector will increase in capacity by one 27 | * each time it runs out of space. if it is not defined, then the vector will 28 | * be liberal, and will double in capacity each time it runs out of space. 29 | * having this defined will minimize how much unused space is allocated. 30 | */ 31 | #define CVECTOR_LINEAR_GROWTH 32 | 33 | #include "cvector.h" 34 | #include 35 | 36 | int main(int argc, char *argv[]) { 37 | 38 | /* this is the variable that will store the array, you can have 39 | * a vector of any type! For example, you may write float *v = NULL, 40 | * and you'd have a vector of floats :-). NULL will have a size 41 | * and capacity of 0. Additionally, vector_begin and vector_end will 42 | * return NULL on a NULL vector. Alternatively, for clarity of writing 43 | * you can use the cvector_vector_type macro to define a vector of a 44 | * given type. 45 | */ 46 | cvector_vector_type(int) v = NULL; 47 | 48 | (void)argc; 49 | (void)argv; 50 | 51 | /* add some elements to the back */ 52 | cvector_push_back(v, 10); 53 | cvector_push_back(v, 20); 54 | cvector_push_back(v, 30); 55 | cvector_push_back(v, 40); 56 | 57 | /* remove an element by specifying an array subscript */ 58 | cvector_erase(v, 2); 59 | 60 | /* remove an element from the back */ 61 | cvector_pop_back(v); 62 | 63 | /* print out some stats about the vector */ 64 | printf("pointer : %p\n", (void *)v); 65 | printf("capacity: %lu\n", cvector_capacity(v)); 66 | printf("size : %lu\n", cvector_size(v)); 67 | 68 | /* iterator over the vector using "iterator" style */ 69 | if (v) { 70 | int *it; 71 | int i = 0; 72 | for (it = cvector_begin(v); it != cvector_end(v); ++it) { 73 | printf("v[%d] = %d\n", i, *it); 74 | ++i; 75 | } 76 | } 77 | 78 | /* iterator over the vector standard indexing too! */ 79 | if (v) { 80 | size_t i; 81 | for (i = 0; i < cvector_size(v); ++i) { 82 | printf("v[%lu] = %d\n", i, v[i]); 83 | } 84 | } 85 | 86 | /* well, we don't have destructors, so let's clean things up */ 87 | cvector_free(v); 88 | 89 | return 0; 90 | } 91 | 92 | ``` 93 | 94 | ### API 95 | 96 | | `std::vector` | `cvector` | 97 | | ------------- | --------- | 98 | | [`std::vector v`](https://en.cppreference.com/w/cpp/container/vector/vector) | `cvector(int) v` | 99 | | [Destructor](https://en.cppreference.com/w/cpp/container/vector/%7Evector) | `cvector_free(v)` | 100 | | [`v.at(3)`](https://en.cppreference.com/w/cpp/container/vector/at) | `cvector_at(v, 3)` | 101 | | [`v[3]`](https://en.cppreference.com/w/cpp/container/vector/operator_at) | `v[3]` | 102 | | [`v.front()`](https://en.cppreference.com/w/cpp/container/vector/front) | `cvector_front(v)` | 103 | | [`v.back()`](https://en.cppreference.com/w/cpp/container/vector/back) | `cvector_back(v)` | 104 | | [`v.begin()`](https://en.cppreference.com/w/cpp/container/vector/begin) | `cvector_begin(v)` | 105 | | [`v.end()`](https://en.cppreference.com/w/cpp/container/vector/begin) | `cvector_end(v)` | 106 | | [`v.empty()`](https://en.cppreference.com/w/cpp/container/vector/empty) | `cvector_empty(v)` | 107 | | [`v.size()`](https://en.cppreference.com/w/cpp/container/vector/size) | `cvector_size(v)` | 108 | | [`v.capacity()`](https://en.cppreference.com/w/cpp/container/vector/capacity) | `cvector_capacity(v)` | 109 | | [`v.shrink_to_fit()`](https://en.cppreference.com/w/cpp/container/vector/shrink_to_fit) | `cvector_shrink_to_fit(v)` | 110 | | [`v.clear()`](https://en.cppreference.com/w/cpp/container/vector/clear) | `cvector_clear(v)` | 111 | | [`v.insert(pos, value)`](https://en.cppreference.com/w/cpp/container/vector/insert) | `cvector_insert(v, pos, value)` | 112 | | [`v.erase(v.begin() + 2)`](https://en.cppreference.com/w/cpp/container/vector/erase) | `cvector_erase(v, 2)` | 113 | | [`v.push_back(value)`](https://en.cppreference.com/w/cpp/container/vector/push_back) | `cvector_push_back(v, value)` | 114 | | [`v.pop_back()`](https://en.cppreference.com/w/cpp/container/vector/pop_back) | `cvector_pop_back(v)` | 115 | | [`v.reserve(new_cap)`](https://en.cppreference.com/w/cpp/container/vector/reserve) | `cvector_reserve(v, new_cap)` | 116 | | [`v.resize(count)`](https://en.cppreference.com/w/cpp/container/vector/resize) | `cvector_resize(v, count)` | 117 | | [`v.swap(other)`](https://en.cppreference.com/w/cpp/container/vector/swap) | `cvector_swap(v, other)` | 118 | | [`std::vector other = v;`](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) | `cvector(int) other; cvector_copy(v, other);` | 119 | 120 | 121 | ### Notes 122 | * If you like this library, [german-one](https://github.com/german-one) has created a string library using this approach: https://github.com/german-one/c-string 123 | 124 | -------------------------------------------------------------------------------- /cvector.h: -------------------------------------------------------------------------------- 1 | #ifndef CVECTOR_H_ 2 | #define CVECTOR_H_ 3 | /** 4 | * @copyright Copyright (c) 2015 Evan Teran, 5 | * License: The MIT License (MIT) 6 | * @brief cvector heap implemented using C library malloc() 7 | * @file cvector.h 8 | */ 9 | 10 | /* in case C library malloc() needs extra protection, 11 | * allow these defines to be overridden. 12 | */ 13 | /* functions for allocation and deallocation need to correspond to each other, fall back to C library functions if not all are overridden */ 14 | #if !defined(cvector_clib_free) || !defined(cvector_clib_malloc) || !defined(cvector_clib_calloc) || !defined(cvector_clib_realloc) 15 | #ifdef cvector_clib_free 16 | #undef cvector_clib_free 17 | #endif 18 | #ifdef cvector_clib_malloc 19 | #undef cvector_clib_malloc 20 | #endif 21 | #ifdef cvector_clib_calloc 22 | #undef cvector_clib_calloc 23 | #endif 24 | #ifdef cvector_clib_realloc 25 | #undef cvector_clib_realloc 26 | #endif 27 | #include 28 | #define cvector_clib_free free 29 | #define cvector_clib_malloc malloc 30 | #define cvector_clib_calloc calloc 31 | #define cvector_clib_realloc realloc 32 | #endif 33 | /* functions independent of memory allocation */ 34 | #ifndef cvector_clib_assert 35 | #include /* for assert */ 36 | #define cvector_clib_assert assert 37 | #endif 38 | #ifndef cvector_clib_memcpy 39 | #include /* for memcpy */ 40 | #define cvector_clib_memcpy memcpy 41 | #endif 42 | #ifndef cvector_clib_memmove 43 | #include /* for memmove */ 44 | #define cvector_clib_memmove memmove 45 | #endif 46 | 47 | /* NOTE: Similar to C's qsort and bsearch, you will receive a T* 48 | * for a vector of Ts. This means that you cannot use `free` directly 49 | * as a destructor. Instead if you have for example a cvector_vector_type(int *) 50 | * you will need to supply a function which casts `elem_ptr` to an `int**` 51 | * and then does a free on what that pointer points to: 52 | * 53 | * ex: 54 | * 55 | * void free_int(void *p) { free(*(int **)p); } 56 | */ 57 | typedef void (*cvector_elem_destructor_t)(void *elem_ptr); 58 | 59 | typedef struct cvector_metadata_t { 60 | size_t size; 61 | size_t capacity; 62 | cvector_elem_destructor_t elem_destructor; 63 | } cvector_metadata_t; 64 | 65 | /** 66 | * @brief cvector_vector_type - The vector type used in this library 67 | * @param type The type of vector to act on. 68 | */ 69 | #define cvector_vector_type(type) type * 70 | 71 | /** 72 | * @brief cvector - Syntactic sugar to retrieve a vector type 73 | * @param type The type of vector to act on. 74 | */ 75 | #define cvector(type) cvector_vector_type(type) 76 | 77 | /** 78 | * @brief cvector_iterator - The iterator type used for cvector 79 | * @param type The type of iterator to act on. 80 | */ 81 | #define cvector_iterator(type) cvector_vector_type(type) 82 | 83 | /** 84 | * @brief cvector_vec_to_base - For internal use, converts a vector pointer to a metadata pointer 85 | * @param vec - the vector 86 | * @return the metadata pointer of the vector 87 | * @internal 88 | */ 89 | #define cvector_vec_to_base(vec) \ 90 | (&((cvector_metadata_t *)(void *)(vec))[-1]) 91 | 92 | /** 93 | * @brief cvector_base_to_vec - For internal use, converts a metadata pointer to a vector pointer 94 | * @param ptr - pointer to the metadata 95 | * @return the vector 96 | * @internal 97 | */ 98 | #define cvector_base_to_vec(ptr) \ 99 | ((void *)&((cvector_metadata_t *)(ptr))[1]) 100 | 101 | /** 102 | * @brief cvector_capacity - gets the current capacity of the vector 103 | * @param vec - the vector 104 | * @return the capacity as a size_t 105 | */ 106 | #define cvector_capacity(vec) \ 107 | ((vec) ? cvector_vec_to_base(vec)->capacity : (size_t)0) 108 | 109 | /** 110 | * @brief cvector_size - gets the current size of the vector 111 | * @param vec - the vector 112 | * @return the size as a size_t 113 | */ 114 | #define cvector_size(vec) \ 115 | ((vec) ? cvector_vec_to_base(vec)->size : (size_t)0) 116 | 117 | /** 118 | * @brief cvector_elem_destructor - get the element destructor function used 119 | * to clean up elements 120 | * @param vec - the vector 121 | * @return the function pointer as cvector_elem_destructor_t 122 | */ 123 | #define cvector_elem_destructor(vec) \ 124 | ((vec) ? cvector_vec_to_base(vec)->elem_destructor : NULL) 125 | 126 | /** 127 | * @brief cvector_empty - returns non-zero if the vector is empty 128 | * @param vec - the vector 129 | * @return non-zero if empty, zero if non-empty 130 | */ 131 | #define cvector_empty(vec) \ 132 | (cvector_size(vec) == 0) 133 | 134 | /** 135 | * @brief cvector_reserve - Requests that the vector capacity be at least enough 136 | * to contain n elements. If n is greater than the current vector capacity, the 137 | * function causes the container to reallocate its storage increasing its 138 | * capacity to n (or greater). 139 | * @param vec - the vector 140 | * @param n - Minimum capacity for the vector. 141 | * @return void 142 | */ 143 | #define cvector_reserve(vec, n) \ 144 | do { \ 145 | size_t cv_reserve_cap__ = cvector_capacity(vec); \ 146 | if (cv_reserve_cap__ < (n)) { \ 147 | cvector_grow((vec), (n)); \ 148 | } \ 149 | } while (0) 150 | 151 | /** 152 | * @brief cvector_init - Initialize a vector. The vector must be NULL for this to do anything. 153 | * @param vec - the vector 154 | * @param capacity - vector capacity to reserve 155 | * @param elem_destructor_fn - element destructor function 156 | * @return void 157 | */ 158 | #define cvector_init(vec, capacity, elem_destructor_fn) \ 159 | do { \ 160 | if (!(vec)) { \ 161 | cvector_reserve((vec), capacity); \ 162 | cvector_set_elem_destructor((vec), (elem_destructor_fn)); \ 163 | } \ 164 | } while (0) 165 | 166 | /** 167 | * @brief cvector_erase - removes the element at index i from the vector 168 | * @param vec - the vector 169 | * @param i - index of element to remove 170 | * @return void 171 | */ 172 | #define cvector_erase(vec, i) \ 173 | do { \ 174 | if (vec) { \ 175 | const size_t cv_erase_sz__ = cvector_size(vec); \ 176 | if ((i) < cv_erase_sz__) { \ 177 | cvector_elem_destructor_t cv_erase_elem_dtor__ = cvector_elem_destructor(vec); \ 178 | if (cv_erase_elem_dtor__) { \ 179 | cv_erase_elem_dtor__(&(vec)[i]); \ 180 | } \ 181 | cvector_set_size((vec), cv_erase_sz__ - 1); \ 182 | cvector_clib_memmove( \ 183 | (vec) + (i), \ 184 | (vec) + (i) + 1, \ 185 | sizeof(*(vec)) * (cv_erase_sz__ - 1 - (i))); \ 186 | } \ 187 | } \ 188 | } while (0) 189 | 190 | /** 191 | * @brief cvector_clear - erase all of the elements in the vector 192 | * @param vec - the vector 193 | * @return void 194 | */ 195 | #define cvector_clear(vec) \ 196 | do { \ 197 | if (vec) { \ 198 | cvector_elem_destructor_t cv_clear_elem_dtor__ = cvector_elem_destructor(vec); \ 199 | if (cv_clear_elem_dtor__) { \ 200 | size_t cv_clear_i__; \ 201 | for (cv_clear_i__ = 0; cv_clear_i__ < cvector_size(vec); ++cv_clear_i__) { \ 202 | cv_clear_elem_dtor__(&(vec)[cv_clear_i__]); \ 203 | } \ 204 | } \ 205 | cvector_set_size(vec, 0); \ 206 | } \ 207 | } while (0) 208 | 209 | /** 210 | * @brief cvector_free - frees all memory associated with the vector 211 | * @param vec - the vector 212 | * @return void 213 | */ 214 | #define cvector_free(vec) \ 215 | do { \ 216 | if (vec) { \ 217 | void *cv_free_p__ = cvector_vec_to_base(vec); \ 218 | cvector_elem_destructor_t cv_free_elem_dtor__ = cvector_elem_destructor(vec); \ 219 | if (cv_free_elem_dtor__) { \ 220 | size_t cv_free_i__; \ 221 | for (cv_free_i__ = 0; cv_free_i__ < cvector_size(vec); ++cv_free_i__) { \ 222 | cv_free_elem_dtor__(&(vec)[cv_free_i__]); \ 223 | } \ 224 | } \ 225 | cvector_clib_free(cv_free_p__); \ 226 | } \ 227 | } while (0) 228 | 229 | /** 230 | * @brief cvector_begin - returns an iterator to first element of the vector 231 | * @param vec - the vector 232 | * @return a pointer to the first element (or NULL) 233 | */ 234 | #define cvector_begin(vec) \ 235 | (vec) 236 | 237 | /** 238 | * @brief cvector_end - returns an iterator to one past the last element of the vector 239 | * @param vec - the vector 240 | * @return a pointer to one past the last element (or NULL) 241 | */ 242 | #define cvector_end(vec) \ 243 | ((vec) ? &((vec)[cvector_size(vec)]) : NULL) 244 | 245 | /* user request to use linear growth algorithm */ 246 | #ifdef CVECTOR_LINEAR_GROWTH 247 | 248 | /** 249 | * @brief cvector_compute_next_grow - returns an the computed size in next vector grow 250 | * size is increased by 1 251 | * @param size - current size 252 | * @return size after next vector grow 253 | */ 254 | #define cvector_compute_next_grow(size) \ 255 | ((size) + 1) 256 | 257 | #else 258 | 259 | /** 260 | * @brief cvector_compute_next_grow - returns an the computed size in next vector grow 261 | * size is increased by multiplication of 2 262 | * @param size - current size 263 | * @return size after next vector grow 264 | */ 265 | #define cvector_compute_next_grow(size) \ 266 | ((size) ? ((size) << 1) : 1) 267 | 268 | #endif /* CVECTOR_LINEAR_GROWTH */ 269 | 270 | /** 271 | * @brief cvector_push_back - adds an element to the end of the vector 272 | * @param vec - the vector 273 | * @param value - the value to add 274 | * @return void 275 | */ 276 | #define cvector_push_back(vec, value) \ 277 | do { \ 278 | size_t cv_push_back_cap__ = cvector_capacity(vec); \ 279 | if (cv_push_back_cap__ <= cvector_size(vec)) { \ 280 | cvector_grow((vec), cvector_compute_next_grow(cv_push_back_cap__)); \ 281 | } \ 282 | (vec)[cvector_size(vec)] = (value); \ 283 | cvector_set_size((vec), cvector_size(vec) + 1); \ 284 | } while (0) 285 | 286 | /** 287 | * @brief cvector_insert - insert element at position pos to the vector 288 | * @param vec - the vector 289 | * @param pos - position in the vector where the new elements are inserted. 290 | * @param val - value to be copied (or moved) to the inserted elements. 291 | * @return void 292 | */ 293 | #define cvector_insert(vec, pos, val) \ 294 | do { \ 295 | size_t cv_insert_cap__ = cvector_capacity(vec); \ 296 | if (cv_insert_cap__ <= cvector_size(vec)) { \ 297 | cvector_grow((vec), cvector_compute_next_grow(cv_insert_cap__)); \ 298 | } \ 299 | if ((pos) < cvector_size(vec)) { \ 300 | cvector_clib_memmove( \ 301 | (vec) + (pos) + 1, \ 302 | (vec) + (pos), \ 303 | sizeof(*(vec)) * ((cvector_size(vec)) - (pos))); \ 304 | } \ 305 | (vec)[(pos)] = (val); \ 306 | cvector_set_size((vec), cvector_size(vec) + 1); \ 307 | } while (0) 308 | 309 | /** 310 | * @brief cvector_pop_back - removes the last element from the vector 311 | * @param vec - the vector 312 | * @return void 313 | */ 314 | #define cvector_pop_back(vec) \ 315 | do { \ 316 | cvector_elem_destructor_t cv_pop_back_elem_dtor__ = cvector_elem_destructor(vec); \ 317 | if (cv_pop_back_elem_dtor__) { \ 318 | cv_pop_back_elem_dtor__(&(vec)[cvector_size(vec) - 1]); \ 319 | } \ 320 | cvector_set_size((vec), cvector_size(vec) - 1); \ 321 | } while (0) 322 | 323 | /** 324 | * @brief cvector_copy - copy a vector 325 | * @param from - the original vector 326 | * @param to - destination to which the function copy to 327 | * @return void 328 | */ 329 | #define cvector_copy(from, to) \ 330 | do { \ 331 | if ((from)) { \ 332 | cvector_grow(to, cvector_size(from)); \ 333 | cvector_set_size(to, cvector_size(from)); \ 334 | cvector_clib_memcpy((to), (from), cvector_size(from) * sizeof(*(from))); \ 335 | } \ 336 | } while (0) 337 | 338 | /** 339 | * @brief cvector_swap - exchanges the content of the vector by the content of another vector of the same type 340 | * @param vec - the original vector 341 | * @param other - the other vector to swap content with 342 | * @param type - the type of both vectors 343 | * @return void 344 | */ 345 | #define cvector_swap(vec, other, type) \ 346 | do { \ 347 | if (vec && other) { \ 348 | cvector_vector_type(type) cv_swap__ = vec; \ 349 | vec = other; \ 350 | other = cv_swap__; \ 351 | } \ 352 | } while (0) 353 | 354 | /** 355 | * @brief cvector_set_capacity - For internal use, sets the capacity variable of the vector 356 | * @param vec - the vector 357 | * @param size - the new capacity to set 358 | * @return void 359 | * @internal 360 | */ 361 | #define cvector_set_capacity(vec, size) \ 362 | do { \ 363 | if (vec) { \ 364 | cvector_vec_to_base(vec)->capacity = (size); \ 365 | } \ 366 | } while (0) 367 | 368 | /** 369 | * @brief cvector_set_size - For internal use, sets the size variable of the vector 370 | * @param vec - the vector 371 | * @param _size - the new capacity to set 372 | * @return void 373 | * @internal 374 | */ 375 | #define cvector_set_size(vec, _size) \ 376 | do { \ 377 | if (vec) { \ 378 | cvector_vec_to_base(vec)->size = (_size); \ 379 | } \ 380 | } while (0) 381 | 382 | /** 383 | * @brief cvector_set_elem_destructor - set the element destructor function 384 | * used to clean up removed elements. The vector must NOT be NULL for this to do anything. 385 | * @param vec - the vector 386 | * @param elem_destructor_fn - function pointer of type cvector_elem_destructor_t used to destroy elements 387 | * @return void 388 | */ 389 | #define cvector_set_elem_destructor(vec, elem_destructor_fn) \ 390 | do { \ 391 | if (vec) { \ 392 | cvector_vec_to_base(vec)->elem_destructor = (elem_destructor_fn); \ 393 | } \ 394 | } while (0) 395 | 396 | /** 397 | * @brief cvector_grow - For internal use, ensures that the vector is at least `count` elements big 398 | * @param vec - the vector 399 | * @param count - the new capacity to set 400 | * @return void 401 | * @internal 402 | */ 403 | #define cvector_grow(vec, count) \ 404 | do { \ 405 | const size_t cv_grow_sz__ = (count) * sizeof(*(vec)) + sizeof(cvector_metadata_t); \ 406 | if (vec) { \ 407 | void *cv_grow_p1__ = cvector_vec_to_base(vec); \ 408 | void *cv_grow_p2__ = cvector_clib_realloc(cv_grow_p1__, cv_grow_sz__); \ 409 | cvector_clib_assert(cv_grow_p2__); \ 410 | (vec) = cvector_base_to_vec(cv_grow_p2__); \ 411 | } else { \ 412 | void *cv_grow_p__ = cvector_clib_malloc(cv_grow_sz__); \ 413 | cvector_clib_assert(cv_grow_p__); \ 414 | (vec) = cvector_base_to_vec(cv_grow_p__); \ 415 | cvector_set_size((vec), 0); \ 416 | cvector_set_elem_destructor((vec), NULL); \ 417 | } \ 418 | cvector_set_capacity((vec), (count)); \ 419 | } while (0) 420 | 421 | /** 422 | * @brief cvector_shrink_to_fit - requests the container to reduce its capacity to fit its size 423 | * @param vec - the vector 424 | * @return void 425 | */ 426 | #define cvector_shrink_to_fit(vec) \ 427 | do { \ 428 | if (vec) { \ 429 | const size_t cv_shrink_to_fit_sz__ = cvector_size(vec); \ 430 | cvector_grow(vec, cv_shrink_to_fit_sz__); \ 431 | } \ 432 | } while (0) 433 | 434 | /** 435 | * @brief cvector_at - returns a reference to the element at position n in the vector. 436 | * @param vec - the vector 437 | * @param n - position of an element in the vector. 438 | * @return the element at the specified position in the vector. 439 | */ 440 | #define cvector_at(vec, n) \ 441 | ((vec) ? (((int)(n) < 0 || (size_t)(n) >= cvector_size(vec)) ? NULL : &(vec)[n]) : NULL) 442 | 443 | /** 444 | * @brief cvector_front - returns a reference to the first element in the vector. Unlike member cvector_begin, which returns an iterator to this same element, this function returns a direct reference. 445 | * @param vec - the vector 446 | * @return a reference to the first element in the vector container. 447 | */ 448 | #define cvector_front(vec) \ 449 | ((vec) ? ((cvector_size(vec) > 0) ? cvector_at(vec, 0) : NULL) : NULL) 450 | 451 | /** 452 | * @brief cvector_back - returns a reference to the last element in the vector.Unlike member cvector_end, which returns an iterator just past this element, this function returns a direct reference. 453 | * @param vec - the vector 454 | * @return a reference to the last element in the vector. 455 | */ 456 | #define cvector_back(vec) \ 457 | ((vec) ? ((cvector_size(vec) > 0) ? cvector_at(vec, cvector_size(vec) - 1) : NULL) : NULL) 458 | 459 | /** 460 | * @brief cvector_resize - resizes the container to contain count elements. 461 | * @param vec - the vector 462 | * @param count - new size of the vector 463 | * @param value - the value to initialize new elements with 464 | * @return void 465 | */ 466 | #define cvector_resize(vec, count, value) \ 467 | do { \ 468 | if (vec) { \ 469 | size_t cv_resize_count__ = (size_t)(count); \ 470 | size_t cv_resize_sz__ = cvector_vec_to_base(vec)->size; \ 471 | if (cv_resize_count__ > cv_resize_sz__) { \ 472 | cvector_reserve((vec), cv_resize_count__); \ 473 | cvector_set_size((vec), cv_resize_count__); \ 474 | do { \ 475 | (vec)[cv_resize_sz__++] = (value); \ 476 | } while (cv_resize_sz__ < cv_resize_count__); \ 477 | } else { \ 478 | while (cv_resize_count__ < cv_resize_sz__--) { \ 479 | cvector_pop_back(vec); \ 480 | } \ 481 | } \ 482 | } \ 483 | } while (0) 484 | 485 | #endif /* CVECTOR_H_ */ 486 | -------------------------------------------------------------------------------- /cvector_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef CVECTOR_UTILS_H_ 2 | #define CVECTOR_UTILS_H_ 3 | /** 4 | * @copyright Copyright (c) 2022 Evan Teran, 5 | * License: The MIT License (MIT) 6 | * @brief extends the cvector library 7 | * @file cvector_utils.h 8 | */ 9 | 10 | /** 11 | * @brief cvector_for_each_in - for header to iterate over vector each element's address 12 | * @param it - iterator of type pointer to vector element 13 | * @param vec - the vector 14 | * @return void 15 | */ 16 | #define cvector_for_each_in(it, vec) \ 17 | for (it = cvector_begin(vec); it < cvector_end(vec); it++) 18 | 19 | /** 20 | * @brief cvector_for_each - call function func on each element of the vector 21 | * @param vec - the vector 22 | * @param func - function to be called on each element that takes each element as argument 23 | * @return void 24 | */ 25 | #define cvector_for_each(vec, func) \ 26 | do { \ 27 | if ((vec) && (func) != NULL) { \ 28 | size_t i; \ 29 | for (i = 0; i < cvector_size(vec); i++) { \ 30 | func((vec)[i]); \ 31 | } \ 32 | } \ 33 | } while (0) 34 | 35 | #endif /* CVECTOR_UTILS_H_ */ 36 | -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | /* if this is defined, then the vector will increase in capacity by one 2 | * each time it runs out of space. if it is not defined, then the vector will 3 | * be liberal, and will double in capacity each time it runs out of space. 4 | * having this defined will minimize how much unused space is allocated. 5 | */ 6 | #define CVECTOR_LINEAR_GROWTH 7 | 8 | #include "cvector.h" 9 | #include 10 | 11 | int main(int argc, char *argv[]) { 12 | 13 | /* this is the variable that will store the array, you can have 14 | * a vector of any type! For example, you may write float *v = NULL, 15 | * and you'd have a vector of floats :-). NULL will have a size 16 | * and capacity of 0. Additionally, vector_begin and vector_end will 17 | * return NULL on a NULL vector. Alternatively, for clarity of writing 18 | * you can use the cvector or cvector_vector_type macros to define a 19 | * vector of a given type. 20 | */ 21 | cvector(int) v = NULL; 22 | 23 | (void)argc; 24 | (void)argv; 25 | 26 | /* add some elements to the back */ 27 | cvector_push_back(v, 10); 28 | cvector_push_back(v, 20); 29 | cvector_push_back(v, 30); 30 | cvector_push_back(v, 40); 31 | 32 | /* remove an element by specifying an array subscript */ 33 | cvector_erase(v, 2); 34 | 35 | int *twenty = cvector_at(v, 1); 36 | printf("twenty : %d\n", *twenty); 37 | 38 | int *front = cvector_front(v); 39 | printf("front : %d\n", *front); 40 | 41 | int *back = cvector_back(v); 42 | printf("back : %d\n", *back); 43 | 44 | /* remove an element from the back */ 45 | cvector_pop_back(v); 46 | 47 | back = cvector_back(v); 48 | printf("back val after pop_back: %d\n", *back); 49 | 50 | /* print out some stats about the vector */ 51 | printf("pointer : %p\n", (void *)v); 52 | printf("capacity: %zu\n", cvector_capacity(v)); 53 | printf("size : %zu\n", cvector_size(v)); 54 | 55 | /* iterator over the vector using "iterator" style */ 56 | if (v) { 57 | cvector_iterator(int) it; 58 | int i = 0; 59 | for (it = cvector_begin(v); it != cvector_end(v); ++it) { 60 | printf("v[%d] = %d\n", i, *it); 61 | ++i; 62 | } 63 | } 64 | 65 | /* iterator over the vector standard indexing too! */ 66 | if (v) { 67 | size_t i; 68 | for (i = 0; i < cvector_size(v); ++i) { 69 | printf("v[%zu] = %d\n", i, v[i]); 70 | } 71 | } 72 | 73 | /* well, we don't have destructors, so let's clean things up */ 74 | cvector_free(v); 75 | 76 | return 0; 77 | } 78 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /* if NDEBUG is defined when assert.h is included, the assert() macro 2 | * does nothing */ 3 | #ifdef NDEBUG 4 | #undef NDEBUG 5 | #endif 6 | #include 7 | #include 8 | #include 9 | 10 | #include "cvector.h" 11 | #include "cvector_utils.h" 12 | 13 | static void free_string(void *str) { 14 | if (str) { 15 | free(*(char **)str); 16 | } 17 | } 18 | 19 | int main() { 20 | 21 | cvector_vector_type(int) v = NULL; 22 | cvector_vector_type(int) a = NULL; 23 | cvector_vector_type(int) b = NULL; 24 | cvector_vector_type(int) c = NULL; 25 | cvector_vector_type(char *) str_vect = NULL; 26 | cvector_init(str_vect, 1, free_string); 27 | 28 | /* add some elements to the back */ 29 | cvector_push_back(v, 10); 30 | cvector_push_back(v, 20); 31 | cvector_push_back(v, 30); 32 | 33 | /* and remove one too */ 34 | cvector_pop_back(v); 35 | 36 | printf("capacity: %zu\n", cvector_capacity(v)); 37 | assert(cvector_capacity(v) == 4); 38 | printf("size : %zu\n", cvector_size(v)); 39 | assert(cvector_size(v) == 2); 40 | 41 | /* iterator over the vector using "iterator" style */ 42 | if (v) { 43 | cvector_iterator(int) it; 44 | int i = 0; 45 | for (it = cvector_begin(v); it != cvector_end(v); ++it) { 46 | printf("v[%d] = %d\n", i, *it); 47 | switch (i) { 48 | case 0: 49 | assert(*it == 10); 50 | break; 51 | case 1: 52 | assert(*it == 20); 53 | } 54 | ++i; 55 | } 56 | } 57 | 58 | /* iterator over the vector standard indexing too! */ 59 | if (v) { 60 | size_t i; 61 | for (i = 0; i < cvector_size(v); ++i) { 62 | printf("v[%zu] = %d\n", i, v[i]); 63 | switch (i) { 64 | case 0: 65 | assert(v[i] == 10); 66 | break; 67 | case 1: 68 | assert(v[i] == 20); 69 | } 70 | } 71 | } 72 | 73 | /* well, we don't have destructors, so let's clean things up */ 74 | cvector_free(v); 75 | 76 | putchar('\n'); 77 | 78 | cvector_push_back(a, 1); 79 | cvector_push_back(a, 5); 80 | cvector_push_back(a, 4); 81 | cvector_pop_back(a); // delete 4 82 | cvector_push_back(a, 5); 83 | cvector_erase(a, 1); // delete 5 84 | cvector_erase(a, 0); 85 | cvector_insert(a, 0, 1); 86 | 87 | printf("a capacity: %zu\n", cvector_capacity(a)); 88 | assert(cvector_capacity(a) == 4); 89 | printf("a size : %zu\n", cvector_size(a)); 90 | assert(cvector_size(a) == 2); 91 | 92 | if (a) { 93 | size_t i; 94 | cvector_copy(a, b); 95 | assert(cvector_size(a) == cvector_size(b)); 96 | for (i = 0; i < cvector_size(b); ++i) { 97 | printf("a[%zu] = %d\n", i, a[i]); 98 | assert(a[i] == b[i]); 99 | } 100 | } 101 | 102 | cvector_free(a); 103 | 104 | printf("After copy:\n"); 105 | printf("b capacity: %zu\n", cvector_capacity(b)); 106 | assert(cvector_capacity(b) == 2); 107 | printf("b size : %zu\n", cvector_size(b)); 108 | assert(cvector_size(b) == 2); 109 | putchar('\n'); 110 | 111 | if (b) { 112 | size_t i; 113 | cvector_insert(b, 0, 0); 114 | cvector_insert(b, 2, 4); 115 | cvector_insert(b, 2, 2); 116 | cvector_insert(b, 3, 3); 117 | printf("b capacity: %zu\n", cvector_capacity(b)); 118 | assert(cvector_capacity(b) == 8); 119 | printf("b size : %zu\n", cvector_size(b)); 120 | assert(cvector_size(b) == 6); 121 | // expected vector: [0, 1, 2, 3, 4, 5] 122 | for (i = 0; i < cvector_size(b); ++i) { 123 | printf("b[%zu] = %d\n", i, b[i]); 124 | assert(b[i] == (int)i); 125 | } 126 | } 127 | 128 | cvector_free(b); 129 | 130 | cvector_reserve(c, 100); 131 | assert(cvector_capacity(c) == 100); 132 | assert(cvector_size(c) == 0); 133 | printf("c capacity: %zu\n", cvector_capacity(c)); 134 | printf("c size : %zu\n", cvector_size(c)); 135 | cvector_push_back(c, 10); 136 | assert(cvector_capacity(c) == 100); 137 | assert(cvector_size(c) == 1); 138 | cvector_reserve(c, 10); 139 | assert(cvector_capacity(c) == 100); 140 | printf("c capacity: %zu\n", cvector_capacity(c)); 141 | printf("c size : %zu\n", cvector_size(c)); 142 | 143 | { 144 | int i; 145 | for (i = 0; i < 100; ++i) { 146 | cvector_push_back(c, i); 147 | } 148 | } 149 | assert(cvector_capacity(c) == 200); 150 | printf("c capacity: %zu\n", cvector_capacity(c)); 151 | printf("c size : %zu\n", cvector_size(c)); 152 | cvector_free(c); 153 | 154 | cvector_push_back(str_vect, strdup("Hello world")); 155 | cvector_push_back(str_vect, strdup("Good bye world")); 156 | cvector_push_back(str_vect, strdup("not today")); 157 | 158 | if (str_vect) { 159 | size_t i; 160 | for (i = 0; i < cvector_size(str_vect); ++i) { 161 | printf("v[%zu] = %s\n", i, str_vect[i]); 162 | } 163 | } 164 | 165 | cvector_free(str_vect); 166 | return 0; 167 | } 168 | -------------------------------------------------------------------------------- /unit-tests.c: -------------------------------------------------------------------------------- 1 | 2 | 3 | #include "cvector.h" 4 | #include "cvector_utils.h" 5 | #include "utest/utest.h" 6 | #include 7 | #include 8 | 9 | static void free_elem(void *ptr) { 10 | if (ptr) { 11 | free(*(void **)ptr); 12 | } 13 | } 14 | 15 | UTEST(test, cvector_back) { 16 | cvector_vector_type(int) v = NULL; 17 | cvector_push_back(v, 0); 18 | cvector_push_back(v, 1); 19 | 20 | ASSERT_TRUE(cvector_size(v) == 2); 21 | 22 | int *back = cvector_back(v); 23 | ASSERT_TRUE(*back == 1); 24 | 25 | cvector_push_back(v, 2); 26 | 27 | back = cvector_back(v); 28 | ASSERT_TRUE(*back == 2); 29 | 30 | cvector_free(v); 31 | } 32 | 33 | UTEST(test, cvector_front) { 34 | cvector_vector_type(int) v = NULL; 35 | cvector_push_back(v, 0); 36 | cvector_push_back(v, 1); 37 | 38 | ASSERT_TRUE(cvector_size(v) == 2); 39 | 40 | int *front = cvector_front(v); 41 | ASSERT_TRUE(*front == 0); 42 | 43 | cvector_erase(v, 0); 44 | front = cvector_front(v); 45 | ASSERT_TRUE(*front == 1); 46 | 47 | cvector_free(v); 48 | } 49 | 50 | UTEST(test, vector_at) { 51 | cvector_vector_type(int) v = NULL; 52 | cvector_push_back(v, 0); 53 | cvector_push_back(v, 1); 54 | cvector_push_back(v, 2); 55 | cvector_push_back(v, 3); 56 | cvector_push_back(v, 4); 57 | 58 | ASSERT_TRUE(cvector_size(v) == 5); 59 | 60 | if (v) { 61 | int i = 0; 62 | for (; i < (int)cvector_size(v); i++) { 63 | ASSERT_TRUE(*cvector_at(v, i) == i); 64 | } 65 | } 66 | 67 | /* test non-exists position */ 68 | int pos_non_exists = 999; 69 | ASSERT_TRUE(cvector_at(v, pos_non_exists) == NULL); 70 | 71 | /* remove last element*/ 72 | cvector_pop_back(v); 73 | ASSERT_TRUE(cvector_at(v, 4) == NULL); 74 | 75 | cvector_free(v); 76 | } 77 | 78 | UTEST(test, vector_empty) { 79 | cvector_vector_type(int) v = NULL; 80 | ASSERT_TRUE(cvector_capacity(v) == 0); 81 | ASSERT_TRUE(cvector_size(v) == 0); 82 | ASSERT_TRUE(cvector_begin(v) == NULL); 83 | ASSERT_TRUE(cvector_end(v) == NULL); 84 | } 85 | 86 | UTEST(test, vector_push_pop) { 87 | cvector_vector_type(int) v = NULL; 88 | 89 | /* add some elements to the back */ 90 | cvector_push_back(v, 10); 91 | cvector_push_back(v, 20); 92 | cvector_push_back(v, 30); 93 | 94 | /* and remove one too */ 95 | cvector_pop_back(v); 96 | 97 | ASSERT_TRUE(cvector_capacity(v) == 4); 98 | ASSERT_TRUE(cvector_size(v) == 2); 99 | 100 | cvector_free(v); 101 | } 102 | 103 | UTEST(test, vector_iterator) { 104 | cvector_vector_type(int) v = NULL; 105 | 106 | /* add some elements to the back */ 107 | cvector_push_back(v, 10); 108 | cvector_push_back(v, 20); 109 | cvector_push_back(v, 30); 110 | 111 | /* and remove one too */ 112 | cvector_pop_back(v); 113 | 114 | ASSERT_TRUE(cvector_capacity(v) == 4); 115 | ASSERT_TRUE(cvector_size(v) == 2); 116 | 117 | /* iterator over the vector using "iterator" style */ 118 | if (v) { 119 | cvector_iterator(int) it; 120 | int i = 0; 121 | for (it = cvector_begin(v); it != cvector_end(v); ++it) { 122 | switch (i) { 123 | case 0: 124 | ASSERT_TRUE(*it == 10); 125 | break; 126 | case 1: 127 | ASSERT_TRUE(*it == 20); 128 | } 129 | ++i; 130 | } 131 | } 132 | 133 | cvector_free(v); 134 | } 135 | 136 | UTEST(test, vector_index) { 137 | cvector_vector_type(int) v = NULL; 138 | 139 | /* add some elements to the back */ 140 | cvector_push_back(v, 10); 141 | cvector_push_back(v, 20); 142 | cvector_push_back(v, 30); 143 | 144 | /* and remove one too */ 145 | cvector_pop_back(v); 146 | 147 | ASSERT_TRUE(cvector_capacity(v) == 4); 148 | ASSERT_TRUE(cvector_size(v) == 2); 149 | 150 | /* iterator over the vector standard indexing too! */ 151 | if (v) { 152 | size_t i; 153 | for (i = 0; i < cvector_size(v); ++i) { 154 | switch (i) { 155 | case 0: 156 | ASSERT_TRUE(v[i] == 10); 157 | break; 158 | case 1: 159 | ASSERT_TRUE(v[i] == 20); 160 | } 161 | } 162 | } 163 | 164 | cvector_free(v); 165 | } 166 | 167 | UTEST(test, vector_insert_delete) { 168 | cvector(int) a = NULL; 169 | 170 | cvector_push_back(a, 1); 171 | cvector_push_back(a, 5); 172 | cvector_push_back(a, 4); 173 | cvector_pop_back(a); // delete 4 174 | cvector_push_back(a, 5); 175 | cvector_erase(a, 1); // delete 5 176 | cvector_erase(a, 0); 177 | cvector_insert(a, 0, 1); 178 | 179 | ASSERT_TRUE(cvector_capacity(a) == 4); 180 | ASSERT_TRUE(cvector_size(a) == 2); 181 | 182 | cvector_free(a); 183 | } 184 | 185 | UTEST(test, vector_copy) { 186 | cvector_vector_type(int) a = NULL; 187 | cvector_vector_type(int) b = NULL; 188 | 189 | cvector_push_back(a, 1); 190 | cvector_push_back(a, 5); 191 | cvector_push_back(a, 4); 192 | cvector_pop_back(a); // delete 4 193 | cvector_push_back(a, 5); 194 | cvector_erase(a, 1); // delete 5 195 | cvector_erase(a, 0); 196 | cvector_insert(a, 0, 1); 197 | 198 | ASSERT_TRUE(cvector_capacity(a) == 4); 199 | ASSERT_TRUE(cvector_size(a) == 2); 200 | 201 | if (a) { 202 | size_t i; 203 | cvector_copy(a, b); 204 | ASSERT_TRUE(cvector_size(a) == cvector_size(b)); 205 | for (i = 0; i < cvector_size(b); ++i) { 206 | ASSERT_TRUE(a[i] == b[i]); 207 | } 208 | } 209 | 210 | cvector_free(a); 211 | cvector_free(b); 212 | } 213 | 214 | UTEST(test, vector_swap) { 215 | cvector_vector_type(int) a = NULL; 216 | cvector_vector_type(int) b = NULL; 217 | 218 | cvector_push_back(a, 1); 219 | cvector_push_back(a, 2); 220 | cvector_push_back(a, 3); 221 | 222 | cvector_push_back(b, 4); 223 | cvector_push_back(b, 5); 224 | cvector_push_back(b, 6); 225 | cvector_push_back(b, 7); 226 | 227 | ASSERT_EQ(cvector_size(a), (size_t)3); 228 | ASSERT_EQ(cvector_size(b), (size_t)4); 229 | 230 | cvector_swap(a, b, int); 231 | 232 | ASSERT_EQ(cvector_size(a), (size_t)4); 233 | ASSERT_EQ(cvector_size(b), (size_t)3); 234 | 235 | ASSERT_EQ(a[0], 4); 236 | ASSERT_EQ(a[1], 5); 237 | ASSERT_EQ(b[0], 1); 238 | ASSERT_EQ(b[1], 2); 239 | 240 | cvector_free(a); 241 | cvector_free(b); 242 | } 243 | 244 | UTEST(test, vector_reserve) { 245 | int i; 246 | cvector_vector_type(int) c = NULL; 247 | 248 | cvector_reserve(c, 100); 249 | ASSERT_TRUE(cvector_capacity(c) == 100); 250 | ASSERT_TRUE(cvector_size(c) == 0); 251 | 252 | cvector_push_back(c, 10); 253 | ASSERT_TRUE(cvector_capacity(c) == 100); 254 | ASSERT_TRUE(cvector_size(c) == 1); 255 | 256 | cvector_reserve(c, 10); 257 | ASSERT_TRUE(cvector_capacity(c) == 100); 258 | 259 | for (i = 0; i < 100; ++i) { 260 | cvector_push_back(c, i); 261 | } 262 | 263 | ASSERT_TRUE(cvector_capacity(c) == 200); 264 | cvector_free(c); 265 | } 266 | 267 | UTEST(test, vector_free_all) { 268 | int i; 269 | cvector_vector_type(char *) v = NULL; 270 | cvector_init(v, 1, free_elem); 271 | for (i = 0; i < 10; ++i) { 272 | char *p = malloc(6); 273 | strcpy(p, "hello"); 274 | cvector_push_back(v, p); 275 | } 276 | 277 | ASSERT_TRUE(cvector_size(v) == 10); 278 | ASSERT_TRUE(cvector_capacity(v) >= 10); 279 | 280 | cvector_free(v); 281 | } 282 | 283 | UTEST(test, vector_for_each_int) { 284 | cvector_iterator(int *) it; 285 | int i; 286 | cvector_vector_type(int *) v = NULL; 287 | cvector_init(v, 1, free_elem); 288 | for (i = 0; i < 10; ++i) { 289 | int *p = malloc(sizeof(int)); 290 | *p = 42; 291 | cvector_push_back(v, p); 292 | } 293 | 294 | ASSERT_TRUE(cvector_size(v) == 10); 295 | ASSERT_TRUE(cvector_capacity(v) >= 10); 296 | 297 | cvector_for_each_in(it, v) { 298 | /* NOTE(eteran): double pointer because we have an interator to an int*, 299 | * so first deref to get the int*, the second to get int. Sure, this is a 300 | * silly thing to do, but this is a test 301 | */ 302 | ASSERT_TRUE(**it == 42); 303 | } 304 | 305 | cvector_free(v); 306 | } 307 | 308 | UTEST(test, vector_shrink_to_fit) { 309 | cvector_vector_type(int) a = NULL; 310 | 311 | cvector_push_back(a, 1); 312 | cvector_push_back(a, 5); 313 | cvector_push_back(a, 4); 314 | 315 | cvector_reserve(a, 50); 316 | ASSERT_EQ(cvector_capacity(a), (size_t)50); 317 | 318 | cvector_shrink_to_fit(a); 319 | ASSERT_EQ(cvector_capacity(a), (size_t)3); 320 | 321 | cvector_free(a); 322 | } 323 | 324 | UTEST(test, vector_resize) { 325 | cvector_vector_type(int) a = NULL; 326 | 327 | cvector_push_back(a, 1); 328 | cvector_push_back(a, 2); 329 | cvector_push_back(a, 3); 330 | 331 | cvector_resize(a, 50, 4); 332 | ASSERT_EQ(cvector_size(a), (size_t)50); 333 | ASSERT_EQ(a[1], 2); 334 | ASSERT_EQ(a[30], 4); 335 | ASSERT_EQ(a[49], 4); 336 | ASSERT_EQ(a[49], 4); 337 | 338 | cvector_resize(a, 10, 8); 339 | ASSERT_EQ(cvector_size(a), (size_t)10); 340 | ASSERT_EQ(a[2], 3); 341 | 342 | cvector_resize(a, 0, 0); 343 | ASSERT_EQ(cvector_size(a), (size_t)0); 344 | 345 | cvector_free(a); 346 | } 347 | 348 | struct data_t { 349 | int num; 350 | int a, b, c, d; 351 | }; 352 | 353 | struct data_t **test(size_t count, ...) { 354 | cvector_vector_type(struct data_t *) vec = NULL; 355 | cvector_init(vec, 1, free_elem); 356 | 357 | size_t i = 0; 358 | 359 | va_list valist; 360 | va_start(valist, count); 361 | 362 | for (i = 0; i < count; i++) { 363 | int num = va_arg(valist, int); 364 | struct data_t *data = calloc(1, sizeof(struct data_t)); 365 | data->num = num; 366 | cvector_insert(vec, 0, data); 367 | } 368 | 369 | va_end(valist); 370 | return vec; 371 | } 372 | 373 | UTEST(test, test_complex_insert) { 374 | 375 | struct data_t **vec = test(4, 1, 2, 3, 4); 376 | ASSERT_TRUE(cvector_size(vec) == 4); 377 | ASSERT_TRUE(cvector_capacity(vec) >= 4); 378 | ASSERT_TRUE(vec[0]->num == 4); 379 | ASSERT_TRUE(vec[1]->num == 3); 380 | ASSERT_TRUE(vec[2]->num == 2); 381 | ASSERT_TRUE(vec[3]->num == 1); 382 | cvector_free(vec); 383 | } 384 | 385 | UTEST(test, derefence_destructor) { 386 | cvector_vector_type(char *) v = NULL; 387 | cvector_init(v, 2, free_elem); 388 | 389 | char *ptr; 390 | ptr = strdup("hello"); 391 | ASSERT_TRUE(!!ptr); 392 | cvector_push_back(v, ptr); 393 | 394 | ptr = strdup("world"); 395 | ASSERT_TRUE(!!ptr); 396 | cvector_push_back(v, ptr); 397 | 398 | cvector_vector_type(char *) *vec_ptr = &v; 399 | cvector_free(*vec_ptr); 400 | } 401 | 402 | UTEST_MAIN(); 403 | -------------------------------------------------------------------------------- /utest/utest.h: -------------------------------------------------------------------------------- 1 | /* 2 | The latest version of this library is available on GitHub; 3 | https://github.com/sheredom/utest.h 4 | */ 5 | 6 | /* 7 | This is free and unencumbered software released into the public domain. 8 | 9 | Anyone is free to copy, modify, publish, use, compile, sell, or 10 | distribute this software, either in source code form or as a compiled 11 | binary, for any purpose, commercial or non-commercial, and by any 12 | means. 13 | 14 | In jurisdictions that recognize copyright laws, the author or authors 15 | of this software dedicate any and all copyright interest in the 16 | software to the public domain. We make this dedication for the benefit 17 | of the public at large and to the detriment of our heirs and 18 | successors. We intend this dedication to be an overt act of 19 | relinquishment in perpetuity of all present and future rights to this 20 | software under copyright law. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 23 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 25 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 26 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 27 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 28 | OTHER DEALINGS IN THE SOFTWARE. 29 | 30 | For more information, please refer to 31 | */ 32 | 33 | #ifndef SHEREDOM_UTEST_H_INCLUDED 34 | #define SHEREDOM_UTEST_H_INCLUDED 35 | 36 | #ifdef _MSC_VER 37 | /* 38 | Disable warning about not inlining 'inline' functions. 39 | */ 40 | #pragma warning(disable : 4710) 41 | 42 | /* 43 | Disable warning about inlining functions that are not marked 'inline'. 44 | */ 45 | #pragma warning(disable : 4711) 46 | 47 | #if _MSC_VER > 1900 48 | /* 49 | Disable warning about preprocessor macros not being defined in MSVC headers. 50 | */ 51 | #pragma warning(disable : 4668) 52 | 53 | /* 54 | Disable warning about no function prototype given in MSVC headers. 55 | */ 56 | #pragma warning(disable : 4255) 57 | 58 | /* 59 | Disable warning about pointer or reference to potentially throwing function. 60 | */ 61 | #pragma warning(disable : 5039) 62 | #endif 63 | 64 | #pragma warning(push, 1) 65 | #endif 66 | 67 | #if defined(_MSC_VER) && (_MSC_VER < 1920) 68 | typedef __int64 utest_int64_t; 69 | typedef unsigned __int64 utest_uint64_t; 70 | typedef unsigned __int32 utest_uint32_t; 71 | #else 72 | #include 73 | typedef int64_t utest_int64_t; 74 | typedef uint64_t utest_uint64_t; 75 | typedef uint32_t utest_uint32_t; 76 | #endif 77 | 78 | #include 79 | #include 80 | #include 81 | #include 82 | #include 83 | 84 | #if defined(_MSC_VER) 85 | #pragma warning(pop) 86 | #endif 87 | 88 | #if defined(__cplusplus) 89 | #define UTEST_C_FUNC extern "C" 90 | #else 91 | #define UTEST_C_FUNC 92 | #endif 93 | 94 | #if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) 95 | 96 | #if defined(__MINGW64__) || defined(__MINGW32__) 97 | #pragma GCC diagnostic push 98 | #pragma GCC diagnostic ignored "-Wpragmas" 99 | #pragma GCC diagnostic ignored "-Wunknown-pragmas" 100 | #endif 101 | 102 | // define UTEST_USE_OLD_QPC before #include "utest.h" to use old 103 | // QueryPerformanceCounter 104 | #ifndef UTEST_USE_OLD_QPC 105 | #pragma warning(push, 0) 106 | #include 107 | #pragma warning(pop) 108 | 109 | typedef LARGE_INTEGER utest_large_integer; 110 | #else 111 | // use old QueryPerformanceCounter definitions (not sure is this needed in some 112 | // edge cases or not) on Win7 with VS2015 these extern declaration cause "second 113 | // C linkage of overloaded function not allowed" error 114 | typedef union { 115 | struct { 116 | unsigned long LowPart; 117 | long HighPart; 118 | } DUMMYSTRUCTNAME; 119 | struct { 120 | unsigned long LowPart; 121 | long HighPart; 122 | } u; 123 | utest_int64_t QuadPart; 124 | } utest_large_integer; 125 | 126 | UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceCounter( 127 | utest_large_integer *); 128 | UTEST_C_FUNC __declspec(dllimport) int __stdcall QueryPerformanceFrequency( 129 | utest_large_integer *); 130 | 131 | #if defined(__MINGW64__) || defined(__MINGW32__) 132 | #pragma GCC diagnostic pop 133 | #endif 134 | #endif 135 | 136 | #elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ 137 | defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ 138 | defined(__HAIKU__) 139 | /* 140 | slightly obscure include here - we need to include glibc's features.h, but 141 | we don't want to just include a header that might not be defined for other 142 | c libraries like musl. Instead we include limits.h, which we know on all 143 | glibc distributions includes features.h 144 | */ 145 | #include 146 | 147 | #if defined(__GLIBC__) && defined(__GLIBC_MINOR__) 148 | #include 149 | 150 | #if ((2 < __GLIBC__) || ((2 == __GLIBC__) && (17 <= __GLIBC_MINOR__))) 151 | /* glibc is version 2.17 or above, so we can just use clock_gettime */ 152 | #define UTEST_USE_CLOCKGETTIME 153 | #else 154 | #include 155 | #include 156 | #endif 157 | #else // Other libc implementations 158 | #include 159 | #define UTEST_USE_CLOCKGETTIME 160 | #endif 161 | 162 | #elif defined(__APPLE__) 163 | #include 164 | #endif 165 | 166 | #if defined(_MSC_VER) && (_MSC_VER < 1920) 167 | #define UTEST_PRId64 "I64d" 168 | #define UTEST_PRIu64 "I64u" 169 | #else 170 | #include 171 | 172 | #define UTEST_PRId64 PRId64 173 | #define UTEST_PRIu64 PRIu64 174 | #endif 175 | 176 | #if defined(__cplusplus) 177 | #define UTEST_INLINE inline 178 | 179 | #if defined(__clang__) 180 | #define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ 181 | _Pragma("clang diagnostic push") \ 182 | _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") 183 | 184 | #define UTEST_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") 185 | #else 186 | #define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS 187 | #define UTEST_INITIALIZER_END_DISABLE_WARNINGS 188 | #endif 189 | 190 | #define UTEST_INITIALIZER(f) \ 191 | struct f##_cpp_struct { \ 192 | f##_cpp_struct(); \ 193 | }; \ 194 | UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS static f##_cpp_struct \ 195 | f##_cpp_global UTEST_INITIALIZER_END_DISABLE_WARNINGS; \ 196 | f##_cpp_struct::f##_cpp_struct() 197 | #elif defined(_MSC_VER) 198 | #define UTEST_INLINE __forceinline 199 | 200 | #if defined(_WIN64) 201 | #define UTEST_SYMBOL_PREFIX 202 | #else 203 | #define UTEST_SYMBOL_PREFIX "_" 204 | #endif 205 | 206 | #if defined(__clang__) 207 | #define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ 208 | _Pragma("clang diagnostic push") \ 209 | _Pragma("clang diagnostic ignored \"-Wmissing-variable-declarations\"") 210 | 211 | #define UTEST_INITIALIZER_END_DISABLE_WARNINGS _Pragma("clang diagnostic pop") 212 | #else 213 | #define UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS 214 | #define UTEST_INITIALIZER_END_DISABLE_WARNINGS 215 | #endif 216 | 217 | #pragma section(".CRT$XCU", read) 218 | #define UTEST_INITIALIZER(f) \ 219 | static void __cdecl f(void); \ 220 | UTEST_INITIALIZER_BEGIN_DISABLE_WARNINGS \ 221 | __pragma(comment(linker, "/include:" UTEST_SYMBOL_PREFIX #f "_")) \ 222 | UTEST_C_FUNC __declspec(allocate(".CRT$XCU")) void(__cdecl * \ 223 | f##_)(void) = f; \ 224 | UTEST_INITIALIZER_END_DISABLE_WARNINGS \ 225 | static void __cdecl f(void) 226 | #else 227 | #if defined(__linux__) 228 | #if defined(__clang__) 229 | #if __has_warning("-Wreserved-id-macro") 230 | #pragma clang diagnostic push 231 | #pragma clang diagnostic ignored "-Wreserved-id-macro" 232 | #endif 233 | #endif 234 | 235 | #define __STDC_FORMAT_MACROS 1 236 | 237 | #if defined(__clang__) 238 | #if __has_warning("-Wreserved-id-macro") 239 | #pragma clang diagnostic pop 240 | #endif 241 | #endif 242 | #endif 243 | 244 | #define UTEST_INLINE inline 245 | 246 | #define UTEST_INITIALIZER(f) \ 247 | static void f(void) __attribute__((constructor)); \ 248 | static void f(void) 249 | #endif 250 | 251 | #if defined(__cplusplus) 252 | #define UTEST_CAST(type, x) static_cast(x) 253 | #define UTEST_PTR_CAST(type, x) reinterpret_cast(x) 254 | #define UTEST_EXTERN extern "C" 255 | #define UTEST_NULL NULL 256 | #else 257 | #define UTEST_CAST(type, x) ((type)(x)) 258 | #define UTEST_PTR_CAST(type, x) ((type)(x)) 259 | #define UTEST_EXTERN extern 260 | #define UTEST_NULL 0 261 | #endif 262 | 263 | #ifdef _MSC_VER 264 | /* 265 | io.h contains definitions for some structures with natural padding. This is 266 | uninteresting, but for some reason MSVC's behaviour is to warn about 267 | including this system header. That *is* interesting 268 | */ 269 | #pragma warning(disable : 4820) 270 | #pragma warning(push, 1) 271 | #include 272 | #pragma warning(pop) 273 | #define UTEST_COLOUR_OUTPUT() (_isatty(_fileno(stdout))) 274 | #else 275 | #if defined(__EMSCRIPTEN__) 276 | #include 277 | #define UTEST_COLOUR_OUTPUT() false 278 | #else 279 | #include 280 | #define UTEST_COLOUR_OUTPUT() (isatty(STDOUT_FILENO)) 281 | #endif 282 | #endif 283 | 284 | static UTEST_INLINE void *utest_realloc(void *const pointer, size_t new_size) { 285 | void *const new_pointer = realloc(pointer, new_size); 286 | 287 | if (UTEST_NULL == new_pointer) { 288 | free(new_pointer); 289 | } 290 | 291 | return new_pointer; 292 | } 293 | 294 | static UTEST_INLINE utest_int64_t utest_ns(void) { 295 | #if defined(_MSC_VER) || defined(__MINGW64__) || defined(__MINGW32__) 296 | utest_large_integer counter; 297 | utest_large_integer frequency; 298 | QueryPerformanceCounter(&counter); 299 | QueryPerformanceFrequency(&frequency); 300 | return UTEST_CAST(utest_int64_t, 301 | (counter.QuadPart * 1000000000) / frequency.QuadPart); 302 | #elif defined(__linux__) && defined(__STRICT_ANSI__) 303 | return UTEST_CAST(utest_int64_t, clock()) * 1000000000 / CLOCKS_PER_SEC; 304 | #elif defined(__linux__) || defined(__FreeBSD__) || defined(__OpenBSD__) || \ 305 | defined(__NetBSD__) || defined(__DragonFly__) || defined(__sun__) || \ 306 | defined(__HAIKU__) 307 | struct timespec ts; 308 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ 309 | !defined(__HAIKU__) 310 | timespec_get(&ts, TIME_UTC); 311 | #else 312 | const clockid_t cid = CLOCK_REALTIME; 313 | #if defined(UTEST_USE_CLOCKGETTIME) 314 | clock_gettime(cid, &ts); 315 | #else 316 | syscall(SYS_clock_gettime, cid, &ts); 317 | #endif 318 | #endif 319 | return UTEST_CAST(utest_int64_t, ts.tv_sec) * 1000 * 1000 * 1000 + ts.tv_nsec; 320 | #elif __APPLE__ 321 | return UTEST_CAST(utest_int64_t, mach_absolute_time()); 322 | #elif __EMSCRIPTEN__ 323 | return emscripten_performance_now() * 1000000.0; 324 | #else 325 | #error Unsupported platform! 326 | #endif 327 | } 328 | 329 | typedef void (*utest_testcase_t)(int *, size_t); 330 | 331 | struct utest_test_state_s { 332 | utest_testcase_t func; 333 | size_t index; 334 | char *name; 335 | }; 336 | 337 | struct utest_state_s { 338 | struct utest_test_state_s *tests; 339 | size_t tests_length; 340 | FILE *output; 341 | }; 342 | 343 | /* extern to the global state utest needs to execute */ 344 | UTEST_EXTERN struct utest_state_s utest_state; 345 | 346 | #if defined(_MSC_VER) 347 | #define UTEST_WEAK __forceinline 348 | #else 349 | #define UTEST_WEAK __attribute__((weak)) 350 | #endif 351 | 352 | #if defined(_MSC_VER) 353 | #define UTEST_UNUSED 354 | #else 355 | #define UTEST_UNUSED __attribute__((unused)) 356 | #endif 357 | 358 | #ifdef __clang__ 359 | #pragma clang diagnostic push 360 | #pragma clang diagnostic ignored "-Wvariadic-macros" 361 | #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" 362 | #endif 363 | #define UTEST_PRINTF(...) \ 364 | if (utest_state.output) { \ 365 | fprintf(utest_state.output, __VA_ARGS__); \ 366 | } \ 367 | printf(__VA_ARGS__) 368 | #ifdef __clang__ 369 | #pragma clang diagnostic pop 370 | #endif 371 | 372 | #ifdef __clang__ 373 | #pragma clang diagnostic push 374 | #pragma clang diagnostic ignored "-Wvariadic-macros" 375 | #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" 376 | #endif 377 | 378 | #ifdef _MSC_VER 379 | #define UTEST_SNPRINTF(BUFFER, N, ...) _snprintf_s(BUFFER, N, N, __VA_ARGS__) 380 | #else 381 | #define UTEST_SNPRINTF(...) snprintf(__VA_ARGS__) 382 | #endif 383 | 384 | #ifdef __clang__ 385 | #pragma clang diagnostic pop 386 | #endif 387 | 388 | #if defined(__cplusplus) 389 | /* if we are using c++ we can use overloaded methods (its in the language) */ 390 | #define UTEST_OVERLOADABLE 391 | #elif defined(__clang__) 392 | /* otherwise, if we are using clang with c - use the overloadable attribute */ 393 | #define UTEST_OVERLOADABLE __attribute__((overloadable)) 394 | #endif 395 | 396 | #if defined(UTEST_OVERLOADABLE) 397 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f); 398 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(float f) { 399 | UTEST_PRINTF("%f", UTEST_CAST(double, f)); 400 | } 401 | 402 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d); 403 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(double d) { 404 | UTEST_PRINTF("%f", d); 405 | } 406 | 407 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d); 408 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long double d) { 409 | UTEST_PRINTF("%Lf", d); 410 | } 411 | 412 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i); 413 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(int i) { 414 | UTEST_PRINTF("%d", i); 415 | } 416 | 417 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i); 418 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(unsigned int i) { 419 | UTEST_PRINTF("%u", i); 420 | } 421 | 422 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i); 423 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long int i) { 424 | UTEST_PRINTF("%ld", i); 425 | } 426 | 427 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i); 428 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long unsigned int i) { 429 | UTEST_PRINTF("%lu", i); 430 | } 431 | 432 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const void *p); 433 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(const void *p) { 434 | UTEST_PRINTF("%p", p); 435 | } 436 | 437 | /* 438 | long long is a c++11 extension 439 | */ 440 | #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) || \ 441 | defined(__cplusplus) && (__cplusplus >= 201103L) 442 | 443 | #ifdef __clang__ 444 | #pragma clang diagnostic push 445 | #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" 446 | #endif 447 | 448 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i); 449 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long int i) { 450 | UTEST_PRINTF("%lld", i); 451 | } 452 | 453 | UTEST_WEAK UTEST_OVERLOADABLE void utest_type_printer(long long unsigned int i); 454 | UTEST_WEAK UTEST_OVERLOADABLE void 455 | utest_type_printer(long long unsigned int i) { 456 | UTEST_PRINTF("%llu", i); 457 | } 458 | 459 | #ifdef __clang__ 460 | #pragma clang diagnostic pop 461 | #endif 462 | 463 | #endif 464 | #elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) 465 | #define utest_type_printer(val) \ 466 | UTEST_PRINTF(_Generic((val), signed char \ 467 | : "%d", unsigned char \ 468 | : "%u", short \ 469 | : "%d", unsigned short \ 470 | : "%u", int \ 471 | : "%d", long \ 472 | : "%ld", long long \ 473 | : "%lld", unsigned \ 474 | : "%u", unsigned long \ 475 | : "%lu", unsigned long long \ 476 | : "%llu", float \ 477 | : "%f", double \ 478 | : "%f", long double \ 479 | : "%Lf", default \ 480 | : _Generic((val - val), ptrdiff_t \ 481 | : "%p", default \ 482 | : "undef")), \ 483 | (val)) 484 | #else 485 | /* 486 | we don't have the ability to print the values we got, so we create a macro 487 | to tell our users we can't do anything fancy 488 | */ 489 | #define utest_type_printer(...) UTEST_PRINTF("undef") 490 | #endif 491 | 492 | #ifdef _MSC_VER 493 | #define UTEST_SURPRESS_WARNING_BEGIN \ 494 | __pragma(warning(push)) __pragma(warning(disable : 4127)) 495 | #define UTEST_SURPRESS_WARNING_END __pragma(warning(pop)) 496 | #else 497 | #define UTEST_SURPRESS_WARNING_BEGIN 498 | #define UTEST_SURPRESS_WARNING_END 499 | #endif 500 | 501 | #if defined(__cplusplus) && (__cplusplus >= 201103L) 502 | #define UTEST_AUTO(x) auto 503 | #elif !defined(__cplusplus) 504 | 505 | #if defined(__clang__) 506 | /* clang-format off */ 507 | /* had to disable clang-format here because it malforms the pragmas */ 508 | #define UTEST_AUTO(x) \ 509 | _Pragma("clang diagnostic push") \ 510 | _Pragma("clang diagnostic ignored \"-Wgnu-auto-type\"") __auto_type \ 511 | _Pragma("clang diagnostic pop") 512 | /* clang-format on */ 513 | #else 514 | #define UTEST_AUTO(x) __typeof__(x + 0) 515 | #endif 516 | 517 | #else 518 | #define UTEST_AUTO(x) typeof(x + 0) 519 | #endif 520 | 521 | #if defined(__clang__) 522 | #define UTEST_STRNCMP(x, y, size) \ 523 | _Pragma("clang diagnostic push") \ 524 | _Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"") \ 525 | strncmp(x, y, size) _Pragma("clang diagnostic pop") 526 | #else 527 | #define UTEST_STRNCMP(x, y, size) strncmp(x, y, size) 528 | #endif 529 | 530 | #if defined(__clang__) 531 | #define UTEST_EXPECT(x, y, cond) \ 532 | UTEST_SURPRESS_WARNING_BEGIN do { \ 533 | _Pragma("clang diagnostic push") \ 534 | _Pragma("clang diagnostic ignored \"-Wlanguage-extension-token\"") \ 535 | _Pragma("clang diagnostic ignored \"-Wc++98-compat-pedantic\"") \ 536 | _Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \ 537 | UTEST_AUTO(x) xEval = (x); \ 538 | UTEST_AUTO(y) yEval = (y); \ 539 | if (!((xEval)cond(yEval))) { \ 540 | _Pragma("clang diagnostic pop") \ 541 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 542 | UTEST_PRINTF(" Expected : ("); \ 543 | UTEST_PRINTF(#x ") " #cond " (" #y); \ 544 | UTEST_PRINTF(")\n"); \ 545 | UTEST_PRINTF(" Actual : "); \ 546 | utest_type_printer(xEval); \ 547 | UTEST_PRINTF(" vs "); \ 548 | utest_type_printer(yEval); \ 549 | UTEST_PRINTF("\n"); \ 550 | *utest_result = 1; \ 551 | } \ 552 | } \ 553 | while (0) \ 554 | UTEST_SURPRESS_WARNING_END 555 | #elif defined(__GNUC__) 556 | #define UTEST_EXPECT(x, y, cond) \ 557 | UTEST_SURPRESS_WARNING_BEGIN do { \ 558 | UTEST_AUTO(x) xEval = (x); \ 559 | UTEST_AUTO(y) yEval = (y); \ 560 | if (!((xEval)cond(yEval))) { \ 561 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 562 | UTEST_PRINTF(" Expected : ("); \ 563 | UTEST_PRINTF(#x ") " #cond " (" #y); \ 564 | UTEST_PRINTF(")\n"); \ 565 | UTEST_PRINTF(" Actual : "); \ 566 | utest_type_printer(xEval); \ 567 | UTEST_PRINTF(" vs "); \ 568 | utest_type_printer(yEval); \ 569 | UTEST_PRINTF("\n"); \ 570 | *utest_result = 1; \ 571 | } \ 572 | } \ 573 | while (0) \ 574 | UTEST_SURPRESS_WARNING_END 575 | #else 576 | #define UTEST_EXPECT(x, y, cond) \ 577 | UTEST_SURPRESS_WARNING_BEGIN do { \ 578 | if (!((x)cond(y))) { \ 579 | UTEST_PRINTF("%s:%u: Failure (Expected " #cond " Actual)\n", __FILE__, \ 580 | __LINE__); \ 581 | *utest_result = 1; \ 582 | } \ 583 | } \ 584 | while (0) \ 585 | UTEST_SURPRESS_WARNING_END 586 | #endif 587 | 588 | #define EXPECT_TRUE(x) \ 589 | UTEST_SURPRESS_WARNING_BEGIN do { \ 590 | if (!(x)) { \ 591 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 592 | UTEST_PRINTF(" Expected : true\n"); \ 593 | UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ 594 | *utest_result = 1; \ 595 | } \ 596 | } \ 597 | while (0) \ 598 | UTEST_SURPRESS_WARNING_END 599 | 600 | #define EXPECT_FALSE(x) \ 601 | UTEST_SURPRESS_WARNING_BEGIN do { \ 602 | if (x) { \ 603 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 604 | UTEST_PRINTF(" Expected : false\n"); \ 605 | UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ 606 | *utest_result = 1; \ 607 | } \ 608 | } \ 609 | while (0) \ 610 | UTEST_SURPRESS_WARNING_END 611 | 612 | #define EXPECT_EQ(x, y) UTEST_EXPECT(x, y, ==) 613 | #define EXPECT_NE(x, y) UTEST_EXPECT(x, y, !=) 614 | #define EXPECT_LT(x, y) UTEST_EXPECT(x, y, <) 615 | #define EXPECT_LE(x, y) UTEST_EXPECT(x, y, <=) 616 | #define EXPECT_GT(x, y) UTEST_EXPECT(x, y, >) 617 | #define EXPECT_GE(x, y) UTEST_EXPECT(x, y, >=) 618 | 619 | #define EXPECT_STREQ(x, y) \ 620 | UTEST_SURPRESS_WARNING_BEGIN do { \ 621 | if (0 != strcmp(x, y)) { \ 622 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 623 | UTEST_PRINTF(" Expected : \"%s\"\n", x); \ 624 | UTEST_PRINTF(" Actual : \"%s\"\n", y); \ 625 | *utest_result = 1; \ 626 | } \ 627 | } \ 628 | while (0) \ 629 | UTEST_SURPRESS_WARNING_END 630 | 631 | #define EXPECT_STRNE(x, y) \ 632 | UTEST_SURPRESS_WARNING_BEGIN do { \ 633 | if (0 == strcmp(x, y)) { \ 634 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 635 | UTEST_PRINTF(" Expected : \"%s\"\n", x); \ 636 | UTEST_PRINTF(" Actual : \"%s\"\n", y); \ 637 | *utest_result = 1; \ 638 | } \ 639 | } \ 640 | while (0) \ 641 | UTEST_SURPRESS_WARNING_END 642 | 643 | #define EXPECT_STRNEQ(x, y, n) \ 644 | UTEST_SURPRESS_WARNING_BEGIN do { \ 645 | if (0 != UTEST_STRNCMP(x, y, n)) { \ 646 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 647 | UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, n), x); \ 648 | UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, n), y); \ 649 | *utest_result = 1; \ 650 | } \ 651 | } \ 652 | while (0) \ 653 | UTEST_SURPRESS_WARNING_END 654 | 655 | #define EXPECT_STRNNE(x, y, n) \ 656 | UTEST_SURPRESS_WARNING_BEGIN do { \ 657 | if (0 == UTEST_STRNCMP(x, y, n)) { \ 658 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 659 | UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, n), x); \ 660 | UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, n), y); \ 661 | *utest_result = 1; \ 662 | } \ 663 | } \ 664 | while (0) \ 665 | UTEST_SURPRESS_WARNING_END 666 | 667 | #define EXPECT_NEAR(x, y, epsilon) \ 668 | UTEST_SURPRESS_WARNING_BEGIN do { \ 669 | if (utest_fabs(UTEST_CAST(double, x) - UTEST_CAST(double, y)) > \ 670 | UTEST_CAST(double, epsilon)) { \ 671 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 672 | UTEST_PRINTF(" Expected : %f\n", UTEST_CAST(double, x)); \ 673 | UTEST_PRINTF(" Actual : %f\n", UTEST_CAST(double, y)); \ 674 | *utest_result = 1; \ 675 | } \ 676 | } \ 677 | while (0) \ 678 | UTEST_SURPRESS_WARNING_END 679 | 680 | #if defined(__clang__) 681 | #define UTEST_ASSERT(x, y, cond) \ 682 | UTEST_SURPRESS_WARNING_BEGIN do { \ 683 | _Pragma("clang diagnostic push") \ 684 | _Pragma("clang diagnostic ignored \"-Wlanguage-extension-token\"") \ 685 | _Pragma("clang diagnostic ignored \"-Wc++98-compat-pedantic\"") \ 686 | _Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \ 687 | UTEST_AUTO(x) xEval = (x); \ 688 | UTEST_AUTO(y) yEval = (y); \ 689 | if (!((xEval)cond(yEval))) { \ 690 | _Pragma("clang diagnostic pop") \ 691 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 692 | UTEST_PRINTF(" Expected : ("); \ 693 | UTEST_PRINTF(#x ") " #cond " (" #y); \ 694 | UTEST_PRINTF(")\n"); \ 695 | UTEST_PRINTF(" Actual : "); \ 696 | utest_type_printer(xEval); \ 697 | UTEST_PRINTF(" vs "); \ 698 | utest_type_printer(yEval); \ 699 | UTEST_PRINTF("\n"); \ 700 | *utest_result = 1; \ 701 | return; \ 702 | } \ 703 | } \ 704 | while (0) \ 705 | UTEST_SURPRESS_WARNING_END 706 | #elif defined(__GNUC__) 707 | #define UTEST_ASSERT(x, y, cond) \ 708 | UTEST_SURPRESS_WARNING_BEGIN do { \ 709 | UTEST_AUTO(x) xEval = (x); \ 710 | UTEST_AUTO(y) yEval = (y); \ 711 | if (!((xEval)cond(yEval))) { \ 712 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 713 | UTEST_PRINTF(" Expected : ("); \ 714 | UTEST_PRINTF(#x ") " #cond " (" #y); \ 715 | UTEST_PRINTF(")\n"); \ 716 | UTEST_PRINTF(" Actual : "); \ 717 | utest_type_printer(xEval); \ 718 | UTEST_PRINTF(" vs "); \ 719 | utest_type_printer(yEval); \ 720 | UTEST_PRINTF("\n"); \ 721 | *utest_result = 1; \ 722 | return; \ 723 | } \ 724 | } \ 725 | while (0) \ 726 | UTEST_SURPRESS_WARNING_END 727 | #else 728 | #define UTEST_ASSERT(x, y, cond) \ 729 | UTEST_SURPRESS_WARNING_BEGIN do { \ 730 | if (!((x)cond(y))) { \ 731 | UTEST_PRINTF("%s:%u: Failure (Expected " #cond " Actual)\n", __FILE__, \ 732 | __LINE__); \ 733 | *utest_result = 1; \ 734 | return; \ 735 | } \ 736 | } \ 737 | while (0) \ 738 | UTEST_SURPRESS_WARNING_END 739 | #endif 740 | 741 | #define ASSERT_TRUE(x) \ 742 | UTEST_SURPRESS_WARNING_BEGIN do { \ 743 | if (!(x)) { \ 744 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 745 | UTEST_PRINTF(" Expected : true\n"); \ 746 | UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ 747 | *utest_result = 1; \ 748 | return; \ 749 | } \ 750 | } \ 751 | while (0) \ 752 | UTEST_SURPRESS_WARNING_END 753 | 754 | #define ASSERT_FALSE(x) \ 755 | UTEST_SURPRESS_WARNING_BEGIN do { \ 756 | if (x) { \ 757 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 758 | UTEST_PRINTF(" Expected : false\n"); \ 759 | UTEST_PRINTF(" Actual : %s\n", (x) ? "true" : "false"); \ 760 | *utest_result = 1; \ 761 | return; \ 762 | } \ 763 | } \ 764 | while (0) \ 765 | UTEST_SURPRESS_WARNING_END 766 | 767 | #define ASSERT_EQ(x, y) UTEST_ASSERT(x, y, ==) 768 | #define ASSERT_NE(x, y) UTEST_ASSERT(x, y, !=) 769 | #define ASSERT_LT(x, y) UTEST_ASSERT(x, y, <) 770 | #define ASSERT_LE(x, y) UTEST_ASSERT(x, y, <=) 771 | #define ASSERT_GT(x, y) UTEST_ASSERT(x, y, >) 772 | #define ASSERT_GE(x, y) UTEST_ASSERT(x, y, >=) 773 | 774 | #define ASSERT_STREQ(x, y) \ 775 | UTEST_SURPRESS_WARNING_BEGIN do { \ 776 | if (0 != strcmp(x, y)) { \ 777 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 778 | UTEST_PRINTF(" Expected : \"%s\"\n", x); \ 779 | UTEST_PRINTF(" Actual : \"%s\"\n", y); \ 780 | *utest_result = 1; \ 781 | return; \ 782 | } \ 783 | } \ 784 | while (0) \ 785 | UTEST_SURPRESS_WARNING_END 786 | 787 | #define ASSERT_STRNE(x, y) \ 788 | UTEST_SURPRESS_WARNING_BEGIN do { \ 789 | if (0 == strcmp(x, y)) { \ 790 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 791 | UTEST_PRINTF(" Expected : \"%s\"\n", x); \ 792 | UTEST_PRINTF(" Actual : \"%s\"\n", y); \ 793 | *utest_result = 1; \ 794 | return; \ 795 | } \ 796 | } \ 797 | while (0) \ 798 | UTEST_SURPRESS_WARNING_END 799 | 800 | #define ASSERT_STRNEQ(x, y, n) \ 801 | UTEST_SURPRESS_WARNING_BEGIN do { \ 802 | if (0 != UTEST_STRNCMP(x, y, n)) { \ 803 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 804 | UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, n), x); \ 805 | UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, n), y); \ 806 | *utest_result = 1; \ 807 | return; \ 808 | } \ 809 | } \ 810 | while (0) \ 811 | UTEST_SURPRESS_WARNING_END 812 | 813 | #define ASSERT_STRNNE(x, y, n) \ 814 | UTEST_SURPRESS_WARNING_BEGIN do { \ 815 | if (0 == UTEST_STRNCMP(x, y, n)) { \ 816 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 817 | UTEST_PRINTF(" Expected : \"%.*s\"\n", UTEST_CAST(int, n), x); \ 818 | UTEST_PRINTF(" Actual : \"%.*s\"\n", UTEST_CAST(int, n), y); \ 819 | *utest_result = 1; \ 820 | return; \ 821 | } \ 822 | } \ 823 | while (0) \ 824 | UTEST_SURPRESS_WARNING_END 825 | 826 | #define ASSERT_NEAR(x, y, epsilon) \ 827 | UTEST_SURPRESS_WARNING_BEGIN do { \ 828 | if (utest_fabs(UTEST_CAST(double, x) - UTEST_CAST(double, y)) > \ 829 | UTEST_CAST(double, epsilon)) { \ 830 | UTEST_PRINTF("%s:%u: Failure\n", __FILE__, __LINE__); \ 831 | UTEST_PRINTF(" Expected : %f\n", UTEST_CAST(double, x)); \ 832 | UTEST_PRINTF(" Actual : %f\n", UTEST_CAST(double, y)); \ 833 | *utest_result = 1; \ 834 | return; \ 835 | } \ 836 | } \ 837 | while (0) \ 838 | UTEST_SURPRESS_WARNING_END 839 | 840 | #define UTEST(SET, NAME) \ 841 | UTEST_EXTERN struct utest_state_s utest_state; \ 842 | static void utest_run_##SET##_##NAME(int *utest_result); \ 843 | static void utest_##SET##_##NAME(int *utest_result, size_t utest_index) { \ 844 | (void)utest_index; \ 845 | utest_run_##SET##_##NAME(utest_result); \ 846 | } \ 847 | UTEST_INITIALIZER(utest_register_##SET##_##NAME) { \ 848 | const size_t index = utest_state.tests_length++; \ 849 | const char *name_part = #SET "." #NAME; \ 850 | const size_t name_size = strlen(name_part) + 1; \ 851 | char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ 852 | utest_state.tests = UTEST_PTR_CAST( \ 853 | struct utest_test_state_s *, \ 854 | utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ 855 | sizeof(struct utest_test_state_s) * \ 856 | utest_state.tests_length)); \ 857 | if (utest_state.tests) { \ 858 | utest_state.tests[index].func = &utest_##SET##_##NAME; \ 859 | utest_state.tests[index].name = name; \ 860 | utest_state.tests[index].index = 0; \ 861 | } \ 862 | UTEST_SNPRINTF(name, name_size, "%s", name_part); \ 863 | } \ 864 | void utest_run_##SET##_##NAME(int *utest_result) 865 | 866 | #define UTEST_F_SETUP(FIXTURE) \ 867 | static void utest_f_setup_##FIXTURE(int *utest_result, \ 868 | struct FIXTURE *utest_fixture) 869 | 870 | #define UTEST_F_TEARDOWN(FIXTURE) \ 871 | static void utest_f_teardown_##FIXTURE(int *utest_result, \ 872 | struct FIXTURE *utest_fixture) 873 | 874 | #if defined(__GNUC__) && __GNUC__ >= 8 && defined(__cplusplus) 875 | #define UTEST_FIXTURE_SURPRESS_WARNINGS_BEGIN \ 876 | _Pragma("GCC diagnostic push") \ 877 | _Pragma("GCC diagnostic ignored \"-Wclass-memaccess\"") 878 | #define UTEST_FIXTURE_SURPRESS_WARNINGS_END _Pragma("GCC diagnostic pop") 879 | #else 880 | #define UTEST_FIXTURE_SURPRESS_WARNINGS_BEGIN 881 | #define UTEST_FIXTURE_SURPRESS_WARNINGS_END 882 | #endif 883 | 884 | #define UTEST_F(FIXTURE, NAME) \ 885 | UTEST_FIXTURE_SURPRESS_WARNINGS_BEGIN \ 886 | UTEST_EXTERN struct utest_state_s utest_state; \ 887 | static void utest_f_setup_##FIXTURE(int *, struct FIXTURE *); \ 888 | static void utest_f_teardown_##FIXTURE(int *, struct FIXTURE *); \ 889 | static void utest_run_##FIXTURE##_##NAME(int *, struct FIXTURE *); \ 890 | static void utest_f_##FIXTURE##_##NAME(int *utest_result, \ 891 | size_t utest_index) { \ 892 | struct FIXTURE fixture; \ 893 | (void)utest_index; \ 894 | memset(&fixture, 0, sizeof(fixture)); \ 895 | utest_f_setup_##FIXTURE(utest_result, &fixture); \ 896 | if (0 != *utest_result) { \ 897 | return; \ 898 | } \ 899 | utest_run_##FIXTURE##_##NAME(utest_result, &fixture); \ 900 | utest_f_teardown_##FIXTURE(utest_result, &fixture); \ 901 | } \ 902 | UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME) { \ 903 | const size_t index = utest_state.tests_length++; \ 904 | const char *name_part = #FIXTURE "." #NAME; \ 905 | const size_t name_size = strlen(name_part) + 1; \ 906 | char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ 907 | utest_state.tests = UTEST_PTR_CAST( \ 908 | struct utest_test_state_s *, \ 909 | utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ 910 | sizeof(struct utest_test_state_s) * \ 911 | utest_state.tests_length)); \ 912 | utest_state.tests[index].func = &utest_f_##FIXTURE##_##NAME; \ 913 | utest_state.tests[index].name = name; \ 914 | UTEST_SNPRINTF(name, name_size, "%s", name_part); \ 915 | } \ 916 | UTEST_FIXTURE_SURPRESS_WARNINGS_END \ 917 | void utest_run_##FIXTURE##_##NAME(int *utest_result, \ 918 | struct FIXTURE *utest_fixture) 919 | 920 | #define UTEST_I_SETUP(FIXTURE) \ 921 | static void utest_i_setup_##FIXTURE( \ 922 | int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) 923 | 924 | #define UTEST_I_TEARDOWN(FIXTURE) \ 925 | static void utest_i_teardown_##FIXTURE( \ 926 | int *utest_result, struct FIXTURE *utest_fixture, size_t utest_index) 927 | 928 | #define UTEST_I(FIXTURE, NAME, INDEX) \ 929 | UTEST_EXTERN struct utest_state_s utest_state; \ 930 | static void utest_run_##FIXTURE##_##NAME##_##INDEX(int *, struct FIXTURE *); \ 931 | static void utest_i_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ 932 | size_t index) { \ 933 | struct FIXTURE fixture; \ 934 | memset(&fixture, 0, sizeof(fixture)); \ 935 | utest_i_setup_##FIXTURE(utest_result, &fixture, index); \ 936 | if (0 != *utest_result) { \ 937 | return; \ 938 | } \ 939 | utest_run_##FIXTURE##_##NAME##_##INDEX(utest_result, &fixture); \ 940 | utest_i_teardown_##FIXTURE(utest_result, &fixture, index); \ 941 | } \ 942 | UTEST_INITIALIZER(utest_register_##FIXTURE##_##NAME##_##INDEX) { \ 943 | size_t i; \ 944 | utest_uint64_t iUp; \ 945 | for (i = 0; i < (INDEX); i++) { \ 946 | const size_t index = utest_state.tests_length++; \ 947 | const char *name_part = #FIXTURE "." #NAME; \ 948 | const size_t name_size = strlen(name_part) + 32; \ 949 | char *name = UTEST_PTR_CAST(char *, malloc(name_size)); \ 950 | utest_state.tests = UTEST_PTR_CAST( \ 951 | struct utest_test_state_s *, \ 952 | utest_realloc(UTEST_PTR_CAST(void *, utest_state.tests), \ 953 | sizeof(struct utest_test_state_s) * \ 954 | utest_state.tests_length)); \ 955 | utest_state.tests[index].func = &utest_i_##FIXTURE##_##NAME##_##INDEX; \ 956 | utest_state.tests[index].index = i; \ 957 | utest_state.tests[index].name = name; \ 958 | iUp = UTEST_CAST(utest_uint64_t, i); \ 959 | UTEST_SNPRINTF(name, name_size, "%s/%" UTEST_PRIu64, name_part, iUp); \ 960 | } \ 961 | } \ 962 | void utest_run_##FIXTURE##_##NAME##_##INDEX(int *utest_result, \ 963 | struct FIXTURE *utest_fixture) 964 | 965 | UTEST_WEAK 966 | double utest_fabs(double d); 967 | UTEST_WEAK 968 | double utest_fabs(double d) { 969 | union { 970 | double d; 971 | utest_uint64_t u; 972 | } both; 973 | both.d = d; 974 | both.u &= 0x7fffffffffffffffu; 975 | return both.d; 976 | } 977 | 978 | UTEST_WEAK 979 | int utest_should_filter_test(const char *filter, const char *testcase); 980 | UTEST_WEAK int utest_should_filter_test(const char *filter, 981 | const char *testcase) { 982 | if (filter) { 983 | const char *filter_cur = filter; 984 | const char *testcase_cur = testcase; 985 | const char *filter_wildcard = UTEST_NULL; 986 | 987 | while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { 988 | if ('*' == *filter_cur) { 989 | /* store the position of the wildcard */ 990 | filter_wildcard = filter_cur; 991 | 992 | /* skip the wildcard character */ 993 | filter_cur++; 994 | 995 | while (('\0' != *filter_cur) && ('\0' != *testcase_cur)) { 996 | if ('*' == *filter_cur) { 997 | /* 998 | we found another wildcard (filter is something like *foo*) so we 999 | exit the current loop, and return to the parent loop to handle 1000 | the wildcard case 1001 | */ 1002 | break; 1003 | } else if (*filter_cur != *testcase_cur) { 1004 | /* otherwise our filter didn't match, so reset it */ 1005 | filter_cur = filter_wildcard; 1006 | } 1007 | 1008 | /* move testcase along */ 1009 | testcase_cur++; 1010 | 1011 | /* move filter along */ 1012 | filter_cur++; 1013 | } 1014 | 1015 | if (('\0' == *filter_cur) && ('\0' == *testcase_cur)) { 1016 | return 0; 1017 | } 1018 | 1019 | /* if the testcase has been exhausted, we don't have a match! */ 1020 | if ('\0' == *testcase_cur) { 1021 | return 1; 1022 | } 1023 | } else { 1024 | if (*testcase_cur != *filter_cur) { 1025 | /* test case doesn't match filter */ 1026 | return 1; 1027 | } else { 1028 | /* move our filter and testcase forward */ 1029 | testcase_cur++; 1030 | filter_cur++; 1031 | } 1032 | } 1033 | } 1034 | 1035 | if (('\0' != *filter_cur) || 1036 | (('\0' != *testcase_cur) && 1037 | ((filter == filter_cur) || ('*' != filter_cur[-1])))) { 1038 | /* we have a mismatch! */ 1039 | return 1; 1040 | } 1041 | } 1042 | 1043 | return 0; 1044 | } 1045 | 1046 | static UTEST_INLINE FILE *utest_fopen(const char *filename, const char *mode) { 1047 | #ifdef _MSC_VER 1048 | FILE *file; 1049 | if (0 == fopen_s(&file, filename, mode)) { 1050 | return file; 1051 | } else { 1052 | return UTEST_NULL; 1053 | } 1054 | #else 1055 | return fopen(filename, mode); 1056 | #endif 1057 | } 1058 | 1059 | static UTEST_INLINE int utest_main(int argc, const char *const argv[]); 1060 | int utest_main(int argc, const char *const argv[]) { 1061 | utest_uint64_t failed = 0; 1062 | size_t index = 0; 1063 | size_t *failed_testcases = UTEST_NULL; 1064 | size_t failed_testcases_length = 0; 1065 | const char *filter = UTEST_NULL; 1066 | utest_uint64_t ran_tests = 0; 1067 | int enable_mixed_units = 0; 1068 | int random_order = 0; 1069 | utest_uint32_t seed = 0; 1070 | 1071 | enum colours { RESET, GREEN, RED }; 1072 | 1073 | const int use_colours = UTEST_COLOUR_OUTPUT(); 1074 | const char *colours[] = {"\033[0m", "\033[32m", "\033[31m"}; 1075 | 1076 | if (!use_colours) { 1077 | for (index = 0; index < sizeof colours / sizeof colours[0]; index++) { 1078 | colours[index] = ""; 1079 | } 1080 | } 1081 | /* loop through all arguments looking for our options */ 1082 | for (index = 1; index < UTEST_CAST(size_t, argc); index++) { 1083 | /* Informational switches */ 1084 | const char help_str[] = "--help"; 1085 | const char list_str[] = "--list-tests"; 1086 | /* Test config switches */ 1087 | const char filter_str[] = "--filter="; 1088 | const char output_str[] = "--output="; 1089 | const char enable_mixed_units_str[] = "--enable-mixed-units"; 1090 | const char random_order_str[] = "--random-order"; 1091 | const char random_order_with_seed_str[] = "--random-order="; 1092 | 1093 | if (0 == UTEST_STRNCMP(argv[index], help_str, strlen(help_str))) { 1094 | printf("utest.h - the single file unit testing solution for C/C++!\n" 1095 | "Command line Options:\n" 1096 | " --help Show this message and exit.\n" 1097 | " --filter= Filter the test cases to run (EG. " 1098 | "MyTest*.a would run MyTestCase.a but not MyTestCase.b).\n" 1099 | " --list-tests List testnames, one per line. Output " 1100 | "names can be passed to --filter.\n"); 1101 | printf(" --output= Output an xunit XML file to the file " 1102 | "specified in .\n" 1103 | " --enable-mixed-units Enable the per-test output to contain " 1104 | "mixed units (s/ms/us/ns).\n" 1105 | " --random-order[=] Randomize the order that the tests are " 1106 | "ran in. If the optional argument is not provided, then a " 1107 | "random starting seed is used.\n"); 1108 | goto cleanup; 1109 | } else if (0 == 1110 | UTEST_STRNCMP(argv[index], filter_str, strlen(filter_str))) { 1111 | /* user wants to filter what test cases run! */ 1112 | filter = argv[index] + strlen(filter_str); 1113 | } else if (0 == 1114 | UTEST_STRNCMP(argv[index], output_str, strlen(output_str))) { 1115 | utest_state.output = utest_fopen(argv[index] + strlen(output_str), "w+"); 1116 | } else if (0 == UTEST_STRNCMP(argv[index], list_str, strlen(list_str))) { 1117 | for (index = 0; index < utest_state.tests_length; index++) { 1118 | UTEST_PRINTF("%s\n", utest_state.tests[index].name); 1119 | } 1120 | /* when printing the test list, don't actually run the tests */ 1121 | return 0; 1122 | } else if (0 == UTEST_STRNCMP(argv[index], enable_mixed_units_str, 1123 | strlen(enable_mixed_units_str))) { 1124 | enable_mixed_units = 1; 1125 | } else if (0 == UTEST_STRNCMP(argv[index], random_order_with_seed_str, 1126 | strlen(random_order_with_seed_str))) { 1127 | seed = 1128 | UTEST_CAST(utest_uint32_t, 1129 | strtoul(argv[index] + strlen(random_order_with_seed_str), 1130 | UTEST_NULL, 10)); 1131 | random_order = 1; 1132 | } else if (0 == UTEST_STRNCMP(argv[index], random_order_str, 1133 | strlen(random_order_str))) { 1134 | const utest_int64_t ns = utest_ns(); 1135 | 1136 | // Some really poor pseudo-random using the current time. I do this 1137 | // because I really want to avoid using C's rand() because that'd mean our 1138 | // random would be affected by any srand() usage by the user (which I 1139 | // don't want). 1140 | seed = UTEST_CAST(utest_uint32_t, ns >> 32) * 31 + 1141 | UTEST_CAST(utest_uint32_t, ns & 0xffffffff); 1142 | random_order = 1; 1143 | } 1144 | } 1145 | 1146 | if (random_order) { 1147 | // Use Fisher-Yates with the Durstenfield's version to randomly re-order the 1148 | // tests. 1149 | for (index = utest_state.tests_length; index > 1; index--) { 1150 | // For the random order we'll use PCG. 1151 | const utest_uint32_t state = seed; 1152 | const utest_uint32_t word = 1153 | ((state >> ((state >> 28u) + 4u)) ^ state) * 277803737u; 1154 | const utest_uint32_t next = ((word >> 22u) ^ word) % index; 1155 | 1156 | // Swap the randomly chosen element into the last location. 1157 | const struct utest_test_state_s copy = utest_state.tests[index - 1]; 1158 | utest_state.tests[index - 1] = utest_state.tests[next]; 1159 | utest_state.tests[next] = copy; 1160 | 1161 | // Move the seed onwards. 1162 | seed = seed * 747796405u + 2891336453u; 1163 | } 1164 | } 1165 | 1166 | for (index = 0; index < utest_state.tests_length; index++) { 1167 | if (utest_should_filter_test(filter, utest_state.tests[index].name)) { 1168 | continue; 1169 | } 1170 | 1171 | ran_tests++; 1172 | } 1173 | 1174 | printf("%s[==========]%s Running %" UTEST_PRIu64 " test cases.\n", 1175 | colours[GREEN], colours[RESET], UTEST_CAST(utest_uint64_t, ran_tests)); 1176 | 1177 | if (utest_state.output) { 1178 | fprintf(utest_state.output, "\n"); 1179 | fprintf(utest_state.output, 1180 | "\n", 1181 | UTEST_CAST(utest_uint64_t, ran_tests)); 1182 | fprintf(utest_state.output, 1183 | "\n", 1184 | UTEST_CAST(utest_uint64_t, ran_tests)); 1185 | } 1186 | 1187 | for (index = 0; index < utest_state.tests_length; index++) { 1188 | int result = 0; 1189 | utest_int64_t ns = 0; 1190 | 1191 | if (utest_should_filter_test(filter, utest_state.tests[index].name)) { 1192 | continue; 1193 | } 1194 | 1195 | printf("%s[ RUN ]%s %s\n", colours[GREEN], colours[RESET], 1196 | utest_state.tests[index].name); 1197 | 1198 | if (utest_state.output) { 1199 | fprintf(utest_state.output, "", 1200 | utest_state.tests[index].name); 1201 | } 1202 | 1203 | ns = utest_ns(); 1204 | errno = 0; 1205 | utest_state.tests[index].func(&result, utest_state.tests[index].index); 1206 | ns = utest_ns() - ns; 1207 | 1208 | if (utest_state.output) { 1209 | fprintf(utest_state.output, "\n"); 1210 | } 1211 | 1212 | // Record the failing test. 1213 | if (0 != result) { 1214 | const size_t failed_testcase_index = failed_testcases_length++; 1215 | failed_testcases = UTEST_PTR_CAST( 1216 | size_t *, utest_realloc(UTEST_PTR_CAST(void *, failed_testcases), 1217 | sizeof(size_t) * failed_testcases_length)); 1218 | if (UTEST_NULL != failed_testcases) { 1219 | failed_testcases[failed_testcase_index] = index; 1220 | } 1221 | failed++; 1222 | } 1223 | 1224 | { 1225 | const char *const units[] = {"ns", "us", "ms", "s", UTEST_NULL}; 1226 | unsigned int unit_index = 0; 1227 | utest_int64_t time = ns; 1228 | 1229 | if (enable_mixed_units) { 1230 | for (unit_index = 0; UTEST_NULL != units[unit_index]; unit_index++) { 1231 | if (10000 > time) { 1232 | break; 1233 | } 1234 | 1235 | time /= 1000; 1236 | } 1237 | } 1238 | 1239 | if (0 != result) { 1240 | printf("%s[ FAILED ]%s %s (%" UTEST_PRId64 "%s)\n", colours[RED], 1241 | colours[RESET], utest_state.tests[index].name, time, 1242 | units[unit_index]); 1243 | } else { 1244 | printf("%s[ OK ]%s %s (%" UTEST_PRId64 "%s)\n", colours[GREEN], 1245 | colours[RESET], utest_state.tests[index].name, time, 1246 | units[unit_index]); 1247 | } 1248 | } 1249 | } 1250 | 1251 | printf("%s[==========]%s %" UTEST_PRIu64 " test cases ran.\n", colours[GREEN], 1252 | colours[RESET], ran_tests); 1253 | printf("%s[ PASSED ]%s %" UTEST_PRIu64 " tests.\n", colours[GREEN], 1254 | colours[RESET], ran_tests - failed); 1255 | 1256 | if (0 != failed) { 1257 | printf("%s[ FAILED ]%s %" UTEST_PRIu64 " tests, listed below:\n", 1258 | colours[RED], colours[RESET], failed); 1259 | for (index = 0; index < failed_testcases_length; index++) { 1260 | printf("%s[ FAILED ]%s %s\n", colours[RED], colours[RESET], 1261 | utest_state.tests[failed_testcases[index]].name); 1262 | } 1263 | } 1264 | 1265 | if (utest_state.output) { 1266 | fprintf(utest_state.output, "\n\n"); 1267 | } 1268 | 1269 | cleanup: 1270 | for (index = 0; index < utest_state.tests_length; index++) { 1271 | free(UTEST_PTR_CAST(void *, utest_state.tests[index].name)); 1272 | } 1273 | 1274 | free(UTEST_PTR_CAST(void *, failed_testcases)); 1275 | free(UTEST_PTR_CAST(void *, utest_state.tests)); 1276 | 1277 | if (utest_state.output) { 1278 | fclose(utest_state.output); 1279 | } 1280 | 1281 | return UTEST_CAST(int, failed); 1282 | } 1283 | 1284 | /* 1285 | we need, in exactly one source file, define the global struct that will hold 1286 | the data we need to run utest. This macro allows the user to declare the 1287 | data without having to use the UTEST_MAIN macro, thus allowing them to write 1288 | their own main() function. 1289 | */ 1290 | #define UTEST_STATE() struct utest_state_s utest_state = {0, 0, 0} 1291 | 1292 | /* 1293 | define a main() function to call into utest.h and start executing tests! A 1294 | user can optionally not use this macro, and instead define their own main() 1295 | function and manually call utest_main. The user must, in exactly one source 1296 | file, use the UTEST_STATE macro to declare a global struct variable that 1297 | utest requires. 1298 | */ 1299 | #define UTEST_MAIN() \ 1300 | UTEST_STATE(); \ 1301 | int main(int argc, const char *const argv[]) { \ 1302 | return utest_main(argc, argv); \ 1303 | } 1304 | 1305 | #endif /* SHEREDOM_UTEST_H_INCLUDED */ --------------------------------------------------------------------------------