├── .clang-format ├── .github └── workflows │ ├── install.yml │ ├── macos.yml │ ├── style.yml │ ├── ubuntu.yml │ └── windows.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── cmake ├── CPM.cmake └── tools.cmake ├── codecov.yaml ├── include └── static_hash │ ├── sha256.h │ └── util │ ├── array_conversion.h │ └── string.h ├── source └── dummy.cpp └── test ├── CMakeLists.txt └── source ├── array_conversion.cpp ├── main.cpp └── sha256.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: Google 3 | AccessModifierOffset: '-2' 4 | AlignTrailingComments: 'true' 5 | AllowAllParametersOfDeclarationOnNextLine: 'false' 6 | AlwaysBreakTemplateDeclarations: 'No' 7 | BreakBeforeBraces: Attach 8 | ColumnLimit: '100' 9 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'true' 10 | IncludeBlocks: Regroup 11 | IndentPPDirectives: AfterHash 12 | IndentWidth: '2' 13 | NamespaceIndentation: All 14 | BreakBeforeBinaryOperators: All 15 | BreakBeforeTernaryOperators: 'true' 16 | ... 17 | -------------------------------------------------------------------------------- /.github/workflows/install.yml: -------------------------------------------------------------------------------- 1 | name: Install 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | env: 12 | CTEST_OUTPUT_ON_FAILURE: 1 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v1 21 | 22 | - name: build and install library 23 | run: | 24 | cmake -H. -Bbuild -DCMAKE_BUILD_TYPE=Release 25 | sudo cmake --build build --target install 26 | rm -rf build 27 | 28 | - name: configure 29 | run: cmake -Htest -Bbuild -DTEST_INSTALLED_VERSION=1 30 | 31 | - name: build 32 | run: cmake --build build --config Debug -j4 33 | 34 | - name: test 35 | run: | 36 | cd build 37 | ctest --build-config Debug 38 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: MacOS 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | env: 12 | CTEST_OUTPUT_ON_FAILURE: 1 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: macos-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v1 21 | 22 | - name: configure 23 | run: cmake -Htest -Bbuild 24 | 25 | - name: build 26 | run: cmake --build build --config Debug -j4 27 | 28 | - name: test 29 | run: | 30 | cd build 31 | ctest --build-config Debug 32 | -------------------------------------------------------------------------------- /.github/workflows/style.yml: -------------------------------------------------------------------------------- 1 | name: Style 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | 14 | runs-on: macos-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v1 18 | 19 | - name: Install clang-format 20 | run: brew install clang-format 21 | 22 | - name: configure 23 | run: cmake -Htest -Bbuild 24 | 25 | - name: check style 26 | run: cmake --build build --target check-format 27 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: Ubuntu 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | env: 12 | CTEST_OUTPUT_ON_FAILURE: 1 13 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 14 | 15 | jobs: 16 | build: 17 | 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v1 22 | 23 | - name: configure 24 | run: cmake -Htest -Bbuild -DENABLE_TEST_COVERAGE=1 25 | 26 | - name: build 27 | run: cmake --build build --config Debug -j4 28 | 29 | - name: test 30 | run: | 31 | cd build 32 | ctest --build-config Debug 33 | 34 | - name: collect code coverage 35 | run: bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports" 36 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: Windows 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | env: 12 | CTEST_OUTPUT_ON_FAILURE: 1 13 | 14 | jobs: 15 | build: 16 | 17 | runs-on: windows-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v1 21 | 22 | - name: configure 23 | run: cmake -Htest -Bbuild 24 | 25 | - name: build 26 | run: cmake --build build --config Debug -j4 27 | 28 | - name: test 29 | run: | 30 | cd build 31 | ctest --build-config Debug 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build* 2 | /.vscode 3 | .DS_Store -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | # ---- Project ---- 4 | 5 | # Note: update this to your new project's name and version 6 | project(StaticHash 7 | VERSION 0.1 8 | LANGUAGES CXX 9 | ) 10 | 11 | # ---- Include guards ---- 12 | 13 | if(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR) 14 | message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.") 15 | endif() 16 | 17 | # ---- Add dependencies via CPM ---- 18 | # see https://github.com/TheLartians/CPM.cmake for more info 19 | 20 | include(cmake/CPM.cmake) 21 | 22 | # PackageProject.cmake will be used to make our target installable 23 | CPMAddPackage( 24 | NAME PackageProject.cmake 25 | GITHUB_REPOSITORY TheLartians/PackageProject.cmake 26 | VERSION 1.2 27 | ) 28 | 29 | # ---- Add source files ---- 30 | 31 | FILE(GLOB_RECURSE headers CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.h") 32 | FILE(GLOB_RECURSE sources CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp") 33 | 34 | # ---- Create library ---- 35 | 36 | add_library(StaticHash ${headers} ${sources}) 37 | set_target_properties(StaticHash PROPERTIES CXX_STANDARD 17) 38 | 39 | # beeing a cross-platform target, we enforce standards conformance on MSVC 40 | target_compile_options(StaticHash PUBLIC "$<$:/permissive->") 41 | 42 | target_include_directories(StaticHash 43 | PUBLIC 44 | $ 45 | $ 46 | ) 47 | 48 | # ---- Create an installable target ---- 49 | # this allows users to install and find the library via `find_package()`. 50 | 51 | packageProject( 52 | NAME ${PROJECT_NAME} 53 | VERSION ${PROJECT_VERSION} 54 | BINARY_DIR ${PROJECT_BINARY_DIR} 55 | INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include 56 | INCLUDE_DESTINATION include/${PROJECT_NAME}-${PROJECT_VERSION} 57 | DEPENDENCIES "" 58 | ) 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Lars Melchior 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 | [![Actions Status](https://github.com/TheLartians/StaticHash/workflows/MacOS/badge.svg)](https://github.com/TheLartians/StaticHash/actions) 2 | [![Actions Status](https://github.com/TheLartians/StaticHash/workflows/Windows/badge.svg)](https://github.com/TheLartians/StaticHash/actions) 3 | [![Actions Status](https://github.com/TheLartians/StaticHash/workflows/Ubuntu/badge.svg)](https://github.com/TheLartians/StaticHash/actions) 4 | [![Actions Status](https://github.com/TheLartians/StaticHash/workflows/Style/badge.svg)](https://github.com/TheLartians/StaticHash/actions) 5 | [![Actions Status](https://github.com/TheLartians/StaticHash/workflows/Install/badge.svg)](https://github.com/TheLartians/StaticHash/actions) 6 | [![codecov](https://codecov.io/gh/TheLartians/StaticHash/branch/master/graph/badge.svg)](https://codecov.io/gh/TheLartians/StaticHash) 7 | 8 | # StaticHash 9 | 10 | Constexpr hash functions [WIP] with a permissive licence (MIT). 11 | Currently implemented: 12 | 13 | - SHA256 14 | -------------------------------------------------------------------------------- /cmake/CPM.cmake: -------------------------------------------------------------------------------- 1 | # TheLartians/CPM - A simple Git dependency manager 2 | # ================================================= 3 | # See https://github.com/TheLartians/CPM for usage and update instructions. 4 | # 5 | # MIT License 6 | # ----------- 7 | #[[ 8 | Copyright (c) 2019 Lars Melchior 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | ]] 28 | 29 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 30 | 31 | set(CURRENT_CPM_VERSION 0.21) 32 | 33 | if(CPM_DIRECTORY) 34 | if(NOT ${CPM_DIRECTORY} MATCHES ${CMAKE_CURRENT_LIST_DIR}) 35 | if (${CPM_VERSION} VERSION_LESS ${CURRENT_CPM_VERSION}) 36 | message(AUTHOR_WARNING "${CPM_INDENT} \ 37 | A dependency is using a more recent CPM version (${CURRENT_CPM_VERSION}) than the current project (${CPM_VERSION}). \ 38 | It is recommended to upgrade CPM to the most recent version. \ 39 | See https://github.com/TheLartians/CPM.cmake for more information." 40 | ) 41 | endif() 42 | return() 43 | endif() 44 | 45 | get_property(CPM_INITIALIZED GLOBAL "" PROPERTY CPM_INITIALIZED SET) 46 | if (CPM_INITIALIZED) 47 | return() 48 | endif() 49 | endif() 50 | 51 | set_property(GLOBAL PROPERTY CPM_INITIALIZED true) 52 | 53 | option(CPM_USE_LOCAL_PACKAGES "Always try to use `find_package` to get dependencies" $ENV{CPM_USE_LOCAL_PACKAGES}) 54 | option(CPM_LOCAL_PACKAGES_ONLY "Only use `find_package` to get dependencies" $ENV{CPM_LOCAL_PACKAGES_ONLY}) 55 | option(CPM_DOWNLOAD_ALL "Always download dependencies from source" $ENV{CPM_DOWNLOAD_ALL}) 56 | option(CPM_DONT_UPDATE_MODULE_PATH "Don't update the module path to allow using find_package" $ENV{CPM_DONT_UPDATE_MODULE_PATH}) 57 | 58 | set(CPM_VERSION ${CURRENT_CPM_VERSION} CACHE INTERNAL "") 59 | set(CPM_DIRECTORY ${CMAKE_CURRENT_LIST_DIR} CACHE INTERNAL "") 60 | set(CPM_FILE ${CMAKE_CURRENT_LIST_FILE} CACHE INTERNAL "") 61 | set(CPM_PACKAGES "" CACHE INTERNAL "") 62 | set(CPM_DRY_RUN OFF CACHE INTERNAL "Don't download or configure dependencies (for testing)") 63 | 64 | if(DEFINED ENV{CPM_SOURCE_CACHE}) 65 | set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE}) 66 | else() 67 | set(CPM_SOURCE_CACHE_DEFAULT OFF) 68 | endif() 69 | 70 | set(CPM_SOURCE_CACHE ${CPM_SOURCE_CACHE_DEFAULT} CACHE PATH "Directory to downlaod CPM dependencies") 71 | 72 | if (NOT CPM_DONT_UPDATE_MODULE_PATH) 73 | set(CPM_MODULE_PATH "${CMAKE_BINARY_DIR}/CPM_modules" CACHE INTERNAL "") 74 | # remove old modules 75 | FILE(REMOVE_RECURSE ${CPM_MODULE_PATH}) 76 | file(MAKE_DIRECTORY ${CPM_MODULE_PATH}) 77 | # locally added CPM modules should override global packages 78 | set(CMAKE_MODULE_PATH "${CPM_MODULE_PATH};${CMAKE_MODULE_PATH}") 79 | endif() 80 | 81 | include(FetchContent) 82 | include(CMakeParseArguments) 83 | 84 | # Initialize logging prefix 85 | if(NOT CPM_INDENT) 86 | set(CPM_INDENT "CPM:") 87 | endif() 88 | 89 | function(cpm_find_package NAME VERSION) 90 | string(REPLACE " " ";" EXTRA_ARGS "${ARGN}") 91 | find_package(${NAME} ${VERSION} ${EXTRA_ARGS} QUIET) 92 | if(${CPM_ARGS_NAME}_FOUND) 93 | message(STATUS "${CPM_INDENT} using local package ${CPM_ARGS_NAME}@${${CPM_ARGS_NAME}_VERSION}") 94 | CPMRegisterPackage(${CPM_ARGS_NAME} "${${CPM_ARGS_NAME}_VERSION}") 95 | set(CPM_PACKAGE_FOUND YES PARENT_SCOPE) 96 | else() 97 | set(CPM_PACKAGE_FOUND NO PARENT_SCOPE) 98 | endif() 99 | endfunction() 100 | 101 | # Create a custom FindXXX.cmake module for a CPM package 102 | # This prevents `find_package(NAME)` from finding the system library 103 | function(CPMCreateModuleFile Name) 104 | if (NOT CPM_DONT_UPDATE_MODULE_PATH) 105 | # erase any previous modules 106 | FILE(WRITE ${CPM_MODULE_PATH}/Find${Name}.cmake "include(${CPM_FILE})\n${ARGN}\nset(${Name}_FOUND TRUE)") 107 | endif() 108 | endfunction() 109 | 110 | # Find a package locally or fallback to CPMAddPackage 111 | function(CPMFindPackage) 112 | set(oneValueArgs 113 | NAME 114 | VERSION 115 | FIND_PACKAGE_ARGUMENTS 116 | ) 117 | 118 | cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "" ${ARGN}) 119 | 120 | if (CPM_DOWNLOAD_ALL) 121 | CPMAddPackage(${ARGN}) 122 | cpm_export_variables() 123 | return() 124 | endif() 125 | 126 | cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) 127 | 128 | if(NOT CPM_PACKAGE_FOUND) 129 | CPMAddPackage(${ARGN}) 130 | cpm_export_variables() 131 | endif() 132 | 133 | endfunction() 134 | 135 | # Download and add a package from source 136 | function(CPMAddPackage) 137 | 138 | set(oneValueArgs 139 | NAME 140 | VERSION 141 | GIT_TAG 142 | DOWNLOAD_ONLY 143 | GITHUB_REPOSITORY 144 | GITLAB_REPOSITORY 145 | SOURCE_DIR 146 | DOWNLOAD_COMMAND 147 | FIND_PACKAGE_ARGUMENTS 148 | ) 149 | 150 | set(multiValueArgs 151 | OPTIONS 152 | ) 153 | 154 | cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") 155 | 156 | if(CPM_USE_LOCAL_PACKAGES OR CPM_LOCAL_PACKAGES_ONLY) 157 | cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) 158 | 159 | if(CPM_PACKAGE_FOUND) 160 | return() 161 | endif() 162 | 163 | if(CPM_LOCAL_PACKAGES_ONLY) 164 | message(SEND_ERROR "CPM: ${CPM_ARGS_NAME} not found via find_package(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION})") 165 | endif() 166 | endif() 167 | 168 | if (NOT DEFINED CPM_ARGS_VERSION) 169 | if (DEFINED CPM_ARGS_GIT_TAG) 170 | cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) 171 | endif() 172 | if (NOT DEFINED CPM_ARGS_VERSION) 173 | set(CPM_ARGS_VERSION 0) 174 | endif() 175 | endif() 176 | 177 | if (NOT DEFINED CPM_ARGS_GIT_TAG) 178 | set(CPM_ARGS_GIT_TAG v${CPM_ARGS_VERSION}) 179 | endif() 180 | 181 | list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_TAG ${CPM_ARGS_GIT_TAG}) 182 | 183 | if(CPM_ARGS_DOWNLOAD_ONLY) 184 | set(DOWNLOAD_ONLY ${CPM_ARGS_DOWNLOAD_ONLY}) 185 | else() 186 | set(DOWNLOAD_ONLY NO) 187 | endif() 188 | 189 | if (CPM_ARGS_GITHUB_REPOSITORY) 190 | list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY "https://github.com/${CPM_ARGS_GITHUB_REPOSITORY}.git") 191 | endif() 192 | 193 | if (CPM_ARGS_GITLAB_REPOSITORY) 194 | list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY "https://gitlab.com/${CPM_ARGS_GITLAB_REPOSITORY}.git") 195 | endif() 196 | 197 | if ("${CPM_ARGS_NAME}" IN_LIST CPM_PACKAGES) 198 | CPMGetPackageVersion(${CPM_ARGS_NAME} CPM_PACKAGE_VERSION) 199 | if(${CPM_PACKAGE_VERSION} VERSION_LESS ${CPM_ARGS_VERSION}) 200 | message(WARNING "${CPM_INDENT} requires a newer version of ${CPM_ARGS_NAME} (${CPM_ARGS_VERSION}) than currently included (${CPM_PACKAGE_VERSION}).") 201 | endif() 202 | if (CPM_ARGS_OPTIONS) 203 | foreach(OPTION ${CPM_ARGS_OPTIONS}) 204 | cpm_parse_option(${OPTION}) 205 | if(NOT "${${OPTION_KEY}}" STREQUAL ${OPTION_VALUE}) 206 | message(WARNING "${CPM_INDENT} ignoring package option for ${CPM_ARGS_NAME}: ${OPTION_KEY} = ${OPTION_VALUE} (${${OPTION_KEY}})") 207 | endif() 208 | endforeach() 209 | endif() 210 | cpm_fetch_package(${CPM_ARGS_NAME} ${DOWNLOAD_ONLY}) 211 | cpm_get_fetch_properties(${CPM_ARGS_NAME}) 212 | SET(${CPM_ARGS_NAME}_SOURCE_DIR "${${CPM_ARGS_NAME}_SOURCE_DIR}") 213 | SET(${CPM_ARGS_NAME}_BINARY_DIR "${${CPM_ARGS_NAME}_BINARY_DIR}") 214 | SET(${CPM_ARGS_NAME}_ADDED NO) 215 | cpm_export_variables() 216 | return() 217 | endif() 218 | 219 | CPMRegisterPackage(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION}) 220 | 221 | if (CPM_ARGS_OPTIONS) 222 | foreach(OPTION ${CPM_ARGS_OPTIONS}) 223 | cpm_parse_option(${OPTION}) 224 | set(${OPTION_KEY} ${OPTION_VALUE} CACHE INTERNAL "") 225 | endforeach() 226 | endif() 227 | 228 | set(FETCH_CONTENT_DECLARE_EXTRA_OPTS "") 229 | 230 | if (DEFINED CPM_ARGS_GIT_TAG) 231 | set(PACKAGE_INFO "${CPM_ARGS_GIT_TAG}") 232 | else() 233 | set(PACKAGE_INFO "${CPM_ARGS_VERSION}") 234 | endif() 235 | 236 | if (DEFINED CPM_ARGS_DOWNLOAD_COMMAND) 237 | set(FETCH_CONTENT_DECLARE_EXTRA_OPTS DOWNLOAD_COMMAND ${CPM_ARGS_DOWNLOAD_COMMAND}) 238 | elseif(DEFINED CPM_ARGS_SOURCE_DIR) 239 | set(FETCH_CONTENT_DECLARE_EXTRA_OPTS SOURCE_DIR ${CPM_ARGS_SOURCE_DIR}) 240 | elseif (CPM_SOURCE_CACHE) 241 | string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) 242 | set(origin_parameters ${CPM_ARGS_UNPARSED_ARGUMENTS}) 243 | list(SORT origin_parameters) 244 | string(SHA1 origin_hash "${origin_parameters}") 245 | set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}) 246 | list(APPEND FETCH_CONTENT_DECLARE_EXTRA_OPTS SOURCE_DIR ${download_directory}) 247 | if (EXISTS ${download_directory}) 248 | # disable the download command to allow offline builds 249 | list(APPEND FETCH_CONTENT_DECLARE_EXTRA_OPTS DOWNLOAD_COMMAND "${CMAKE_COMMAND}") 250 | set(PACKAGE_INFO "${download_directory}") 251 | else() 252 | # remove timestamps so CMake will re-download the dependency 253 | file(REMOVE_RECURSE ${CMAKE_BINARY_DIR}/_deps/${lower_case_name}-subbuild) 254 | set(PACKAGE_INFO "${PACKAGE_INFO} -> ${download_directory}") 255 | endif() 256 | endif() 257 | 258 | cpm_declare_fetch(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION} ${PACKAGE_INFO} "${CPM_ARGS_UNPARSED_ARGUMENTS}" ${FETCH_CONTENT_DECLARE_EXTRA_OPTS}) 259 | cpm_fetch_package(${CPM_ARGS_NAME} ${DOWNLOAD_ONLY}) 260 | cpm_get_fetch_properties(${CPM_ARGS_NAME}) 261 | CPMCreateModuleFile(${CPM_ARGS_NAME} "CPMAddPackage(${ARGN})") 262 | SET(${CPM_ARGS_NAME}_ADDED YES) 263 | cpm_export_variables() 264 | endfunction() 265 | 266 | # export variables available to the caller to the parent scope 267 | # expects ${CPM_ARGS_NAME} to be set 268 | macro(cpm_export_variables) 269 | SET(${CPM_ARGS_NAME}_SOURCE_DIR "${${CPM_ARGS_NAME}_SOURCE_DIR}" PARENT_SCOPE) 270 | SET(${CPM_ARGS_NAME}_BINARY_DIR "${${CPM_ARGS_NAME}_BINARY_DIR}" PARENT_SCOPE) 271 | SET(${CPM_ARGS_NAME}_ADDED "${${CPM_ARGS_NAME}_ADDED}" PARENT_SCOPE) 272 | endmacro() 273 | 274 | # declares that a package has been added to CPM 275 | function(CPMRegisterPackage PACKAGE VERSION) 276 | list(APPEND CPM_PACKAGES ${PACKAGE}) 277 | set(CPM_PACKAGES ${CPM_PACKAGES} CACHE INTERNAL "") 278 | set("CPM_PACKAGE_${PACKAGE}_VERSION" ${VERSION} CACHE INTERNAL "") 279 | endfunction() 280 | 281 | # retrieve the current version of the package to ${OUTPUT} 282 | function(CPMGetPackageVersion PACKAGE OUTPUT) 283 | set(${OUTPUT} "${CPM_PACKAGE_${PACKAGE}_VERSION}" PARENT_SCOPE) 284 | endfunction() 285 | 286 | # declares a package in FetchContent_Declare 287 | function (cpm_declare_fetch PACKAGE VERSION INFO) 288 | message(STATUS "${CPM_INDENT} adding package ${PACKAGE}@${VERSION} (${INFO})") 289 | 290 | if (${CPM_DRY_RUN}) 291 | message(STATUS "${CPM_INDENT} package not declared (dry run)") 292 | return() 293 | endif() 294 | 295 | FetchContent_Declare( 296 | ${PACKAGE} 297 | ${ARGN} 298 | ) 299 | endfunction() 300 | 301 | # returns properties for a package previously defined by cpm_declare_fetch 302 | function (cpm_get_fetch_properties PACKAGE) 303 | if (${CPM_DRY_RUN}) 304 | return() 305 | endif() 306 | FetchContent_GetProperties(${PACKAGE}) 307 | string(TOLOWER ${PACKAGE} lpackage) 308 | SET(${PACKAGE}_SOURCE_DIR "${${lpackage}_SOURCE_DIR}" PARENT_SCOPE) 309 | SET(${PACKAGE}_BINARY_DIR "${${lpackage}_BINARY_DIR}" PARENT_SCOPE) 310 | endfunction() 311 | 312 | # downloads a previously declared package via FetchContent 313 | function (cpm_fetch_package PACKAGE DOWNLOAD_ONLY) 314 | 315 | if (${CPM_DRY_RUN}) 316 | message(STATUS "${CPM_INDENT} package ${PACKAGE} not fetched (dry run)") 317 | return() 318 | endif() 319 | 320 | set(CPM_OLD_INDENT "${CPM_INDENT}") 321 | set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:") 322 | if(${DOWNLOAD_ONLY}) 323 | if(NOT "${PACKAGE}_POPULATED") 324 | FetchContent_Populate(${PACKAGE}) 325 | endif() 326 | else() 327 | FetchContent_MakeAvailable(${PACKAGE}) 328 | endif() 329 | set(CPM_INDENT "${CPM_OLD_INDENT}") 330 | endfunction() 331 | 332 | # splits a package option 333 | function(cpm_parse_option OPTION) 334 | string(REGEX MATCH "^[^ ]+" OPTION_KEY ${OPTION}) 335 | string(LENGTH ${OPTION} OPTION_LENGTH) 336 | string(LENGTH ${OPTION_KEY} OPTION_KEY_LENGTH) 337 | if (OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH) 338 | # no value for key provided, assume user wants to set option to "ON" 339 | set(OPTION_VALUE "ON") 340 | else() 341 | math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1") 342 | string(SUBSTRING ${OPTION} "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE) 343 | endif() 344 | set(OPTION_KEY "${OPTION_KEY}" PARENT_SCOPE) 345 | set(OPTION_VALUE "${OPTION_VALUE}" PARENT_SCOPE) 346 | endfunction() 347 | 348 | # guesses the package version from a git tag 349 | function(cpm_get_version_from_git_tag GIT_TAG RESULT) 350 | string(LENGTH ${GIT_TAG} length) 351 | if (length EQUAL 40) 352 | # GIT_TAG is probably a git hash 353 | SET(${RESULT} 0 PARENT_SCOPE) 354 | else() 355 | string(REGEX MATCH "v?([0123456789.]*).*" _ ${GIT_TAG}) 356 | SET(${RESULT} ${CMAKE_MATCH_1} PARENT_SCOPE) 357 | endif() 358 | endfunction() 359 | -------------------------------------------------------------------------------- /cmake/tools.cmake: -------------------------------------------------------------------------------- 1 | # this file contains a list of tools that can be activated and downloaded on-demand 2 | # each tool is enabled during configuration by passing an additional `-DUSE_=` argument to CMake 3 | 4 | # only activate tools for top level project 5 | if (NOT PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 6 | return() 7 | endif() 8 | 9 | include(${CMAKE_CURRENT_LIST_DIR}/CPM.cmake) 10 | 11 | # enables sanitizers support using the the `USE_SANITIZER` flag 12 | # available values are: Address, Memory, MemoryWithOrigins, Undefined, Thread, Leak, 'Address;Undefined' 13 | if (USE_SANITIZER) 14 | CPMAddPackage( 15 | NAME StableCoder-cmake-scripts 16 | GITHUB_REPOSITORY StableCoder/cmake-scripts 17 | GIT_TAG 3a469d8251660a97dbf9e0afff0a242965d40277 18 | ) 19 | 20 | include(${StableCoder-cmake-scripts_SOURCE_DIR}/sanitizers.cmake) 21 | endif() 22 | 23 | # enables CCACHE support through the USE_CCACHE flag 24 | # possible values are: YES, NO or equivalent 25 | if (USE_CCACHE) 26 | CPMAddPackage( 27 | NAME Ccache.cmake 28 | GITHUB_REPOSITORY TheLartians/Ccache.cmake 29 | VERSION 1.1 30 | ) 31 | endif() 32 | -------------------------------------------------------------------------------- /codecov.yaml: -------------------------------------------------------------------------------- 1 | ignore: 2 | - "test" 3 | 4 | comment: 5 | require_changes: true -------------------------------------------------------------------------------- /include/static_hash/sha256.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // The MIT License (MIT) 4 | 5 | // Copyright (c) 2015, 2016 Ben Deane 6 | 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | 14 | // The above copyright notice and this permission notice shall be included in 15 | // all copies or substantial portions of the Software. 16 | 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | // THE SOFTWARE. 24 | 25 | // Modified by Lars Melchior 26 | 27 | #include 28 | 29 | #include 30 | 31 | //---------------------------------------------------------------------------- 32 | // constexpr string hashing: sha-256 33 | 34 | namespace static_hash { 35 | 36 | struct SHA256 { 37 | using Data = std::array; 38 | Data data; 39 | 40 | constexpr bool operator==(SHA256 other) const { 41 | for (size_t i = 0; i < data.size(); ++i) { 42 | if (data[i] != other.data[i]) return false; 43 | } 44 | return true; 45 | } 46 | 47 | constexpr bool operator!=(SHA256 other) const { return !((*this) == other); } 48 | constexpr Data asLittleEndian() const; 49 | }; 50 | 51 | namespace detail { 52 | namespace sha256 { 53 | // magic round constants (actually the fractional parts of 54 | // the cubes roots of the first 64 primes 2..311) 55 | constexpr uint32_t roundconst[64] 56 | = {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 57 | 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 58 | 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 59 | 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 60 | 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 61 | 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 62 | 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 63 | 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 64 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 65 | 0xc67178f2}; 66 | 67 | // a schedule is the chunk of buffer to work on, extended to 64 words 68 | struct schedule { 69 | uint32_t w[64]; 70 | }; 71 | 72 | // add two SHA256s 73 | constexpr SHA256 sumadd(const SHA256& s1, const SHA256& s2) { 74 | return {{s1.data[0] + s2.data[0], s1.data[1] + s2.data[1], s1.data[2] + s2.data[2], 75 | s1.data[3] + s2.data[3], s1.data[4] + s2.data[4], s1.data[5] + s2.data[5], 76 | s1.data[6] + s2.data[6], s1.data[7] + s2.data[7]}}; 77 | } 78 | // initial SHA256 79 | constexpr SHA256 init() { 80 | return {{0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 81 | 0x5be0cd19}}; 82 | } 83 | // schedule from an existing buffer 84 | template constexpr schedule init(T buf) { 85 | return {{word32be(buf), word32be(buf + 4), word32be(buf + 8), word32be(buf + 12), 86 | word32be(buf + 16), word32be(buf + 20), word32be(buf + 24), word32be(buf + 28), 87 | word32be(buf + 32), word32be(buf + 36), word32be(buf + 40), word32be(buf + 44), 88 | word32be(buf + 48), word32be(buf + 52), word32be(buf + 56), word32be(buf + 60)}}; 89 | } 90 | 91 | // computing leftovers is messy: we need to pad the empty space to a 92 | // multiple of 64 bytes. the first pad byte is 0x80, the rest are 0. 93 | // the original length (in bits) is the last 8 bytes of padding. 94 | constexpr uint32_t pad(int len) { 95 | return len == 3 ? 0x00000080 96 | : len == 2 ? 0x00008000 : len == 1 ? 0x00800000 : len == 0 ? 0x80000000 : 0; 97 | } 98 | constexpr uint32_t origlenbytes(int origlen, int origlenpos) { 99 | return origlenpos == -4 100 | ? uint32_t(static_cast(origlen) * 8 & 0xffffffff) 101 | : origlenpos == 0 ? uint32_t(static_cast(origlen) >> 29) : uint32_t(0); 102 | } 103 | template constexpr schedule leftover(T buf, int len, int origlen, int origlenpos) { 104 | return {{word32be(buf, len) | pad(len) | origlenbytes(origlen, origlenpos), 105 | word32be(len >= 4 ? buf + 4 : buf, len - 4) | pad(len - 4) 106 | | origlenbytes(origlen, origlenpos - 4), 107 | word32be(len >= 8 ? buf + 8 : buf, len - 8) | pad(len - 8) 108 | | origlenbytes(origlen, origlenpos - 8), 109 | word32be(len >= 12 ? buf + 12 : buf, len - 12) | pad(len - 12) 110 | | origlenbytes(origlen, origlenpos - 12), 111 | word32be(len >= 16 ? buf + 16 : buf, len - 16) | pad(len - 16) 112 | | origlenbytes(origlen, origlenpos - 16), 113 | word32be(len >= 20 ? buf + 20 : buf, len - 20) | pad(len - 20) 114 | | origlenbytes(origlen, origlenpos - 20), 115 | word32be(len >= 24 ? buf + 24 : buf, len - 24) | pad(len - 24) 116 | | origlenbytes(origlen, origlenpos - 24), 117 | word32be(len >= 28 ? buf + 28 : buf, len - 28) | pad(len - 28) 118 | | origlenbytes(origlen, origlenpos - 28), 119 | word32be(len >= 32 ? buf + 32 : buf, len - 32) | pad(len - 32) 120 | | origlenbytes(origlen, origlenpos - 32), 121 | word32be(len >= 36 ? buf + 36 : buf, len - 36) | pad(len - 36) 122 | | origlenbytes(origlen, origlenpos - 36), 123 | word32be(len >= 40 ? buf + 40 : buf, len - 40) | pad(len - 40) 124 | | origlenbytes(origlen, origlenpos - 40), 125 | word32be(len >= 44 ? buf + 44 : buf, len - 44) | pad(len - 44) 126 | | origlenbytes(origlen, origlenpos - 44), 127 | word32be(len >= 48 ? buf + 48 : buf, len - 48) | pad(len - 48) 128 | | origlenbytes(origlen, origlenpos - 48), 129 | word32be(len >= 52 ? buf + 52 : buf, len - 52) | pad(len - 52) 130 | | origlenbytes(origlen, origlenpos - 52), 131 | word32be(len >= 56 ? buf + 56 : buf, len - 56) | pad(len - 56) 132 | | origlenbytes(origlen, origlenpos - 56), 133 | word32be(len >= 60 ? buf + 60 : buf, len - 60) | pad(len - 60) 134 | | origlenbytes(origlen, origlenpos - 60)}}; 135 | } 136 | 137 | constexpr uint32_t rotateR(uint32_t x, int n) { return (x << (32 - n)) | (x >> n); } 138 | constexpr uint32_t s0(uint32_t x) { return rotateR(x, 7) ^ rotateR(x, 18) ^ (x >> 3); } 139 | constexpr uint32_t s1(uint32_t x) { return rotateR(x, 17) ^ rotateR(x, 19) ^ (x >> 10); } 140 | 141 | constexpr uint32_t extendvalue(const uint32_t* w, int i, int n) { 142 | return i < n ? w[i] 143 | : extendvalue(w, i - 16, n) + extendvalue(w, i - 7, n) 144 | + s0(extendvalue(w, i - 15, n)) + s1(extendvalue(w, i - 2, n)); 145 | } 146 | 147 | // extend the 16 words in the schedule to the whole 64 148 | // to avoid hitting the max step limit, we'll do this by 16s 149 | constexpr schedule sha256extend16(const schedule& s) { 150 | return {{s.w[0], 151 | s.w[1], 152 | s.w[2], 153 | s.w[3], 154 | s.w[4], 155 | s.w[5], 156 | s.w[6], 157 | s.w[7], 158 | s.w[8], 159 | s.w[9], 160 | s.w[10], 161 | s.w[11], 162 | s.w[12], 163 | s.w[13], 164 | s.w[14], 165 | s.w[15], 166 | extendvalue(s.w, 16, 16), 167 | extendvalue(s.w, 17, 16), 168 | extendvalue(s.w, 18, 16), 169 | extendvalue(s.w, 19, 16), 170 | extendvalue(s.w, 20, 16), 171 | extendvalue(s.w, 21, 16), 172 | extendvalue(s.w, 22, 16), 173 | extendvalue(s.w, 23, 16), 174 | extendvalue(s.w, 24, 16), 175 | extendvalue(s.w, 25, 16), 176 | extendvalue(s.w, 26, 16), 177 | extendvalue(s.w, 27, 16), 178 | extendvalue(s.w, 28, 16), 179 | extendvalue(s.w, 29, 16), 180 | extendvalue(s.w, 30, 16), 181 | extendvalue(s.w, 31, 16)}}; 182 | } 183 | constexpr schedule sha256extend32(const schedule& s) { 184 | return {{s.w[0], 185 | s.w[1], 186 | s.w[2], 187 | s.w[3], 188 | s.w[4], 189 | s.w[5], 190 | s.w[6], 191 | s.w[7], 192 | s.w[8], 193 | s.w[9], 194 | s.w[10], 195 | s.w[11], 196 | s.w[12], 197 | s.w[13], 198 | s.w[14], 199 | s.w[15], 200 | s.w[16], 201 | s.w[17], 202 | s.w[18], 203 | s.w[19], 204 | s.w[20], 205 | s.w[21], 206 | s.w[22], 207 | s.w[23], 208 | s.w[24], 209 | s.w[25], 210 | s.w[26], 211 | s.w[27], 212 | s.w[28], 213 | s.w[29], 214 | s.w[30], 215 | s.w[31], 216 | extendvalue(s.w, 32, 32), 217 | extendvalue(s.w, 33, 32), 218 | extendvalue(s.w, 34, 32), 219 | extendvalue(s.w, 35, 32), 220 | extendvalue(s.w, 36, 32), 221 | extendvalue(s.w, 37, 32), 222 | extendvalue(s.w, 38, 32), 223 | extendvalue(s.w, 39, 32), 224 | extendvalue(s.w, 40, 32), 225 | extendvalue(s.w, 41, 32), 226 | extendvalue(s.w, 42, 32), 227 | extendvalue(s.w, 43, 32), 228 | extendvalue(s.w, 44, 32), 229 | extendvalue(s.w, 45, 32), 230 | extendvalue(s.w, 46, 32), 231 | extendvalue(s.w, 47, 32)}}; 232 | } 233 | constexpr schedule sha256extend48(const schedule& s) { 234 | return {{s.w[0], 235 | s.w[1], 236 | s.w[2], 237 | s.w[3], 238 | s.w[4], 239 | s.w[5], 240 | s.w[6], 241 | s.w[7], 242 | s.w[8], 243 | s.w[9], 244 | s.w[10], 245 | s.w[11], 246 | s.w[12], 247 | s.w[13], 248 | s.w[14], 249 | s.w[15], 250 | s.w[16], 251 | s.w[17], 252 | s.w[18], 253 | s.w[19], 254 | s.w[20], 255 | s.w[21], 256 | s.w[22], 257 | s.w[23], 258 | s.w[24], 259 | s.w[25], 260 | s.w[26], 261 | s.w[27], 262 | s.w[28], 263 | s.w[29], 264 | s.w[30], 265 | s.w[31], 266 | s.w[32], 267 | s.w[33], 268 | s.w[34], 269 | s.w[35], 270 | s.w[36], 271 | s.w[37], 272 | s.w[38], 273 | s.w[39], 274 | s.w[40], 275 | s.w[41], 276 | s.w[42], 277 | s.w[43], 278 | s.w[44], 279 | s.w[45], 280 | s.w[46], 281 | s.w[47], 282 | extendvalue(s.w, 48, 48), 283 | extendvalue(s.w, 49, 48), 284 | extendvalue(s.w, 50, 48), 285 | extendvalue(s.w, 51, 48), 286 | extendvalue(s.w, 52, 48), 287 | extendvalue(s.w, 53, 48), 288 | extendvalue(s.w, 54, 48), 289 | extendvalue(s.w, 55, 48), 290 | extendvalue(s.w, 56, 48), 291 | extendvalue(s.w, 57, 48), 292 | extendvalue(s.w, 58, 48), 293 | extendvalue(s.w, 59, 48), 294 | extendvalue(s.w, 60, 48), 295 | extendvalue(s.w, 61, 48), 296 | extendvalue(s.w, 62, 48), 297 | extendvalue(s.w, 63, 48)}}; 298 | } 299 | constexpr schedule sha256extend(const schedule& s) { 300 | return sha256extend48(sha256extend32(sha256extend16(s))); 301 | } 302 | 303 | // the compression function, in 64 rounds 304 | constexpr uint32_t S1(uint32_t e) { return rotateR(e, 6) ^ rotateR(e, 11) ^ rotateR(e, 25); } 305 | constexpr uint32_t ch(uint32_t e, uint32_t f, uint32_t g) { return (e & f) ^ (~e & g); } 306 | constexpr uint32_t temp1(const SHA256& sum, int i) { 307 | return sum.data[7] + S1(sum.data[4]) + ch(sum.data[4], sum.data[5], sum.data[6]) 308 | + roundconst[i]; 309 | } 310 | constexpr uint32_t S0(uint32_t a) { return rotateR(a, 2) ^ rotateR(a, 13) ^ rotateR(a, 22); } 311 | constexpr uint32_t maj(uint32_t a, uint32_t b, uint32_t c) { 312 | return (a & b) ^ (a & c) ^ (b & c); 313 | } 314 | constexpr uint32_t temp2(const SHA256& sum) { 315 | return S0(sum.data[0]) + maj(sum.data[0], sum.data[1], sum.data[2]); 316 | } 317 | 318 | // rotate SHA256s right and left (each round step does this) 319 | constexpr SHA256 rotateCR(const SHA256& sum) { 320 | return {{sum.data[7], sum.data[0], sum.data[1], sum.data[2], sum.data[3], sum.data[4], 321 | sum.data[5], sum.data[6]}}; 322 | } 323 | constexpr SHA256 rotateCL(const SHA256& sum) { 324 | return {{sum.data[1], sum.data[2], sum.data[3], sum.data[4], sum.data[5], sum.data[6], 325 | sum.data[7], sum.data[0]}}; 326 | } 327 | 328 | constexpr SHA256 sha256round(const SHA256& sum, uint32_t t1, uint32_t t2) { 329 | return {{sum.data[0], sum.data[1], sum.data[2], sum.data[3] + t1, sum.data[4], sum.data[5], 330 | sum.data[6], t1 + t2}}; 331 | } 332 | constexpr SHA256 sha256compress(const SHA256& sum, const schedule& s, int step) { 333 | return step == 64 ? sum 334 | : rotateCL(sha256compress( 335 | rotateCR(sha256round(sum, temp1(sum, step) + s.w[step], temp2(sum))), 336 | s, step + 1)); 337 | } 338 | 339 | // the complete transform, for a message that is a multiple of 64 bytes 340 | constexpr SHA256 sha256transform(const SHA256& sum, const schedule& s) { 341 | return sumadd(sha256compress(sum, sha256extend(s), 0), sum); 342 | } 343 | 344 | // three conditions: 345 | // 1. as long as we have a 64-byte block to do, we'll recurse on that 346 | // 2. when we have 56 bytes or more, we need to do a whole empty block to 347 | // fit the 8 bytes of length after padding 348 | // 3. otherwise we have a block that will fit both padding and the length 349 | template 350 | constexpr SHA256 sha256update(const SHA256& sum, T msg, int len, int origlen) { 351 | return len >= 64 352 | ? sha256update(sha256transform(sum, init(msg)), msg + 64, len - 64, origlen) 353 | : len >= 56 ? sha256update(sha256transform(sum, leftover(msg, len, origlen, 64)), 354 | msg + len, -1, origlen) 355 | : sha256transform(sum, leftover(msg, len, origlen, 56)); 356 | } 357 | 358 | template constexpr SHA256 sha256withlen(T msg, int len) { 359 | return sha256update(init(), msg, len, len); 360 | } 361 | 362 | template constexpr SHA256 sha256(T msg) { 363 | if constexpr (std::is_pointer::value) { 364 | return sha256withlen(msg, strlen(msg)); 365 | } else { 366 | static_assert(sizeof(typename T::value_type) == sizeof(char)); 367 | return sha256withlen(msg.data(), int(msg.size())); 368 | } 369 | } 370 | 371 | // convert a SHA256 to little-endian 372 | constexpr SHA256::Data sha256tole(const SHA256& sum) { 373 | return { 374 | endianswap(sum.data[0]), endianswap(sum.data[1]), endianswap(sum.data[2]), 375 | endianswap(sum.data[3]), endianswap(sum.data[4]), endianswap(sum.data[5]), 376 | endianswap(sum.data[6]), endianswap(sum.data[7]), 377 | }; 378 | } 379 | } // namespace sha256 380 | } // namespace detail 381 | 382 | template constexpr SHA256 sha256(T s) { return detail::sha256::sha256(s); } 383 | 384 | constexpr SHA256::Data SHA256::asLittleEndian() const { 385 | return detail::sha256::sha256tole(*this); 386 | } 387 | 388 | } // namespace static_hash 389 | -------------------------------------------------------------------------------- /include/static_hash/util/array_conversion.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace static_hash { 7 | 8 | template 9 | constexpr auto convertBinaryArrayType(std::array input) { 10 | static_assert(std::is_unsigned::value); 11 | static_assert(std::is_unsigned::value); 12 | if constexpr (sizeof(T) > sizeof(C)) { 13 | constexpr auto ratio = sizeof(T) / sizeof(C); 14 | constexpr auto padding = input.size() % ratio == 0 ? 0 : 1; 15 | static_assert(ALLOW_PADDING || (padding == 0)); 16 | std::array arr{}; 17 | for (size_t i = 0; i < arr.size(); ++i) { 18 | arr[i] = 0; 19 | for (size_t j = 0; j < ratio; ++j) { 20 | auto idx = i * ratio + j; 21 | auto offset = (ratio - j - 1) * sizeof(C) * CHAR_BIT; 22 | auto v = idx < input.size() ? input[idx] : T(0); 23 | arr[i] |= T(v << offset); 24 | } 25 | } 26 | return arr; 27 | } else { 28 | constexpr auto ratio = sizeof(C) / sizeof(T); 29 | std::array arr{}; 30 | for (size_t i = 0; i < arr.size(); ++i) { 31 | auto j = i % ratio; 32 | arr[i] = T(C(input[i / ratio]) >> ((ratio - j - 1) * sizeof(T) * CHAR_BIT)); 33 | } 34 | return arr; 35 | } 36 | } 37 | 38 | } // namespace static_hash -------------------------------------------------------------------------------- /include/static_hash/util/string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | //---------------------------------------------------------------------------- 6 | // constexpr utils 7 | 8 | namespace static_hash { 9 | // computing strlen with naive recursion will likely exceed the max recursion 10 | // depth on long strings, so compute in a way that limits the recursion depth 11 | // (a really long string will still have problems, but that's unavoidable: we 12 | // have to use recursion in C++11 after all) 13 | 14 | namespace detail_s { 15 | struct str { 16 | const char* s; 17 | int len; 18 | }; 19 | constexpr str stradd(const str& a, const str& b) { return {b.s, a.len + b.len}; } 20 | constexpr str strlen(const str p, int maxdepth) { 21 | return (*p.s == 0 || maxdepth == 0) ? p : strlen({p.s + 1, p.len + 1}, maxdepth - 1); 22 | } 23 | constexpr str strlen_bychunk(const str p, int maxdepth) { 24 | return *p.s == 0 ? p 25 | : strlen_bychunk(stradd({0, p.len}, strlen({p.s, 0}, maxdepth)), maxdepth); 26 | } 27 | } // namespace detail_s 28 | // max recursion = 256 (strlen, especially of a long string, often happens at 29 | // the beginning of an algorithm, so that should be fine) 30 | constexpr int strlen(const char* s) { 31 | return detail_s::strlen_bychunk(detail_s::strlen({s, 0}, 256), 256).len; 32 | } 33 | 34 | constexpr int strcmp(const char* a, const char* b) { 35 | return *a == 0 && *b == 0 36 | ? 0 37 | : *a == 0 38 | ? -1 39 | : *b == 0 ? 1 40 | : *a < *b ? -1 41 | : *a > *b ? 1 42 | : *a == *b ? strcmp(a + 1, b + 1) 43 | : throw "err::strcmp_runtime_error"; 44 | } 45 | constexpr int strless(const char* a, const char* b) { return strcmp(a, b) == -1; } 46 | 47 | // convert char* buffer (fragment) to uint32_t (little-endian) 48 | constexpr uint32_t word32le(const char* s, int len) { 49 | return (len > 0 ? static_cast(s[0]) : 0) 50 | + (len > 1 ? (static_cast(s[1]) << 8) : 0) 51 | + (len > 2 ? (static_cast(s[2]) << 16) : 0) 52 | + (len > 3 ? (static_cast(s[3]) << 24) : 0); 53 | } 54 | // convert char* buffer (complete) to uint32_t (little-endian) 55 | template constexpr uint32_t word32le(T s) { return word32le(s, 4); } 56 | 57 | // convert char* buffer (fragment) to uint32_t (big-endian) 58 | template constexpr uint32_t word32be(T s, int len) { 59 | return (len > 0 ? (static_cast(s[0]) << 24) : 0) 60 | + (len > 1 ? (static_cast(s[1]) << 16) : 0) 61 | + (len > 2 ? (static_cast(s[2]) << 8) : 0) 62 | + (len > 3 ? static_cast(s[3]) : 0); 63 | } 64 | // convert char* buffer (complete) to uint32_t (big-endian) 65 | template constexpr uint32_t word32be(T s) { return word32be(s, 4); } 66 | 67 | // swap endianness of various size integral types 68 | constexpr uint64_t endianswap(uint64_t x) { 69 | return ((x & 0xff) << 56) | (((x >> 8) & 0xff) << 48) | (((x >> 16) & 0xff) << 40) 70 | | (((x >> 24) & 0xff) << 32) | (((x >> 32) & 0xff) << 24) | (((x >> 40) & 0xff) << 16) 71 | | (((x >> 48) & 0xff) << 8) | ((x >> 56) & 0xff); 72 | } 73 | constexpr uint32_t endianswap(uint32_t x) { 74 | return ((x & 0xff) << 24) | (((x >> 8) & 0xff) << 16) | (((x >> 16) & 0xff) << 8) 75 | | ((x >> 24) & 0xff); 76 | } 77 | constexpr uint16_t endianswap(uint16_t x) { 78 | return uint16_t((x & 0xff) << 8) | uint16_t((x >> 8) & 0xff); 79 | } 80 | } // namespace static_hash 81 | -------------------------------------------------------------------------------- /source/dummy.cpp: -------------------------------------------------------------------------------- 1 | // dummy cpp file to allow creating a standard cmake library -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5 FATAL_ERROR) 2 | 3 | project(StaticHashTests 4 | LANGUAGES CXX 5 | ) 6 | 7 | # ---- Options ---- 8 | 9 | option(ENABLE_TEST_COVERAGE "Enable test coverage" OFF) 10 | option(TEST_INSTALLED_VERSION "Test the version found by find_package" OFF) 11 | 12 | # --- Import tools ---- 13 | 14 | include(../cmake/tools.cmake) 15 | 16 | # ---- Dependencies ---- 17 | 18 | include(../cmake/CPM.cmake) 19 | 20 | CPMAddPackage( 21 | NAME doctest 22 | GITHUB_REPOSITORY onqtam/doctest 23 | GIT_TAG 2.3.7 24 | ) 25 | 26 | if (TEST_INSTALLED_VERSION) 27 | find_package(StaticHash REQUIRED) 28 | else() 29 | CPMAddPackage( 30 | NAME StaticHash 31 | SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/.. 32 | ) 33 | endif() 34 | 35 | CPMAddPackage( 36 | NAME Format.cmake 37 | GITHUB_REPOSITORY TheLartians/Format.cmake 38 | VERSION 1.3 39 | ) 40 | 41 | # ---- Create binary ---- 42 | 43 | file(GLOB sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp) 44 | add_executable(StaticHashTests ${sources}) 45 | target_link_libraries(StaticHashTests doctest StaticHash) 46 | 47 | set_target_properties(StaticHashTests PROPERTIES CXX_STANDARD 17) 48 | 49 | # enable compiler warnings 50 | if (NOT TEST_INSTALLED_VERSION) 51 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") 52 | target_compile_options(StaticHash PUBLIC -Wall -pedantic -Wextra -Werror) 53 | elseif(MSVC) 54 | target_compile_options(StaticHash PUBLIC /W4 /WX) 55 | target_compile_definitions(StaticHashTests PUBLIC DOCTEST_CONFIG_USE_STD_HEADERS) 56 | endif() 57 | endif() 58 | 59 | # ---- Add StaticHashTests ---- 60 | 61 | ENABLE_TESTING() 62 | 63 | include(${doctest_SOURCE_DIR}/scripts/cmake/doctest.cmake) 64 | doctest_discover_tests(StaticHashTests) 65 | 66 | # ---- code coverage ---- 67 | 68 | if (ENABLE_TEST_COVERAGE) 69 | target_compile_options(StaticHash PUBLIC -O0 -g -fprofile-arcs -ftest-coverage) 70 | target_link_options(StaticHash PUBLIC -fprofile-arcs -ftest-coverage) 71 | endif() 72 | -------------------------------------------------------------------------------- /test/source/array_conversion.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | template auto checkArrayConversion() { 5 | constexpr auto a = std::array( 6 | {{0, 1, 2, 3, std::numeric_limits::max(), std::numeric_limits::max() - 1, 7 | std::numeric_limits::max() - 2, std::numeric_limits::max() - 3}}); 8 | constexpr auto converted = static_hash::convertBinaryArrayType(a); 9 | constexpr auto back = static_hash::convertBinaryArrayType(converted); 10 | CHECK(a == back); 11 | } 12 | 13 | TEST_CASE("SHA256") { 14 | checkArrayConversion(); 15 | checkArrayConversion(); 16 | checkArrayConversion(); 17 | checkArrayConversion(); 18 | } 19 | -------------------------------------------------------------------------------- /test/source/main.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | 3 | #include 4 | -------------------------------------------------------------------------------- /test/source/sha256.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | 7 | inline std::string string_to_hex(std::string_view input) { 8 | static const char hex_digits[] = "0123456789abcdef"; 9 | 10 | std::string output; 11 | output.reserve(input.length() * 2); 12 | for (auto v : input) { 13 | auto c = (unsigned char)(v); 14 | output.push_back(hex_digits[c >> 4]); 15 | output.push_back(hex_digits[c & 15]); 16 | } 17 | return output; 18 | } 19 | 20 | inline std::string hash_to_string(const static_hash::SHA256 &hash) { 21 | auto le = hash.asLittleEndian(); 22 | return string_to_hex(std::string_view(reinterpret_cast(le.data()), 23 | le.size() * sizeof(decltype(le)::value_type))); 24 | } 25 | 26 | template inline constexpr auto toArray(std::string_view input) { 27 | std::array inputVector; 28 | for (size_t i = 0; i < N; ++i) inputVector[i] = (unsigned char)(input[i]); 29 | return input; 30 | } 31 | 32 | TEST_CASE("SHA256 consistency") { 33 | using namespace static_hash; 34 | constexpr std::string_view input = "Hello, World!"; 35 | std::string reference = "dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f"; 36 | 37 | SUBCASE("from string_view") { 38 | constexpr auto hash = static_hash::sha256(std::string_view(input)); 39 | CHECK(hash_to_string(hash) == reference); 40 | } 41 | 42 | SUBCASE("from string") { 43 | auto hash = static_hash::sha256(std::string(input)); 44 | CHECK(hash_to_string(hash) == reference); 45 | } 46 | 47 | SUBCASE("from vector") { 48 | std::vector inputVector(input.begin(), input.end()); 49 | auto hash = static_hash::sha256(inputVector); 50 | CHECK(hash_to_string(hash) == reference); 51 | } 52 | 53 | SUBCASE("from array") { 54 | auto inputArray = toArray("Hello, World!"); 55 | auto hash = static_hash::sha256(inputArray); 56 | CHECK(hash_to_string(hash) == reference); 57 | } 58 | } 59 | 60 | TEST_CASE("SHA256 constexpr construction and comparison") { 61 | constexpr auto hash1 = static_hash::sha256(std::array{1, 2, 3, 4, 5}); 62 | constexpr auto hash2 = static_hash::sha256(std::array{1, 3, 2, 4, 5}); 63 | static_assert(hash1 == hash1); 64 | static_assert(hash2 == hash2); 65 | static_assert(hash1 != hash2); 66 | } 67 | --------------------------------------------------------------------------------