├── .clang-format ├── .clang-tidy ├── .github └── workflows │ ├── build_cmake.yml │ └── codeql-analysis.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── Cache.cmake ├── CompilerWarnings.cmake ├── Conan.cmake ├── PreventInSourceBuilds.cmake ├── Sanitizers.cmake ├── StandardProjectSettings.cmake └── StaticAnalyzers.cmake ├── include └── crc_cpp.h └── test ├── CMakeLists.txt └── test.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: WebKit 3 | Language: Cpp 4 | ColumnLimit: 180 5 | AlignAfterOpenBracket: Align 6 | AlignEscapedNewlines: DontAlign 7 | AlignOperands: 'true' 8 | AllowShortFunctionsOnASingleLine: true 9 | BreakBeforeBraces: Custom 10 | NamespaceIndentation: None 11 | BraceWrapping: 12 | AfterClass: true 13 | AfterCaseLabel: true 14 | AfterControlStatement: true 15 | AfterEnum: true 16 | AfterFunction: true 17 | AfterNamespace: false 18 | AfterStruct: true 19 | AfterUnion: true 20 | AfterExternBlock: true 21 | BeforeCatch: true 22 | BeforeElse: true 23 | IndentBraces: false 24 | SplitEmptyFunction: false 25 | SplitEmptyRecord: false 26 | BreakBeforeBinaryOperators: NonAssignment 27 | BreakConstructorInitializers: BeforeColon 28 | BinPackArguments: false 29 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 30 | Cpp11BracedListStyle: true 31 | DerivePointerAlignment: false 32 | FixNamespaceComments: true 33 | IncludeBlocks: Regroup 34 | IndentCaseLabels: true 35 | KeepEmptyLinesAtTheStartOfBlocks: false 36 | PointerAlignment: Left 37 | SortIncludes: true 38 | SortUsingDeclarations: true 39 | SpaceAfterCStyleCast: true 40 | SpaceAfterTemplateKeyword: false 41 | SpacesBeforeTrailingComments: 2 42 | SpaceBeforeCpp11BracedList: false 43 | SpaceInEmptyBlock: false 44 | TabWidth: 4 45 | UseTab: Never 46 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | --- 2 | Checks: 'clang-diagnostic-*,clang-analyzer-*,cppcoreguidelines-*,-cppcoreguidelines-avoid-magic-numbers' 3 | WarningsAsErrors: '*' 4 | HeaderFilterRegex: '.*' 5 | AnalyzeTemporaryDtors: false 6 | FormatStyle: none 7 | CheckOptions: 8 | - key: cert-dcl16-c.NewSuffixes 9 | value: 'L;LL;LU;LLU' 10 | - key: cert-oop54-cpp.WarnOnlyIfThisHasSuspiciousField 11 | value: '0' 12 | - key: cppcoreguidelines-explicit-virtual-functions.IgnoreDestructors 13 | value: '1' 14 | - key: cppcoreguidelines-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic 15 | value: '1' 16 | - key: google-readability-braces-around-statements.ShortStatementLines 17 | value: '1' 18 | - key: google-readability-function-size.StatementThreshold 19 | value: '800' 20 | - key: google-readability-namespace-comments.ShortNamespaceLines 21 | value: '10' 22 | - key: google-readability-namespace-comments.SpacesBeforeComments 23 | value: '2' 24 | - key: modernize-loop-convert.MaxCopySize 25 | value: '16' 26 | - key: modernize-loop-convert.MinConfidence 27 | value: reasonable 28 | - key: modernize-loop-convert.NamingStyle 29 | value: CamelCase 30 | - key: modernize-pass-by-value.IncludeStyle 31 | value: llvm 32 | - key: modernize-replace-auto-ptr.IncludeStyle 33 | value: llvm 34 | - key: modernize-use-nullptr.NullMacros 35 | value: 'NULL' 36 | ... 37 | 38 | -------------------------------------------------------------------------------- /.github/workflows/build_cmake.yml: -------------------------------------------------------------------------------- 1 | name: C++ CMake CI 2 | 3 | on: 4 | push: 5 | workflow_dispatch: 6 | 7 | env: 8 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 9 | BUILD_TYPE: RelWithDebInfo 10 | 11 | # Conan cache environment variables 12 | CONAN_SYSREQUIRES_MODE: enabled 13 | CONAN_USER_HOME: "${{ github.workspace }}/conan-cache" 14 | CONAN_USER_HOME_SHORT: "${{ github.workspace }}/conan-cache/short" 15 | 16 | jobs: 17 | linux: 18 | name: ${{ matrix.os }}, ${{ matrix.compiler.name }}, C++${{ matrix.cxx_std }} 19 | runs-on: ${{ matrix.os }} 20 | strategy: 21 | fail-fast: false # 'false' means Don't stop matrix workflows even if some matrix entry fails. 22 | matrix: 23 | os: [ubuntu-22.04] 24 | cxx_std: [17, 20] 25 | compiler: [ {name: 'GCC 10', cpp: g++-10, c: gcc-10, pkgs: 'gcc-10 g++-10 lib32gcc-10-dev gcc-multilib'}, 26 | {name: 'GCC 11', cpp: g++-11, c: gcc-11, pkgs: 'gcc-11 g++-11 lib32gcc-11-dev gcc-multilib'}, 27 | {name: 'Clang 12', cpp: clang++-12, c: clang-12, pkgs: 'clang-12 llvm-12'}, 28 | {name: 'Clang 13', cpp: clang++-13, c: clang-13, pkgs: 'clang-13 llvm-13'}, 29 | {name: 'Clang 14', cpp: clang++-14, c: clang-14, pkgs: 'clang-14 llvm-14'}, 30 | {name: 'Clang 15', cpp: clang++-15, c: clang-15, pkgs: 'clang-15 llvm-15'} 31 | ] 32 | steps: 33 | - uses: actions/checkout@v3 34 | 35 | - name: Cache 36 | uses: actions/cache@v3 37 | env: 38 | cache-name: cache-conan-modules 39 | with: 40 | path: | 41 | ${{ env.CONAN_USER_HOME }} 42 | ~/.cache/pip 43 | key: ${{ runner.os }}-${{ env.BUILD_TYPE }}-${{ hashFiles('CMakeLists.txt') }}-${{ hashFiles('cmake/Conan.cmake') }} 44 | 45 | - name: Create Build Environment 46 | # Some projects don't allow in-source building, so create a separate build directory 47 | # We'll use this as our working directory for all subsequent commands 48 | run: | 49 | cmake -E make_directory ${{runner.workspace}}/build 50 | sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" 51 | sudo apt update 52 | sudo apt install ${{ matrix.compiler.pkgs }} 53 | shell: bash 54 | 55 | - name: Install conan 56 | shell: bash 57 | run: source ~/.profile 58 | 59 | - name: Configure CMake 60 | # Use a bash shell so we can use the same syntax for environment variable 61 | # access regardless of the host operating system 62 | shell: bash 63 | working-directory: ${{runner.workspace}}/build 64 | env: 65 | CC: ${{ matrix.compiler.c }} 66 | CXX: ${{ matrix.compiler.cpp }} 67 | # Note the current convention is to use the -S and -B options here to specify source 68 | # and build directories, but this is only available with CMake 3.13 and higher. 69 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 70 | # 71 | # We need to source the profile file to make sure conan is in PATH 72 | run: | 73 | source ~/.profile 74 | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCXX_STANDARD=${{ matrix.cxx_std }} 75 | 76 | - name: Build 77 | working-directory: ${{runner.workspace}}/build 78 | shell: bash 79 | # Execute the build. You can specify a specific target with "--target " 80 | run: cmake --build . --config $BUILD_TYPE 81 | 82 | - name: Test 83 | working-directory: ${{runner.workspace}}/build 84 | shell: bash 85 | # Execute tests defined by the CMake configuration. 86 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 87 | run: ctest -C $BUILD_TYPE 88 | windows: 89 | name: ${{ matrix.os }}, C++${{ matrix.cxx_std }} 90 | runs-on: ${{ matrix.os }} 91 | strategy: 92 | fail-fast: false # 'false' means Don't stop matrix workflows even if some matrix entry fails. 93 | matrix: 94 | cxx_std: [17, 20] 95 | os: [windows-latest, windows-2019] 96 | steps: 97 | - uses: actions/checkout@v3 98 | - name: Install conan 99 | shell: bash 100 | # conan 2 is not supported at the moment 101 | run: | 102 | python3 -m pip install --upgrade pip setuptools 103 | python3 -m pip install conan~=1.0 104 | - name: Create Build Environment 105 | # Some projects don't allow in-source building, so create a separate build directory 106 | # We'll use this as our working directory for all subsequent commands 107 | run: cmake -E make_directory ${{github.workspace}}/build 108 | - name: Configure CMake 109 | # Use a bash shell so we can use the same syntax for environment variable 110 | shell: bash 111 | # access regardless of the host operating system 112 | working-directory: ${{github.workspace}}/build 113 | # Note the current convention is to use the -S and -B options here to specify source 114 | # and build directories, but this is only available with CMake 3.13 and higher. 115 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 116 | # 117 | # We need to source the profile file to make sure conan is in PATH 118 | run: cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_CXX_STANDARD=${{ matrix.cxx_std }} 119 | - name: Build 120 | shell: bash 121 | working-directory: ${{github.workspace}}/build 122 | # Execute the build. You can specify a specific target with "--target " 123 | run: cmake --build . --config $BUILD_TYPE 124 | - name: Test 125 | shell: bash 126 | working-directory: ${{github.workspace}}/build 127 | # Execute tests defined by the CMake configuration. 128 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 129 | run: ctest -C $BUILD_TYPE 130 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: [ main ] 20 | schedule: 21 | - cron: '39 5 * * 0' 22 | workflow_dispatch: 23 | 24 | env: 25 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 26 | BUILD_TYPE: RelWithDebInfo 27 | 28 | # Conan cache environment variables 29 | CONAN_SYSREQUIRES_MODE: enabled 30 | CONAN_USER_HOME: "${{ github.workspace }}/conan-cache" 31 | CONAN_USER_HOME_SHORT: "${{ github.workspace }}/conan-cache/short" 32 | 33 | jobs: 34 | analyze: 35 | name: Analyze ${{ matrix.compiler.name }}, C++${{ matrix.cxx_std }} 36 | runs-on: ubuntu-latest 37 | permissions: 38 | actions: read 39 | contents: read 40 | security-events: write 41 | 42 | strategy: 43 | fail-fast: false 44 | matrix: 45 | # We will only analyse with the latest GCC and Clang, but for C++17 and C++20 modes. 46 | # The Build CI steps will test code in more versions. 47 | language: [ 'cpp' ] 48 | compiler: [ {name: "GCC 11", cpp: g++-11, c: gcc-11, pkgs: 'gcc-11 g++-11 lib32gcc-11-dev gcc-multilib'}, 49 | {name: "Clang 15", cpp: clang++-15, c: clang-15, pkgs: 'clang-15 llvm-15'} 50 | ] 51 | cxx_std: [17, 20] 52 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 53 | # Learn more: 54 | # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed 55 | 56 | steps: 57 | - name: Checkout repository 58 | uses: actions/checkout@v3 59 | 60 | # Initializes the CodeQL tools for scanning. 61 | - name: Initialize CodeQL 62 | uses: github/codeql-action/init@v2 63 | with: 64 | languages: ${{ matrix.language }} 65 | # If you wish to specify custom queries, you can do so here or in a config file. 66 | # By default, queries listed here will override any specified in a config file. 67 | # Prefix the list here with "+" to use these queries and those in the config file. 68 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 69 | 70 | - name: Cache 71 | uses: actions/cache@v3 72 | env: 73 | cache-name: cache-conan-modules 74 | with: 75 | path: | 76 | ${{ env.CONAN_USER_HOME }} 77 | ~/.cache/pip 78 | key: ${{ runner.os }}-${{ env.BUILD_TYPE }}-${{ hashFiles('CMakeLists.txt') }}-${{ hashFiles('cmake/Conan.cmake') }} 79 | 80 | - name: Create Build Environment 81 | # Some projects don't allow in-source building, so create a separate build directory 82 | # We'll use this as our working directory for all subsequent commands 83 | run: | 84 | sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" 85 | sudo apt update 86 | sudo apt install ${{ matrix.compiler.pkgs }} 87 | cmake -E make_directory ${{runner.workspace}}/build 88 | shell: bash 89 | 90 | - name: Install conan 91 | shell: bash 92 | run: | 93 | source ~/.profile 94 | 95 | - name: Configure CMake 96 | # Use a bash shell so we can use the same syntax for environment variable 97 | # access regardless of the host operating system 98 | shell: bash 99 | working-directory: ${{runner.workspace}}/build 100 | env: 101 | CC: ${{ matrix.compiler.c }} 102 | CXX: ${{ matrix.compiler.cpp }} 103 | # Note the current convention is to use the -S and -B options here to specify source 104 | # and build directories, but this is only available with CMake 3.13 and higher. 105 | # The CMake binaries on the Github Actions machines are (as of this writing) 3.12 106 | # 107 | # We need to source the profile file to make sure conan is in PATH 108 | run: | 109 | source ~/.profile 110 | cmake $GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCXX_STANDARD=${{ matrix.cxx_std }} 111 | 112 | - name: Build 113 | working-directory: ${{runner.workspace}}/build 114 | shell: bash 115 | # Execute the build. You can specify a specific target with "--target " 116 | run: cmake --build . --config $BUILD_TYPE 117 | 118 | - name: Perform CodeQL Analysis 119 | uses: github/codeql-action/analyze@v2 120 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | bin/ 35 | .idea/ 36 | 37 | cmake-*/ 38 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | CMAKE_MINIMUM_REQUIRED(VERSION 3.16) 2 | 3 | PROJECT(crc_cpp CXX) 4 | SET(CMAKE_BUILD_PARALLEL_LEVEL 4) 5 | IF(NOT DEFINED CXX_STANDARD) 6 | SET(CMAKE_CXX_STANDARD 20) 7 | ELSE() 8 | SET(CMAKE_CXX_STANDARD ${CXX_STANDARD}) 9 | ENDIF() 10 | MESSAGE("Using C++ standard ${CMAKE_CXX_STANDARD}") 11 | 12 | INCLUDE(cmake/StandardProjectSettings.cmake) 13 | INCLUDE(cmake/PreventInSourceBuilds.cmake) 14 | 15 | FIND_PACKAGE(Python COMPONENTS Interpreter REQUIRED) 16 | 17 | # install PyPI Python packages using pip 18 | # Can't update to conan2 yet, not supported by clion or Conan.cmake, so force to 1.x 19 | # NOTE: also change .github/build_cmake.yml windows build process when fixed. 20 | EXECUTE_PROCESS(COMMAND ${Python_EXECUTABLE} -m pip install --user --upgrade pip setuptools jinja2 conan~=1.0) 21 | 22 | # Link this 'library' to set the c++ standard / compile-time options requested 23 | ADD_LIBRARY(project_options INTERFACE) 24 | TARGET_COMPILE_FEATURES(project_options INTERFACE cxx_std_${CMAKE_CXX_STANDARD}) 25 | 26 | IF(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 27 | OPTION(ENABLE_BUILD_WITH_TIME_TRACE "Enable -ftime-trace to generate time tracing .json files on clang" OFF) 28 | IF(ENABLE_BUILD_WITH_TIME_TRACE) 29 | TARGET_COMPILE_OPTIONS(project_options INTERFACE -ftime-trace) 30 | ENDIF() 31 | ENDIF() 32 | 33 | # Link this 'library' to use the warnings specified in CompilerWarnings.cmake 34 | ADD_LIBRARY(project_warnings INTERFACE) 35 | 36 | # enable cache system 37 | INCLUDE(cmake/Cache.cmake) 38 | 39 | # standard compiler warnings 40 | INCLUDE(cmake/CompilerWarnings.cmake) 41 | SET_PROJECT_WARNINGS(project_warnings) 42 | 43 | # sanitizer options if supported by compiler 44 | INCLUDE(cmake/Sanitizers.cmake) 45 | ENABLE_SANITIZERS(project_options) 46 | 47 | # allow for static analysis options 48 | INCLUDE(cmake/StaticAnalyzers.cmake) 49 | 50 | OPTION(BUILD_SHARED_LIBS "Enable compilation of shared libraries" OFF) 51 | OPTION(ENABLE_TESTING "Enable Test Builds" ON) 52 | 53 | # Set up some extra Conan dependencies based on our needs before loading Conan 54 | SET(CONAN_EXTRA_REQUIRES "") 55 | SET(CONAN_EXTRA_OPTIONS "") 56 | 57 | INCLUDE(cmake/Conan.cmake) 58 | RUN_CONAN() 59 | 60 | IF(ENABLE_TESTING) 61 | ENABLE_TESTING() 62 | MESSAGE("Building Tests. Be sure to check out test/constexpr_tests for constexpr testing") 63 | ADD_SUBDIRECTORY(test) 64 | ENDIF() 65 | 66 | OPTION(ENABLE_UNITY "Enable Unity builds of projects" OFF) 67 | IF(ENABLE_UNITY) 68 | # Add for any project you want to apply unity builds for 69 | SET_TARGET_PROPERTIES(TypeRegisterExample PROPERTIES UNITY_BUILD ON) 70 | ENDIF() 71 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ashley Roll 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![C++ CMake CI](https://github.com/AshleyRoll/crc_cpp/actions/workflows/build_cmake.yml/badge.svg)](https://github.com/AshleyRoll/crc_cpp/actions/workflows/build_cmake.yml) 2 | [![CodeQL](https://github.com/AshleyRoll/crc_cpp/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/AshleyRoll/crc_cpp/actions/workflows/codeql-analysis.yml) 3 | [![Codacy Badge](https://app.codacy.com/project/badge/Grade/02c708fb7c554faabfccbaf04bfe5c14)](https://www.codacy.com/gh/AshleyRoll/crc_cpp/dashboard?utm_source=github.com&utm_medium=referral&utm_content=AshleyRoll/crc_cpp&utm_campaign=Badge_Grade) 4 | 5 | # `crc_cpp`: CRC Routines 6 | 7 | A Header only, `constexpr` / compile time small-table based CRC library. 8 | 9 | Currently only supporting: 10 | - C++20 and above 11 | - C++17 using a fall back static table generator pattern 12 | 13 | Unfortunately older C++ standards will not allow the complex statements in 14 | `constexpr` functions. 15 | 16 | This library implements most of the common CRC checks from 8 to 64 bits. 17 | 18 | The calculation is performed using a compile time generated lookup table with 19 | only 16 entries (by default) to minimise memory footprint - this is designed 20 | for embedded systems where RAM and program memory are at a premium. 21 | 22 | If you are able to provide data to the CRC at compile time, the entire CRC is 23 | able to be computed at compile time. 24 | 25 | ## Conan 26 | 27 | This library is available through Conan. 28 | 29 | [crc_cpp on Conan.io](https://conan.io/center/crc_cpp) 30 | 31 | ## Usage 32 | 33 | Simply create an instance of the correct algorithm type, and continually 34 | `update()` it with bytes. Call `final()` to get the CRC result. 35 | 36 | If you wish to reuse the crc object, you can call `reset()` to initialise it 37 | again. 38 | 39 | ```cpp 40 | 41 | #include "crc_cpp.h" 42 | 43 | [[nodiscard]] constexpr auto compute_crc16ccit(std::vector const &message) 44 | { 45 | crc_cpp::crc16_ccit crc; 46 | 47 | for(auto c : message) { 48 | crc.update(c); 49 | } 50 | 51 | return crc.final(); 52 | } 53 | 54 | int main() 55 | { 56 | std::vector const message{'1', '2', '3', '4', '5', '6', '7', '8', '9'}; 57 | 58 | return 0x29B1 == compute_crc16ccit(message); 59 | } 60 | 61 | ``` 62 | [![Compiler Explorer](https://godbolt.org/favicon.ico) Try it on Compiler Explorer](https://godbolt.org/z/6Wc17zfze) 63 | 64 | For each different CRC you use in the program, you will get a new table 65 | generated as they all will contain different values. 66 | 67 | ### Trading size and speed 68 | 69 | By default the implementation will select the `small` implementation, a 70 | 4-bit (16 entry table) and process each byte value as 2 nibbles. 71 | 72 | However the crc_cpp library also supports a `tiny` and `large` implementation. 73 | 74 | | Implementation | Bits | Table Size | 75 | | ------------------- | ---- | ---------- | 76 | | `crc_cpp::tiny::*` | 2 | 4 entries | 77 | | `crc_cpp::small::*` | 4 | 16 entries | 78 | | `crc_cpp::large::*` | 8 | 256 entries | 79 | 80 | Where the `*` is replaced with the name of the CRC algorithm. See 81 | [inlcude/crc_cpp.h](include/crc_cpp.h) for the full list of supported 82 | algorithms. 83 | 84 | The bits column indicates how many bits of each byte is processed in a single 85 | step. 8 bits means the entire bytes is used in a single step. 4 bits means that 86 | we process a nibble (4 bits) at a time, and so on. 87 | 88 | Each entry in the table is the size of the CRC register (8, 16, 32 or 64 bits). 89 | 90 | ### Define your own 91 | 92 | If you have a CRC algorithm that isn't already baked in, feel free to define 93 | your own: 94 | 95 | ```cpp 96 | 97 | // Don't use this, seriously.. This is not a real polynomial :) 98 | class mycrc : public crc_cpp::impl::crc< 99 | // size, polynomial, initial, final xor, reverse? 100 | crc_cpp::impl::crc_algorithm, 101 | // Select table size 102 | crc_cpp::table_size::small 103 | >{}; 104 | ``` 105 | 106 | Then just use it like any other one. 107 | 108 | If the reverse parameter is `false`, the bits are rotated through the register 109 | MSB to LSB (rotate left). If it is `true` the reverse happens and bits are shifted through 110 | LSB to MSB (rotate right). 111 | 112 | ## Supported CRC Algorithms 113 | 114 | ### 8 Bit 115 | 116 | - crc8 117 | - crc8_cdma2000 118 | - crc8_darc 119 | - crc8_dvbs2 120 | - crc8_ebu 121 | - crc8_icode 122 | - crc8_itu 123 | - crc8_maxim 124 | - crc8_rohc 125 | - crc8_wcdma 126 | 127 | ### 16 Bit 128 | 129 | - crc16_ccit 130 | - crc16_arc 131 | - crc16_augccit 132 | - crc16_buypass 133 | - crc16_cdma2000 134 | - crc16_dds110 135 | - crc16_dectr 136 | - crc16_dectx 137 | - crc16_dnp 138 | - crc16_en13757 139 | - crc16_genibus 140 | - crc16_maxim 141 | - crc16_mcrf4xx 142 | - crc16_riello 143 | - crc16_t10dif 144 | - crc16_teledisk 145 | - crc16_tms37157 146 | - crc16_usb 147 | - crc16_a 148 | - crc16_kermit 149 | - crc16_modbus 150 | - crc16_x25 151 | - crc16_xmodem 152 | 153 | ### 32 Bit 154 | 155 | - crc32 156 | - crc32_bzip2 157 | - crc32_c 158 | - crc32_d 159 | - crc32_mpeg2 160 | - crc32_posix 161 | - crc32_q 162 | - crc32_jamcrc 163 | - crc32_xfer 164 | 165 | ### 64 Bit 166 | 167 | - crc64_ecma 168 | 169 | ## Limitations 170 | 171 | Support is only provided for CRC algorithms with a register size of 8, 16, 32 172 | or 64 bits. 173 | 174 | ## Future 175 | 176 | Things to work on in the future: 177 | - Try to back-port to older C++ standards (help appreciated) 178 | -------------------------------------------------------------------------------- /cmake/Cache.cmake: -------------------------------------------------------------------------------- 1 | OPTION(ENABLE_CACHE "Enable cache if available" ON) 2 | IF(NOT ENABLE_CACHE) 3 | RETURN() 4 | ENDIF() 5 | 6 | SET(CACHE_OPTION 7 | "ccache" 8 | CACHE STRING "Compiler cache to be used") 9 | SET(CACHE_OPTION_VALUES "ccache" "sccache") 10 | SET_PROPERTY(CACHE CACHE_OPTION PROPERTY STRINGS ${CACHE_OPTION_VALUES}) 11 | LIST( 12 | FIND 13 | CACHE_OPTION_VALUES 14 | ${CACHE_OPTION} 15 | CACHE_OPTION_INDEX) 16 | 17 | IF(${CACHE_OPTION_INDEX} EQUAL -1) 18 | MESSAGE( 19 | STATUS 20 | "Using custom compiler cache system: '${CACHE_OPTION}', explicitly supported entries are ${CACHE_OPTION_VALUES}") 21 | ENDIF() 22 | 23 | FIND_PROGRAM(CACHE_BINARY ${CACHE_OPTION}) 24 | IF(CACHE_BINARY) 25 | MESSAGE(STATUS "${CACHE_OPTION} found and enabled") 26 | SET(CMAKE_CXX_COMPILER_LAUNCHER ${CACHE_BINARY}) 27 | ELSE() 28 | MESSAGE(WARNING "${CACHE_OPTION} is enabled but was not found. Not using it") 29 | ENDIF() 30 | -------------------------------------------------------------------------------- /cmake/CompilerWarnings.cmake: -------------------------------------------------------------------------------- 1 | # from here: 2 | # 3 | # https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md 4 | 5 | FUNCTION(SET_PROJECT_WARNINGS project_name) 6 | OPTION(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" TRUE) 7 | 8 | SET(MSVC_WARNINGS 9 | /W4 # Baseline reasonable warnings 10 | /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data 11 | /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data 12 | /w14263 # 'function': member function does not override any base class virtual member function 13 | /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this class may not 14 | # be destructed correctly 15 | /w14287 # 'operator': unsigned/negative constant mismatch 16 | /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is used outside 17 | # the for-loop scope 18 | /w14296 # 'operator': expression is always 'boolean_value' 19 | /w14311 # 'variable': pointer truncation from 'type1' to 'type2' 20 | /w14545 # expression before comma evaluates to a function which is missing an argument list 21 | /w14546 # function call before comma missing argument list 22 | /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect 23 | /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'? 24 | /w14555 # expression has no effect; expected expression with side- effect 25 | /w14619 # pragma warning: there is no warning number 'number' 26 | /w14640 # Enable warning on thread un-safe static member initialization 27 | /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime behavior. 28 | /w14905 # wide string literal cast to 'LPSTR' 29 | /w14906 # string literal cast to 'LPWSTR' 30 | /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly applied 31 | /permissive- # standards conformance mode for MSVC compiler. 32 | ) 33 | 34 | SET(CLANG_WARNINGS 35 | -Wall 36 | -Wextra # reasonable and standard 37 | -Wshadow # warn the user if a variable declaration shadows one from a parent context 38 | -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. This helps 39 | # catch hard to track down memory errors 40 | -Wold-style-cast # warn for c-style casts 41 | -Wcast-align # warn for potential performance problem casts 42 | -Wunused # warn on anything being unused 43 | -Woverloaded-virtual # warn if you overload (not override) a virtual function 44 | -Wpedantic # warn if non-standard C++ is used 45 | -Wconversion # warn on type conversions that may lose data 46 | -Wsign-conversion # warn on sign conversions 47 | -Wnull-dereference # warn if a null dereference is detected 48 | -Wdouble-promotion # warn if float is implicit promoted to double 49 | -Wformat=2 # warn on security issues around functions that format output (ie printf) 50 | ) 51 | 52 | IF(WARNINGS_AS_ERRORS) 53 | SET(CLANG_WARNINGS ${CLANG_WARNINGS} -Werror) 54 | #set(MSVC_WARNINGS ${MSVC_WARNINGS} /WX) 55 | ENDIF() 56 | 57 | SET(GCC_WARNINGS 58 | ${CLANG_WARNINGS} 59 | -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist 60 | -Wduplicated-cond # warn if if / else chain has duplicated conditions 61 | -Wduplicated-branches # warn if if / else branches have duplicated code 62 | -Wlogical-op # warn about logical operations being used where bitwise were probably wanted 63 | -Wuseless-cast # warn if you perform a cast to the same type 64 | ) 65 | 66 | IF(MSVC) 67 | SET(PROJECT_WARNINGS ${MSVC_WARNINGS}) 68 | ELSEIF(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 69 | SET(PROJECT_WARNINGS ${CLANG_WARNINGS}) 70 | ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 71 | SET(PROJECT_WARNINGS ${GCC_WARNINGS}) 72 | ELSE() 73 | MESSAGE(AUTHOR_WARNING "No compiler warnings set for '${CMAKE_CXX_COMPILER_ID}' compiler.") 74 | ENDIF() 75 | 76 | TARGET_COMPILE_OPTIONS(${project_name} INTERFACE ${PROJECT_WARNINGS}) 77 | 78 | ENDFUNCTION() 79 | -------------------------------------------------------------------------------- /cmake/Conan.cmake: -------------------------------------------------------------------------------- 1 | MACRO(RUN_CONAN) 2 | # Download automatically, you can also just copy the conan.cmake file 3 | IF(NOT EXISTS "${CMAKE_BINARY_DIR}/conan.cmake") 4 | MESSAGE(STATUS "Downloading conan.cmake from https://github.com/conan-io/cmake-conan") 5 | file(DOWNLOAD "https://github.com/conan-io/cmake-conan/raw/0.18.1/conan.cmake" "${CMAKE_BINARY_DIR}/conan.cmake" TLS_VERIFY ON) 6 | ENDIF() 7 | 8 | INCLUDE(${CMAKE_BINARY_DIR}/conan.cmake) 9 | 10 | CONAN_CMAKE_RUN( 11 | REQUIRES 12 | ${CONAN_EXTRA_REQUIRES} 13 | catch2/3.2.1 14 | OPTIONS 15 | ${CONAN_EXTRA_OPTIONS} 16 | SETTINGS 17 | compiler.cppstd=${CMAKE_CXX_STANDARD} 18 | BASIC_SETUP 19 | CMAKE_TARGETS # individual targets to link to 20 | BUILD 21 | missing) 22 | ENDMACRO() 23 | -------------------------------------------------------------------------------- /cmake/PreventInSourceBuilds.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # This function will prevent in-source builds 3 | FUNCTION(ASSUREOUTOFSOURCEBUILDS) 4 | # make sure the user doesn't play dirty with symlinks 5 | GET_FILENAME_COMPONENT(srcdir "${CMAKE_SOURCE_DIR}" REALPATH) 6 | GET_FILENAME_COMPONENT(bindir "${CMAKE_BINARY_DIR}" REALPATH) 7 | 8 | # disallow in-source builds 9 | IF("${srcdir}" STREQUAL "${bindir}") 10 | MESSAGE("######################################################") 11 | MESSAGE("Warning: in-source builds are disabled") 12 | MESSAGE("Please create a separate build directory and run cmake from there") 13 | MESSAGE("######################################################") 14 | MESSAGE(FATAL_ERROR "Quitting configuration") 15 | ENDIF() 16 | ENDFUNCTION() 17 | 18 | ASSUREOUTOFSOURCEBUILDS() 19 | -------------------------------------------------------------------------------- /cmake/Sanitizers.cmake: -------------------------------------------------------------------------------- 1 | FUNCTION(ENABLE_SANITIZERS project_name) 2 | 3 | IF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 4 | OPTION(ENABLE_COVERAGE "Enable coverage reporting for gcc/clang" FALSE) 5 | 6 | IF(ENABLE_COVERAGE) 7 | TARGET_COMPILE_OPTIONS(${project_name} INTERFACE --coverage -O0 -g) 8 | TARGET_LINK_LIBRARIES(${project_name} INTERFACE --coverage) 9 | ENDIF() 10 | 11 | SET(SANITIZERS "") 12 | 13 | OPTION(ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" FALSE) 14 | IF(ENABLE_SANITIZER_ADDRESS) 15 | LIST(APPEND SANITIZERS "address") 16 | ENDIF() 17 | 18 | OPTION(ENABLE_SANITIZER_LEAK "Enable leak sanitizer" FALSE) 19 | IF(ENABLE_SANITIZER_LEAK) 20 | LIST(APPEND SANITIZERS "leak") 21 | ENDIF() 22 | 23 | OPTION(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR "Enable undefined behavior sanitizer" FALSE) 24 | IF(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR) 25 | LIST(APPEND SANITIZERS "undefined") 26 | ENDIF() 27 | 28 | OPTION(ENABLE_SANITIZER_THREAD "Enable thread sanitizer" FALSE) 29 | IF(ENABLE_SANITIZER_THREAD) 30 | IF("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS) 31 | MESSAGE(WARNING "Thread sanitizer does not work with Address and Leak sanitizer enabled") 32 | ELSE() 33 | LIST(APPEND SANITIZERS "thread") 34 | ENDIF() 35 | ENDIF() 36 | 37 | OPTION(ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" FALSE) 38 | IF(ENABLE_SANITIZER_MEMORY AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 39 | IF("address" IN_LIST SANITIZERS 40 | OR "thread" IN_LIST SANITIZERS 41 | OR "leak" IN_LIST SANITIZERS) 42 | MESSAGE(WARNING "Memory sanitizer does not work with Address, Thread and Leak sanitizer enabled") 43 | ELSE() 44 | LIST(APPEND SANITIZERS "memory") 45 | ENDIF() 46 | ENDIF() 47 | 48 | LIST( 49 | JOIN 50 | SANITIZERS 51 | "," 52 | LIST_OF_SANITIZERS) 53 | 54 | ENDIF() 55 | 56 | IF(LIST_OF_SANITIZERS) 57 | IF(NOT 58 | "${LIST_OF_SANITIZERS}" 59 | STREQUAL 60 | "") 61 | TARGET_COMPILE_OPTIONS(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) 62 | TARGET_LINK_OPTIONS(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS}) 63 | ENDIF() 64 | ENDIF() 65 | 66 | ENDFUNCTION() 67 | -------------------------------------------------------------------------------- /cmake/StandardProjectSettings.cmake: -------------------------------------------------------------------------------- 1 | # Set a default build type if none was specified 2 | IF(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 3 | MESSAGE(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") 4 | SET(CMAKE_BUILD_TYPE 5 | RelWithDebInfo 6 | CACHE STRING "Choose the type of build." FORCE) 7 | # Set the possible values of build type for cmake-gui, ccmake 8 | SET_PROPERTY( 9 | CACHE CMAKE_BUILD_TYPE 10 | PROPERTY STRINGS 11 | "Debug" 12 | "Release" 13 | "MinSizeRel" 14 | "RelWithDebInfo") 15 | ENDIF() 16 | 17 | # Generate compile_commands.json to make it easier to work with clang based tools 18 | SET(CMAKE_EXPORT_COMPILE_COMMANDS ON) 19 | 20 | OPTION(ENABLE_IPO "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)" OFF) 21 | 22 | IF(ENABLE_IPO) 23 | INCLUDE(CheckIPOSupported) 24 | CHECK_IPO_SUPPORTED( 25 | RESULT 26 | result 27 | OUTPUT 28 | output) 29 | IF(result) 30 | SET(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) 31 | ELSE() 32 | MESSAGE(SEND_ERROR "IPO is not supported: ${output}") 33 | ENDIF() 34 | ENDIF() 35 | IF(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 36 | ADD_COMPILE_OPTIONS(-fcolor-diagnostics) 37 | ELSEIF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 38 | ADD_COMPILE_OPTIONS(-fdiagnostics-color=always) 39 | ELSE() 40 | MESSAGE(STATUS "No colored compiler diagnostic set for '${CMAKE_CXX_COMPILER_ID}' compiler.") 41 | ENDIF() 42 | 43 | -------------------------------------------------------------------------------- /cmake/StaticAnalyzers.cmake: -------------------------------------------------------------------------------- 1 | OPTION(ENABLE_CPPCHECK "Enable static analysis with cppcheck" OFF) 2 | OPTION(ENABLE_CLANG_TIDY "Enable static analysis with clang-tidy" OFF) 3 | OPTION(ENABLE_INCLUDE_WHAT_YOU_USE "Enable static analysis with include-what-you-use" OFF) 4 | 5 | IF(ENABLE_CPPCHECK) 6 | FIND_PROGRAM(CPPCHECK cppcheck) 7 | IF(CPPCHECK) 8 | SET(CMAKE_CXX_CPPCHECK 9 | ${CPPCHECK} 10 | --suppress=missingInclude 11 | --enable=all 12 | --inline-suppr 13 | --inconclusive 14 | -i 15 | ${CMAKE_SOURCE_DIR}/imgui/lib) 16 | ELSE() 17 | MESSAGE(SEND_ERROR "cppcheck requested but executable not found") 18 | ENDIF() 19 | ENDIF() 20 | 21 | IF(ENABLE_CLANG_TIDY) 22 | FIND_PROGRAM(CLANGTIDY clang-tidy) 23 | IF(CLANGTIDY) 24 | SET(CMAKE_CXX_CLANG_TIDY ${CLANGTIDY} -extra-arg=-Wno-unknown-warning-option) 25 | ELSE() 26 | MESSAGE(SEND_ERROR "clang-tidy requested but executable not found") 27 | ENDIF() 28 | ENDIF() 29 | 30 | IF(ENABLE_INCLUDE_WHAT_YOU_USE) 31 | FIND_PROGRAM(INCLUDE_WHAT_YOU_USE include-what-you-use) 32 | IF(INCLUDE_WHAT_YOU_USE) 33 | SET(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE}) 34 | ELSE() 35 | MESSAGE(SEND_ERROR "include-what-you-use requested but executable not found") 36 | ENDIF() 37 | ENDIF() 38 | -------------------------------------------------------------------------------- /include/crc_cpp.h: -------------------------------------------------------------------------------- 1 | #ifndef CRC_CPP_H_INCLUDED 2 | #define CRC_CPP_H_INCLUDED 3 | /* 4 | * MIT License 5 | * 6 | * Copyright (c) 2020 Ashley Roll 7 | * https://github.com/AshleyRoll/crc_cpp 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | 31 | 32 | // 33 | // C++20 feature toggle 34 | // 35 | #if __cplusplus >= 202002L 36 | #define CRC_CPP_STD20_MODE 1 37 | #endif 38 | 39 | 40 | namespace crc_cpp 41 | { 42 | // Select the table size to use. This trades speed for size. 43 | enum class table_size 44 | { 45 | undefined, // not initialized 46 | tiny, // 4 Entries, 2 bits per chunk 47 | small, // 16 Entries, 4 bits per chunk 48 | large // 256 Entries, 8 bits per chunk 49 | }; 50 | 51 | namespace util 52 | { 53 | // 54 | // Efficient bit reversal routines 55 | // 56 | template constexpr T reverse_bits(T value); 57 | 58 | template<> [[nodiscard]] constexpr uint8_t reverse_bits(uint8_t value) 59 | { 60 | value = static_cast(((value & 0xAA) >> 1) | ((value & 0x55) << 1)); 61 | value = static_cast(((value & 0xCC) >> 2) | ((value & 0x33) << 2)); 62 | value = static_cast(((value & 0xF0) >> 4) | ((value & 0x0F) << 4)); 63 | return value; 64 | } 65 | 66 | template<> [[nodiscard]] constexpr uint16_t reverse_bits(uint16_t value) 67 | { 68 | value = static_cast(((value & 0xAAAA) >> 1) | ((value & 0x5555) << 1)); 69 | value = static_cast(((value & 0xCCCC) >> 2) | ((value & 0x3333) << 2)); 70 | value = static_cast(((value & 0xF0F0) >> 4) | ((value & 0x0F0F) << 4)); 71 | value = static_cast((value >> 8) | (value << 8)); 72 | return value; 73 | } 74 | 75 | template<> [[nodiscard]] constexpr uint32_t reverse_bits(uint32_t value) 76 | { 77 | value = ((value & 0xAAAAAAAA) >> 1) | ((value & 0x55555555) << 1); 78 | value = ((value & 0xCCCCCCCC) >> 2) | ((value & 0x33333333) << 2); 79 | value = ((value & 0xF0F0F0F0) >> 4) | ((value & 0x0F0F0F0F) << 4); 80 | value = ((value & 0xFF00FF00) >> 8) | ((value & 0x00FF00FF) << 8); 81 | value = (value >> 16) | (value << 16); 82 | return value; 83 | } 84 | 85 | template<> [[nodiscard]] constexpr uint64_t reverse_bits(uint64_t value) 86 | { 87 | value = ((value & 0xAAAAAAAAAAAAAAAA) >> 1) | ((value & 0x5555555555555555) << 1); 88 | value = ((value & 0xCCCCCCCCCCCCCCCC) >> 2) | ((value & 0x3333333333333333) << 2); 89 | value = ((value & 0xF0F0F0F0F0F0F0F0) >> 4) | ((value & 0x0F0F0F0F0F0F0F0F) << 4); 90 | value = ((value & 0xFF00FF00FF00FF00) >> 8) | ((value & 0x00FF00FF00FF00FF) << 8); 91 | value = ((value & 0xFFFF0000FFFF0000) >> 16) | ((value & 0x0000FFFF0000FFFF) << 16); 92 | value = (value >> 32) | (value << 32); 93 | return value; 94 | } 95 | 96 | } // namespace util 97 | 98 | 99 | namespace impl 100 | { 101 | // 102 | // Define the basic traits we use in our CRC accumulators and Table Lookup 103 | // 104 | template 105 | struct crc_traits {}; 106 | 107 | template 108 | struct crc_traits 109 | { 110 | static constexpr std::size_t ACCUMULATOR_BITS = sizeof(TAccumulator) * 8; 111 | static constexpr std::size_t CHUNK_BITS = 2; 112 | static constexpr uint8_t CHUNK_MASK = 0x03u; 113 | static constexpr std::size_t TABLE_ENTRIES = 1u << CHUNK_BITS; 114 | 115 | using table_type = std::array; 116 | }; 117 | 118 | template 119 | struct crc_traits 120 | { 121 | static constexpr std::size_t ACCUMULATOR_BITS = sizeof(TAccumulator) * 8; 122 | static constexpr std::size_t CHUNK_BITS = 4; 123 | static constexpr uint8_t CHUNK_MASK = 0x0Fu; 124 | static constexpr std::size_t TABLE_ENTRIES = 1u << CHUNK_BITS; 125 | 126 | using table_type = std::array; 127 | }; 128 | 129 | template 130 | struct crc_traits 131 | { 132 | static constexpr std::size_t ACCUMULATOR_BITS = sizeof(TAccumulator) * 8; 133 | static constexpr std::size_t CHUNK_BITS = 8; 134 | static constexpr uint8_t CHUNK_MASK = 0xFFu; 135 | static constexpr std::size_t TABLE_ENTRIES = 1u << CHUNK_BITS; 136 | 137 | using table_type = std::array; 138 | }; 139 | 140 | // 141 | // CRC rotation policies. 142 | // 143 | // Forward rotation means that we clock in data MSB->LSB and rotate the Accumulator register left 144 | // Reverse rotation means that we clock in data LSB->MSB and rotate the Accumulator register right 145 | // 146 | 147 | template 148 | struct crc_forward_policy 149 | { 150 | using traits = crc_traits; 151 | 152 | [[nodiscard]] static constexpr TAccumulator update_impl_tiny( 153 | TAccumulator crc, uint8_t value, typename traits::table_type const &table) 154 | { 155 | crc = update_chunk(crc, (value >> (traits::CHUNK_BITS*3)) & traits::CHUNK_MASK, table); // high chunk 156 | crc = update_chunk(crc, (value >> (traits::CHUNK_BITS*2)) & traits::CHUNK_MASK, table); 157 | crc = update_chunk(crc, (value >> (traits::CHUNK_BITS*1)) & traits::CHUNK_MASK, table); 158 | crc = update_chunk(crc, value & traits::CHUNK_MASK, table); // low chunk 159 | return crc; 160 | } 161 | 162 | [[nodiscard]] static constexpr TAccumulator update_impl_small( 163 | TAccumulator crc, uint8_t value, typename traits::table_type const &table) 164 | { 165 | crc = update_chunk(crc, value >> traits::CHUNK_BITS, table); // high nibble 166 | crc = update_chunk(crc, value & traits::CHUNK_MASK, table); // low nibble 167 | return crc; 168 | } 169 | 170 | [[nodiscard]] static constexpr TAccumulator update_impl_large( 171 | TAccumulator crc, uint8_t value, typename traits::table_type const &table) 172 | { 173 | crc = update_chunk(crc, value, table); // full byte 174 | return crc; 175 | } 176 | 177 | [[nodiscard]] static constexpr TAccumulator update_chunk( 178 | TAccumulator crc, uint8_t value, typename traits::table_type const &table) 179 | { 180 | // ensure we have only 4 bits 181 | value &= traits::CHUNK_MASK; 182 | 183 | // Extract the most significant nibble of the crc and xor with the value nibble 184 | auto t = static_cast((crc >> (traits::ACCUMULATOR_BITS - traits::CHUNK_BITS)) ^ value); 185 | 186 | // special case for when chunk size is same as accumulator size. 187 | if constexpr(traits::ACCUMULATOR_BITS > traits::CHUNK_BITS) { 188 | // shit crc left the size of the nibble 189 | crc <<= traits::CHUNK_BITS; 190 | 191 | // xor in the table data 192 | crc ^= table[t]; 193 | } else { 194 | crc = table[t]; 195 | } 196 | 197 | return crc; 198 | } 199 | 200 | [[nodiscard]] static constexpr TAccumulator generate_entry(TAccumulator const polynomial, std::size_t const index) 201 | { 202 | // initialise with the register in the upper bits 203 | auto entry = static_cast(index << (traits::ACCUMULATOR_BITS - traits::CHUNK_BITS)); 204 | 205 | for(std::size_t i = 0; i < traits::CHUNK_BITS; i++) 206 | { 207 | // We are processing MSBs / rotating left so we need to check the high bit 208 | if(entry & (TAccumulator(1u) << (traits::ACCUMULATOR_BITS - 1))) { 209 | entry = static_cast((entry << 1) ^ polynomial); 210 | } else { 211 | entry = static_cast(entry << 1); 212 | } 213 | } 214 | 215 | return entry; 216 | } 217 | 218 | [[nodiscard]] static constexpr TAccumulator make_initial_value(TAccumulator init) 219 | { 220 | return init; 221 | } 222 | }; 223 | 224 | template 225 | struct crc_reverse_policy 226 | { 227 | using traits = crc_traits; 228 | 229 | 230 | [[nodiscard]] static constexpr TAccumulator update_impl_tiny( 231 | TAccumulator crc, uint8_t value, typename traits::table_type const &table) 232 | { 233 | crc = update_chunk(crc, value & traits::CHUNK_MASK, table); // low chunk 234 | crc = update_chunk(crc, (value >> (traits::CHUNK_BITS*1)) & traits::CHUNK_MASK, table); 235 | crc = update_chunk(crc, (value >> (traits::CHUNK_BITS*2)) & traits::CHUNK_MASK, table); 236 | crc = update_chunk(crc, (value >> (traits::CHUNK_BITS*3)) & traits::CHUNK_MASK, table); // high chunk 237 | return crc; 238 | } 239 | 240 | [[nodiscard]] static constexpr TAccumulator update_impl_small( 241 | TAccumulator crc, uint8_t value, typename traits::table_type const &table) 242 | { 243 | crc = update_chunk(crc, value & traits::CHUNK_MASK, table); // low nibble 244 | crc = update_chunk(crc, value >> traits::CHUNK_BITS, table); // high nibble 245 | return crc; 246 | } 247 | 248 | [[nodiscard]] static constexpr TAccumulator update_impl_large( 249 | TAccumulator crc, uint8_t value, typename traits::table_type const &table) 250 | { 251 | crc = update_chunk(crc, value, table); // full byte 252 | return crc; 253 | } 254 | 255 | [[nodiscard]] static constexpr TAccumulator update_chunk( 256 | TAccumulator crc, uint8_t value, typename traits::table_type const &table) 257 | { 258 | // ensure we have only 4 bits 259 | value &= traits::CHUNK_MASK; 260 | 261 | // Extract the least significant nibble of the crc and xor with the value nibble 262 | auto t = static_cast((crc & traits::CHUNK_MASK) ^ value); 263 | 264 | // special case for when chunk size is same as accumulator size. 265 | if constexpr(traits::ACCUMULATOR_BITS > traits::CHUNK_BITS) { 266 | // shit crc right the size of a nibble 267 | crc >>= traits::CHUNK_BITS; 268 | 269 | // xor in the table data 270 | crc ^= table[t]; 271 | } else { 272 | crc = table[t]; 273 | } 274 | 275 | return crc; 276 | } 277 | 278 | [[nodiscard]] static constexpr TAccumulator generate_entry(TAccumulator const polynomial, uint8_t const index) 279 | { 280 | // initialise with the register in the lower bits 281 | TAccumulator entry = TAccumulator(index); 282 | 283 | for(std::size_t i = 0; i < traits::CHUNK_BITS; i++) 284 | { 285 | // we are processing LSBs/rotating right 286 | if(entry & 0x1u) { 287 | entry = (entry >> 1) ^ util::reverse_bits(polynomial); 288 | } else { 289 | entry = (entry >> 1); 290 | } 291 | } 292 | 293 | return entry; 294 | } 295 | 296 | [[nodiscard]] static constexpr TAccumulator make_initial_value(TAccumulator init) 297 | { 298 | return util::reverse_bits(init); 299 | } 300 | }; 301 | 302 | 303 | // 304 | // A generic CRC lookup table sized for computing a nibble (4 bits) at a time. 305 | // 306 | // This is a large reduction of table space storage for embedded devices. 307 | // 308 | template ::value>> 313 | class crc_chunk_table 314 | { 315 | public: 316 | using policy = typename std::conditional, 318 | crc_forward_policy>::type; 319 | 320 | using traits = typename policy::traits; 321 | 322 | 323 | // update the given crc accumulator with the value 324 | [[nodiscard]] static constexpr TAccumulator update(TAccumulator crc, uint8_t value) 325 | { 326 | static_assert(TABLE_SIZE != table_size::undefined, "Unknown table_size"); 327 | 328 | if constexpr(TABLE_SIZE == table_size::tiny) { 329 | return policy::update_impl_tiny(crc, value, m_Table); 330 | } else if constexpr(TABLE_SIZE == table_size::small) { 331 | return policy::update_impl_small(crc, value, m_Table); 332 | } else if constexpr(TABLE_SIZE == table_size::large) { 333 | return policy::update_impl_large(crc, value, m_Table); 334 | } 335 | } 336 | 337 | // the crc accumulator initial value may need to be modified by the policy 338 | // to account for rotation direction 339 | [[nodiscard]] static constexpr TAccumulator make_initial_value(TAccumulator init) 340 | { 341 | return policy::make_initial_value(init); 342 | } 343 | 344 | private: 345 | 346 | // If we are C++20 or above, we can leverage cleaner constexpr initialisation 347 | // otherwise we will attempt to use a static table builder metaprogramming pattern 348 | // NOTE: Only C++17 will work, other constexpr code prevents C++14 and below working. 349 | #ifdef CRC_CPP_STD20_MODE 350 | [[nodiscard]] static constexpr typename traits::table_type Generate() 351 | { 352 | typename traits::table_type table; 353 | 354 | for(std::size_t nibble = 0; nibble < traits::TABLE_ENTRIES; ++nibble) 355 | { 356 | table[nibble] = policy::generate_entry(POLYNOMIAL, static_cast(nibble)); 357 | } 358 | 359 | return table; 360 | } 361 | 362 | static constexpr typename traits::table_type m_Table = Generate(); 363 | #else 364 | // table builder for C++17 365 | 366 | // recursive case 367 | template 368 | struct table_builder : table_builder {}; 369 | 370 | // termination of recursion at table length 371 | template 372 | struct table_builder 373 | { 374 | static constexpr typename traits::table_type table = {D...}; 375 | }; 376 | 377 | static constexpr typename traits::table_type m_Table = table_builder<>::table; 378 | #endif 379 | }; 380 | 381 | // 382 | // Define the CRC algorithm parameters 383 | // 384 | template < 385 | typename TAccumulator, 386 | const TAccumulator POLYNOMIAL, 387 | const TAccumulator INITIAL, 388 | const TAccumulator XOR_OUT, 389 | const bool REVERSE> 390 | struct crc_algorithm 391 | { 392 | using accumulator_type = TAccumulator; 393 | static constexpr TAccumulator polynomial = POLYNOMIAL; 394 | static constexpr TAccumulator initial_value = INITIAL; 395 | static constexpr TAccumulator xor_out_value = XOR_OUT; 396 | static constexpr bool reverse = REVERSE; 397 | }; 398 | 399 | // 400 | // The generic CRC accumulator that is table driven 401 | // 402 | template 403 | class crc 404 | { 405 | public: 406 | using algorithm = TAlgorithm; 407 | using accumulator_type = typename algorithm::accumulator_type; 408 | 409 | // 410 | // Update the accumulator with a new byte 411 | // 412 | constexpr void update(uint8_t value) { m_Crc = table_impl::update(m_Crc, value); } 413 | 414 | // 415 | // Extract the final value of the accumulator. 416 | // 417 | [[nodiscard]] constexpr accumulator_type final() { return m_Crc ^ algorithm::xor_out_value; } 418 | 419 | // 420 | // Reset the state of the accumulator back to the INITIAL value. 421 | // 422 | constexpr void reset() { m_Crc = table_impl::make_initial_value(algorithm::initial_value); } 423 | 424 | 425 | private: 426 | using table_impl = crc_chunk_table; 427 | 428 | accumulator_type m_Crc = table_impl::make_initial_value(algorithm::initial_value); 429 | }; 430 | 431 | } // namespace impl 432 | 433 | namespace alg 434 | { 435 | //------------------------------------------------------------------------ 436 | // 437 | // Define the set of CRC algorithms 438 | // 439 | // The algorihms define the parameters for the calculation 440 | // 441 | //------------------------------------------------------------------------ 442 | 443 | // size, poly, init, xor, reverse 444 | using crc8 = impl::crc_algorithm; 445 | using crc8_cdma2000 = impl::crc_algorithm; 446 | using crc8_darc = impl::crc_algorithm; 447 | using crc8_dvbs2 = impl::crc_algorithm; 448 | using crc8_ebu = impl::crc_algorithm; 449 | using crc8_icode = impl::crc_algorithm; 450 | using crc8_itu = impl::crc_algorithm; 451 | using crc8_maxim = impl::crc_algorithm; 452 | using crc8_rohc = impl::crc_algorithm; 453 | using crc8_wcdma = impl::crc_algorithm; 454 | 455 | // size, poly, init, xor, reverse 456 | using crc16_ccit = impl::crc_algorithm; 457 | using crc16_arc = impl::crc_algorithm; 458 | using crc16_augccit = impl::crc_algorithm; 459 | using crc16_buypass = impl::crc_algorithm; 460 | using crc16_cdma2000 = impl::crc_algorithm; 461 | using crc16_dds110 = impl::crc_algorithm; 462 | using crc16_dectr = impl::crc_algorithm; 463 | using crc16_dectx = impl::crc_algorithm; 464 | using crc16_dnp = impl::crc_algorithm; 465 | using crc16_en13757 = impl::crc_algorithm; 466 | using crc16_genibus = impl::crc_algorithm; 467 | using crc16_maxim = impl::crc_algorithm; 468 | using crc16_mcrf4xx = impl::crc_algorithm; 469 | using crc16_riello = impl::crc_algorithm; 470 | using crc16_t10dif = impl::crc_algorithm; 471 | using crc16_teledisk = impl::crc_algorithm; 472 | using crc16_tms37157 = impl::crc_algorithm; 473 | using crc16_usb = impl::crc_algorithm; 474 | using crc16_a = impl::crc_algorithm; 475 | using crc16_kermit = impl::crc_algorithm; 476 | using crc16_modbus = impl::crc_algorithm; 477 | using crc16_x25 = impl::crc_algorithm; 478 | using crc16_xmodem = impl::crc_algorithm; 479 | using crc16_m17lsf = impl::crc_algorithm; 480 | 481 | // size, poly, init, xor, reverse 482 | using crc32 = impl::crc_algorithm; 483 | using crc32_bzip2 = impl::crc_algorithm; 484 | using crc32_c = impl::crc_algorithm; 485 | using crc32_d = impl::crc_algorithm; 486 | using crc32_mpeg2 = impl::crc_algorithm; 487 | using crc32_posix = impl::crc_algorithm; 488 | using crc32_q = impl::crc_algorithm; 489 | using crc32_jamcrc = impl::crc_algorithm; 490 | using crc32_xfer = impl::crc_algorithm; 491 | 492 | // size, poly, init, xor, reverse 493 | using crc64_ecma = impl::crc_algorithm; 494 | 495 | } // namespace alg 496 | 497 | 498 | 499 | namespace family 500 | { 501 | //------------------------------------------------------------------------ 502 | // 503 | // Define Algorithm Families 504 | // 505 | // Each algorithm can be implemented using a table of varying sizes 506 | // which allows the user to trade off space and speed. 507 | // 508 | // We are creating a template that is parameterised with the size for 509 | // each algorithm here. 510 | // 511 | //------------------------------------------------------------------------ 512 | 513 | template class crc8 : public impl::crc{}; 514 | template class crc8_cdma2000 : public impl::crc{}; 515 | template class crc8_darc : public impl::crc{}; 516 | template class crc8_dvbs2 : public impl::crc{}; 517 | template class crc8_ebu : public impl::crc{}; 518 | template class crc8_icode : public impl::crc{}; 519 | template class crc8_itu : public impl::crc{}; 520 | template class crc8_maxim : public impl::crc{}; 521 | template class crc8_rohc : public impl::crc{}; 522 | template class crc8_wcdma : public impl::crc{}; 523 | 524 | template class crc16_ccit : public impl::crc{}; 525 | template class crc16_arc : public impl::crc{}; 526 | template class crc16_augccit : public impl::crc{}; 527 | template class crc16_buypass : public impl::crc{}; 528 | template class crc16_cdma2000 : public impl::crc{}; 529 | template class crc16_dds110 : public impl::crc{}; 530 | template class crc16_dectr : public impl::crc{}; 531 | template class crc16_dectx : public impl::crc{}; 532 | template class crc16_dnp : public impl::crc{}; 533 | template class crc16_en13757 : public impl::crc{}; 534 | template class crc16_genibus : public impl::crc{}; 535 | template class crc16_maxim : public impl::crc{}; 536 | template class crc16_mcrf4xx : public impl::crc{}; 537 | template class crc16_riello : public impl::crc{}; 538 | template class crc16_t10dif : public impl::crc{}; 539 | template class crc16_teledisk : public impl::crc{}; 540 | template class crc16_tms37157 : public impl::crc{}; 541 | template class crc16_usb : public impl::crc{}; 542 | template class crc16_a : public impl::crc{}; 543 | template class crc16_kermit : public impl::crc{}; 544 | template class crc16_modbus : public impl::crc{}; 545 | template class crc16_x25 : public impl::crc{}; 546 | template class crc16_xmodem : public impl::crc{}; 547 | template class crc16_m17lsf : public impl::crc{}; 548 | 549 | template class crc32 : public impl::crc{}; 550 | template class crc32_bzip2 : public impl::crc{}; 551 | template class crc32_c : public impl::crc{}; 552 | template class crc32_d : public impl::crc{}; 553 | template class crc32_mpeg2 : public impl::crc{}; 554 | template class crc32_posix : public impl::crc{}; 555 | template class crc32_q : public impl::crc{}; 556 | template class crc32_jamcrc : public impl::crc{}; 557 | template class crc32_xfer : public impl::crc{}; 558 | 559 | template class crc64_ecma : public impl::crc{}; 560 | 561 | } // namespace family 562 | 563 | 564 | // Default implementation "size" selected using inline namespace 565 | inline namespace small { 566 | //------------------------------------------------------------------------ 567 | // 568 | // Define the set of default CRC implementations using small table size 569 | // 570 | // These are the defined in the top level crc_cpp namespace 571 | // 572 | //------------------------------------------------------------------------ 573 | 574 | using crc8 = family::crc8 ; 575 | using crc8_cdma2000 = family::crc8_cdma2000 ; 576 | using crc8_darc = family::crc8_darc ; 577 | using crc8_dvbs2 = family::crc8_dvbs2 ; 578 | using crc8_ebu = family::crc8_ebu ; 579 | using crc8_icode = family::crc8_icode ; 580 | using crc8_itu = family::crc8_itu ; 581 | using crc8_maxim = family::crc8_maxim ; 582 | using crc8_rohc = family::crc8_rohc ; 583 | using crc8_wcdma = family::crc8_wcdma ; 584 | 585 | using crc16_ccit = family::crc16_ccit ; 586 | using crc16_arc = family::crc16_arc ; 587 | using crc16_augccit = family::crc16_augccit ; 588 | using crc16_buypass = family::crc16_buypass ; 589 | using crc16_cdma2000 = family::crc16_cdma2000 ; 590 | using crc16_dds110 = family::crc16_dds110 ; 591 | using crc16_dectr = family::crc16_dectr ; 592 | using crc16_dectx = family::crc16_dectx ; 593 | using crc16_dnp = family::crc16_dnp ; 594 | using crc16_en13757 = family::crc16_en13757 ; 595 | using crc16_genibus = family::crc16_genibus ; 596 | using crc16_maxim = family::crc16_maxim ; 597 | using crc16_mcrf4xx = family::crc16_mcrf4xx ; 598 | using crc16_riello = family::crc16_riello ; 599 | using crc16_t10dif = family::crc16_t10dif ; 600 | using crc16_teledisk = family::crc16_teledisk ; 601 | using crc16_tms37157 = family::crc16_tms37157 ; 602 | using crc16_usb = family::crc16_usb ; 603 | using crc16_a = family::crc16_a ; 604 | using crc16_kermit = family::crc16_kermit ; 605 | using crc16_modbus = family::crc16_modbus ; 606 | using crc16_x25 = family::crc16_x25 ; 607 | using crc16_xmodem = family::crc16_xmodem ; 608 | using crc16_m17lsf = family::crc16_m17lsf ; 609 | 610 | using crc32 = family::crc32 ; 611 | using crc32_bzip2 = family::crc32_bzip2 ; 612 | using crc32_c = family::crc32_c ; 613 | using crc32_d = family::crc32_d ; 614 | using crc32_mpeg2 = family::crc32_mpeg2 ; 615 | using crc32_posix = family::crc32_posix ; 616 | using crc32_q = family::crc32_q ; 617 | using crc32_jamcrc = family::crc32_jamcrc ; 618 | using crc32_xfer = family::crc32_xfer ; 619 | 620 | using crc64_ecma = family::crc64_ecma ; 621 | } // namespace small 622 | 623 | namespace large 624 | { 625 | //------------------------------------------------------------------------ 626 | // 627 | // Define the set of large table CRC implementations 628 | // 629 | //------------------------------------------------------------------------ 630 | using crc8 = family::crc8 ; 631 | using crc8_cdma2000 = family::crc8_cdma2000 ; 632 | using crc8_darc = family::crc8_darc ; 633 | using crc8_dvbs2 = family::crc8_dvbs2 ; 634 | using crc8_ebu = family::crc8_ebu ; 635 | using crc8_icode = family::crc8_icode ; 636 | using crc8_itu = family::crc8_itu ; 637 | using crc8_maxim = family::crc8_maxim ; 638 | using crc8_rohc = family::crc8_rohc ; 639 | using crc8_wcdma = family::crc8_wcdma ; 640 | 641 | using crc16_ccit = family::crc16_ccit ; 642 | using crc16_arc = family::crc16_arc ; 643 | using crc16_augccit = family::crc16_augccit ; 644 | using crc16_buypass = family::crc16_buypass ; 645 | using crc16_cdma2000 = family::crc16_cdma2000 ; 646 | using crc16_dds110 = family::crc16_dds110 ; 647 | using crc16_dectr = family::crc16_dectr ; 648 | using crc16_dectx = family::crc16_dectx ; 649 | using crc16_dnp = family::crc16_dnp ; 650 | using crc16_en13757 = family::crc16_en13757 ; 651 | using crc16_genibus = family::crc16_genibus ; 652 | using crc16_maxim = family::crc16_maxim ; 653 | using crc16_mcrf4xx = family::crc16_mcrf4xx ; 654 | using crc16_riello = family::crc16_riello ; 655 | using crc16_t10dif = family::crc16_t10dif ; 656 | using crc16_teledisk = family::crc16_teledisk ; 657 | using crc16_tms37157 = family::crc16_tms37157 ; 658 | using crc16_usb = family::crc16_usb ; 659 | using crc16_a = family::crc16_a ; 660 | using crc16_kermit = family::crc16_kermit ; 661 | using crc16_modbus = family::crc16_modbus ; 662 | using crc16_x25 = family::crc16_x25 ; 663 | using crc16_xmodem = family::crc16_xmodem ; 664 | using crc16_m17lsf = family::crc16_m17lsf ; 665 | 666 | using crc32 = family::crc32 ; 667 | using crc32_bzip2 = family::crc32_bzip2 ; 668 | using crc32_c = family::crc32_c ; 669 | using crc32_d = family::crc32_d ; 670 | using crc32_mpeg2 = family::crc32_mpeg2 ; 671 | using crc32_posix = family::crc32_posix ; 672 | using crc32_q = family::crc32_q ; 673 | using crc32_jamcrc = family::crc32_jamcrc ; 674 | using crc32_xfer = family::crc32_xfer ; 675 | 676 | using crc64_ecma = family::crc64_ecma ; 677 | 678 | } // namespace large 679 | 680 | namespace tiny 681 | { 682 | //------------------------------------------------------------------------ 683 | // 684 | // Define the set of tiny table CRC implementations 685 | // 686 | //------------------------------------------------------------------------ 687 | using crc8 = family::crc8 ; 688 | using crc8_cdma2000 = family::crc8_cdma2000 ; 689 | using crc8_darc = family::crc8_darc ; 690 | using crc8_dvbs2 = family::crc8_dvbs2 ; 691 | using crc8_ebu = family::crc8_ebu ; 692 | using crc8_icode = family::crc8_icode ; 693 | using crc8_itu = family::crc8_itu ; 694 | using crc8_maxim = family::crc8_maxim ; 695 | using crc8_rohc = family::crc8_rohc ; 696 | using crc8_wcdma = family::crc8_wcdma ; 697 | 698 | using crc16_ccit = family::crc16_ccit ; 699 | using crc16_arc = family::crc16_arc ; 700 | using crc16_augccit = family::crc16_augccit ; 701 | using crc16_buypass = family::crc16_buypass ; 702 | using crc16_cdma2000 = family::crc16_cdma2000 ; 703 | using crc16_dds110 = family::crc16_dds110 ; 704 | using crc16_dectr = family::crc16_dectr ; 705 | using crc16_dectx = family::crc16_dectx ; 706 | using crc16_dnp = family::crc16_dnp ; 707 | using crc16_en13757 = family::crc16_en13757 ; 708 | using crc16_genibus = family::crc16_genibus ; 709 | using crc16_maxim = family::crc16_maxim ; 710 | using crc16_mcrf4xx = family::crc16_mcrf4xx ; 711 | using crc16_riello = family::crc16_riello ; 712 | using crc16_t10dif = family::crc16_t10dif ; 713 | using crc16_teledisk = family::crc16_teledisk ; 714 | using crc16_tms37157 = family::crc16_tms37157 ; 715 | using crc16_usb = family::crc16_usb ; 716 | using crc16_a = family::crc16_a ; 717 | using crc16_kermit = family::crc16_kermit ; 718 | using crc16_modbus = family::crc16_modbus ; 719 | using crc16_x25 = family::crc16_x25 ; 720 | using crc16_xmodem = family::crc16_xmodem ; 721 | using crc16_m17lsf = family::crc16_m17lsf ; 722 | 723 | using crc32 = family::crc32 ; 724 | using crc32_bzip2 = family::crc32_bzip2 ; 725 | using crc32_c = family::crc32_c ; 726 | using crc32_d = family::crc32_d ; 727 | using crc32_mpeg2 = family::crc32_mpeg2 ; 728 | using crc32_posix = family::crc32_posix ; 729 | using crc32_q = family::crc32_q ; 730 | using crc32_jamcrc = family::crc32_jamcrc ; 731 | using crc32_xfer = family::crc32_xfer ; 732 | 733 | using crc64_ecma = family::crc64_ecma ; 734 | 735 | } // namespace tiny 736 | 737 | } // namespace crc_cpp 738 | 739 | #undef CRC_CPP_STD20_MODE 740 | #undef CRC_CPP_API_CONSTEXPR 741 | 742 | 743 | #endif // CRC_CPP_H_INCLUDED 744 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Automatically enable catch2 to generate ctest targets 2 | IF(CONAN_CATCH2_ROOT_DEBUG) 3 | INCLUDE(${CONAN_CATCH2_ROOT_DEBUG}/lib/cmake/Catch2/Catch.cmake) 4 | ELSE() 5 | INCLUDE(${CONAN_CATCH2_ROOT}/lib/cmake/Catch2/Catch.cmake) 6 | ENDIF() 7 | 8 | ADD_EXECUTABLE(tests test.cpp) 9 | TARGET_LINK_LIBRARIES(tests PRIVATE project_warnings project_options CONAN_PKG::catch2) 10 | TARGET_INCLUDE_DIRECTORIES(tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include) 11 | 12 | # automatically discover tests that are defined in catch based test files you can modify the unittests. Set TEST_PREFIX 13 | # to whatever you want, or use different for different binaries 14 | CATCH_DISCOVER_TESTS( 15 | tests 16 | TEST_PREFIX 17 | "unittests." 18 | REPORTER 19 | JUnit 20 | OUTPUT_DIR 21 | . 22 | OUTPUT_PREFIX 23 | "unittests." 24 | OUTPUT_SUFFIX 25 | .xml) 26 | -------------------------------------------------------------------------------- /test/test.cpp: -------------------------------------------------------------------------------- 1 | #include "crc_cpp.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using namespace crc_cpp; 8 | 9 | template bool is_expected(T const result, T const expected) 10 | { 11 | return result == expected; 12 | } 13 | 14 | template bool test_reverse_bits() 15 | { 16 | bool status = true; 17 | 18 | int const bits = sizeof(T) * 8; 19 | 20 | for (T i = 0; i < bits; i++) 21 | { 22 | // careful with types to ensure we don't end up with signed ints 23 | T const test = static_cast(T{0x01u} << i); 24 | T const expected = static_cast(T{0x01u} << (bits - 1 - i)); 25 | 26 | auto const result = crc_cpp::util::reverse_bits(test); 27 | 28 | status &= is_expected(result, expected); 29 | } 30 | 31 | return status; 32 | } 33 | 34 | template bool test_crc(std::vector const& message, typename TCrc::accumulator_type expected) 35 | { 36 | TCrc crc; // initialised by construction 37 | 38 | // Assert that the crc register is only the size of accululator. 39 | static_assert(sizeof(TCrc) == sizeof(typename TCrc::accumulator_type), "Unexpected CRC object size"); 40 | 41 | for (auto const& c : message) 42 | { 43 | crc.update(c); 44 | } 45 | 46 | auto const result = crc.final(); 47 | 48 | return is_expected(result, expected); 49 | } 50 | 51 | // 52 | // Helper to test a family of the same algorithm implemented with different table sizes 53 | // 54 | template class TCrc> 55 | bool test_crc(std::vector const& message, 56 | // hack to get to the accumulator type by fully quallifying one of the instances 57 | typename TCrc::accumulator_type expected) 58 | { 59 | bool result = true; 60 | 61 | // expand into the supported table sizes for the family 62 | result &= test_crc>(message, expected); 63 | result &= test_crc>(message, expected); 64 | result &= test_crc>(message, expected); 65 | 66 | return result; 67 | } 68 | 69 | //------------------------------------------------------------------------ 70 | // Constexpr test 71 | // 72 | // Verify that the CRC can be calculated in a constexpr (compile-time) context 73 | template constexpr bool constexpr_check_message(std::array data, uint8_t const expected) 74 | { 75 | crc_cpp::crc8 crc; 76 | for (auto const& b : data) 77 | { 78 | crc.update(b); 79 | } 80 | return expected == crc.final(); 81 | } 82 | 83 | constexpr std::array constexpr_message{'1', '2', '3', '4', '5', '6', '7', '8', '9'}; 84 | [[maybe_unused]] constexpr bool constexpr_test_result = constexpr_check_message(constexpr_message, 0xF4); 85 | 86 | static_assert(constexpr_test_result, "Failed to compute crc at compile time"); 87 | 88 | TEST_CASE("BitReversing", "TestHelperFunction") 89 | { 90 | REQUIRE(test_reverse_bits()); 91 | REQUIRE(test_reverse_bits()); 92 | REQUIRE(test_reverse_bits()); 93 | } 94 | 95 | TEST_CASE("Algorithm", "TestCRC") 96 | { 97 | const std::vector message{ '1', '2', '3', '4', '5', '6', '7', '8', '9' }; 98 | REQUIRE(test_crc(message, 0xF4)); 99 | REQUIRE(test_crc(message, 0xDA)); 100 | REQUIRE(test_crc(message, 0x15)); 101 | REQUIRE(test_crc(message, 0xBC)); 102 | REQUIRE(test_crc(message, 0x97)); 103 | REQUIRE(test_crc(message, 0x7E)); 104 | REQUIRE(test_crc(message, 0xA1)); 105 | REQUIRE(test_crc(message, 0xA1)); 106 | REQUIRE(test_crc(message, 0xD0)); 107 | REQUIRE(test_crc(message, 0x25)); 108 | 109 | REQUIRE(test_crc(message, 0x29B1)); 110 | REQUIRE(test_crc(message, 0xBB3D)); 111 | REQUIRE(test_crc(message, 0xE5CC)); 112 | REQUIRE(test_crc(message, 0xFEE8)); 113 | REQUIRE(test_crc(message, 0x4C06)); 114 | REQUIRE(test_crc(message, 0x9ECF)); 115 | REQUIRE(test_crc(message, 0x007E)); 116 | REQUIRE(test_crc(message, 0x007F)); 117 | REQUIRE(test_crc(message, 0xEA82)); 118 | REQUIRE(test_crc(message, 0xC2B7)); 119 | REQUIRE(test_crc(message, 0xD64E)); 120 | REQUIRE(test_crc(message, 0x44C2)); 121 | REQUIRE(test_crc( message, 0x6F91)); 122 | REQUIRE(test_crc( message, 0x63D0)); 123 | REQUIRE(test_crc( message, 0xD0DB)); 124 | REQUIRE(test_crc( message, 0x0FB3)); 125 | REQUIRE(test_crc(message, 0x26B1)); 126 | REQUIRE(test_crc(message, 0xB4C8)); 127 | REQUIRE(test_crc(message, 0xBF05)); 128 | REQUIRE(test_crc( message, 0x2189)); 129 | REQUIRE(test_crc(message, 0x4B37)); 130 | REQUIRE(test_crc( message, 0x906E)); 131 | REQUIRE(test_crc( message, 0x31C3)); 132 | REQUIRE(test_crc( message, 0x772B)); 133 | 134 | REQUIRE(test_crc(message, 0xCBF43926)); 135 | REQUIRE(test_crc( message, 0xFC891918)); 136 | REQUIRE(test_crc(message, 0xE3069283)); 137 | REQUIRE(test_crc( message, 0x87315576)); 138 | REQUIRE(test_crc(message, 0x0376E6E7)); 139 | REQUIRE(test_crc(message, 0x765E7680)); 140 | REQUIRE(test_crc(message, 0x3010BF7F)); 141 | REQUIRE(test_crc(message, 0x340BC6D9)); 142 | REQUIRE(test_crc(message, 0xBD0BE338)); 143 | 144 | REQUIRE(test_crc( message, 0x6C40DF5F0B497347U)); 145 | } 146 | --------------------------------------------------------------------------------