diff --git a/.github/workflows/filc_build.yml b/.github/workflows/filc_build.yml new file mode 100644 index 00000000..824df20b --- /dev/null +++ b/.github/workflows/filc_build.yml @@ -0,0 +1,78 @@ +name: fil-c-build + +on: + pull_request: + workflow_dispatch: + +jobs: + check-diff: + runs-on: ubuntu-latest + outputs: + handle: ${{ steps.check.outputs.run }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + fetch-depth: 2 + + - name: Check the diff + id: check + run: | + echo "Here are the changed build/C files:" + echo "$(git --no-pager diff --name-only HEAD HEAD~1 | grep -E '\.(c|h)$|\.cmake|CMakeLists|Config\.cmake\.in')" + if git diff --name-only HEAD HEAD~1 | grep -qE '\.(c|h)$|\.cmake|CMakeLists|Config\.cmake\.in'; then + echo "run=true" >> $GITHUB_OUTPUT + echo "Implementation and/or build files have changed, rebuilding..." + else + echo "run=false" >> $GITHUB_OUTPUT + echo "Implementation and/or build files have NOT changed, skipping build..." + fi + + build: + runs-on: ubuntu-latest + name: Fil-C Build ${{ matrix.build == 'debug' && 'Debug' || 'Release' }} - ${{ matrix.portable == 'ON' && 'Portable SIMD Flat Hash Map' || 'Platform SIMD Flat Hash Map' }} + needs: check-diff + if: ${{ needs.check-diff.outputs.handle == 'true' }} + + strategy: + matrix: + build: [debug, release] + portable: [OFF, ON] + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Download & Install Fil-C + run: | + # Download Fil-C archive + gh release download --repo pizlonator/fil-c --pattern 'filc-*-linux-x86_64.tar.xz' --clobber + + # Extract archive into $HOME/filc + mkdir -p $HOME/filc + tar -xf filc-*-linux-x86_64.tar.xz -C $HOME/filc --strip-components=1 + + # Run setup script + cd $HOME/filc + ./setup.sh + + # Add BOTH bin directories to GITHUB_PATH for subsequent steps + echo "$HOME/filc/build/bin" >> $GITHUB_PATH + echo "$HOME/filc/bin" >> $GITHUB_PATH + env: + GH_TOKEN: ${{ github.token }} + + - uses: lukka/get-cmake@latest + + - name: Build and Run Tests with Fil-C + run: | + make clean || true + cmake --preset=filc-${{ matrix.build }} \ + -DCMAKE_C_COMPILER=$HOME/filc/build/bin/clang + -DCCC_BUILD_SPECIALIZED=ON \ + -DCCC_FLAT_HASH_MAP_PORTABLE=${{ matrix.portable }} && \ + cmake --build build -j$(nproc) --target ccc tests + + make test diff --git a/CMakePresets.json b/CMakePresets.json index 71c9e4d3..d9fc71ed 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -124,6 +124,22 @@ "CMAKE_EXE_LINKER_FLAGS": "$env{CCC_MEMORY_SANITIZER_LINKER_FLAGS}" } }, + { + "name": "filc-debug", + "inherits": "default-debug", + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_C_FLAGS": "-g3 -O0 $env{CCC_WARNING_C_FLAGS}" + } + }, + { + "name": "filc-release", + "inherits": "default-release", + "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_C_FLAGS": "-O2 $env{CCC_WARNING_C_FLAGS}" + } + }, { "name": "gcc-gcov", "inherits": ["default-debug"], diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 95bb8805..53d27bb2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -150,6 +150,8 @@ Now that tooling is set up, the workflow is roughly as follows. - Run `make clean && cmake --preset=my-sanitize-release && cmake --build build -j8 --target ccc tests samples`. Replace the `-j8` flag with the number of cores on your system. This is the same as the previous step just in release mode. Sometimes the compiler can optimize in such a way to create different issues the sanitizer can catch. Run `make test`. Tests will run the PR remotely in case you forget, but feedback is faster locally. - Mark the pr as ready for review when all CI checks pass and tools show no errors locally. +When the pull request is a draft or ready for review, be sure to check if the [Fil-C](https://fil-c.org/) build variants are passing. Fil-C is an excellent compiler intended to guarantee memory safety. Therefore, it can catch even more memory related bugs in the containers than traditional sanitizer builds. + ## Targets - `ccc` - The core C Container Collection library. diff --git a/README.md b/README.md index e20069c4..d05a69b5 100644 --- a/README.md +++ b/README.md @@ -1288,7 +1288,7 @@ make test ## Quality Control -The C Container Collection is extensively tested and analyzed both statically and at runtime. See the `tests/` repository which is always seeking valuable additions. Also, visit `CONTRIBUTING.md` to see the extensive tooling that is constantly run locally and over CI via actions in the `.github/workflows` folder. Tools include `clang-format`, `clang-tidy`, `pre-commit`, `GCC's -fanalyzer`, and GCC's extensive address and undefined behavior sanitizers. Builds for both Linux `x86` and MacOS `ARM` run on every commit in pull requests, with additional portable implementation variants built. I aim to deliver a high quality library and welcome any suggestions for tools to further improve code quality. +The C Container Collection is extensively tested and analyzed both statically and at runtime. See the `tests/` repository which is always seeking valuable additions. Also, visit `CONTRIBUTING.md` to see the extensive tooling that is constantly run locally and over CI via actions in the `.github/workflows` folder. Tools include `clang-format`, `clang-tidy`, `pre-commit`, `GCC's -fanalyzer`, Fil-C builds, and GCC's extensive address and undefined behavior sanitizers. Builds for both Linux `x86` and MacOS `ARM` run on every commit in pull requests, with additional portable implementation variants built. I aim to deliver a high quality library and welcome any suggestions for tools to further improve code quality. If you are interested in contributing, tests that increase code coverage are a good way to start. View the [coverage report here](https://skeletoss.github.io/ccc/coverage). diff --git a/ccc/flat_bitset.h b/ccc/flat_bitset.h index c8f8b8ba..3007d88c 100644 --- a/ccc/flat_bitset.h +++ b/ccc/flat_bitset.h @@ -18,7 +18,7 @@ limitations under the License. A bit set offers efficient set membership operations when the range of values can be tracked via an index. Both a fixed size and dynamic variant are possible -depending on initialization options. +depending on desired behavior when passing an allocator. Conceptually, the bit set can be thought of as an arbitrary length integer with index `0` being the Least Significant Bit and index `N - 1` being the Most diff --git a/ccc/flat_hash_map.h b/ccc/flat_hash_map.h index fd4c9918..31bd54ea 100644 --- a/ccc/flat_hash_map.h +++ b/ccc/flat_hash_map.h @@ -18,8 +18,8 @@ limitations under the License. A Flat Hash Map stores elements in a contiguous array and allows the user to query the map by key in amortized `O(1)` time. Elements in the table may be -copied and moved, especially when rehashing occurs, so no pointer stability is -available in this implementation. +copied and moved, especially when rehashing occurs, so stored elements are not +pointer stable. A flat hash map requires the user to provide a pointer to the map, a type, a key field, a hash function, and a key three way comparator function. The hasher diff --git a/ccc/flat_priority_queue.h b/ccc/flat_priority_queue.h index 712081e7..c15097a2 100644 --- a/ccc/flat_priority_queue.h +++ b/ccc/flat_priority_queue.h @@ -18,7 +18,7 @@ limitations under the License. A flat priority queue is a contiguous container storing elements in heap order. This offers tightly packed data for efficient push, pop, min/max operations in -`O(lg N)` time. +`O(lg N)` time. See the sort.h interface for heap related sorting. A flat priority queue can use memory sources from the stack, heap, or data segment and can be initialized at compile or runtime. The container offers