├── .clang-format ├── .gitignore ├── CHANGES.md ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUES.md ├── LICENSE ├── NOTICE ├── README.md ├── SECURITY.md ├── cmake ├── ExternalGTest.cmake └── KukuConfig.cmake.in ├── config └── packages.config ├── dotnet ├── KukuNet.sln ├── examples │ ├── KukuNetExamples.csproj.in │ └── Program.cs ├── nuget │ ├── KukuNet-multi.nuspec.in │ ├── KukuNet.nuspec.in │ ├── KukuNet.targets │ └── NUGET.md ├── src │ ├── Common.cs │ ├── Item.cs │ ├── KukuNet.csproj.in │ ├── KukuTable.cs │ ├── KukuTableParameters.cs │ ├── NativeMethods.cs │ └── QueryResult.cs └── tests │ ├── Item.cs │ ├── KukuNetTests.csproj.in │ ├── KukuTable.cs │ ├── Properties │ └── launchSettings.json │ └── Utilities.cs ├── examples ├── CMakeLists.txt └── example.cpp ├── pipelines ├── jobs.yml ├── nix.yml ├── nuget.yml ├── pipeline-CI-Debug-Linux.yml ├── pipeline-CI-Debug-Windows.yml ├── pipeline-CI-Debug-macOS.yml ├── pipeline-CI-Release-All.yml ├── pipeline-Nuget.yml ├── pipeline-PR-Debug-All.yml └── windows.yml ├── scripts └── clang-format-all.sh ├── src └── kuku │ ├── CMakeLists.txt │ ├── c │ ├── CMakeLists.txt │ ├── framework.h │ ├── kuku_ref.cpp │ ├── kuku_ref.h │ ├── pch.cpp │ └── pch.h │ ├── common.h │ ├── internal │ ├── blake2-impl.h │ ├── blake2.h │ ├── blake2b.c │ ├── blake2xb.c │ ├── cgmanifest.json │ ├── config.h.in │ └── hash.h │ ├── kuku.cpp │ ├── kuku.h │ └── locfunc.h └── tests └── kuku ├── CMakeLists.txt ├── common.cpp ├── kuku.cpp ├── locfunc.cpp └── testrunner.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Microsoft 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: AlwaysBreak 6 | AlignConsecutiveMacros: false 7 | AlignConsecutiveAssignments: false 8 | AlignConsecutiveDeclarations: false 9 | AlignEscapedNewlines: Left 10 | AlignOperands: true 11 | AlignTrailingComments: true 12 | AllowAllArgumentsOnNextLine: true # \todo rethink 13 | AllowAllConstructorInitializersOnNextLine: true # \todo rethink 14 | AllowAllParametersOfDeclarationOnNextLine: true # \todo rethink 15 | AllowShortBlocksOnASingleLine: false 16 | AllowShortCaseLabelsOnASingleLine: false 17 | AllowShortFunctionsOnASingleLine: None 18 | AllowShortLambdasOnASingleLine: Inline 19 | AllowShortIfStatementsOnASingleLine: Never 20 | AllowShortLoopsOnASingleLine: false 21 | AlwaysBreakAfterDefinitionReturnType: None 22 | AlwaysBreakAfterReturnType: None 23 | AlwaysBreakBeforeMultilineStrings: false 24 | AlwaysBreakTemplateDeclarations: Yes 25 | BinPackArguments: true 26 | BinPackParameters: true 27 | BraceWrapping: 28 | AfterCaseLabel: true 29 | AfterClass: true 30 | AfterControlStatement: true 31 | AfterEnum: true 32 | AfterFunction: true 33 | AfterNamespace: true 34 | AfterObjCDeclaration: true 35 | AfterStruct: true 36 | AfterUnion: true 37 | AfterExternBlock: true 38 | BeforeCatch: true 39 | BeforeElse: true 40 | IndentBraces: false 41 | SplitEmptyFunction: false 42 | SplitEmptyRecord: false 43 | SplitEmptyNamespace: false 44 | BreakBeforeBinaryOperators: None 45 | BreakBeforeBraces: Custom # Allman 46 | BreakBeforeInheritanceComma: false 47 | BreakInheritanceList: BeforeColon 48 | BreakBeforeTernaryOperators: true 49 | BreakConstructorInitializersBeforeComma: false 50 | BreakConstructorInitializers: BeforeColon 51 | BreakAfterJavaFieldAnnotations: false 52 | BreakStringLiterals: true 53 | ColumnLimit: 120 54 | CommentPragmas: '^ IWYU pragma:' 55 | CompactNamespaces: false 56 | ConstructorInitializerAllOnOneLineOrOnePerLine: false # \todo rethink 57 | ConstructorInitializerIndentWidth: 4 58 | ContinuationIndentWidth: 4 59 | Cpp11BracedListStyle: false 60 | DerivePointerAlignment: false 61 | DisableFormat: false 62 | ExperimentalAutoDetectBinPacking: false 63 | FixNamespaceComments: true 64 | ForEachMacros: 65 | - foreach 66 | - Q_FOREACH 67 | - BOOST_FOREACH 68 | IncludeBlocks: Merge 69 | IncludeCategories: 70 | - Regex: '<.*>' 71 | Priority: 1 72 | - Regex: '"kuku/.*"' 73 | Priority: -3 74 | IncludeIsMainRegex: '(Test)?$' 75 | IndentCaseLabels: false 76 | IndentPPDirectives: None 77 | IndentWidth: 4 78 | IndentWrappedFunctionNames: false # \todo rethink 79 | JavaScriptQuotes: Leave 80 | JavaScriptWrapImports: true 81 | KeepEmptyLinesAtTheStartOfBlocks: false 82 | MacroBlockBegin: '' 83 | MacroBlockEnd: '' 84 | MaxEmptyLinesToKeep: 1 85 | NamespaceIndentation: All 86 | ObjCBinPackProtocolList: Auto 87 | ObjCBlockIndentWidth: 2 88 | ObjCSpaceAfterProperty: false 89 | ObjCSpaceBeforeProtocolList: true 90 | PenaltyBreakAssignment: 2 91 | PenaltyBreakBeforeFirstCallParameter: 19 92 | PenaltyBreakComment: 300 93 | PenaltyBreakFirstLessLess: 120 94 | PenaltyBreakString: 1000 95 | PenaltyBreakTemplateDeclaration: 10 96 | PenaltyExcessCharacter: 1000000 97 | PenaltyReturnTypeOnItsOwnLine: 1000 98 | PointerAlignment: Right 99 | ReflowComments: true 100 | SortIncludes: true 101 | SortUsingDeclarations: true 102 | SpaceAfterCStyleCast: false 103 | SpaceAfterLogicalNot: false 104 | SpaceAfterTemplateKeyword: true 105 | SpaceBeforeAssignmentOperators: true 106 | SpaceBeforeCpp11BracedList: false 107 | SpaceBeforeCtorInitializerColon: true 108 | SpaceBeforeInheritanceColon: true 109 | SpaceBeforeParens: ControlStatements 110 | SpaceBeforeRangeBasedForLoopColon: true 111 | SpaceInEmptyParentheses: false 112 | SpacesBeforeTrailingComments: 1 113 | SpacesInAngles: false 114 | SpacesInContainerLiterals: true 115 | SpacesInCStyleCastParentheses: false 116 | SpacesInParentheses: false 117 | SpacesInSquareBrackets: false 118 | Standard: Auto 119 | StatementMacros: 120 | - Q_UNUSED 121 | - QT_REQUIRE_VERSION 122 | TabWidth: 4 123 | UseTab: Never 124 | ... 125 | 126 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | *.user 3 | */x64/ 4 | x64/ 5 | src/obj/ 6 | bin/ 7 | lib/ 8 | packages/ 9 | cmake/ 10 | build/ 11 | thirdparty/ 12 | !cmake/KukuConfig.cmake.in 13 | !cmake/ExternalGTest.cmake 14 | src/kuku/internal/config.h 15 | **/compile_commands.json 16 | **/install_manifest.txt 17 | **/CMakeCache.txt 18 | **/CMakeFiles/ 19 | **/Makefile 20 | **/cmake_install.cmake 21 | */obj/* 22 | *.exe 23 | dotnet/tests/obj/* 24 | dotnet/src/obj 25 | dotnet/src/Properties/launchSettings.json 26 | dotnet/examples/obj 27 | dotnet/examples/Properties/launchSettings.json 28 | .vscode/settings.json 29 | thirdparty/googletest/ 30 | *.nupkg 31 | !dotnet/nuget/*.nuspec.in 32 | dotnet/nuget/*.nuspec 33 | !dotnet/src/KukuNet.csproj.in 34 | dotnet/src/KukuNet.csproj 35 | !dotnet/tests/KukuNetTests.csproj.in 36 | dotnet/tests/KukuNetTests.csproj 37 | !dotnet/examples/KukuNetExamples.csproj.in 38 | dotnet/examples/KukuNetExamples.csproj 39 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # List of Changes 2 | 3 | ## Version 2.1 4 | 5 | ### 2.1.0 6 | 7 | - Improved CMake build system. 8 | - Added support for using only a single hash function (not really cuckoo hashing). 9 | - In the C++ library `item_type` is now an array of `unsigned char`. 10 | 11 | ## Version 2.0 12 | 13 | ### 2.0.0 14 | 15 | - Switched to using faster non-cryptographic hash functions and a faster insertion method. 16 | - Created .NET Standard wrappers for the public API. 17 | - Kuku is not a CMake project (with a top-level CMake file). 18 | - Kuku now allows any size hash table -- not just a power of two. 19 | Note the change in the constructor of the `KukuTable` class that now accepts the table size directly, and not its base-2 logarithm. 20 | - Changed name `KukuDotNet` to `KukuNet`. 21 | - Added NuGet package. 22 | 23 | ## Version 1.1 24 | 25 | ### 1.1.1 26 | 27 | - Added `table_size_type` to use instead of `std::size_t` to be compatible with `location_type`. 28 | 29 | ### 1.1.0 30 | 31 | - Enabled interoperability between 32-bit and 64-bit versions by limiting location size to 32 bits. 32 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. 3 | 4 | cmake_minimum_required(VERSION 3.12) 5 | 6 | ################################################### 7 | # Project Kuku includes the following components: # 8 | # 1. Kuku C++ library # 9 | # 2. Kuku C export library # 10 | # 3. Kuku C++ examples # 11 | # 4. Kuku C++ tests # 12 | ################################################### 13 | 14 | # [option] CMAKE_BUILD_TYPE (default: "Release") 15 | # Build in one of the following modes: Release, Debug, MiniSizeRel, or RelWithDebInfo. 16 | # Most generators recognize these and can set the compiler flags accordingly. We set 17 | # the build type here before creating the project to prevent the CMake generator from 18 | # overriding our default of "Release". 19 | if(NOT CMAKE_BUILD_TYPE) 20 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE) 21 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 22 | "Release" "Debug" "MinSizeRel" "RelWithDebInfo") 23 | endif() 24 | message(STATUS "Build type (CMAKE_BUILD_TYPE): ${CMAKE_BUILD_TYPE}") 25 | 26 | project(Kuku VERSION 2.1.0 LANGUAGES CXX C) 27 | 28 | ######################## 29 | # Global configuration # 30 | ######################## 31 | 32 | include(CMakePushCheckState) 33 | include(CMakeDependentOption) 34 | include(CheckIncludeFiles) 35 | include(CheckCXXSourceRuns) 36 | include(CheckTypeSize) 37 | include(FetchContent) 38 | 39 | mark_as_advanced(FETCHCONTENT_BASE_DIR) 40 | mark_as_advanced(FETCHCONTENT_FULLY_DISCONNECTED) 41 | mark_as_advanced(FETCHCONTENT_UPDATES_DISCONNECTED) 42 | mark_as_advanced(FETCHCONTENT_QUIET) 43 | 44 | # Extra modules 45 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) 46 | 47 | # Always build position-independent-code 48 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 49 | 50 | # Make the install target depend on the all target (required by vcpkg) 51 | set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY OFF) 52 | 53 | # In Debug mode, enable KUKU_DEBUG 54 | if(${CMAKE_BUILD_TYPE} STREQUAL "Debug") 55 | set(KUKU_DEBUG ON) 56 | else() 57 | set(KUKU_DEBUG OFF) 58 | endif() 59 | message(STATUS "Kuku debug mode: ${KUKU_DEBUG}") 60 | 61 | # Enable security-related compile options (MSVC only) 62 | set(KUKU_SECURE_COMPILE_OPTIONS_OPTIONS_STR "Enable Control Flow Guard and Spectre mitigations (MSVC only)") 63 | option(KUKU_SECURE_COMPILE_OPTIONS ${KUKU_SECURE_COMPILE_OPTIONS_OPTIONS_STR} OFF) 64 | mark_as_advanced(KUKU_SECURE_COMPILE_OPTIONS) 65 | 66 | # Required files and directories 67 | include(GNUInstallDirs) 68 | 69 | # Source Tree 70 | set(KUKU_INCLUDES_DIR ${CMAKE_CURRENT_LIST_DIR}/src) 71 | set(KUKU_THIRDPARTY_DIR ${CMAKE_CURRENT_LIST_DIR}/thirdparty) 72 | set(KUKU_CONFIG_IN_FILENAME ${CMAKE_CURRENT_LIST_DIR}/cmake/KukuConfig.cmake.in) 73 | set(KUKU_CONFIG_H_IN_FILENAME ${KUKU_INCLUDES_DIR}/kuku/internal/config.h.in) 74 | 75 | # Build tree 76 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) 77 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) 78 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) 79 | set(KUKU_TARGETS_FILENAME ${CMAKE_CURRENT_BINARY_DIR}/cmake/KukuTargets.cmake) 80 | set(KUKU_CONFIG_FILENAME ${CMAKE_CURRENT_BINARY_DIR}/cmake/KukuConfig.cmake) 81 | set(KUKU_CONFIG_VERSION_FILENAME ${CMAKE_CURRENT_BINARY_DIR}/cmake/KukuConfigVersion.cmake) 82 | set(KUKU_CONFIG_H_FILENAME ${CMAKE_CURRENT_BINARY_DIR}/src/kuku/internal/config.h) 83 | 84 | # Install 85 | set(KUKU_CONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/Kuku-${Kuku_VERSION_MAJOR}.${Kuku_VERSION_MINOR}) 86 | set(KUKU_INCLUDES_INSTALL_DIR ${CMAKE_INSTALL_INCLUDEDIR}/Kuku-${Kuku_VERSION_MAJOR}.${Kuku_VERSION_MINOR}) 87 | 88 | ################## 89 | # Various macros # 90 | ################## 91 | 92 | # Set the C++ language version 93 | macro(kuku_set_language target) 94 | target_compile_features(${target} PUBLIC cxx_std_14) 95 | endmacro() 96 | 97 | # Set the VERSION property 98 | macro(kuku_set_version target) 99 | set_target_properties(${target} PROPERTIES VERSION ${Kuku_VERSION}) 100 | endmacro() 101 | 102 | # Set the library filename to reflect version 103 | macro(kuku_set_version_filename target) 104 | set_target_properties(${target} PROPERTIES 105 | OUTPUT_NAME ${target}-${Kuku_VERSION_MAJOR}.${Kuku_VERSION_MINOR}) 106 | endmacro() 107 | 108 | # Set the SOVERSION property 109 | macro(kuku_set_soversion target) 110 | set_target_properties(${target} PROPERTIES 111 | SOVERSION ${Kuku_VERSION_MAJOR}.${Kuku_VERSION_MINOR}) 112 | endmacro() 113 | 114 | # Set include directories for build and install interfaces 115 | macro(kuku_set_include_directories target) 116 | target_include_directories(${target} PUBLIC 117 | $ 118 | $) 119 | endmacro() 120 | 121 | # Set include directories for build and install interfaces 122 | macro(kuku_set_include_directories target) 123 | target_include_directories(${target} PUBLIC 124 | $ 125 | $) 126 | target_include_directories(${target} PUBLIC 127 | $ 128 | $) 129 | endmacro() 130 | 131 | # Include target to given export 132 | macro(kuku_install_target target export) 133 | install(TARGETS ${target} EXPORT ${export} 134 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 135 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 136 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 137 | endmacro() 138 | 139 | # Add secure compile options 140 | macro(kuku_set_secure_compile_options target scope) 141 | if(MSVC) 142 | # Build debug symbols for static analysis tools 143 | target_link_options(${target} ${scope} /DEBUG) 144 | 145 | # Control Flow Guard / Spectre 146 | target_compile_options(${target} ${scope} /guard:cf) 147 | target_compile_options(${target} ${scope} /Qspectre) 148 | target_link_options(${target} ${scope} /guard:cf) 149 | target_link_options(${target} ${scope} /DYNAMICBASE) 150 | endif() 151 | endmacro() 152 | 153 | # Include a file to fetch thirdparty content 154 | macro(kuku_fetch_thirdparty_content content_file) 155 | set(KUKU_FETCHCONTENT_BASE_DIR_OLD ${FETCHCONTENT_BASE_DIR}) 156 | set(FETCHCONTENT_BASE_DIR ${KUKU_THIRDPARTY_DIR} CACHE STRING "" FORCE) 157 | include(${content_file}) 158 | set(FETCHCONTENT_BASE_DIR ${KUKU_FETCHCONTENT_BASE_DIR_OLD} CACHE STRING "" FORCE) 159 | unset(KUKU_FETCHCONTENT_BASE_DIR_OLD) 160 | endmacro() 161 | 162 | #################### 163 | # Kuku C++ library # 164 | #################### 165 | 166 | # Should we build also the shared library? 167 | set(BUILD_SHARED_LIBS_STR "Build shared library") 168 | option(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_STR} OFF) 169 | if(WIN32 AND BUILD_SHARED_LIBS) 170 | message(FATAL_ERROR "On Windows only static build is supported; set `BUILD_SHARED_LIBS=OFF`") 171 | endif() 172 | 173 | # Add source files to library and header files to install 174 | set(KUKU_SOURCE_FILES "") 175 | add_subdirectory(src/kuku) 176 | 177 | # Create the config file 178 | configure_file(${KUKU_CONFIG_H_IN_FILENAME} ${KUKU_CONFIG_H_FILENAME}) 179 | install( 180 | FILES ${KUKU_CONFIG_H_FILENAME} 181 | DESTINATION ${KUKU_INCLUDES_INSTALL_DIR}/kuku/internal) 182 | 183 | set(KUKU_LIBRARY_NAME "") 184 | if(NOT BUILD_SHARED_LIBS) 185 | add_library(kuku STATIC ${KUKU_SOURCE_FILES}) 186 | kuku_set_version_filename(kuku) 187 | kuku_set_language(kuku) 188 | kuku_set_include_directories(kuku) 189 | kuku_set_version(kuku) 190 | kuku_install_target(kuku KukuTargets) 191 | set(KUKU_LIBRARY_NAME "kuku") 192 | 193 | # Set secure compile options if KUKU_SECURE_COMPILE_OPTIONS is ON; only supported on MSVC 194 | if(KUKU_SECURE_COMPILE_OPTIONS) 195 | kuku_set_secure_compile_options(kuku PUBLIC) 196 | endif() 197 | 198 | # Build only a shared library 199 | else() 200 | add_library(kuku_shared SHARED ${KUKU_SOURCE_FILES}) 201 | kuku_set_soversion(kuku_shared) 202 | set_target_properties(kuku_shared PROPERTIES OUTPUT_NAME kuku) 203 | kuku_set_language(kuku_shared) 204 | kuku_set_include_directories(kuku_shared) 205 | kuku_set_version(kuku_shared) 206 | kuku_install_target(kuku_shared KukuTargets) 207 | set(KUKU_LIBRARY_NAME "kuku_shared") 208 | endif() 209 | 210 | # Add standard alias targets for Kuku::kuku and Kuku::kuku_shared 211 | if(TARGET kuku) 212 | add_library(Kuku::kuku ALIAS kuku) 213 | endif() 214 | if(TARGET kuku_shared) 215 | add_library(Kuku::kuku_shared ALIAS kuku_shared) 216 | endif() 217 | 218 | ######################### 219 | # Kuku C export library # 220 | ######################### 221 | 222 | set(KUKU_BUILD_KUKU_C_OPTION_STR "Build C export library for Kuku") 223 | option(KUKU_BUILD_KUKU_C ${KUKU_BUILD_KUKU_C_OPTION_STR} OFF) 224 | 225 | # Create shared Kuku_C library but add no source files yet 226 | if(KUKU_BUILD_KUKU_C) 227 | # Check that size_t is 8 bytes 228 | include(CheckTypeSize) 229 | check_type_size("size_t" SIZET LANGUAGE C) 230 | if(NOT ${SIZET} EQUAL 8) 231 | unset(SIZET CACHE) 232 | unset(HAVE_SIZET CACHE) 233 | message(FATAL_ERROR "Kuku_C requires 64-bit platform") 234 | endif() 235 | unset(SIZET CACHE) 236 | unset(HAVE_SIZET CACHE) 237 | 238 | if(BUILD_SHARED_LIBS) 239 | message(FATAL_ERROR "Kuku_C requires `BUILD_SHARED_LIBS=OFF`") 240 | endif() 241 | 242 | add_library(kukuc SHARED) 243 | 244 | # Add source files to library and header files to install 245 | add_subdirectory(src/kuku/c) 246 | kuku_set_version(kukuc) 247 | kuku_set_soversion(kukuc) 248 | kuku_set_language(kukuc) 249 | kuku_set_include_directories(kukuc) 250 | 251 | target_link_libraries(kukuc PUBLIC ${KUKU_LIBRARY_NAME}) 252 | 253 | kuku_install_target(kukuc KukuTargets) 254 | endif() 255 | 256 | # Add standard alias target for Kuku::kukuc 257 | if(TARGET kukuc) 258 | add_library(Kuku::kukuc ALIAS kukuc) 259 | endif() 260 | 261 | ################################# 262 | # Installation and CMake config # 263 | ################################# 264 | 265 | # Create the CMake config file 266 | include(CMakePackageConfigHelpers) 267 | configure_package_config_file( 268 | ${KUKU_CONFIG_IN_FILENAME} ${KUKU_CONFIG_FILENAME} 269 | INSTALL_DESTINATION ${KUKU_CONFIG_INSTALL_DIR}) 270 | 271 | # Install the export 272 | install( 273 | EXPORT KukuTargets 274 | NAMESPACE Kuku:: 275 | DESTINATION ${KUKU_CONFIG_INSTALL_DIR}) 276 | 277 | # Version file; we require exact version match for downstream 278 | write_basic_package_version_file( 279 | ${KUKU_CONFIG_VERSION_FILENAME} 280 | VERSION ${Kuku_VERSION} 281 | COMPATIBILITY SameMinorVersion) 282 | 283 | # Install config and version files 284 | install( 285 | FILES 286 | ${KUKU_CONFIG_FILENAME} 287 | ${KUKU_CONFIG_VERSION_FILENAME} 288 | DESTINATION ${KUKU_CONFIG_INSTALL_DIR}) 289 | 290 | # We export KukuTargets from the build tree so it can be used by other projects 291 | # without requiring an install. 292 | export( 293 | EXPORT KukuTargets 294 | NAMESPACE Kuku:: 295 | FILE ${KUKU_TARGETS_FILENAME}) 296 | 297 | ##################### 298 | # Kuku C++ examples # 299 | ##################### 300 | 301 | set(KUKU_BUILD_EXAMPLES_OPTION_STR "Build C++ examples for Kuku") 302 | option(KUKU_BUILD_EXAMPLES ${KUKU_BUILD_EXAMPLES_OPTION_STR} OFF) 303 | 304 | if(KUKU_BUILD_EXAMPLES) 305 | add_executable(kukuexamples) 306 | add_subdirectory(examples) 307 | target_link_libraries(kukuexamples PRIVATE ${KUKU_LIBRARY_NAME}) 308 | endif() 309 | 310 | ################## 311 | # Kuku C++ tests # 312 | ################## 313 | 314 | set(KUKU_BUILD_TESTS_OPTION_STR "Build C++ tests for Kuku") 315 | option(KUKU_BUILD_TESTS ${KUKU_BUILD_TESTS_OPTION_STR} OFF) 316 | 317 | if(KUKU_BUILD_TESTS) 318 | set(KUKU_THIRDPARTY_DIR ${CMAKE_CURRENT_LIST_DIR}/thirdparty) 319 | set(THIRDPARTY_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/thirdparty) 320 | kuku_fetch_thirdparty_content(ExternalGTest) 321 | add_library(GTest::gtest ALIAS gtest) 322 | 323 | add_executable(kukutest) 324 | add_subdirectory(tests/kuku) 325 | target_link_libraries(kukutest PRIVATE ${KUKU_LIBRARY_NAME} GTest::gtest) 326 | endif() 327 | 328 | ####################################### 329 | # Configure KukuNet and NuGet package # 330 | ####################################### 331 | 332 | # In Windows we will set the Kuku_C library path according to the CMake generator 333 | set(KUKU_WINDOWS_KUKU_C_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}) 334 | get_property(KUKU_IS_MULTI_CONFIG_GENERATOR GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 335 | if(KUKU_IS_MULTI_CONFIG_GENERATOR) 336 | # Is this the Visual Studio generator? If so, the output path will contain the configuration. 337 | # We shall use CMAKE_BUILD_TYPE here, which by default will be "Release". The user has the 338 | # option of changing this by explicitly specifying CMAKE_BUILD_TYPE, which multi-config 339 | # generators otherwise ignore. 340 | set(KUKU_WINDOWS_KUKU_C_DIRECTORY ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${CMAKE_BUILD_TYPE}) 341 | endif() 342 | 343 | # Create KukuNet.csproj, KukuNetExamples.csproj and KukuNetTest.csproj 344 | configure_file( 345 | ${CMAKE_CURRENT_LIST_DIR}/dotnet/src/KukuNet.csproj.in 346 | ${CMAKE_CURRENT_LIST_DIR}/dotnet/src/KukuNet.csproj 347 | @ONLY) 348 | configure_file( 349 | ${CMAKE_CURRENT_LIST_DIR}/dotnet/tests/KukuNetTests.csproj.in 350 | ${CMAKE_CURRENT_LIST_DIR}/dotnet/tests/KukuNetTests.csproj 351 | @ONLY) 352 | configure_file( 353 | ${CMAKE_CURRENT_LIST_DIR}/dotnet/examples/KukuNetExamples.csproj.in 354 | ${CMAKE_CURRENT_LIST_DIR}/dotnet/examples/KukuNetExamples.csproj 355 | @ONLY) 356 | 357 | # Set the kukuc dynamic library file names to be included in creating 358 | # the NuGet package. When building a multi-platform NuGet package, the 359 | # dynamic library paths need to be specified explicitly in the NuGet 360 | # command. See dotnet/nuget/KukuNet.nuspec.in. 361 | 362 | # Create KukuNet-multi.nuspec for a multi-platform NuGet package 363 | configure_file( 364 | ${CMAKE_CURRENT_LIST_DIR}/dotnet/nuget/KukuNet-multi.nuspec.in 365 | ${CMAKE_CURRENT_LIST_DIR}/dotnet/nuget/KukuNet-multi.nuspec 366 | @ONLY) 367 | 368 | set(NUGET_WINDOWS_KUKU_C_PATH "") 369 | set(NUGET_LINUX_KUKU_C_PATH "") 370 | set(NUGET_MACOS_KUKU_C_PATH "") 371 | 372 | # Supporting local building of NuGet package 373 | if(WIN32) 374 | set(NUGET_WINDOWS_KUKU_C_PATH ${KUKU_WINDOWS_KUKU_C_DIRECTORY}/kukuc.dll) 375 | elseif(UNIX AND NOT APPLE) 376 | set(NUGET_LINUX_KUKU_C_PATH ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libkukuc.so) 377 | elseif(APPLE) 378 | set(NUGET_MACOS_KUKU_C_PATH ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libkukuc.dylib) 379 | endif() 380 | 381 | # Create KukuNet.nuspec for a local NuGet pack from KukuNet.nuspec.in 382 | configure_file( 383 | ${CMAKE_CURRENT_LIST_DIR}/dotnet/nuget/KukuNet.nuspec.in 384 | ${CMAKE_CURRENT_LIST_DIR}/dotnet/nuget/KukuNet.nuspec 385 | @ONLY) 386 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 5 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 6 | 7 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 8 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 9 | provided by the bot. You will only need to do this once across all repos using our CLA. 10 | 11 | **IMPORTANT:** Pull requests must be submitted to the branch called *contrib*. Pull requests to any other branch will not be accepted. 12 | 13 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 14 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 15 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 16 | -------------------------------------------------------------------------------- /ISSUES.md: -------------------------------------------------------------------------------- 1 | # Issues 2 | 3 | We appreciate community efforts to find and fix bugs and issues in Kuku. 4 | If you believe you have found a bug or want to report some other issue, please 5 | do so on [GitHub](https://github.com/Microsoft/Kuku/issues). 6 | 7 | ## Critical security issues 8 | 9 | For reporting critical security issues, see [Security.md](Security.md). 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 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 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | This software uses the BLAKE2 library (https://github.com/BLAKE2/BLAKE2) 2 | The BLAKE2 library is licensed under CC0 Universal, version 1.0. You can find a copy of this license at https://creativecommons.org/publicdomain/zero/1.0/legalcode 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kuku 2 | 3 | Kuku is a simple open-source ([MIT licensed](LICENSE)) cuckoo hashing library developed by the Cryptography and Privacy Research Group at Microsoft. 4 | Kuku is written in modern standard C++ and has no external dependencies, making it easy to compile and run in many different environments. 5 | 6 | ## Contents 7 | - [Getting Started](#getting-started) 8 | - [Cuckoo Hashing](#cuckoo-hashing) 9 | - [Kuku](#kuku-1) 10 | - [Installing from NuGet Package](#installing-from-nuget-package-windows-linux-macos) 11 | - [Building Kuku Manually](#building-kuku-manually) 12 | - [Building C++ Components](#building-c-components) 13 | - [Building Kuku](#building-kuku) 14 | - [Installing Kuku](#installing-kuku) 15 | - [Building and Installing on Windows](#building-and-installing-on-windows) 16 | - [CMake Options](#cmake-options) 17 | - [Linking with Kuku through CMake](#linking-with-kuku-through-cmake) 18 | - [Building .NET Components](#building-net-components) 19 | - [Windows, Linux, and macOS](#windows-linux-and-macos) 20 | - [Using Kuku for .NET](#using-kuku-for-net) 21 | - [Building Your Own NuGet Package](#building-your-own-nuget-package) 22 | - [Using Kuku](#using-kuku) 23 | - [Contributing](#contributing) 24 | 25 | ## Getting Started 26 | 27 | ### Cuckoo Hashing 28 | 29 | [Cuckoo hashing](https://en.wikipedia.org/wiki/Cuckoo_hashing) is a hashing technique that can achieve very high fill rates, and in particular create efficient hash tables with a single item per bin. 30 | This is achieved by using multiple (often 2, 3, or 4) different hash functions as follows: 31 | 1. Denote the hash functions `H_1`, `H_2`, ..., `H_k`. 32 | 1. When an item `X` is to be inserted, choose one of the hash functions, `H_j`, and 33 | check whether the corresponding bin is empty. 34 | If it is empty, insert `X` in the bin denoted by `H_j(X)`, and return `true`. 35 | Otherwise, remove the existing value, `Y`, from the bin denoted by `H_j(X)`, and insert X in its place. 36 | Repeat the process for the item `Y`. 37 | 1. If the process fails to terminate after a pre-determined number of attempts, 38 | place the leftover item in a stash of a pre-determined maximum size, and return `true`. 39 | 1. If the stash had already reached its maximum size, store the leftover item into 40 | a known location and return `false`. 41 | 42 | To check whether an item `Z` is in the hash table, it is necessary to check all possible locations, i.e., `H_1(Z)`, `H_2(Z)`, ..., `H_k(Z)` for its presence, as well as the stash. 43 | It is not necessary to use a stash at all, in which case the stash would have size zero and obviously would not need to be checked. 44 | 45 | ### Kuku 46 | 47 | Kuku is a minimalistic library that enables a certain variant of cuckoo hashing, as described above. 48 | It uses [tabulation hashing](https://en.wikipedia.org/wiki/Tabulation_hashing) for the hash functions. 49 | The item length in Kuku is exactly 128 bits and cannot be increased; however, longer items can always be hashed to 128 bits using some other hash function that accepts arbitrary length inputs, and the outputs can subsequently be used in Kuku. 50 | 51 | ### Installing from NuGet Package (Windows, Linux, macOS) 52 | 53 | For .NET developers the easiest way of installing Kuku is by using the multiplatform NuGet package available at [NuGet.org](https://www.nuget.org/packages/Microsoft.Research.Kuku). 54 | Simply add this package into your .NET project as a dependency and you are ready to go. 55 | 56 | ## Building Kuku Manually 57 | 58 | ### Building C++ Components 59 | 60 | On all platforms Kuku is built with CMake. 61 | We recommend using out-of-source build although in-source build works. 62 | Below we give instructions for how to configure, build, and install Kuku either globally (system-wide), or locally (for a single user). 63 | A global install requires elevated (root or administrator) privileges. 64 | 65 | #### Building Kuku 66 | 67 | We assume that Kuku has been cloned into a directory called `Kuku` and all commands presented below are assumed to be executed in the directory `Kuku`. 68 | 69 | You can build the Kuku library (out-of-source) for your machine by executing the following commands: 70 | 71 | ```PowerShell 72 | cmake -S . -B build 73 | cmake --build build 74 | ``` 75 | 76 | After the build completes, the output binaries can be found in `build/lib/` and `build/bin/` directories. 77 | 78 | #### Installing Kuku 79 | 80 | If you have root access to the system you can install Kuku globally as follows: 81 | 82 | ```PowerShell 83 | cmake -S . -B build 84 | cmake --build build 85 | sudo cmake --install build 86 | ``` 87 | 88 | To instead install Kuku locally, e.g., to `~/mylibs/`, do the following: 89 | 90 | ```PowerShell 91 | cmake -S . -B build -DCMAKE_INSTALL_PREFIX=~/mylibs 92 | cmake --build build 93 | sudo cmake --install build 94 | ``` 95 | 96 | #### Building and Installing on Windows 97 | 98 | On Windows the same scripts above work in a developer command prompt for Visual Studio using either the Ninja or "Visual Studio 16 2019" generators. 99 | 100 | When using the Ninja generator, please use the appropriate command prompt depending on the platform you want to build for. If you want to build for x64, please use the **x64 Native Tools Command Prompt for Visual Studio 2019** command prompt to configure and build the library. If you want to build for x86, please use the **x86 Native Tools Command Prompt for Visual Studio 2019** command prompt to configure and build the library. To build using Ninja, type 101 | 102 | ```PowerShell 103 | cmake -S . -B build -G Ninja 104 | cmake --build build 105 | ``` 106 | 107 | When using the "Visual Studio 16 2019" generator you can use the **Developer Command Prompt for VS 2019** command prompt to configure and build the library. By default the generated platform will be x64. You can specify the desired platform using the architecture flag `-A ` and the desired configuration using `--config `. 108 | 109 | ```PowerShell 110 | # Generate and build for x64 in Release mode 111 | cmake -S . -B build -G "Visual Studio 16 2019" -A x64 112 | cmake --build build --config Release 113 | ``` 114 | ```PowerShell 115 | # Generate and build for x86 in Release mode 116 | cmake -S . -B build -G "Visual Studio 16 2019" -A Win32 117 | cmake --build build --config Release 118 | ``` 119 | 120 | Installing the library in Windows works as well. Instead of using the `sudo` command, however, you need to run `cmake --install build` from a command prompt with Administrator permissions. Files will be installed by default to `C:\Program Files (x86)\Kuku`. 121 | 122 | Visual Studio 2019 provides support for CMake-based projects. You can select the menu option `File / Open / Folder...` and navigate to the folder where the Kuku repository is located. After opening the folder, Visual Studio will detect that this is a CMake-based project and will enable the menu command `Project / CMake settings for Kuku`. This will open the CMake settings editor that provides a user interface where you can create different configurations and set different CMake options. 123 | 124 | After the build completes, the output static library `kuku-.lib` can be found in `build\lib\` or `build\lib\Release\`. 125 | When linking with applications, you need to add `src\` (full path) as an include directory to locate the Kuku header files, or use CMake as is explained in [Linking with Kuku through CMake](#linking-with-kuku-through-cmake). 126 | 127 | #### CMake Options 128 | 129 | The following options can be used with CMake to configure the build. The default value for each option is denoted with boldface in the **Values** column. 130 | 131 | | CMake option | Values | Information | 132 | | ------------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 133 | | CMAKE_BUILD_TYPE | **Release**
Debug
RelWithDebInfo
MinSizeRel
| `Debug` and `MinSizeRel` have worse run-time performance. `Debug` inserts additional assertion code. Set to `Release` unless you are developing Kuku itself or debugging some complex issue. | 134 | | KUKU_BUILD_EXAMPLES | ON / **OFF** | Build the C++ examples in [examples](examples). | 135 | | KUKU_BUILD_TESTS | ON / **OFF** | Build the tests to check that Kuku works correctly. | 136 | | BUILD_SHARED_LIBS | ON / **OFF** | Set to `ON` to build a shared library instead of a static library. Not supported in Windows. | 137 | | KUKU_BUILD_KUKU_C | ON / **OFF** | Build the C wrapper library Kuku_C. This is used by the C# wrapper and most users should have no reason to build it. | 138 | 139 | As usual, these options can be passed to CMake with the `-D` flag. 140 | For example, one could run 141 | ```PowerShell 142 | cmake -S . -B build -DKUKU_BUILD_EXAMPLES=ON 143 | ``` 144 | to configure a release build of a static Kuku library and also build the examples. 145 | 146 | #### Linking with Kuku through CMake 147 | 148 | It is very easy to link your own applications and libraries with Kuku if you use CMake. 149 | Simply add the following to your `CMakeLists.txt`: 150 | 151 | ```PowerShell 152 | find_package(Kuku 2.1 REQUIRED) 153 | target_link_libraries( Kuku::kuku) 154 | ``` 155 | 156 | If Kuku was installed globally, the above `find_package` command will likely find the library automatically. 157 | To link with a Kuku installed locally, e.g., installed in `~/mylibs` as described above, you may need to tell CMake where to look for Kuku when you configure your application by running: 158 | 159 | ```PowerShell 160 | cd 161 | cmake . -DCMAKE_PREFIX_PATH=~/mylibs 162 | ``` 163 | 164 | If Kuku was installed using a package manager like vcpkg or Homebrew, please refer to their documentation for how to link with the installed library. For example, vcpkg requires you to specify the vcpkg CMake toolchain file when configuring your project. 165 | 166 | ### Building .NET Components 167 | 168 | Kuku provides a .NET Standard library that wraps the functionality in Kuku for use in .NET development. 169 | Using the existing [NuGet package](https://www.nuget.org/packages/Microsoft.Research.Kuku) is highly recommended, unless development of Kuku or building a custom NuGet package is intended. 170 | Prior to building .NET components, the C wrapper library Kuku_C must be built following [Building C++ Components](#building-c-components). 171 | The Kuku_C library is meant to be used only by the .NET library, not by end-users. 172 | 173 | **Note**: Kuku_C and the .NET library only support 64-bit platforms. 174 | 175 | #### Windows, Linux, and macOS 176 | 177 | For compiling .NET code you will need to install a [.NET Core SDK (>= 3.1)](https://dotnet.microsoft.com/download). 178 | Building the Kuku_C library with CMake will generate project files for the .NET wrapper library, examples, and unit tests. 179 | The Kuku_C library must be discoverable when running a .NET application, e.g., be present in the same directory as your executable, which is taken care of by the .NET examples and tests project files. 180 | Run the following scripts to build each project: 181 | 182 | ```PowerShell 183 | dotnet build dotnet/src --configuration # Build .NET wrapper library 184 | dotnet test dotnet/tests # Build and run .NET unit tests 185 | dotnet run -p dotnet/examples # Build and run .NET examples 186 | ``` 187 | 188 | You can use `--configuration ` to run `Debug` or `Release` examples and unit tests. 189 | You can use `--verbosity detailed` to print the list of unit tests that are being run. 190 | 191 | On Windows, you can also use the Microsoft Visual Studio 2019 solution file `dotnet/KukuNet.sln` to build all three projects. 192 | 193 | #### Using Kuku for .NET 194 | 195 | To use Kuku for .NET in your own application you need to: 196 | 197 | 1. Add a reference in your project to `KukuNet.dll`; 198 | 1. Ensure the native shared library is available for your application when run. 199 | The easiest way to ensure this is to copy the native shared library to the same directory where your application's executable is located. 200 | 201 | #### Building Your Own NuGet Package 202 | 203 | You can build your own NuGet package for Kuku by following the instructions in [NUGET.md](dotnet/nuget/NUGET.md). 204 | 205 | ## Using Kuku 206 | 207 | ### C++ 208 | The cuckoo hash table is represented by an instance of the `KukuTable` class. The 209 | constructor of `KukuTable` takes as input the size of the hash table (`table_size`), 210 | the size of the stash (`stash_size`), the number of hash functions (`loc_func_count`), 211 | a seed for the hash functions (`loc_func_seed`), the number of iterations allowed in 212 | the insertion process, and a value the hash table should contain to signal an empty 213 | slot (`empty_item`). The hash tables item are restricted to 128-bit integer data types 214 | (`item_type`). These can be created from a pair of 64-bit integers using the `make_item` 215 | function. 216 | 217 | Once the table has been created, items can be inserted using the member function `insert`. 218 | Items can be queried with the member function `query`, which returns a `QueryResult` 219 | object. The `QueryResult` contains information about the location in the `KukuTable` where 220 | the queried item was found, as well as the hash function that was used to eventually insert 221 | it. `QueryResult` has an `operator bool()` defined which returns whether the queried item 222 | was found in the hash table. 223 | 224 | If Kuku fails to insert an item to the table or to the stash, the `insert` function will 225 | return false, and a leftover item will be stored in a member variable that can be read with 226 | `leftover_item()`. The same item cannot be inserted multiple times: `insert` will return 227 | `false` in this case. 228 | 229 | ### .NET 230 | 231 | Much like in the native library, the cuckoo hash table is represented by an instance of the 232 | `KukuTable` class. The constructor of `KukuTable` takes as input a set of parameters, 233 | defined by the `KukuTableParameters` class. The parameters contain the table size 234 | `(TableSize`), the size of the stash (`StashSize`), the number of hash functions 235 | (`LocFuncCount`), a seed for the hash functions (`LocFuncSeed`), the number of iterations 236 | allowed in the insertion process, and a value the hash table should contain to signal 237 | an empty slot (`EmptyItem`). The hash tables items are restricted to 128-bit integer data 238 | types. These can be created from an array of size 2 of 64-bit integers by instantiating 239 | the `Item` class and setting its `Data` property with a `ulong` array of size 2. 240 | 241 | Once the table has been created, items can be inserted using the member function `Insert`. 242 | Items can be queried with the member function `Query`, which returns a `QueryResult` 243 | object. The `QueryResult` contains information about whether the queried item was 244 | found in the hash table, the location where it was found, as well as the hash function that 245 | was used to eventually insert it. 246 | 247 | If `KukuTable.Insert` fails to insert an item to the table or to the stash, it will 248 | return `false`, and a leftover item will be stored in a member variable that can be read 249 | with `KukuTable.LastInsertFailItem()`. The same item cannot be inserted multiple times: 250 | `Insert` will return `false` in this case. 251 | 252 | ## Contributing 253 | 254 | For contributing to Kuku, please see [CONTRIBUTING.md](CONTRIBUTING.md). -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /cmake/ExternalGTest.cmake: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. 3 | 4 | FetchContent_Declare( 5 | googletest 6 | GIT_REPOSITORY https://github.com/google/googletest.git 7 | GIT_TAG 703bd9caab50b139428cea1aaff9974ebee5742e # 1.10.0 8 | ) 9 | FetchContent_GetProperties(googletest) 10 | 11 | if(NOT googletest_POPULATED) 12 | FetchContent_Populate(googletest) 13 | 14 | set(BUILD_GMOCK OFF CACHE BOOL "" FORCE) 15 | set(INSTALL_GTEST OFF CACHE BOOL "" FORCE) 16 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 17 | mark_as_advanced(BUILD_GMOCK) 18 | mark_as_advanced(INSTALL_GTEST) 19 | mark_as_advanced(FETCHCONTENT_SOURCE_DIR_GOOGLETEST) 20 | mark_as_advanced(FETCHCONTENT_UPDATES_DISCONNECTED_GOOGLETEST) 21 | 22 | add_subdirectory( 23 | ${googletest_SOURCE_DIR} 24 | ${THIRDPARTY_BINARY_DIR}/googletest-src 25 | EXCLUDE_FROM_ALL) 26 | endif() 27 | -------------------------------------------------------------------------------- /cmake/KukuConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. 3 | 4 | # Exports target Kuku::kuku 5 | # 6 | # Creates variables: 7 | # Kuku_FOUND : If either a static or a shared Kuku library was found 8 | # Kuku_STATIC_FOUND : If a static Kuku library was found 9 | # Kuku_SHARED_FOUND : If a shared Kuku library was found 10 | # Kuku_C_FOUND : If a Kuku C export library was found 11 | # Kuku_VERSION : The full version number 12 | # Kuku_VERSION_MAJOR : The major version number 13 | # Kuku_VERSION_MINOR : The minor version number 14 | # Kuku_VERSION_PATH : The patch version number 15 | # Kuku_BUILD_TYPE : The build type (e.g., "Release" or "Debug") 16 | # Kuku_DEBUG : Set to non-zero value if Kuku is compiled with extra debugging code 17 | 18 | @PACKAGE_INIT@ 19 | 20 | set(Kuku_FOUND FALSE) 21 | set(Kuku_STATIC_FOUND FALSE) 22 | set(Kuku_SHARED_FOUND FALSE) 23 | set(Kuku_C_FOUND FALSE) 24 | 25 | set(Kuku_VERSION @Kuku_VERSION@) 26 | set(Kuku_VERSION_MAJOR @Kuku_VERSION_MAJOR@) 27 | set(Kuku_VERSION_MINOR @Kuku_VERSION_MINOR@) 28 | set(Kuku_VERSION_PATCH @Kuku_VERSION_PATCH@) 29 | 30 | set(Kuku_BUILD_TYPE @CMAKE_BUILD_TYPE@) 31 | set(Kuku_DEBUG @KUKU_DEBUG@) 32 | 33 | # Add the current directory to the module search path 34 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) 35 | 36 | include(${CMAKE_CURRENT_LIST_DIR}/KukuTargets.cmake) 37 | 38 | if(TARGET Kuku::kuku) 39 | set(Kuku_FOUND TRUE) 40 | set(Kuku_STATIC_FOUND TRUE) 41 | endif() 42 | 43 | if(TARGET Kuku::kuku_shared) 44 | set(Kuku_FOUND TRUE) 45 | set(Kuku_SHARED_FOUND TRUE) 46 | endif() 47 | 48 | if(TARGET Kuku::kukuc) 49 | set(Kuku_FOUND TRUE) 50 | set(Kuku_C_FOUND TRUE) 51 | endif() 52 | 53 | if(Kuku_FOUND) 54 | if(NOT Kuku_FIND_QUIETLY) 55 | message(STATUS "Kuku -> Version ${Kuku_VERSION} detected") 56 | endif() 57 | if(Kuku_DEBUG AND NOT Kuku_FIND_QUIETLY) 58 | message(STATUS "Performance warning: Kuku compiled in debug mode") 59 | endif() 60 | set(KUKU_TARGETS_AVAILABLE "Kuku -> Targets available:") 61 | 62 | if(Kuku_STATIC_FOUND) 63 | string(APPEND KUKU_TARGETS_AVAILABLE " Kuku::kuku") 64 | endif() 65 | if(Kuku_SHARED_FOUND) 66 | string(APPEND KUKU_TARGETS_AVAILABLE " Kuku::kuku_shared") 67 | endif() 68 | if(Kuku_C_FOUND) 69 | string(APPEND KUKU_TARGETS_AVAILABLE " Kuku::kukuc") 70 | endif() 71 | if(NOT Kuku_FIND_QUIETLY) 72 | message(STATUS ${KUKU_TARGETS_AVAILABLE}) 73 | endif() 74 | else() 75 | if(NOT Kuku_FIND_QUIETLY) 76 | message(WARNING "Kuku -> NOT FOUND") 77 | endif() 78 | endif() 79 | -------------------------------------------------------------------------------- /config/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /dotnet/KukuNet.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30011.22 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KukuNet", "src\KukuNet.csproj", "{029CC83E-9B1F-4EA2-A54B-899A75E9A97B}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KukuNetExamples", "examples\KukuNetExamples.csproj", "{8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}" 9 | EndProject 10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "KukuNetTests", "tests\KukuNetTests.csproj", "{4013D47D-E62F-4F75-A3EA-27C2520CB983}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Debug|x64 = Debug|x64 16 | Debug|x86 = Debug|x86 17 | Release|Any CPU = Release|Any CPU 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {029CC83E-9B1F-4EA2-A54B-899A75E9A97B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {029CC83E-9B1F-4EA2-A54B-899A75E9A97B}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {029CC83E-9B1F-4EA2-A54B-899A75E9A97B}.Debug|x64.ActiveCfg = Debug|Any CPU 25 | {029CC83E-9B1F-4EA2-A54B-899A75E9A97B}.Debug|x64.Build.0 = Debug|Any CPU 26 | {029CC83E-9B1F-4EA2-A54B-899A75E9A97B}.Debug|x86.ActiveCfg = Debug|Any CPU 27 | {029CC83E-9B1F-4EA2-A54B-899A75E9A97B}.Debug|x86.Build.0 = Debug|Any CPU 28 | {029CC83E-9B1F-4EA2-A54B-899A75E9A97B}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {029CC83E-9B1F-4EA2-A54B-899A75E9A97B}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {029CC83E-9B1F-4EA2-A54B-899A75E9A97B}.Release|x64.ActiveCfg = Release|Any CPU 31 | {029CC83E-9B1F-4EA2-A54B-899A75E9A97B}.Release|x64.Build.0 = Release|Any CPU 32 | {029CC83E-9B1F-4EA2-A54B-899A75E9A97B}.Release|x86.ActiveCfg = Release|Any CPU 33 | {029CC83E-9B1F-4EA2-A54B-899A75E9A97B}.Release|x86.Build.0 = Release|Any CPU 34 | {8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}.Debug|x64.ActiveCfg = Debug|Any CPU 37 | {8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}.Debug|x64.Build.0 = Debug|Any CPU 38 | {8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}.Debug|x86.ActiveCfg = Debug|Any CPU 39 | {8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}.Debug|x86.Build.0 = Debug|Any CPU 40 | {8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}.Release|x64.ActiveCfg = Release|Any CPU 43 | {8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}.Release|x64.Build.0 = Release|Any CPU 44 | {8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}.Release|x86.ActiveCfg = Release|Any CPU 45 | {8D7AF1DE-3603-4922-BC13-5AD41ED9D8EF}.Release|x86.Build.0 = Release|Any CPU 46 | {4013D47D-E62F-4F75-A3EA-27C2520CB983}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {4013D47D-E62F-4F75-A3EA-27C2520CB983}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {4013D47D-E62F-4F75-A3EA-27C2520CB983}.Debug|x64.ActiveCfg = Debug|Any CPU 49 | {4013D47D-E62F-4F75-A3EA-27C2520CB983}.Debug|x64.Build.0 = Debug|Any CPU 50 | {4013D47D-E62F-4F75-A3EA-27C2520CB983}.Debug|x86.ActiveCfg = Debug|Any CPU 51 | {4013D47D-E62F-4F75-A3EA-27C2520CB983}.Debug|x86.Build.0 = Debug|Any CPU 52 | {4013D47D-E62F-4F75-A3EA-27C2520CB983}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {4013D47D-E62F-4F75-A3EA-27C2520CB983}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {4013D47D-E62F-4F75-A3EA-27C2520CB983}.Release|x64.ActiveCfg = Release|Any CPU 55 | {4013D47D-E62F-4F75-A3EA-27C2520CB983}.Release|x64.Build.0 = Release|Any CPU 56 | {4013D47D-E62F-4F75-A3EA-27C2520CB983}.Release|x86.ActiveCfg = Release|Any CPU 57 | {4013D47D-E62F-4F75-A3EA-27C2520CB983}.Release|x86.Build.0 = Release|Any CPU 58 | EndGlobalSection 59 | GlobalSection(SolutionProperties) = preSolution 60 | HideSolutionNode = FALSE 61 | EndGlobalSection 62 | EndGlobal 63 | -------------------------------------------------------------------------------- /dotnet/examples/KukuNetExamples.csproj.in: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | Microsoft Research 7 | Microsoft Corporation 8 | .NET wrapper examples for Kuku 9 | Microsoft Corporation 2020 10 | 11 | 12 | 13 | x64 14 | @CMAKE_RUNTIME_OUTPUT_DIRECTORY@/dotnet/$(Configuration) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /dotnet/examples/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Linq; 6 | using Microsoft.Research.Kuku; 7 | 8 | namespace KukuNetExample 9 | { 10 | public class Program 11 | { 12 | static void Main(string[] args) 13 | { 14 | var parameters = ParseParameters(args); 15 | RunExample(parameters); 16 | } 17 | 18 | static void RunExample(KukuTableParameters parameters) 19 | { 20 | var kukuTable = new KukuTable(parameters); 21 | 22 | ulong roundCounter = 0; 23 | while (true) 24 | { 25 | Console.WriteLine($"Inserted {roundCounter * 20} items."); 26 | Console.WriteLine($"Fill rate: {kukuTable.FillRate}."); 27 | 28 | Console.ReadKey(); 29 | 30 | for (ulong i = 0; i < 20; i++) 31 | { 32 | var item = Item.MakeItem(i + 1, roundCounter + 1); 33 | if (!kukuTable.Insert(item)) 34 | { 35 | Console.WriteLine($"Insertion failed: round_counter = {roundCounter}, i = {i}"); 36 | Console.WriteLine($"Inserted successfully {roundCounter * 20 + i} items."); 37 | Console.WriteLine($"Fill rate: {kukuTable.FillRate}."); 38 | 39 | var lastItem = kukuTable.LeftoverItem; 40 | Console.WriteLine($"Leftover item: [{string.Join(',', lastItem)}]."); 41 | break; 42 | } 43 | } 44 | 45 | PrintTable(kukuTable); 46 | 47 | if (!kukuTable.IsEmptyItem(kukuTable.LeftoverItem)) 48 | { 49 | break; 50 | } 51 | 52 | roundCounter++; 53 | } 54 | 55 | while (true) 56 | { 57 | Console.WriteLine("Query item: "); 58 | var input = Console.ReadLine(); 59 | Item item; 60 | try 61 | { 62 | if (input == null) continue; 63 | 64 | var data = input.Split(",").Select(ulong.Parse).ToArray(); 65 | item = Item.MakeItem(data[0], data[1]); 66 | } 67 | catch 68 | { 69 | Console.WriteLine($"Incorrect format. Expected: ,"); 70 | continue; 71 | } 72 | 73 | var result = kukuTable.Query(item); 74 | Console.WriteLine($"Found: {result.Found}"); 75 | if (result.Found) 76 | { 77 | Console.WriteLine($"Location: {result.Location}"); 78 | Console.WriteLine($"In stash: {result.InStash}"); 79 | Console.WriteLine($"Hash function index: {result.LocFuncIndex}"); 80 | } 81 | } 82 | } 83 | 84 | static long LongRandom(long min, long max, Random rand) 85 | { 86 | byte[] buf = new byte[8]; 87 | rand.NextBytes(buf); 88 | long longRand = BitConverter.ToInt64(buf, 0); 89 | 90 | return (Math.Abs(longRand % (max - min)) + min); 91 | } 92 | 93 | private static KukuTableParameters ParseParameters(string[] args) 94 | { 95 | if (args.Length != 4) 96 | { 97 | Console.WriteLine("Incorrect number of arguments. Expected: 4."); 98 | ShowHelp(); 99 | Environment.Exit(1); 100 | } 101 | 102 | if (!uint.TryParse(args[0], out uint tableSize)) 103 | { 104 | Console.WriteLine($"Invalid value for parameter {nameof(tableSize)}. Expected uint."); 105 | ShowHelp(); 106 | Environment.Exit(1); 107 | } 108 | 109 | if (!uint.TryParse(args[1], out uint stashSize)) 110 | { 111 | Console.WriteLine($"Invalid value for parameter {nameof(stashSize)}. Expected uint."); 112 | ShowHelp(); 113 | Environment.Exit(1); 114 | } 115 | 116 | if (!uint.TryParse(args[2], out uint locFuncCount)) 117 | { 118 | Console.WriteLine($"Invalid value for parameter {nameof(locFuncCount)}. Expected uint."); 119 | ShowHelp(); 120 | Environment.Exit(1); 121 | } 122 | 123 | if (!ulong.TryParse(args[3], out ulong maxProbe)) 124 | { 125 | Console.WriteLine($"Invalid value for parameter {nameof(maxProbe)}. Expected uint."); 126 | ShowHelp(); 127 | Environment.Exit(1); 128 | } 129 | 130 | var parameters = new KukuTableParameters() 131 | { 132 | TableSize = tableSize, StashSize = stashSize, LocFuncCount = locFuncCount, MaxProbe = maxProbe 133 | }; 134 | 135 | var random = new Random(); 136 | parameters.LocFuncSeed = Item.MakeRandomItem(); 137 | parameters.EmptyItem = Item.MakeZeroItem(); 138 | 139 | return parameters; 140 | } 141 | 142 | private static void ShowHelp() 143 | { 144 | var name = typeof(Program).Assembly.GetName().Name; 145 | Console.WriteLine($"Usage: dotnet {name}.dll "); 146 | } 147 | 148 | static void PrintTable(KukuTable kukuTable) 149 | { 150 | Console.WriteLine($"Table size: { kukuTable.Table.Size }"); 151 | Console.WriteLine($"{kukuTable.Table}"); 152 | 153 | Console.WriteLine($"Stash size: { kukuTable.Stash.Size }"); 154 | Console.WriteLine($"{kukuTable.Stash}"); 155 | } 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /dotnet/nuget/KukuNet-multi.nuspec.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | Microsoft.Research.Kuku 9 | @Kuku_VERSION@ 10 | Microsoft Kuku 11 | Microsoft 12 | Microsoft 13 | https://github.com/microsoft/Kuku 14 | LICENSE 15 | false 16 | Kuku is a simple open-source (MIT licensed) cuckoo hashing library developed by the Cryptography and Privacy Research group at Microsoft. Kuku is written in modern standard C++ and has no external dependencies, making it easy to compile and run in many different environments. 17 | https://github.com/microsoft/Kuku 18 | © Microsoft Corporation. All rights reserved. 19 | c# hash hashing cuckoo 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /dotnet/nuget/KukuNet.nuspec.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | Microsoft.Research.Kuku 9 | @Kuku_VERSION@ 10 | Microsoft Kuku 11 | Microsoft 12 | Microsoft 13 | https://github.com/microsoft/Kuku 14 | LICENSE 15 | false 16 | Kuku is a simple open-source (MIT licensed) cuckoo hashing library developed by the Cryptography and Privacy Research group at Microsoft. Kuku is written in modern standard C++ and has no external dependencies, making it easy to compile and run in many different environments. 17 | https://github.com/microsoft/Kuku 18 | © Microsoft Corporation. All rights reserved. 19 | c# hash hashing cuckoo 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /dotnet/nuget/KukuNet.targets: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | kukuc.dll 15 | libkukuc.so 16 | libkukuc.dylib 17 | PreserveNewest 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /dotnet/nuget/NUGET.md: -------------------------------------------------------------------------------- 1 | # Creating a NuGet package 2 | 3 | After building `dotnet\src\KukuDotNet.csproj` you can create a NuGet package that you can 4 | use to easily add Microsoft Kuku capabilities to all of your .NET projects. Currently 5 | the NuGet package is only supported in Windows. 6 | 7 | You will need to: 8 | 1. Compile both Kuku and Kuku_C projects with CMake. Build `dotnet\src\KukuNet.csproj`. 9 | 1. [Download and install the NuGet command line tool](https://docs.microsoft.com/en-us/nuget/install-nuget-client-tools). 10 | 4. Run the command below to create NuGet package 11 | 5. Add NuGet package reference to your .NET projects 12 | 13 | The command to create the NuGet package after compiling binaries is the following: 14 | 15 | ```` 16 | cd dotnet\nuget 17 | nuget pack KukuNet.nuspec -properties Configuration=Release -Verbosity detailed -OutputDir Release 18 | cd ..\.. 19 | ```` 20 | 21 | After the package is created, copy it from `dotnet\nuget\Release` to a known location (e.g., `C:\NuGetPackages`). 22 | 23 | To add a reference to the NuGet package, you will need to configure Visual Studio so it can find 24 | packages in this known location. In Microsoft Visual Studio 2019, for example, you can: 25 | 1. Select the menu uption `Tools / Options...` 26 | 2. On the left pane of the Options dialog, navigate to `NuGet Package Manager / Package Sources` 27 | 3. On the right pane of the Options dialog, add a new package source that points to the directory 28 | where you copied the NuGet package (e.g., `C:\NuGetPackages`) 29 | 30 | After this, you should be able to add a reference to this package in your own .NET project. After 31 | creating or opening your project in Visual Studio, you can right click on the project in the 32 | Solution Explorer window, and select `Manage NuGet packages...`. In the window that appears 33 | you will be able to select the `Microsoft.Research.Kuku` NuGet package to add to your project. -------------------------------------------------------------------------------- /dotnet/src/Common.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | namespace Microsoft.Research.Kuku 5 | { 6 | /// 7 | /// Set of common properties 8 | /// 9 | public static class Common 10 | { 11 | /// The smallest allowed table size. 12 | public static uint MinTableSize => NativeMethods.Common_MinTableSize(); 13 | 14 | /// The largest allowed table size. 15 | public static uint MaxTableSize => NativeMethods.Common_MaxTableSize(); 16 | 17 | /// The smallest allowed number of hash functions. 18 | public static uint MinLocFuncCount => NativeMethods.Common_MinLocFuncCount(); 19 | 20 | /// The largest allowed number of hash functions. This must be a power of two for correct behavior. 21 | public static uint MaxLocFuncCount => NativeMethods.Common_MaxLocFuncCount(); 22 | } 23 | } -------------------------------------------------------------------------------- /dotnet/src/Item.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | 6 | namespace Microsoft.Research.Kuku 7 | { 8 | /// Represents a 128-bit item that can be added to the hash table. 9 | public class Item : IEquatable 10 | { 11 | /// Creates a new hash table item with all zero bits. 12 | public Item() 13 | { 14 | } 15 | 16 | /// Creates a copy of a given hash table item. 17 | /// The hash table item to copy from 18 | public Item(Item copy) 19 | { 20 | Data = copy.Data; 21 | } 22 | 23 | /// Assigns this hash table item to be equal to a given item. 24 | /// The hash table item to assign from 25 | public void Set(Item assign) => Data = assign.Data; 26 | 27 | /// The hash table item value represented as a pair of 64-bit words. 28 | public (ulong, ulong) Data { get; set; } = (0, 0); 29 | 30 | /// Creates a hash table item and sets its value from a pair of 64-bit words. 31 | /// Pair of high and low words representing the value of the hash table item 32 | public static Item MakeItem((ulong, ulong) data) => new Item(data); 33 | 34 | /// Creates a hash table item and sets its value from a pair of 64-bit words. 35 | /// The low 64 bits of the value of the item 36 | /// The high 64 bits of the value of the item 37 | public static Item MakeItem(ulong lw, ulong hw) => new Item((lw, hw)); 38 | 39 | /// Sets the value of the hash table item from a pair of 64-bit words. 40 | /// Pair of high and low words representing the value of the hash table item 41 | public void SetItem((ulong, ulong) data) => Data = data; 42 | 43 | /// Sets the value of the hash table item from a pair of 64-bit words. 44 | /// The low 64 bits of the value of the item 45 | /// The high 64 bits of the value of the item 46 | public void SetItem(ulong lw, ulong hw) => SetItem((lw, hw)); 47 | 48 | /// Creates a zero hash table item. 49 | public static Item MakeZeroItem() => new Item(); 50 | 51 | /// Sets the value of this hash table item to zero. 52 | public void SetZeroItem() => Data = (0, 0); 53 | 54 | /// Creates a hash table item and sets its value to all one-bits. 55 | public static Item MakeAllOnesItem() => new Item((~(ulong)0, ~(ulong)0)); 56 | 57 | /// Sets the value of a given hash table item to all one-bits. 58 | public void SetAllOnesItem() => Data = (~(ulong)0, ~(ulong)0); 59 | 60 | /// Returns whether a given hash table item is zero. 61 | public bool IsZeroItem() => Data == (0, 0); 62 | 63 | /// Returns whether a given has table item has all one-bits. 64 | public bool IsAllOnesItem() => Data == (~(ulong)0, ~(ulong)0); 65 | 66 | /// Returns whether two hash table items are equal. 67 | /// An Item object to be compared with this item 68 | public override bool Equals(object obj) => Equals(obj as Item); 69 | 70 | /// Returns whether two hash table items are equal. 71 | /// An item to be compared with this item 72 | public bool Equals(Item other) => Data == other.Data; 73 | 74 | /// Returns the hash code for this item. 75 | public override int GetHashCode() 76 | { 77 | return Data.GetHashCode(); 78 | } 79 | 80 | /// Creates a random hash table item. 81 | public static Item MakeRandomItem() 82 | { 83 | ulong[] itemArray = new ulong[2]; 84 | NativeMethods.Common_SetRandomItem(itemArray); 85 | return new Item((itemArray[0], itemArray[1])); 86 | } 87 | 88 | /// Sets this hash table item to a random value. 89 | public void SetRandomItem() 90 | { 91 | ulong[] itemArray = new ulong[2]; 92 | NativeMethods.Common_SetRandomItem(itemArray); 93 | Data = (itemArray[0], itemArray[1]); 94 | } 95 | 96 | /// Interprets a hash table item as a 128-bit integer and increments its value by one. 97 | public void IncrementItem() 98 | { 99 | ulong[] itemArray = new ulong[2] { Data.Item1, Data.Item2 }; 100 | NativeMethods.Common_IncrementItem(itemArray); 101 | Data = (itemArray[0], itemArray[1]); 102 | } 103 | 104 | /// Returns the item as a formatted string with high and low words separated by a comma. 105 | public override string ToString() 106 | { 107 | return string.Format($"{Data.Item1},{Data.Item2}"); 108 | } 109 | 110 | private Item((ulong, ulong) data) => Data = data; 111 | } 112 | } -------------------------------------------------------------------------------- /dotnet/src/KukuNet.csproj.in: -------------------------------------------------------------------------------- 1 |  2 | 3 | netstandard2.0 4 | false 5 | Microsoft Research 6 | Microsoft Corporation 7 | .NET wrapper library for Kuku 8 | Microsoft Corporation 2020 9 | true 10 | KukuNetCert.snk 11 | true 12 | 13 | 14 | @CMAKE_RUNTIME_OUTPUT_DIRECTORY@\dotnet\$(Configuration)/KukuNet.xml 15 | x64 16 | @CMAKE_RUNTIME_OUTPUT_DIRECTORY@\dotnet\$(Configuration) 17 | 18 | 19 | pdbonly 20 | true 21 | 22 | 23 | $(DefineConstants);DEBUG;TRACE 24 | 25 | 26 | -------------------------------------------------------------------------------- /dotnet/src/KukuTableParameters.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System.Runtime.Serialization; 5 | 6 | namespace Microsoft.Research.Kuku 7 | { 8 | /// 9 | /// Parameters for creating a Kuku table 10 | /// 11 | [DataContract] 12 | public class KukuTableParameters 13 | { 14 | /// The size of the hash table. 15 | [DataMember(Order = 1)] 16 | public uint TableSize { get; set; } 17 | 18 | /// The size of the stash (possibly zero). 19 | [DataMember(Order = 2)] 20 | public uint StashSize { get; set; } 21 | 22 | /// The number of location functions (hash functions) to use. 23 | [DataMember(Order = 3)] 24 | public uint LocFuncCount { get; set; } 25 | 26 | /// The 128-bit seed for the location functions, represented as a hash table item. 27 | [DataMember(Order = 4)] 28 | public Item LocFuncSeed { get; set; } 29 | 30 | /// The maximum number of random walk steps taken in attempting to insert an item. 31 | [DataMember(Order = 5)] 32 | public ulong MaxProbe { get; set; } 33 | 34 | /// A hash table item that represents an empty location in the table. 35 | [DataMember(Order = 6)] 36 | public Item EmptyItem { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /dotnet/src/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using System; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace Microsoft.Research.Kuku 8 | { 9 | internal class NativeMethods 10 | { 11 | private const string DllName = "kukuc"; 12 | 13 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 14 | public static extern IntPtr KukuTable_Create(uint table_size, uint stash_size, uint loc_func_count, ulong[] loc_func_seed, ulong max_probe, ulong[] empty_item); 15 | 16 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 17 | [return: MarshalAs(UnmanagedType.I1)] 18 | internal static extern bool KukuTable_Insert(IntPtr kuku_table, ulong[] item); 19 | 20 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 21 | [return: MarshalAs(UnmanagedType.I1)] 22 | internal static extern bool KukuTable_Query(IntPtr kuku_table, ulong[] item, ref QueryResultData query_result); 23 | 24 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 25 | [return: MarshalAs(UnmanagedType.I1)] 26 | internal static extern bool KukuTable_IsEmptyItem(IntPtr kuku_table, ulong[] item); 27 | 28 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 29 | internal static extern void KukuTable_EmptyItem(IntPtr kuku_table, ulong[] item); 30 | 31 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 32 | internal static extern void KukuTable_LeftoverItem(IntPtr kuku_table, ulong[] item); 33 | 34 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 35 | internal static extern double KukuTable_FillRate(IntPtr kuku_table); 36 | 37 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 38 | internal static extern uint KukuTable_LocFuncCount(IntPtr kuku_table); 39 | 40 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 41 | internal static extern void KukuTable_Table(IntPtr kuku_table, uint index, ulong[] item); 42 | 43 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 44 | internal static extern uint KukuTable_TableSize(IntPtr kuku_table); 45 | 46 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 47 | internal static extern void KukuTable_Stash(IntPtr kuku_table, uint index, ulong[] item); 48 | 49 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 50 | internal static extern uint KukuTable_StashSize(IntPtr kuku_table); 51 | 52 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 53 | internal static extern uint KukuTable_Location(IntPtr kuku_table, ulong[] item, uint loc_func_index); 54 | 55 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 56 | internal static extern void KukuTable_AllLocations(IntPtr kuku_table, ulong[] item, uint[] locations, out uint count); 57 | 58 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 59 | internal static extern void KukuTable_ClearTable(IntPtr kuku_table); 60 | 61 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 62 | internal static extern void Common_SetRandomItem(ulong[] item); 63 | 64 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 65 | internal static extern void Common_IncrementItem(ulong[] item); 66 | 67 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 68 | internal static extern uint Common_MinTableSize(); 69 | 70 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 71 | internal static extern uint Common_MaxTableSize(); 72 | 73 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 74 | internal static extern uint Common_MinLocFuncCount(); 75 | 76 | [DllImport(DllName, CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)] 77 | internal static extern uint Common_MaxLocFuncCount(); 78 | } 79 | 80 | [StructLayout(LayoutKind.Sequential)] 81 | internal struct QueryResultData 82 | { 83 | [MarshalAs(UnmanagedType.I1)] 84 | public bool found; 85 | [MarshalAs(UnmanagedType.I1)] 86 | public bool in_stash; 87 | public uint location; 88 | public uint loc_func_index; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /dotnet/src/QueryResult.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | namespace Microsoft.Research.Kuku 5 | { 6 | /// The QueryResult class represents the result of a hash table query. 7 | /// 8 | /// The QueryResult class represents the result of a hash table query. It includes information about whether a 9 | /// queried item was found in the hash table, its location in the hash table or stash (if found), and the index of 10 | /// the location function (hash function) that was used to insert it. QueryResult objects are returned by the 11 | /// function. 12 | /// 13 | public class QueryResult 14 | { 15 | private readonly QueryResultData _resultStruct; 16 | 17 | internal QueryResult(QueryResultData resultStruct) 18 | { 19 | _resultStruct = resultStruct; 20 | } 21 | 22 | /// Returns whether the queried item was found in the hash table or in the stash. 23 | public bool Found => _resultStruct.found; 24 | 25 | /// Returns whether the queried item was found in the stash. 26 | public bool InStash => _resultStruct.in_stash; 27 | 28 | /// Returns the hash table or stash location represented by this QueryResult. 29 | public uint Location => _resultStruct.location; 30 | 31 | /// Returns the index of the location function that was used to insert the queried item. 32 | /// 33 | /// Returns the index of the location function that was used to insert the queried item. This value is 34 | /// meaningless when is true. A value equal to indicates 35 | /// the item was not found in the table or stash. 36 | /// 37 | public uint LocFuncIndex => _resultStruct.loc_func_index; 38 | } 39 | } -------------------------------------------------------------------------------- /dotnet/tests/Item.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using Microsoft.Research.Kuku; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using System; 7 | 8 | namespace KukuNetTest 9 | { 10 | [TestClass] 11 | public class ItemTests 12 | { 13 | [TestMethod] 14 | public void SetItem() 15 | { 16 | Item bl = new Item(); 17 | bl.SetItem((0, 0)); 18 | Assert.AreEqual((0ul, 0ul), bl.Data); 19 | 20 | bl.SetItem((1, 0)); 21 | Assert.AreEqual((1ul, 0ul), bl.Data); 22 | 23 | bl.SetItem((0, 1)); 24 | Assert.AreEqual((0ul, 1ul), bl.Data); 25 | 26 | bl.SetItem((0xF00F, 0xBABA)); 27 | Assert.AreEqual((0xF00Ful, 0xBABAul), bl.Data); 28 | 29 | bl.SetItem((0xF00FF00FF00FF00Ful, 0xBABABABABABABABAul)); 30 | Assert.AreEqual((0xF00FF00FF00FF00Ful, 0xBABABABABABABABAul), bl.Data); 31 | } 32 | 33 | [TestMethod] 34 | public void SetZeroItem() 35 | { 36 | Item bl = new Item(); 37 | bl.SetZeroItem(); 38 | Assert.AreEqual((0ul, 0ul), bl.Data); 39 | 40 | bl.SetItem((0xF00FF00FF00FF00F, 0xBABABABABABABABA)); 41 | bl.SetZeroItem(); 42 | Assert.AreEqual((0ul, 0ul), bl.Data); 43 | } 44 | 45 | [TestMethod] 46 | public void SetAllOnesItem() 47 | { 48 | Item bl = new Item(); 49 | bl.SetZeroItem(); 50 | bl.SetAllOnesItem(); 51 | Assert.AreEqual((0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul), bl.Data); 52 | 53 | bl.SetItem((0xF00FF00FF00FF00F, 0xBABABABABABABABA)); 54 | bl.SetAllOnesItem(); 55 | Assert.AreEqual((0xFFFFFFFFFFFFFFFFul, 0xFFFFFFFFFFFFFFFFul), bl.Data); 56 | } 57 | 58 | [TestMethod] 59 | public void IsZeroItem() 60 | { 61 | Item bl = new Item(); 62 | bl.SetItem((0, 0)); 63 | Assert.IsTrue(bl.IsZeroItem()); 64 | 65 | bl.SetItem((1, 0)); 66 | Assert.IsFalse(bl.IsZeroItem()); 67 | 68 | bl.SetItem((0, 1)); 69 | Assert.IsFalse(bl.IsZeroItem()); 70 | 71 | bl.SetItem((0xF00FF00FF00FF00F, 0xBABABABABABABABA)); 72 | Assert.IsFalse(bl.IsZeroItem()); 73 | } 74 | 75 | [TestMethod] 76 | public void IsAllOnesItem() 77 | { 78 | Item bl = new Item(); 79 | bl.SetAllOnesItem(); 80 | Assert.IsTrue(bl.IsAllOnesItem()); 81 | 82 | bl.SetZeroItem(); 83 | Assert.IsFalse(bl.IsAllOnesItem()); 84 | 85 | bl.SetItem((1, 0)); 86 | Assert.IsFalse(bl.IsAllOnesItem()); 87 | 88 | bl.SetItem((0, 1)); 89 | Assert.IsFalse(bl.IsAllOnesItem()); 90 | 91 | bl.SetItem((0xF00FF00FF00FF00F, 0xBABABABABABABABA)); 92 | Assert.IsFalse(bl.IsAllOnesItem()); 93 | } 94 | 95 | [TestMethod] 96 | public void SetRandomItem() 97 | { 98 | Item bl = new Item(); 99 | bl.SetRandomItem(); 100 | Assert.IsFalse(bl.IsZeroItem()); 101 | Item bl2 = new Item(bl); 102 | bl.SetRandomItem(); 103 | Assert.AreNotEqual(bl, bl2); 104 | } 105 | 106 | [TestMethod] 107 | public void ZeroItem() 108 | { 109 | Item bl = Item.MakeRandomItem(); 110 | Assert.IsFalse(bl.IsZeroItem()); 111 | bl.SetZeroItem(); 112 | Assert.IsTrue(bl.IsZeroItem()); 113 | } 114 | 115 | [TestMethod] 116 | public void IncrementItem() 117 | { 118 | Item bl = Item.MakeItem((0, 0)); 119 | bl.IncrementItem(); 120 | Assert.AreEqual((1ul, 0ul), bl.Data); 121 | 122 | bl.SetItem((0xF00F, 0xBAAB)); 123 | bl.IncrementItem(); 124 | Assert.AreEqual((0xF010ul, 0xBAABul), bl.Data); 125 | 126 | bl.SetItem((0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFE)); 127 | bl.IncrementItem(); 128 | Assert.AreEqual((0ul, 0xFFFFFFFFFFFFFFFFul), bl.Data); 129 | 130 | bl.SetItem((0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF)); 131 | bl.IncrementItem(); 132 | Assert.AreEqual((0ul, 0ul), bl.Data); 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /dotnet/tests/KukuNetTests.csproj.in: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | false 6 | Microsoft Research 7 | Microsoft Corporation 8 | .NET wrapper unit tests for Kuku 9 | Microsoft Corporation 2020 10 | 11 | 12 | 13 | x64 14 | @CMAKE_RUNTIME_OUTPUT_DIRECTORY@/dotnet/$(Configuration) 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /dotnet/tests/KukuTable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using Microsoft.Research.Kuku; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using System; 7 | using System.Collections.Generic; 8 | 9 | namespace KukuNetTest 10 | { 11 | [TestClass] 12 | public class KukuTableTests 13 | { 14 | [TestMethod] 15 | public void CreateTest() 16 | { 17 | KukuTable table = null; 18 | 19 | Utilities.AssertThrows(() => table = new KukuTable( 20 | new KukuTableParameters 21 | { 22 | TableSize = 0, 23 | StashSize = 0, 24 | LocFuncCount = 2, 25 | LocFuncSeed = Item.MakeZeroItem(), 26 | MaxProbe = 1, 27 | EmptyItem = Item.MakeZeroItem() 28 | }) 29 | ); 30 | 31 | Utilities.AssertThrows(() => table = new KukuTable( 32 | new KukuTableParameters 33 | { 34 | TableSize = 1, 35 | StashSize = 0, 36 | LocFuncCount = 0, 37 | LocFuncSeed = Item.MakeZeroItem(), 38 | MaxProbe = 1, 39 | EmptyItem = Item.MakeZeroItem() 40 | }) 41 | ); 42 | 43 | Utilities.AssertThrows(() => table = new KukuTable( 44 | new KukuTableParameters 45 | { 46 | TableSize = 1, 47 | StashSize = 0, 48 | LocFuncCount = 2, 49 | LocFuncSeed = Item.MakeZeroItem(), 50 | MaxProbe = 0, 51 | EmptyItem = Item.MakeZeroItem() 52 | }) 53 | ); 54 | 55 | table = new KukuTable( 56 | new KukuTableParameters 57 | { 58 | TableSize = Common.MinTableSize, 59 | StashSize = 0, 60 | LocFuncCount = 2, 61 | LocFuncSeed = Item.MakeZeroItem(), 62 | MaxProbe = 1, 63 | EmptyItem = Item.MakeZeroItem() 64 | } 65 | ); 66 | 67 | table = new KukuTable( 68 | new KukuTableParameters 69 | { 70 | TableSize = 1, 71 | StashSize = 0, 72 | LocFuncCount = 1, 73 | LocFuncSeed = Item.MakeZeroItem(), 74 | MaxProbe = 1, 75 | EmptyItem = Item.MakeZeroItem() 76 | } 77 | ); 78 | } 79 | 80 | [TestMethod] 81 | public void Populate1() 82 | { 83 | KukuTable ct = new KukuTable( 84 | new KukuTableParameters 85 | { 86 | TableSize = 1, 87 | StashSize = 0, 88 | LocFuncCount = 1, 89 | LocFuncSeed = Item.MakeZeroItem(), 90 | MaxProbe = 10, 91 | EmptyItem = Item.MakeZeroItem() 92 | } 93 | ); 94 | 95 | Assert.IsTrue(ct.IsEmptyItem(ct[0])); 96 | Assert.IsTrue(ct.Insert(Item.MakeItem(1, 0))); 97 | Assert.IsFalse(ct.Insert(Item.MakeItem(0, 1))); 98 | Utilities.AssertThrows(() => ct.Insert(ct.EmptyItem)); 99 | Utilities.AssertThrows(() => ct.Insert(Item.MakeZeroItem())); 100 | Assert.IsFalse(ct.IsEmpty(0)); 101 | 102 | ct = new KukuTable( 103 | new KukuTableParameters 104 | { 105 | TableSize = 1, 106 | StashSize = 0, 107 | LocFuncCount = 2, 108 | LocFuncSeed = Item.MakeZeroItem(), 109 | MaxProbe = 10, 110 | EmptyItem = Item.MakeZeroItem() 111 | } 112 | ); 113 | 114 | Assert.IsTrue(ct.IsEmptyItem(ct[0])); 115 | Assert.IsTrue(ct.Insert(Item.MakeItem(1, 0))); 116 | Assert.IsFalse(ct.Insert(Item.MakeItem(0, 1))); 117 | Utilities.AssertThrows(() => ct.Insert(ct.EmptyItem)); 118 | Utilities.AssertThrows(() => ct.Insert(Item.MakeZeroItem())); 119 | Assert.IsFalse(ct.IsEmpty(0)); 120 | 121 | ct = new KukuTable( 122 | new KukuTableParameters 123 | { 124 | TableSize = 2, 125 | StashSize = 0, 126 | LocFuncCount = 1, 127 | LocFuncSeed = Item.MakeZeroItem(), 128 | MaxProbe = 10, 129 | EmptyItem = Item.MakeZeroItem() 130 | } 131 | ); 132 | 133 | Assert.IsTrue(ct.IsEmptyItem(ct[0])); 134 | Assert.IsTrue(ct.Insert(Item.MakeItem(1, 0))); 135 | 136 | // Collision 137 | Assert.IsFalse(ct.Insert(Item.MakeItem(0, 1))); 138 | 139 | // No collision 140 | Assert.IsTrue(ct.Insert(Item.MakeItem(0, 2))); 141 | 142 | Assert.IsFalse(ct.IsEmpty(0)); 143 | Assert.IsFalse(ct.IsEmpty(1)); 144 | } 145 | 146 | [TestMethod] 147 | public void Populate2() 148 | { 149 | KukuTable ct = new KukuTable( 150 | new KukuTableParameters 151 | { 152 | TableSize = 1 << 10, 153 | StashSize = 0, 154 | LocFuncCount = 2, 155 | LocFuncSeed = Item.MakeZeroItem(), 156 | MaxProbe = 10, 157 | EmptyItem = Item.MakeZeroItem() 158 | } 159 | ); 160 | 161 | for (uint i = 0; i < ct.TableSize; i++) 162 | { 163 | Assert.IsTrue(ct.IsEmpty(i)); 164 | } 165 | 166 | Assert.IsTrue(ct.Insert(Item.MakeItem(1, 0))); 167 | Assert.IsTrue(ct.Insert(Item.MakeItem(0, 1))); 168 | Assert.IsTrue(ct.Insert(Item.MakeItem(1, 1))); 169 | Assert.IsTrue(ct.Insert(Item.MakeItem(2, 2))); 170 | Utilities.AssertThrows(() => ct.Insert(ct.EmptyItem)); 171 | Utilities.AssertThrows(() => ct.Insert(Item.MakeZeroItem())); 172 | 173 | int nonEmpties = 0; 174 | for (uint i = 0; i < ct.TableSize; i++) 175 | { 176 | nonEmpties += ct.IsEmpty(i) ? 0 : 1; 177 | } 178 | Assert.AreEqual(4, nonEmpties); 179 | 180 | Assert.IsTrue(ct.Query(Item.MakeItem(1, 0)).Found); 181 | Assert.IsTrue(ct.Query(Item.MakeItem(0, 1)).Found); 182 | Assert.IsTrue(ct.Query(Item.MakeItem(1, 1)).Found); 183 | Assert.IsTrue(ct.Query(Item.MakeItem(2, 2)).Found); 184 | Assert.IsFalse(ct.Query(Item.MakeItem(3, 3)).Found); 185 | } 186 | 187 | [TestMethod] 188 | public void Populate3() 189 | { 190 | KukuTable ct = new KukuTable( 191 | new KukuTableParameters 192 | { 193 | TableSize = 1 << 10, 194 | StashSize = 0, 195 | LocFuncCount = 2, 196 | LocFuncSeed = Item.MakeZeroItem(), 197 | MaxProbe = 10, 198 | EmptyItem = Item.MakeRandomItem() 199 | } 200 | ); 201 | 202 | for (uint i = 0; i < ct.TableSize; i++) 203 | { 204 | Assert.IsTrue(ct.IsEmpty(i)); 205 | } 206 | 207 | Assert.IsTrue(ct.Insert(Item.MakeItem(0, 0))); 208 | Assert.IsTrue(ct.Insert(Item.MakeItem(1, 0))); 209 | Assert.IsTrue(ct.Insert(Item.MakeItem(0, 1))); 210 | Assert.IsTrue(ct.Insert(Item.MakeItem(1, 1))); 211 | Assert.IsTrue(ct.Insert(Item.MakeItem(2, 2))); 212 | Utilities.AssertThrows(() => ct.Insert(ct.EmptyItem)); 213 | 214 | // Fails 215 | Assert.IsFalse(ct.Insert(Item.MakeItem(2, 2))); 216 | 217 | int nonEmpties = 0; 218 | for (uint i = 0; i < ct.TableSize; i++) 219 | { 220 | nonEmpties += ct.IsEmpty(i) ? 0 : 1; 221 | } 222 | Assert.AreEqual(5, nonEmpties); 223 | 224 | Assert.IsTrue(ct.Query(Item.MakeItem(0, 0)).Found); 225 | Assert.IsTrue(ct.Query(Item.MakeItem(1, 0)).Found); 226 | Assert.IsTrue(ct.Query(Item.MakeItem(0, 1)).Found); 227 | Assert.IsTrue(ct.Query(Item.MakeItem(1, 1)).Found); 228 | Assert.IsTrue(ct.Query(Item.MakeItem(2, 2)).Found); 229 | Assert.IsFalse(ct.Query(Item.MakeItem(3, 3)).Found); 230 | } 231 | 232 | [TestMethod] 233 | public void Fill1() 234 | { 235 | KukuTable ct = new KukuTable( 236 | new KukuTableParameters 237 | { 238 | TableSize = 1 << 10, 239 | StashSize = 0, 240 | LocFuncCount = 2, 241 | LocFuncSeed = Item.MakeZeroItem(), 242 | MaxProbe = 10, 243 | EmptyItem = Item.MakeRandomItem() 244 | } 245 | ); 246 | 247 | List insertedItems = new List(); 248 | for (uint i = 0; i < 100; i++) 249 | { 250 | insertedItems.Add(Item.MakeRandomItem()); 251 | Assert.IsTrue(ct.Insert(insertedItems[insertedItems.Count - 1])); 252 | } 253 | foreach (Item it in insertedItems) 254 | { 255 | Assert.IsTrue(ct.Query(it).Found); 256 | } 257 | Assert.IsFalse(ct.Query(Item.MakeRandomItem()).Found); 258 | } 259 | 260 | [TestMethod] 261 | public void Fill2() 262 | { 263 | KukuTable ct = new KukuTable( 264 | new KukuTableParameters 265 | { 266 | TableSize = (1 << 10) - 1, 267 | StashSize = 0, 268 | LocFuncCount = 4, 269 | LocFuncSeed = Item.MakeZeroItem(), 270 | MaxProbe = 100, 271 | EmptyItem = Item.MakeRandomItem() 272 | } 273 | ); 274 | 275 | List insertedItems = new List(); 276 | for (uint i = 0; i < 600; i++) 277 | { 278 | insertedItems.Add(Item.MakeRandomItem()); 279 | Assert.IsTrue(ct.Insert(insertedItems[insertedItems.Count - 1])); 280 | } 281 | foreach (Item it in insertedItems) 282 | { 283 | Assert.IsTrue(ct.Query(it).Found); 284 | } 285 | Assert.IsFalse(ct.Query(Item.MakeRandomItem()).Found); 286 | } 287 | 288 | [TestMethod] 289 | public void Fill3() 290 | { 291 | KukuTable ct = new KukuTable( 292 | new KukuTableParameters 293 | { 294 | TableSize = (1 << 10) + 1, 295 | StashSize = 4, 296 | LocFuncCount = 2, 297 | LocFuncSeed = Item.MakeZeroItem(), 298 | MaxProbe = 100, 299 | EmptyItem = Item.MakeRandomItem() 300 | } 301 | ); 302 | 303 | List insertedItems = new List(); 304 | for (uint i = 0; i < 950; i++) 305 | { 306 | insertedItems.Add(Item.MakeRandomItem()); 307 | if (!ct.Insert(insertedItems[insertedItems.Count - 1])) 308 | { 309 | Item it = insertedItems.Find((Item it) => it.Equals(ct.LeftoverItem)); 310 | Assert.IsNotNull(it); 311 | Assert.IsFalse(ct.Query(ct.LeftoverItem).Found); 312 | insertedItems.Remove(it); 313 | } 314 | } 315 | 316 | foreach (Item it in insertedItems) 317 | { 318 | Assert.IsTrue(ct.Query(it).Found); 319 | } 320 | Assert.IsFalse(ct.Query(Item.MakeRandomItem()).Found); 321 | 322 | } 323 | 324 | [TestMethod] 325 | public void Locations() 326 | { 327 | uint lfc = 2; 328 | KukuTable ct = new KukuTable( 329 | new KukuTableParameters 330 | { 331 | TableSize = (1 << 10) + 1, 332 | StashSize = 4, 333 | LocFuncCount = lfc, 334 | LocFuncSeed = Item.MakeZeroItem(), 335 | MaxProbe = 100, 336 | EmptyItem = Item.MakeRandomItem() 337 | } 338 | ); 339 | 340 | for (ulong k = 0; k < 20; k++) 341 | { 342 | Item it = Item.MakeRandomItem(); 343 | HashSet allLocs = ct.AllLocations(it); 344 | 345 | bool collisionFound = false; 346 | for (uint i = 0; i < lfc; i++) 347 | { 348 | for (uint j = 0; j < i; j++) 349 | { 350 | collisionFound = collisionFound || (ct.Location(it, i) == ct.Location(it, j)); 351 | } 352 | } 353 | 354 | Assert.AreEqual(allLocs.Count < lfc, collisionFound); 355 | } 356 | } 357 | 358 | [TestMethod] 359 | public void RepeatedInsert() 360 | { 361 | KukuTable ct = new KukuTable( 362 | new KukuTableParameters 363 | { 364 | TableSize = 1 << 10, 365 | StashSize = 0, 366 | LocFuncCount = 4, 367 | LocFuncSeed = Item.MakeZeroItem(), 368 | MaxProbe = 10, 369 | EmptyItem = Item.MakeZeroItem() 370 | } 371 | ); 372 | 373 | Assert.IsTrue(ct.Insert(Item.MakeItem(1, 0))); 374 | Assert.IsTrue(ct.Insert(Item.MakeItem(0, 1))); 375 | Assert.IsTrue(ct.Insert(Item.MakeItem(1, 1))); 376 | Assert.IsTrue(ct.Insert(Item.MakeItem(2, 2))); 377 | 378 | Assert.IsFalse(ct.Insert(Item.MakeItem(1, 0))); 379 | Assert.IsFalse(ct.Insert(Item.MakeItem(0, 1))); 380 | Assert.IsFalse(ct.Insert(Item.MakeItem(1, 1))); 381 | Assert.IsFalse(ct.Insert(Item.MakeItem(2, 2))); 382 | } 383 | } 384 | } 385 | -------------------------------------------------------------------------------- /dotnet/tests/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "KukuDotNetTests": { 4 | "commandName": "Project", 5 | "nativeDebugging": false 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /dotnet/tests/Utilities.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using System; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Runtime.CompilerServices; 9 | 10 | namespace KukuNetTest 11 | { 12 | /// 13 | /// Test utilities 14 | /// 15 | public static class Utilities 16 | { 17 | internal static int WorkaroundInstanceCount { get; private set; } = 0; 18 | 19 | /// 20 | /// Assert that an exception of the given type is thrown. 21 | /// 22 | /// This is a workaround for a unit testing issue in VS 2019. 23 | /// When running unit tests a couple of them fail because of a FileNotFoundException being thrown instead 24 | /// of the expected exception. The FileNotFoundException is thrown in the boundary between a .Net call 25 | /// and a native method, so there is not really much we can do to fix it. As a workaround this method 26 | /// works as Assert.ThrowsException, but allows FileNotFoundException as well, and outputs a warning when 27 | /// it is found. 28 | /// 29 | /// Expected exception type 30 | /// Function to run that should throw an exception 31 | /// Path to the source file that called this method 32 | /// Line in the source file that called this method 33 | public static void AssertThrows(Func action, [CallerFilePath] string caller = "", [CallerLineNumber] int line = 0) where T : Exception 34 | { 35 | DoAssertThrow(() => { var result = action(); }, caller, line); 36 | } 37 | 38 | /// 39 | /// Assert that an exception of the given type is thrown. 40 | /// 41 | /// This is a workaround for a unit testing issue in VS 2019. 42 | /// When running unit tests a couple of them fail because of a FileNotFoundException being thrown instead 43 | /// of the expected exception. The FileNotFoundException is thrown in the boundary between a .Net call 44 | /// and a native method, so there is not really much we can do to fix it. As a workaround this method 45 | /// works as Assert.ThrowsException, but allows FileNotFoundException as well, and outputs a warning when 46 | /// it is found. 47 | /// 48 | /// Expected exception type 49 | /// Action to run that should throw an exception 50 | /// Path to the source file that called this method 51 | /// Line in the source file that called this method 52 | public static void AssertThrows(Action action, [CallerFilePath] string caller = "", [CallerLineNumber] int line = 0) where T : Exception 53 | { 54 | DoAssertThrow(action, caller, line); 55 | } 56 | 57 | private static void DoAssertThrow(Action action, string caller, int line) where T : Exception 58 | { 59 | string expectedStr = typeof(T).ToString(); 60 | 61 | try 62 | { 63 | action(); 64 | } 65 | catch (Exception ex) 66 | { 67 | if (ex is T) 68 | { 69 | // Expected exception has been thrown 70 | return; 71 | } 72 | 73 | // Workaround: Check if exception is FileNotFoundException 74 | if (ex is FileNotFoundException workaroundExc) 75 | { 76 | string workaroundStr = workaroundExc.GetType().ToString(); 77 | Trace.WriteLine($"WARNING: {caller}:{line}: Expected exception of type '{expectedStr}', got type '{workaroundStr}' instead."); 78 | WorkaroundInstanceCount++; 79 | return; 80 | } 81 | 82 | // Any other exception should fail. 83 | string actualStr = ex.GetType().ToString(); 84 | Assert.Fail($"{caller}:{line}: Expected exception of type '{expectedStr}', got type '{actualStr}' instead."); 85 | } 86 | 87 | Assert.Fail($"{caller}:{line}: Expected exception of type '{expectedStr}', no exception thrown."); 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. 3 | 4 | target_sources(kukuexamples 5 | PRIVATE 6 | ${CMAKE_CURRENT_LIST_DIR}/example.cpp 7 | ) 8 | -------------------------------------------------------------------------------- /examples/example.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | using namespace kuku; 11 | 12 | ostream &operator<<(ostream &stream, item_type item) 13 | { 14 | stream << item[1] << " " << item[0]; 15 | return stream; 16 | } 17 | 18 | void print_table(const KukuTable &table) 19 | { 20 | table_size_type col_count = 8; 21 | for (table_size_type i = 0; i < table.table_size(); i++) 22 | { 23 | const auto &item = table.table(i); 24 | cout << setw(5) 25 | << i << ": " << setw(5) << get_high_word(item) << "," << get_low_word(item) 26 | << ((i % col_count == col_count - 1) ? "\n" : "\t"); 27 | } 28 | 29 | cout << endl << endl << "Stash: " << endl; 30 | for (table_size_type i = 0; i < table.stash().size(); i++) 31 | { 32 | const auto &item = table.stash(i); 33 | cout << i << ": " << get_high_word(item) << "," << get_low_word(item) << endl; 34 | } 35 | cout << endl; 36 | } 37 | 38 | int main(int argc, char *argv[]) 39 | { 40 | if (argc != 5) 41 | { 42 | cout << "Usage: ./example table_size stash_size loc_func_count max_probe" << endl; 43 | cout << "E.g., ./example 256 2 4 100" << endl; 44 | 45 | return 0; 46 | } 47 | 48 | auto table_size = static_cast(atoi(argv[1])); 49 | auto stash_size = static_cast(atoi(argv[2])); 50 | uint8_t loc_func_count = static_cast(atoi(argv[3])); 51 | item_type loc_func_seed = make_random_item(); 52 | uint64_t max_probe = static_cast(atoi(argv[4])); 53 | item_type empty_item = make_item(0, 0); 54 | 55 | KukuTable table(table_size, stash_size, loc_func_count, loc_func_seed, max_probe, empty_item); 56 | 57 | uint64_t round_counter = 0; 58 | while (true) 59 | { 60 | cout << "Inserted " << round_counter * 20 << " items" << endl; 61 | cout << "Fill rate: " << table.fill_rate() << endl; 62 | 63 | char c; 64 | cin.get(c); 65 | 66 | for (uint64_t i = 0; i < 20; i++) 67 | { 68 | if (!table.insert(make_item(i + 1, round_counter + 1))) 69 | { 70 | cout << "Insertion failed: round_counter = " << round_counter << ", i = " << i << endl; 71 | cout << "Inserted successfully " << round_counter * 20 + i << " items" << endl; 72 | cout << "Fill rate: " << table.fill_rate() << endl; 73 | const auto &item = table.leftover_item(); 74 | cout << "Leftover item: " << get_high_word(item) << "," << get_low_word(item) << endl << endl; 75 | break; 76 | } 77 | } 78 | 79 | print_table(table); 80 | 81 | if (!table.is_empty_item(table.leftover_item())) 82 | { 83 | break; 84 | } 85 | 86 | round_counter++; 87 | } 88 | 89 | while (true) 90 | { 91 | cout << "Query item: "; 92 | char hw[64]; 93 | char lw[64]; 94 | cin.getline(hw, 10, ','); 95 | cin.getline(lw, 10, '\n'); 96 | item_type item = make_item(static_cast(atoi(lw)), static_cast(atoi(hw))); 97 | QueryResult res = table.query(item); 98 | cout << "Found: " << boolalpha << !!res << endl; 99 | if (res) 100 | { 101 | cout << "Location: " << res.location() << endl; 102 | cout << "In stash: " << boolalpha << res.in_stash() << endl; 103 | cout << "Hash function index: " << res.loc_func_index() << endl << endl; 104 | } 105 | } 106 | 107 | return 0; 108 | } 109 | -------------------------------------------------------------------------------- /pipelines/jobs.yml: -------------------------------------------------------------------------------- 1 | # This file selects the correct job definition based on system and buildType. 2 | 3 | parameters: 4 | debug: 'false' 5 | 6 | jobs: 7 | 8 | - ${{ if eq(parameters.name, 'Windows') }}: 9 | - job: ${{ parameters.name }} 10 | displayName: ${{ parameters.name }} 11 | pool: 12 | vmImage: 'windows-latest' 13 | steps: 14 | - template: windows.yml 15 | parameters: 16 | nuget_version: '5.4.0' 17 | ${{ if eq(parameters.debug, 'true') }}: 18 | configuration: 'debug' 19 | path: 'Debug' 20 | ${{ if eq(parameters.debug, 'false') }}: 21 | configuration: 'release' 22 | path: 'Release' 23 | 24 | - ${{ if eq(parameters.name, 'Linux') }}: 25 | - job: ${{ parameters.name }} 26 | displayName: ${{ parameters.name }} 27 | pool: 28 | vmImage: 'ubuntu-latest' 29 | steps: 30 | - script: | 31 | sudo add-apt-repository ppa:ubuntu-toolchain-r/test 32 | sudo apt-get update 33 | sudo apt-get install clang-9 34 | sudo update-alternatives --install /usr/bin/cc cc /usr/bin/clang-9 50 35 | sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++-9 50 36 | clang-9 --version 37 | clang++-9 --version 38 | which clang-9 39 | which clang++-9 40 | - template: nix.yml 41 | parameters: 42 | artifactName: linux-drop 43 | ${{ if eq(parameters.debug, 'true') }}: 44 | configuration: 'Debug' 45 | ${{ if eq(parameters.debug, 'false') }}: 46 | configuration: 'Release' 47 | 48 | - ${{ if eq(parameters.name, 'macOS') }}: 49 | - job: ${{ parameters.name }} 50 | displayName: ${{ parameters.name }} 51 | pool: 52 | vmImage: 'macos-latest' 53 | steps: 54 | - template: nix.yml 55 | parameters: 56 | artifactName: macos-drop 57 | ${{ if eq(parameters.debug, 'true') }}: 58 | configuration: 'Debug' 59 | ${{ if eq(parameters.debug, 'false') }}: 60 | configuration: 'Release' 61 | 62 | - ${{ if eq(parameters.name, 'Nuget') }}: 63 | - job: ${{ parameters.name }} 64 | displayName: ${{ parameters.name }} 65 | dependsOn: [Windows, Linux, macOs] 66 | pool: 67 | vmImage: 'windows-latest' 68 | steps: 69 | - template: nuget.yml 70 | parameters: 71 | nuget_version: '5.4.0' 72 | configuration: 'release' 73 | path: 'Release' 74 | -------------------------------------------------------------------------------- /pipelines/nix.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: CMake@1 3 | displayName: 'CMake Kuku' 4 | inputs: 5 | workingDirectory: '$(Build.SourcesDirectory)' 6 | cmakeArgs: '-DCMAKE_BUILD_TYPE=${{ parameters.configuration }} -DKUKU_BUILD_TESTS=ON -DKUKU_BUILD_KUKU_C=ON -DKUKU_BUILD_EXAMPLES=ON .' 7 | 8 | - script: | 9 | cd $BUILD_SOURCESDIRECTORY 10 | make 11 | displayName: 'Build Kuku' 12 | 13 | - script: | 14 | cd $BUILD_SOURCESDIRECTORY 15 | ./bin/kukutest 16 | displayName: 'Run unit tests' 17 | 18 | - task: UseDotNet@2 19 | displayName: 'Get .NET Core 3.1 SDK' 20 | inputs: 21 | packageType: 'sdk' 22 | version: '3.1.x' 23 | 24 | - task: DotNetCoreCLI@2 25 | displayName: 'Run dotnet unit tests' 26 | inputs: 27 | command: test 28 | projects: '**/KukuNetTests.csproj' 29 | arguments: '--configuration ${{ parameters.configuration }} --verbosity detailed' 30 | 31 | - task: CopyFiles@2 32 | displayName: 'Copy Files to: $(Build.ArtifactStagingDirectory)' 33 | inputs: 34 | SourceFolder: '$(Build.SourcesDirectory)/lib' 35 | Contents: 'libkukuc.*' 36 | TargetFolder: '$(Build.ArtifactStagingDirectory)/lib' 37 | 38 | - task: PublishBuildArtifacts@1 39 | displayName: 'Publish Artifact: drop' 40 | inputs: 41 | PathtoPublish: '$(Build.ArtifactStagingDirectory)' 42 | artifactName: ${{ parameters.artifactName }} 43 | -------------------------------------------------------------------------------- /pipelines/nuget.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: NuGetToolInstaller@1 3 | displayName: 'Use NuGet' 4 | inputs: 5 | versionSpec: ${{ parameters.nuget_version }} 6 | 7 | - task: DownloadBuildArtifacts@0 8 | inputs: 9 | downloadType: 'single' 10 | artifactName: 'windows-drop' 11 | downloadPath: '$(Build.ArtifactStagingDirectory)' 12 | 13 | - task: DownloadBuildArtifacts@0 14 | inputs: 15 | downloadType: 'single' 16 | artifactName: 'linux-drop' 17 | downloadPath: '$(Build.ArtifactStagingDirectory)' 18 | 19 | - task: DownloadBuildArtifacts@0 20 | inputs: 21 | downloadType: 'single' 22 | artifactName: 'macos-drop' 23 | downloadPath: '$(Build.ArtifactStagingDirectory)' 24 | 25 | - task: CopyFiles@2 26 | displayName: 'Copy Targets File to: $(Build.ArtifactStagingDirectory)' 27 | inputs: 28 | SourceFolder: '$(Build.SourcesDirectory)\dotnet\nuget\' 29 | Contents: 'KukuNet.targets' 30 | TargetFolder: '$(Build.ArtifactStagingDirectory)\windows-drop\dotnet\nuget\' 31 | 32 | - task: CopyFiles@2 33 | displayName: 'Copy License File to: $(Build.ArtifactStagingDirectory)' 34 | inputs: 35 | SourceFolder: '$(Build.SourcesDirectory)' 36 | Contents: 'LICENSE' 37 | TargetFolder: '$(Build.ArtifactStagingDirectory)\windows-drop\' 38 | 39 | - task: CopyFiles@2 40 | displayName: 'Copy Linux Files to: $(Build.ArtifactStagingDirectory)' 41 | inputs: 42 | SourceFolder: '$(Build.ArtifactStagingDirectory)\linux-drop\lib\' 43 | Contents: '*' 44 | TargetFolder: '$(Build.ArtifactStagingDirectory)\windows-drop\lib\' 45 | 46 | - task: CopyFiles@2 47 | displayName: 'Copy macOS Files to: $(Build.ArtifactStagingDirectory)' 48 | inputs: 49 | SourceFolder: '$(Build.ArtifactStagingDirectory)\macos-drop\lib\' 50 | Contents: '*' 51 | TargetFolder: '$(Build.ArtifactStagingDirectory)\windows-drop\lib\' 52 | 53 | - task: NuGetCommand@2 54 | displayName: 'Build NuGet Package' 55 | inputs: 56 | command: 'pack' 57 | packagesToPack: '$(Build.ArtifactStagingDirectory)\windows-drop\dotnet\nuget\KukuNet-multi.nuspec' 58 | packDestination: '$(Build.ArtifactStagingDirectory)\windows-drop\dotnet\nuget\Release' 59 | buildProperties: 'Configuration=Release;NUGET_WINDOWS_KUKU_C_PATH=$(Build.ArtifactStagingDirectory)\windows-drop\build\bin\Release\kukuc.dll;NUGET_LINUX_KUKU_C_PATH=$(Build.ArtifactStagingDirectory)\windows-drop\lib\libkukuc.so;NUGET_MACOS_KUKU_C_PATH=$(Build.ArtifactStagingDirectory)\windows-drop\lib\libkukuc.dylib' 60 | 61 | - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 62 | displayName: 'ESRP CodeSigning NuGet package' 63 | inputs: 64 | ConnectedServiceName: 'Kuku ESRP' 65 | FolderPath: '$(Build.ArtifactStagingDirectory)\windows-drop\dotnet\nuget\Release' 66 | Pattern: '*.nupkg' 67 | signConfigType: inlineSignParams 68 | inlineOperation: | 69 | [ 70 | { 71 | "keyCode": "CP-401405", 72 | "operationSetCode": "NuGetSign", 73 | "parameters": [ ], 74 | "toolName": "sign", 75 | "toolVersion": "1.0" 76 | }, 77 | { 78 | "keyCode": "CP-401405", 79 | "operationSetCode": "NuGetVerify", 80 | "parameters": [ ], 81 | "toolName": "sign", 82 | "toolVersion": "1.0" 83 | } 84 | ] 85 | 86 | - task: PublishBuildArtifacts@1 87 | displayName: 'Publish Artifact: drop' 88 | inputs: 89 | PathtoPublish: '$(Build.ArtifactStagingDirectory)' 90 | -------------------------------------------------------------------------------- /pipelines/pipeline-CI-Debug-Linux.yml: -------------------------------------------------------------------------------- 1 | # This defines a pipeline to build on Linux in Debug mode. 2 | 3 | trigger: 4 | batch: true 5 | branches: 6 | include: 7 | - master 8 | 9 | stages: 10 | - stage: build 11 | displayName: Build 12 | jobs: 13 | - template: jobs.yml 14 | parameters: 15 | debug: true 16 | name: Linux -------------------------------------------------------------------------------- /pipelines/pipeline-CI-Debug-Windows.yml: -------------------------------------------------------------------------------- 1 | # This defines a pipeline to build on Windows in Debug mode. 2 | 3 | trigger: 4 | batch: true 5 | branches: 6 | include: 7 | - master 8 | 9 | stages: 10 | - stage: build 11 | displayName: Build 12 | jobs: 13 | - template: jobs.yml 14 | parameters: 15 | debug: true 16 | name: Windows -------------------------------------------------------------------------------- /pipelines/pipeline-CI-Debug-macOS.yml: -------------------------------------------------------------------------------- 1 | # This defines a pipeline to build on macOS in Debug mode. 2 | 3 | trigger: 4 | batch: true 5 | branches: 6 | include: 7 | - master 8 | 9 | stages: 10 | - stage: build 11 | displayName: Build 12 | jobs: 13 | - template: jobs.yml 14 | parameters: 15 | debug: true 16 | name: macOS -------------------------------------------------------------------------------- /pipelines/pipeline-CI-Release-All.yml: -------------------------------------------------------------------------------- 1 | # This defines a pipeline to build on Windows, Linux, and macOS in Release mode. 2 | 3 | trigger: none 4 | 5 | stages: 6 | - stage: build 7 | displayName: Build 8 | jobs: 9 | - template: jobs.yml 10 | parameters: 11 | debug: false 12 | name: Windows 13 | - template: jobs.yml 14 | parameters: 15 | debug: false 16 | name: Linux 17 | - template: jobs.yml 18 | parameters: 19 | debug: false 20 | name: macOS 21 | - template: jobs.yml 22 | parameters: 23 | debug: false 24 | name: Nuget -------------------------------------------------------------------------------- /pipelines/pipeline-Nuget.yml: -------------------------------------------------------------------------------- 1 | # This defines a pipeline to pack into Nuget. 2 | 3 | trigger: none 4 | 5 | stages: 6 | - stage: build 7 | displayName: Build 8 | jobs: 9 | - template: jobs.yml 10 | parameters: 11 | debug: false 12 | name: Windows 13 | - template: jobs.yml 14 | parameters: 15 | debug: false 16 | name: Linux 17 | - template: jobs.yml 18 | parameters: 19 | debug: false 20 | name: macOS 21 | - template: jobs.yml 22 | parameters: 23 | debug: false 24 | name: Nuget -------------------------------------------------------------------------------- /pipelines/pipeline-PR-Debug-All.yml: -------------------------------------------------------------------------------- 1 | # This defines a pipeline to build on Windows, Linux, and macOS in Debug mode. 2 | 3 | pr: 4 | batch: true 5 | branches: 6 | include: 7 | - contrib 8 | 9 | stages: 10 | - stage: build 11 | displayName: Build 12 | jobs: 13 | - template: jobs.yml 14 | parameters: 15 | debug: true 16 | name: Windows 17 | - template: jobs.yml 18 | parameters: 19 | debug: true 20 | name: Linux 21 | - template: jobs.yml 22 | parameters: 23 | debug: true 24 | name: macOS -------------------------------------------------------------------------------- /pipelines/windows.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - task: NuGetToolInstaller@1 3 | displayName: 'Use NuGet' 4 | inputs: 5 | versionSpec: ${{ parameters.nuget_version }} 6 | 7 | - task: NuGetCommand@2 8 | displayName: 'NuGet restore Configuration Packages' 9 | inputs: 10 | restoreSolution: config/packages.config 11 | restoreDirectory: ConfigPackages 12 | 13 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2 14 | displayName: 'Run CredScan' 15 | inputs: 16 | toolMajorVersion: 'V2' 17 | outputFormat: sarif 18 | debugMode: false 19 | 20 | - ${{ if eq(parameters.configuration, 'Release') }}: 21 | - task: UseDotNet@2 22 | displayName: 'Get .NET Core 2.1 SDK' 23 | inputs: 24 | packageType: 'sdk' 25 | version: '2.1.x' 26 | 27 | - task: UseDotNet@2 28 | displayName: 'Get .NET Core 3.1 SDK' 29 | inputs: 30 | packageType: 'sdk' 31 | version: '3.1.302' 32 | 33 | - ${{ if eq(parameters.configuration, 'Release') }}: 34 | - task: PowerShell@2 35 | displayName: 'Download Strong Name certificate' 36 | inputs: 37 | targetType: 'inline' 38 | script: | 39 | # Get signing certificate 40 | $CertOutFile = Join-Path -Path $env:BUILD_SOURCESDIRECTORY -ChildPath dotnet\src\KukuNetCert.snk 41 | if (Test-Path env:KukuNetSigningCertificate) { 42 | Invoke-WebRequest -Uri "$env:KukuNetSigningCertificate" -OutFile $CertOutFile 43 | } 44 | 45 | - task: CMake@1 46 | displayName: 'Configure Kuku' 47 | inputs: 48 | cmakeArgs: .. -DCMAKE_BUILD_TYPE='${{ parameters.configuration }}' -DKUKU_BUILD_TESTS=ON -DKUKU_BUILD_EXAMPLES=ON -DKUKU_BUILD_KUKU_C=ON -DKUKU_SECURE_COMPILE_OPTIONS=1 49 | workingDirectory: '$(Build.SourcesDirectory)/build' 50 | 51 | - task: MSBuild@1 52 | displayName: 'Build Kuku' 53 | inputs: 54 | solution: '$(Build.SourcesDirectory)/build/kuku.sln' 55 | msbuildArchitecture: 'x64' 56 | platform: 'x64' 57 | configuration: '${{ parameters.configuration }}' 58 | 59 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-roslynanalyzers.RoslynAnalyzers@2 60 | displayName: 'Run Roslyn Analyzers' 61 | 62 | - task: VSTest@2 63 | displayName: 'VsTest - native tests' 64 | inputs: 65 | testAssemblyVer2: | 66 | **\${{ parameters.configuration }}\*test*.dll 67 | **\${{ parameters.configuration }}\*test*.exe 68 | !**\obj\** 69 | platform: 'x64' 70 | configuration: ${{ parameters.configuration }} 71 | diagnosticsEnabled: True 72 | 73 | - task: NuGetCommand@2 74 | displayName: 'NuGet restore from Solution' 75 | inputs: 76 | command: 'restore' 77 | restoreSolution: 'dotnet/KukuNet.sln' 78 | feedsToUse: 'select' 79 | 80 | - task: MSBuild@1 81 | displayName: 'Build KukuNet' 82 | inputs: 83 | solution: '$(Build.SourcesDirectory)/dotnet/KukuNet.sln' 84 | msbuildArchitecture: 'x64' 85 | platform: 'x64' 86 | configuration: '${{ parameters.configuration }}' 87 | 88 | - task: VSTest@2 89 | displayName: 'VsTest - dotnet tests' 90 | inputs: 91 | testAssemblyVer2: | 92 | **\${{ parameters.configuration }}\**\KukuNetTests.dll 93 | !**\obj\** 94 | configuration: ${{ parameters.configuration }} 95 | diagnosticsEnabled: True 96 | 97 | - task: PublishSymbols@2 98 | displayName: 'Publish symbols path' 99 | inputs: 100 | searchPattern: '**\bin\**\*.pdb' 101 | PublishSymbols: false 102 | continueOnError: true 103 | 104 | - ${{ if eq(parameters.configuration, 'Release') }}: 105 | - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 106 | displayName: 'ESRP Add Strong Name' 107 | inputs: 108 | ConnectedServiceName: 'Kuku ESRP' 109 | FolderPath: build/bin/dotnet/${{ parameters.configuration }}/netstandard2.0 110 | Pattern: 'KukuNet.dll' 111 | signConfigType: inlineSignParams 112 | inlineOperation: | 113 | [ 114 | { 115 | "KeyCode" : "CP-235845-SN", 116 | "OperationCode" : "StrongNameSign", 117 | "Parameters" : {}, 118 | "ToolName" : "sign", 119 | "ToolVersion" : "1.0" 120 | }, 121 | { 122 | "KeyCode" : "CP-235845-SN", 123 | "OperationCode" : "StrongNameVerify", 124 | "Parameters" : {}, 125 | "ToolName" : "sign", 126 | "ToolVersion" : "1.0" 127 | } 128 | ] 129 | 130 | - ${{ if eq(parameters.configuration, 'Release') }}: 131 | - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 132 | displayName: 'ESRP CodeSigning .NET Standard 2.0' 133 | inputs: 134 | ConnectedServiceName: 'Kuku ESRP' 135 | FolderPath: build/bin/dotnet/${{ parameters.configuration }}/netstandard2.0 136 | Pattern: 'KukuNet.dll' 137 | signConfigType: inlineSignParams 138 | inlineOperation: | 139 | [ 140 | { 141 | "KeyCode": "CP-230012", 142 | "OperationCode": "SigntoolSign", 143 | "Parameters": { 144 | "OpusName": "Microsoft.Research.Kuku", 145 | "OpusInfo": "https://github.com/microsoft/Kuku", 146 | "FileDigest": "/fd \"SHA256\"", 147 | "PageHash": "/PH", 148 | "TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" 149 | }, 150 | "ToolName": "sign", 151 | "ToolVersion": "1.0" 152 | }, 153 | { 154 | "KeyCode": "CP-230012", 155 | "OperationCode": "SigntoolVerify", 156 | "Parameters": {}, 157 | "ToolName": "sign", 158 | "ToolVersion": "1.0" 159 | } 160 | ] 161 | 162 | - ${{ if eq(parameters.configuration, 'Release') }}: 163 | - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 164 | displayName: 'ESRP CodeSigning native' 165 | inputs: 166 | ConnectedServiceName: 'Kuku ESRP' 167 | FolderPath: build/bin/${{ parameters.configuration }} 168 | Pattern: 'kukuc.dll' 169 | signConfigType: inlineSignParams 170 | inlineOperation: | 171 | [ 172 | { 173 | "KeyCode": "CP-230012", 174 | "OperationCode": "SigntoolSign", 175 | "Parameters": { 176 | "OpusName": "Microsoft.Research.Kuku", 177 | "OpusInfo": "https://github.com/microsoft/Kuku", 178 | "FileDigest": "/fd \"SHA256\"", 179 | "PageHash": "/PH", 180 | "TimeStamp": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" 181 | }, 182 | "ToolName": "sign", 183 | "ToolVersion": "1.0" 184 | }, 185 | { 186 | "KeyCode": "CP-230012", 187 | "OperationCode": "SigntoolVerify", 188 | "Parameters": {}, 189 | "ToolName": "sign", 190 | "ToolVersion": "1.0" 191 | } 192 | ] 193 | 194 | - task: CopyFiles@2 195 | displayName: 'Copy Files to: $(Build.ArtifactStagingDirectory)' 196 | inputs: 197 | SourceFolder: '$(Build.SourcesDirectory)' 198 | Contents: '**\${{ parameters.configuration }}\**\?(*.exe|*.dll|*.lib|*.xml)' 199 | TargetFolder: '$(Build.ArtifactStagingDirectory)' 200 | 201 | - task: CopyFiles@2 202 | displayName: 'Copy Nuspec File to: $(Build.ArtifactStagingDirectory)' 203 | inputs: 204 | SourceFolder: '$(Build.SourcesDirectory)\dotnet\nuget\' 205 | Contents: 'KukuNet-multi.nuspec' 206 | TargetFolder: '$(Build.ArtifactStagingDirectory)\dotnet\nuget\' 207 | 208 | - task: CopyFiles@2 209 | displayName: 'Copy Targets File to: $(Build.ArtifactStagingDirectory)' 210 | inputs: 211 | SourceFolder: '$(Build.SourcesDirectory)\dotnet\nuget\' 212 | Contents: 'KukuNet.targets' 213 | TargetFolder: '$(Build.ArtifactStagingDirectory)\dotnet\nuget\' 214 | 215 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-binskim.BinSkim@3 216 | displayName: 'Run BinSkim' 217 | inputs: 218 | InputType: Basic 219 | AnalyzeTarget: '$(Build.ArtifactStagingDirectory)\**\${{ parameters.configuration }}\**\kukuc.dll' 220 | AnalyzeSymPath: '**\bin\**\*.pdb' 221 | enabled: true 222 | 223 | - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 224 | displayName: 'Component Detection' 225 | 226 | - task: securedevelopmentteam.vss-secure-development-tools.build-task-publishsecurityanalysislogs.PublishSecurityAnalysisLogs@2 227 | displayName: 'Publish Security Analysis Logs' 228 | 229 | - task: PublishBuildArtifacts@1 230 | displayName: 'Publish Artifact: drop' 231 | inputs: 232 | PathtoPublish: '$(Build.ArtifactStagingDirectory)' 233 | artifactName: windows-drop 234 | -------------------------------------------------------------------------------- /scripts/clang-format-all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright (c) Microsoft Corporation. All rights reserved. 4 | # Licensed under the MIT license. 5 | 6 | BASE_DIR=$(dirname "$0") 7 | KUKU_ROOT_DIR=$BASE_DIR/../ 8 | shopt -s globstar 9 | clang-format -i $KUKU_ROOT_DIR/src/**/*.h 10 | clang-format -i $KUKU_ROOT_DIR/src/**/*.cpp -------------------------------------------------------------------------------- /src/kuku/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. 3 | 4 | # Source files in this directory 5 | set(KUKU_SOURCE_FILES ${KUKU_SOURCE_FILES} 6 | ${CMAKE_CURRENT_LIST_DIR}/internal/blake2b.c 7 | ${CMAKE_CURRENT_LIST_DIR}/internal/blake2xb.c 8 | ${CMAKE_CURRENT_LIST_DIR}/kuku.cpp 9 | ) 10 | 11 | # Add header files for installation 12 | install( 13 | FILES 14 | ${CMAKE_CURRENT_LIST_DIR}/internal/blake2.h 15 | ${CMAKE_CURRENT_LIST_DIR}/internal/blake2-impl.h 16 | ${CMAKE_CURRENT_LIST_DIR}/internal/hash.h 17 | DESTINATION 18 | ${KUKU_INCLUDES_INSTALL_DIR}/kuku/internal 19 | ) 20 | 21 | install( 22 | FILES 23 | ${CMAKE_CURRENT_LIST_DIR}/common.h 24 | ${CMAKE_CURRENT_LIST_DIR}/kuku.h 25 | ${CMAKE_CURRENT_LIST_DIR}/locfunc.h 26 | DESTINATION 27 | ${KUKU_INCLUDES_INSTALL_DIR}/kuku 28 | ) 29 | 30 | set(KUKU_SOURCE_FILES ${KUKU_SOURCE_FILES} PARENT_SCOPE) 31 | -------------------------------------------------------------------------------- /src/kuku/c/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. 3 | 4 | # Source files in this directory 5 | target_sources(kukuc PRIVATE 6 | ${CMAKE_CURRENT_LIST_DIR}/kuku_ref.cpp 7 | ) 8 | 9 | # Add header files for installation 10 | install( 11 | FILES 12 | ${CMAKE_CURRENT_LIST_DIR}/kuku_ref.h 13 | DESTINATION 14 | ${KUKU_INCLUDES_INSTALL_DIR}/kuku/c 15 | ) 16 | -------------------------------------------------------------------------------- /src/kuku/c/framework.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #pragma once 5 | 6 | #if defined(_MSC_VER) 7 | // Microsoft 8 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers 9 | // Windows Header Files 10 | #include 11 | #endif 12 | -------------------------------------------------------------------------------- /src/kuku/c/kuku_ref.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #include "pch.h" 5 | #include "kuku_ref.h" 6 | #include 7 | 8 | KUKU_C_FUNC(void *) KukuTable_Create( 9 | uint32_t table_size, uint32_t stash_size, uint32_t loc_func_count, uint64_t *loc_func_seed, uint64_t max_probe, 10 | uint64_t *empty_item) 11 | { 12 | kuku::item_type kuku_loc_func_seed = kuku::make_item(loc_func_seed[0], loc_func_seed[1]); 13 | kuku::item_type kuku_empty_item = kuku::make_item(empty_item[0], empty_item[1]); 14 | return new kuku::KukuTable(table_size, stash_size, loc_func_count, kuku_loc_func_seed, max_probe, kuku_empty_item); 15 | } 16 | 17 | KUKU_C_FUNC(bool) KukuTable_Insert(void *kuku_table, uint64_t *item) 18 | { 19 | auto ptr = reinterpret_cast(kuku_table); 20 | kuku::item_type kuku_item = kuku::make_item(item[0], item[1]); 21 | bool b = ptr->insert(kuku_item); 22 | return b; 23 | } 24 | 25 | KUKU_C_FUNC(bool) KukuTable_Query(void *kuku_table, uint64_t *item, QueryResultData *query_result) 26 | { 27 | auto ptr = reinterpret_cast(kuku_table); 28 | kuku::item_type kuku_item = kuku::make_item(item[0], item[1]); 29 | kuku::QueryResult res = ptr->query(kuku_item); 30 | query_result->found = !!res; 31 | query_result->in_stash = res.in_stash(); 32 | query_result->location = res.location(); 33 | query_result->loc_func_index = res.loc_func_index(); 34 | return query_result->found; 35 | } 36 | 37 | KUKU_C_FUNC(bool) KukuTable_IsEmptyItem(void *kuku_table, uint64_t *item) 38 | { 39 | auto ptr = reinterpret_cast(kuku_table); 40 | kuku::item_type kuku_item = kuku::make_item(item[0], item[1]); 41 | return ptr->is_empty_item(kuku_item); 42 | } 43 | 44 | KUKU_C_FUNC(void) KukuTable_EmptyItem(void *kuku_table, uint64_t *item) 45 | { 46 | auto ptr = reinterpret_cast(kuku_table); 47 | kuku::item_type kuku_item = ptr->empty_item(); 48 | item[0] = kuku::get_low_word(kuku_item); 49 | item[1] = kuku::get_high_word(kuku_item); 50 | } 51 | 52 | KUKU_C_FUNC(void) KukuTable_LeftoverItem(void *kuku_table, uint64_t *item) 53 | { 54 | auto ptr = reinterpret_cast(kuku_table); 55 | kuku::item_type kuku_item = ptr->leftover_item(); 56 | item[0] = kuku::get_low_word(kuku_item); 57 | item[1] = kuku::get_high_word(kuku_item); 58 | } 59 | 60 | KUKU_C_FUNC(double) KukuTable_FillRate(void *kuku_table) 61 | { 62 | auto ptr = reinterpret_cast(kuku_table); 63 | return ptr->fill_rate(); 64 | } 65 | 66 | KUKU_C_FUNC(uint32_t) KukuTable_LocFuncCount(void *kuku_table) 67 | { 68 | auto ptr = reinterpret_cast(kuku_table); 69 | return ptr->loc_func_count(); 70 | } 71 | 72 | KUKU_C_FUNC(void) KukuTable_Table(void *kuku_table, uint32_t index, uint64_t *item) 73 | { 74 | auto ptr = reinterpret_cast(kuku_table); 75 | kuku::item_type kuku_item = ptr->table(index); 76 | item[0] = kuku::get_low_word(kuku_item); 77 | item[1] = kuku::get_high_word(kuku_item); 78 | } 79 | 80 | KUKU_C_FUNC(uint32_t) KukuTable_TableSize(void *kuku_table) 81 | { 82 | auto ptr = reinterpret_cast(kuku_table); 83 | return ptr->table_size(); 84 | } 85 | 86 | KUKU_C_FUNC(void) KukuTable_Stash(void *kuku_table, uint32_t index, uint64_t *item) 87 | { 88 | auto ptr = reinterpret_cast(kuku_table); 89 | kuku::item_type kuku_item = ptr->stash(index); 90 | item[0] = kuku::get_low_word(kuku_item); 91 | item[1] = kuku::get_high_word(kuku_item); 92 | } 93 | 94 | KUKU_C_FUNC(uint32_t) KukuTable_StashSize(void *kuku_table) 95 | { 96 | auto ptr = reinterpret_cast(kuku_table); 97 | return ptr->stash_size(); 98 | } 99 | 100 | KUKU_C_FUNC(uint32_t) KukuTable_Location(void *kuku_table, uint64_t *item, uint32_t loc_func_index) 101 | { 102 | auto ptr = reinterpret_cast(kuku_table); 103 | kuku::item_type kuku_item = kuku::make_item(item[0], item[1]); 104 | return ptr->location(kuku_item, loc_func_index); 105 | } 106 | 107 | KUKU_C_FUNC(void) KukuTable_AllLocations(void *kuku_table, uint64_t *item, uint32_t *locations, uint32_t *count) 108 | { 109 | auto ptr = reinterpret_cast(kuku_table); 110 | kuku::item_type kuku_item = kuku::make_item(item[0], item[1]); 111 | std::set loc_set = ptr->all_locations(kuku_item); 112 | for (auto loc : loc_set) 113 | { 114 | *locations++ = loc; 115 | } 116 | *count = static_cast(loc_set.size()); 117 | } 118 | 119 | KUKU_C_FUNC(void) KukuTable_ClearTable(void *kuku_table) 120 | { 121 | auto ptr = reinterpret_cast(kuku_table); 122 | ptr->clear_table(); 123 | } 124 | 125 | KUKU_C_FUNC(void) Common_SetRandomItem(uint64_t *item) 126 | { 127 | kuku::item_type kuku_item = kuku::make_random_item(); 128 | item[0] = kuku::get_low_word(kuku_item); 129 | item[1] = kuku::get_high_word(kuku_item); 130 | } 131 | 132 | KUKU_C_FUNC(void) Common_IncrementItem(uint64_t *item) 133 | { 134 | kuku::item_type kuku_item = kuku::make_item(item[0], item[1]); 135 | kuku::increment_item(kuku_item); 136 | item[0] = kuku::get_low_word(kuku_item); 137 | item[1] = kuku::get_high_word(kuku_item); 138 | } 139 | 140 | KUKU_C_FUNC(uint32_t) Common_MinTableSize() 141 | { 142 | return kuku::min_table_size; 143 | } 144 | 145 | KUKU_C_FUNC(uint32_t) Common_MaxTableSize() 146 | { 147 | return kuku::max_table_size; 148 | } 149 | 150 | KUKU_C_FUNC(uint32_t) Common_MinLocFuncCount() 151 | { 152 | return kuku::min_loc_func_count; 153 | } 154 | 155 | KUKU_C_FUNC(uint32_t) Common_MaxLocFuncCount() 156 | { 157 | return kuku::max_loc_func_count; 158 | } 159 | -------------------------------------------------------------------------------- /src/kuku/c/kuku_ref.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #pragma once 5 | 6 | #include "kuku/kuku.h" 7 | #include 8 | #include 9 | 10 | // Check that std::size_t is 64 bits 11 | static_assert(sizeof(std::size_t) == 8, "Require sizeof(std::size_t) == 8"); 12 | 13 | #ifdef _MSC_VER 14 | 15 | // Check that architecture (platform) is x64 16 | #ifndef _WIN64 17 | static_assert(false, "Require architecture == x64"); 18 | #endif 19 | 20 | #if defined(KUKU_C_EXPORTS) || defined(kuku_c_EXPORTS) || defined(kukuc_EXPORTS) 21 | #define KUKU_C_DECOR extern "C" __declspec(dllexport) 22 | #else 23 | #define KUKU_C_DECOR extern "C" __declspec(dllimport) 24 | #endif 25 | 26 | #define KUKU_C_CALL __cdecl 27 | 28 | #else // _MSC_VER 29 | 30 | #define KUKU_C_DECOR extern "C" 31 | #define KUKU_C_CALL 32 | 33 | #endif // _MSC_VER 34 | 35 | #define KUKU_C_FUNC(x) KUKU_C_DECOR x KUKU_C_CALL 36 | 37 | typedef struct 38 | { 39 | bool found; 40 | bool in_stash; 41 | uint32_t location; 42 | uint32_t loc_func_index; 43 | } QueryResultData; 44 | 45 | KUKU_C_FUNC(void *) 46 | KukuTable_Create( 47 | uint32_t table_size, uint32_t stash_size, uint32_t loc_func_count, uint64_t *loc_func_seed, uint64_t max_probe, 48 | uint64_t *empty_item); 49 | 50 | KUKU_C_FUNC(bool) KukuTable_Insert(void *kuku_table, uint64_t *item); 51 | 52 | KUKU_C_FUNC(bool) KukuTable_Query(void *kuku_table, uint64_t *item, QueryResultData *query_result); 53 | 54 | KUKU_C_FUNC(bool) KukuTable_IsEmptyItem(void *kuku_table, uint64_t *item); 55 | 56 | KUKU_C_FUNC(void) KukuTable_EmptyItem(void *kuku_table, uint64_t *item); 57 | 58 | KUKU_C_FUNC(void) KukuTable_LeftoverItem(void *kuku_table, uint64_t *item); 59 | 60 | KUKU_C_FUNC(double) KukuTable_FillRate(void *kuku_table); 61 | 62 | KUKU_C_FUNC(uint32_t) KukuTable_LocFuncCount(void *kuku_table); 63 | 64 | KUKU_C_FUNC(void) KukuTable_Table(void *kuku_table, uint32_t index, uint64_t *item); 65 | 66 | KUKU_C_FUNC(uint32_t) KukuTable_TableSize(void *kuku_table); 67 | 68 | KUKU_C_FUNC(void) KukuTable_Stash(void *kuku_table, uint32_t index, uint64_t *item); 69 | 70 | KUKU_C_FUNC(uint32_t) KukuTable_StashSize(void *kuku_table); 71 | 72 | KUKU_C_FUNC(uint32_t) KukuTable_Location(void *kuku_table, uint64_t *item, uint32_t loc_func_index); 73 | 74 | KUKU_C_FUNC(void) KukuTable_AllLocations(void *kuku_table, uint64_t *item, uint32_t *locations, uint32_t *count); 75 | 76 | KUKU_C_FUNC(void) KukuTable_ClearTable(void *kuku_table); 77 | 78 | KUKU_C_FUNC(void) Common_SetRandomItem(uint64_t *item); 79 | 80 | KUKU_C_FUNC(void) Common_IncrementItem(uint64_t *item); 81 | 82 | KUKU_C_FUNC(uint32_t) Common_MinTableSize(); 83 | 84 | KUKU_C_FUNC(uint32_t) Common_MaxTableSize(); 85 | 86 | KUKU_C_FUNC(uint32_t) Common_MinLocFuncCount(); 87 | 88 | KUKU_C_FUNC(uint32_t) Common_MaxLocFuncCount(); 89 | -------------------------------------------------------------------------------- /src/kuku/c/pch.cpp: -------------------------------------------------------------------------------- 1 | // pch.cpp: source file corresponding to the pre-compiled header 2 | 3 | #include "pch.h" 4 | 5 | // When you are using pre-compiled headers, this source file is necessary for compilation to succeed. 6 | -------------------------------------------------------------------------------- /src/kuku/c/pch.h: -------------------------------------------------------------------------------- 1 | // pch.h: This is a precompiled header file. 2 | // Files listed below are compiled only once, improving build performance for future builds. 3 | // This also affects IntelliSense performance, including code completion and many code browsing features. 4 | // However, files listed here are ALL re-compiled if any one of them is updated between builds. 5 | // Do not add files here that you will be updating frequently as this negates the performance advantage. 6 | 7 | #ifndef PCH_H 8 | #define PCH_H 9 | 10 | // add headers that you want to pre-compile here 11 | #include "framework.h" 12 | 13 | #endif // PCH_H 14 | -------------------------------------------------------------------------------- /src/kuku/common.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #pragma once 5 | 6 | // Include kuku/config.h only on platforms with CMake configuration (not MSVC). 7 | #ifdef _MSC_VER 8 | #ifdef _DEBUG 9 | #define KUKU_DEBUG 10 | #else 11 | #undef KUKU_DEBUG 12 | #endif 13 | #else 14 | #include "kuku/internal/config.h" 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | namespace kuku 27 | { 28 | /** 29 | The type that represents a 128-bit item that can be added to the hash table. 30 | */ 31 | using item_type = std::array; 32 | 33 | /** 34 | The type that represents a location in the hash table. 35 | */ 36 | using location_type = std::uint32_t; 37 | 38 | /** 39 | The type that represents the size of a hash table. 40 | */ 41 | using table_size_type = location_type; 42 | 43 | /** 44 | The smallest allowed table size. 45 | */ 46 | constexpr table_size_type min_table_size = table_size_type(1); 47 | 48 | /** 49 | The largest allowed table size. 50 | */ 51 | constexpr table_size_type max_table_size = table_size_type(1) << 30; 52 | 53 | /** 54 | The smallest allowed number of hash functions. 55 | */ 56 | constexpr std::uint32_t min_loc_func_count = 1; 57 | 58 | /** 59 | The largest allowed number of hash functions. This must be a power of two for correct behavior. 60 | */ 61 | constexpr std::uint32_t max_loc_func_count = 32; 62 | 63 | constexpr int bytes_per_uint64 = sizeof(std::uint64_t); 64 | 65 | constexpr int bytes_per_item = sizeof(item_type); 66 | 67 | /** 68 | Generates a random 64-bit unsigned integer. 69 | */ 70 | inline std::uint64_t random_uint64() 71 | { 72 | std::random_device rd; 73 | return (static_cast(rd()) << 32) | static_cast(rd()); 74 | } 75 | 76 | /** 77 | Return a reference to the low-word of the item. 78 | */ 79 | inline std::uint64_t &get_low_word(item_type &item) 80 | { 81 | return *reinterpret_cast(item.data()); 82 | } 83 | 84 | /** 85 | Return a reference to the high-word of the item. 86 | */ 87 | inline std::uint64_t &get_high_word(item_type &item) 88 | { 89 | return *reinterpret_cast(item.data() + 8); 90 | } 91 | 92 | /** 93 | Return a reference to the low-word of the item. 94 | */ 95 | inline std::uint64_t get_low_word(const item_type &item) 96 | { 97 | return *reinterpret_cast(item.data()); 98 | } 99 | 100 | /** 101 | Return a reference to the high-word of the item. 102 | */ 103 | inline std::uint64_t get_high_word(const item_type &item) 104 | { 105 | return *reinterpret_cast(item.data() + 8); 106 | } 107 | 108 | /** 109 | Sets the value of a given hash table item from a given buffer. 110 | 111 | @param[in] in The buffer to set the value from 112 | @param[out] destination The hash table item whose value is to be set 113 | */ 114 | inline void set_item(const unsigned char *in, item_type &destination) noexcept 115 | { 116 | std::copy_n(in, bytes_per_item, destination.data()); 117 | } 118 | 119 | /** 120 | Creates a new hash table item and sets its value from a given buffer. 121 | 122 | @param[in] in The buffer to set the value from 123 | */ 124 | inline item_type make_item(const unsigned char *in) noexcept 125 | { 126 | item_type out; 127 | set_item(in, out); 128 | return out; 129 | } 130 | 131 | /** 132 | Sets the value of a given hash table item from a pair of two 64-bit words. 133 | 134 | @param[in] low_word The low 64 bits of the value of the item 135 | @param[in] high_word The high 64 bits of the value of the item 136 | @param[out] destination The hash table item whose value is to be set 137 | */ 138 | inline void set_item(std::uint64_t low_word, std::uint64_t high_word, item_type &destination) noexcept 139 | { 140 | get_low_word(destination) = low_word; 141 | get_high_word(destination) = high_word; 142 | } 143 | 144 | /** 145 | Creates a hash table item and sets its value from a pair of two 64-bit words. 146 | 147 | @param[in] low_word The lowest 64 bits of the value of the item 148 | @param[in] high_word The highest 64 bits of the value of the item 149 | */ 150 | inline item_type make_item(std::uint64_t low_word, std::uint64_t high_word) noexcept 151 | { 152 | item_type item; 153 | set_item(low_word, high_word, item); 154 | return item; 155 | } 156 | 157 | /** 158 | Creates a zero hash table item. 159 | */ 160 | inline item_type make_zero_item() noexcept 161 | { 162 | return item_type{}; 163 | } 164 | 165 | /** 166 | Sets a given hash table item to zero. 167 | 168 | @param[out] destination The hash table item whose value is to be set 169 | */ 170 | inline void set_zero_item(item_type &destination) noexcept 171 | { 172 | destination = item_type{}; 173 | } 174 | 175 | /** 176 | Sets the value of a given hash table item to all one-bits. 177 | 178 | @param[out] destination The hash table item whose value is to be set 179 | */ 180 | inline void set_all_ones_item(item_type &destination) noexcept 181 | { 182 | get_low_word(destination) = ~std::uint64_t(0); 183 | get_high_word(destination) = ~std::uint64_t(0); 184 | } 185 | 186 | /** 187 | Creates a hash table item and sets its value to all one-bits. 188 | */ 189 | inline item_type make_all_ones_item() noexcept 190 | { 191 | item_type item; 192 | set_all_ones_item(item); 193 | return item; 194 | } 195 | 196 | /** 197 | Returns whether a given hash table item is zero. 198 | 199 | @param[in] in The hash table item to test 200 | */ 201 | inline bool is_zero_item(const item_type &in) noexcept 202 | { 203 | return !(get_low_word(in) | get_high_word(in)); 204 | } 205 | 206 | /** 207 | Returns whether a given has table item has all one-bits. 208 | 209 | @param[in] in The hash table item to test 210 | */ 211 | inline bool is_all_ones_item(const item_type &in) noexcept 212 | { 213 | return !(~get_low_word(in) | ~get_high_word(in)); 214 | } 215 | 216 | /** 217 | Returns whether two hash table items are equal. 218 | 219 | @param[in] in1 The first hash table item 220 | @param[in] in2 The second hash table item 221 | */ 222 | inline bool are_equal_item(const item_type &in1, const item_type &in2) noexcept 223 | { 224 | return (get_low_word(in1) == get_low_word(in2)) && (get_high_word(in1) == get_high_word(in2)); 225 | } 226 | 227 | /** 228 | Sets a given hash table item to a random value. 229 | 230 | @param[out] destination The hash table item whose value is to be set 231 | */ 232 | inline void set_random_item(item_type &destination) noexcept 233 | { 234 | set_item(random_uint64(), random_uint64(), destination); 235 | } 236 | 237 | /** 238 | Creates a random hash table item. 239 | */ 240 | inline item_type make_random_item() noexcept 241 | { 242 | item_type out; 243 | set_random_item(out); 244 | return out; 245 | } 246 | 247 | /** 248 | Interprets a hash table item as a 128-bit integer and increments its value by one. 249 | 250 | @param[in,out] in The hash table item whose value is to be incremented 251 | */ 252 | inline void increment_item(item_type &in) noexcept 253 | { 254 | get_low_word(in) += 1; 255 | get_high_word(in) += !get_low_word(in) ? 1 : 0; 256 | } 257 | } // namespace kuku 258 | -------------------------------------------------------------------------------- /src/kuku/internal/blake2-impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Copyright 2012, Samuel Neves . You may use this under the 5 | terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 6 | your option. The terms of these licenses can be found at: 7 | 8 | - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 9 | - OpenSSL license : https://www.openssl.org/source/license.html 10 | - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | More information about the BLAKE2 hash function can be found at 13 | https://blake2.net. 14 | */ 15 | #ifndef BLAKE2_IMPL_H 16 | #define BLAKE2_IMPL_H 17 | 18 | #include 19 | #include 20 | 21 | #if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) 22 | #if defined(_MSC_VER) 23 | #define BLAKE2_INLINE __inline 24 | #elif defined(__GNUC__) 25 | #define BLAKE2_INLINE __inline__ 26 | #else 27 | #define BLAKE2_INLINE 28 | #endif 29 | #else 30 | #define BLAKE2_INLINE inline 31 | #endif 32 | 33 | static BLAKE2_INLINE uint32_t load32( const void *src ) 34 | { 35 | #if defined(NATIVE_LITTLE_ENDIAN) 36 | uint32_t w; 37 | memcpy(&w, src, sizeof w); 38 | return w; 39 | #else 40 | const uint8_t *p = ( const uint8_t * )src; 41 | return (( uint32_t )( p[0] ) << 0) | 42 | (( uint32_t )( p[1] ) << 8) | 43 | (( uint32_t )( p[2] ) << 16) | 44 | (( uint32_t )( p[3] ) << 24) ; 45 | #endif 46 | } 47 | 48 | static BLAKE2_INLINE uint64_t load64( const void *src ) 49 | { 50 | #if defined(NATIVE_LITTLE_ENDIAN) 51 | uint64_t w; 52 | memcpy(&w, src, sizeof w); 53 | return w; 54 | #else 55 | const uint8_t *p = ( const uint8_t * )src; 56 | return (( uint64_t )( p[0] ) << 0) | 57 | (( uint64_t )( p[1] ) << 8) | 58 | (( uint64_t )( p[2] ) << 16) | 59 | (( uint64_t )( p[3] ) << 24) | 60 | (( uint64_t )( p[4] ) << 32) | 61 | (( uint64_t )( p[5] ) << 40) | 62 | (( uint64_t )( p[6] ) << 48) | 63 | (( uint64_t )( p[7] ) << 56) ; 64 | #endif 65 | } 66 | 67 | static BLAKE2_INLINE uint16_t load16( const void *src ) 68 | { 69 | #if defined(NATIVE_LITTLE_ENDIAN) 70 | uint16_t w; 71 | memcpy(&w, src, sizeof w); 72 | return w; 73 | #else 74 | const uint8_t *p = ( const uint8_t * )src; 75 | return ( uint16_t )((( uint32_t )( p[0] ) << 0) | 76 | (( uint32_t )( p[1] ) << 8)); 77 | #endif 78 | } 79 | 80 | static BLAKE2_INLINE void store16( void *dst, uint16_t w ) 81 | { 82 | #if defined(NATIVE_LITTLE_ENDIAN) 83 | memcpy(dst, &w, sizeof w); 84 | #else 85 | uint8_t *p = ( uint8_t * )dst; 86 | *p++ = ( uint8_t )w; w >>= 8; 87 | *p++ = ( uint8_t )w; 88 | #endif 89 | } 90 | 91 | static BLAKE2_INLINE void store32( void *dst, uint32_t w ) 92 | { 93 | #if defined(NATIVE_LITTLE_ENDIAN) 94 | memcpy(dst, &w, sizeof w); 95 | #else 96 | uint8_t *p = ( uint8_t * )dst; 97 | p[0] = (uint8_t)(w >> 0); 98 | p[1] = (uint8_t)(w >> 8); 99 | p[2] = (uint8_t)(w >> 16); 100 | p[3] = (uint8_t)(w >> 24); 101 | #endif 102 | } 103 | 104 | static BLAKE2_INLINE void store64( void *dst, uint64_t w ) 105 | { 106 | #if defined(NATIVE_LITTLE_ENDIAN) 107 | memcpy(dst, &w, sizeof w); 108 | #else 109 | uint8_t *p = ( uint8_t * )dst; 110 | p[0] = (uint8_t)(w >> 0); 111 | p[1] = (uint8_t)(w >> 8); 112 | p[2] = (uint8_t)(w >> 16); 113 | p[3] = (uint8_t)(w >> 24); 114 | p[4] = (uint8_t)(w >> 32); 115 | p[5] = (uint8_t)(w >> 40); 116 | p[6] = (uint8_t)(w >> 48); 117 | p[7] = (uint8_t)(w >> 56); 118 | #endif 119 | } 120 | 121 | static BLAKE2_INLINE uint64_t load48( const void *src ) 122 | { 123 | const uint8_t *p = ( const uint8_t * )src; 124 | return (( uint64_t )( p[0] ) << 0) | 125 | (( uint64_t )( p[1] ) << 8) | 126 | (( uint64_t )( p[2] ) << 16) | 127 | (( uint64_t )( p[3] ) << 24) | 128 | (( uint64_t )( p[4] ) << 32) | 129 | (( uint64_t )( p[5] ) << 40) ; 130 | } 131 | 132 | static BLAKE2_INLINE void store48( void *dst, uint64_t w ) 133 | { 134 | uint8_t *p = ( uint8_t * )dst; 135 | p[0] = (uint8_t)(w >> 0); 136 | p[1] = (uint8_t)(w >> 8); 137 | p[2] = (uint8_t)(w >> 16); 138 | p[3] = (uint8_t)(w >> 24); 139 | p[4] = (uint8_t)(w >> 32); 140 | p[5] = (uint8_t)(w >> 40); 141 | } 142 | 143 | static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c ) 144 | { 145 | return ( w >> c ) | ( w << ( 32 - c ) ); 146 | } 147 | 148 | static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) 149 | { 150 | return ( w >> c ) | ( w << ( 64 - c ) ); 151 | } 152 | 153 | /* prevents compiler optimizing out memset() */ 154 | static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) 155 | { 156 | static void *(*const volatile memset_v)(void *, int, size_t) = &memset; 157 | memset_v(v, 0, n); 158 | } 159 | 160 | #endif 161 | -------------------------------------------------------------------------------- /src/kuku/internal/blake2.h: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Copyright 2012, Samuel Neves . You may use this under the 5 | terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 6 | your option. The terms of these licenses can be found at: 7 | 8 | - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 9 | - OpenSSL license : https://www.openssl.org/source/license.html 10 | - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | More information about the BLAKE2 hash function can be found at 13 | https://blake2.net. 14 | */ 15 | 16 | /* 17 | Minor modifications to the original file have been made and marked 18 | as `EDIT: ...`. The sole purpose of these edits is to silence misleading 19 | warnings in Visual Studio. 20 | */ 21 | 22 | #ifndef BLAKE2_H 23 | #define BLAKE2_H 24 | 25 | #include 26 | #include 27 | 28 | #if defined(_MSC_VER) 29 | #define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) 30 | #else 31 | #define BLAKE2_PACKED(x) x __attribute__((packed)) 32 | #endif 33 | 34 | #if defined(__cplusplus) 35 | extern "C" { 36 | #endif 37 | 38 | enum blake2s_constant 39 | { 40 | BLAKE2S_BLOCKBYTES = 64, 41 | BLAKE2S_OUTBYTES = 32, 42 | BLAKE2S_KEYBYTES = 32, 43 | BLAKE2S_SALTBYTES = 8, 44 | BLAKE2S_PERSONALBYTES = 8 45 | }; 46 | 47 | enum blake2b_constant 48 | { 49 | BLAKE2B_BLOCKBYTES = 128, 50 | BLAKE2B_OUTBYTES = 64, 51 | BLAKE2B_KEYBYTES = 64, 52 | BLAKE2B_SALTBYTES = 16, 53 | BLAKE2B_PERSONALBYTES = 16 54 | }; 55 | 56 | typedef struct blake2s_state__ 57 | { 58 | uint32_t h[8]; 59 | uint32_t t[2]; 60 | uint32_t f[2]; 61 | uint8_t buf[BLAKE2S_BLOCKBYTES]; 62 | size_t buflen; 63 | size_t outlen; 64 | uint8_t last_node; 65 | } blake2s_state; 66 | 67 | typedef struct blake2b_state__ 68 | { 69 | uint64_t h[8]; 70 | uint64_t t[2]; 71 | uint64_t f[2]; 72 | uint8_t buf[BLAKE2B_BLOCKBYTES]; 73 | size_t buflen; 74 | size_t outlen; 75 | uint8_t last_node; 76 | } blake2b_state; 77 | 78 | typedef struct blake2sp_state__ 79 | { 80 | blake2s_state S[8][1]; 81 | blake2s_state R[1]; 82 | uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; 83 | size_t buflen; 84 | size_t outlen; 85 | } blake2sp_state; 86 | 87 | typedef struct blake2bp_state__ 88 | { 89 | blake2b_state S[4][1]; 90 | blake2b_state R[1]; 91 | uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; 92 | size_t buflen; 93 | size_t outlen; 94 | } blake2bp_state; 95 | 96 | 97 | BLAKE2_PACKED(struct blake2s_param__ 98 | { 99 | uint8_t digest_length; /* 1 */ 100 | uint8_t key_length; /* 2 */ 101 | uint8_t fanout; /* 3 */ 102 | uint8_t depth; /* 4 */ 103 | uint32_t leaf_length; /* 8 */ 104 | uint32_t node_offset; /* 12 */ 105 | uint16_t xof_length; /* 14 */ 106 | uint8_t node_depth; /* 15 */ 107 | uint8_t inner_length; /* 16 */ 108 | /* uint8_t reserved[0]; */ 109 | uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ 110 | uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ 111 | }); 112 | 113 | typedef struct blake2s_param__ blake2s_param; 114 | 115 | BLAKE2_PACKED(struct blake2b_param__ 116 | { 117 | uint8_t digest_length; /* 1 */ 118 | uint8_t key_length; /* 2 */ 119 | uint8_t fanout; /* 3 */ 120 | uint8_t depth; /* 4 */ 121 | uint32_t leaf_length; /* 8 */ 122 | uint32_t node_offset; /* 12 */ 123 | uint32_t xof_length; /* 16 */ 124 | uint8_t node_depth; /* 17 */ 125 | uint8_t inner_length; /* 18 */ 126 | uint8_t reserved[14]; /* 32 */ 127 | uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ 128 | uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ 129 | }); 130 | 131 | typedef struct blake2b_param__ blake2b_param; 132 | 133 | typedef struct blake2xs_state__ 134 | { 135 | blake2s_state S[1]; 136 | blake2s_param P[1]; 137 | } blake2xs_state; 138 | 139 | typedef struct blake2xb_state__ 140 | { 141 | blake2b_state S[1]; 142 | blake2b_param P[1]; 143 | } blake2xb_state; 144 | 145 | /* Padded structs result in a compile-time error */ 146 | /* 147 | EDIT: explicit cast to int 148 | */ 149 | enum { 150 | BLAKE2_DUMMY_1 = 1/(int)(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), 151 | BLAKE2_DUMMY_2 = 1/(int)(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) 152 | }; 153 | 154 | /* Streaming API */ 155 | int blake2s_init( blake2s_state *S, size_t outlen ); 156 | int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ); 157 | int blake2s_init_param( blake2s_state *S, const blake2s_param *P ); 158 | int blake2s_update( blake2s_state *S, const void *in, size_t inlen ); 159 | int blake2s_final( blake2s_state *S, void *out, size_t outlen ); 160 | 161 | int blake2b_init( blake2b_state *S, size_t outlen ); 162 | int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); 163 | int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); 164 | int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); 165 | int blake2b_final( blake2b_state *S, void *out, size_t outlen ); 166 | 167 | int blake2sp_init( blake2sp_state *S, size_t outlen ); 168 | int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ); 169 | int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen ); 170 | int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ); 171 | 172 | int blake2bp_init( blake2bp_state *S, size_t outlen ); 173 | int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ); 174 | int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen ); 175 | int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ); 176 | 177 | /* Variable output length API */ 178 | int blake2xs_init( blake2xs_state *S, const size_t outlen ); 179 | int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ); 180 | int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ); 181 | int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); 182 | 183 | int blake2xb_init( blake2xb_state *S, const size_t outlen ); 184 | int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen ); 185 | int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ); 186 | int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); 187 | 188 | /* Simple API */ 189 | int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 190 | int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 191 | 192 | int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 193 | int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 194 | 195 | int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 196 | int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 197 | 198 | /* This is simply an alias for blake2b */ 199 | int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); 200 | 201 | #if defined(__cplusplus) 202 | } 203 | #endif 204 | 205 | #endif 206 | -------------------------------------------------------------------------------- /src/kuku/internal/blake2b.c: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Copyright 2012, Samuel Neves . You may use this under the 5 | terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 6 | your option. The terms of these licenses can be found at: 7 | 8 | - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 9 | - OpenSSL license : https://www.openssl.org/source/license.html 10 | - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | More information about the BLAKE2 hash function can be found at 13 | https://blake2.net. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "kuku/internal/blake2.h" 21 | #include "kuku/internal/blake2-impl.h" 22 | 23 | static const uint64_t blake2b_IV[8] = 24 | { 25 | 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 26 | 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, 27 | 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 28 | 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL 29 | }; 30 | 31 | static const uint8_t blake2b_sigma[12][16] = 32 | { 33 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , 34 | { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , 35 | { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , 36 | { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , 37 | { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , 38 | { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , 39 | { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , 40 | { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , 41 | { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , 42 | { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , 43 | { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , 44 | { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } 45 | }; 46 | 47 | 48 | static void blake2b_set_lastnode( blake2b_state *S ) 49 | { 50 | S->f[1] = (uint64_t)-1; 51 | } 52 | 53 | /* Some helper functions, not necessarily useful */ 54 | static int blake2b_is_lastblock( const blake2b_state *S ) 55 | { 56 | return S->f[0] != 0; 57 | } 58 | 59 | static void blake2b_set_lastblock( blake2b_state *S ) 60 | { 61 | if( S->last_node ) blake2b_set_lastnode( S ); 62 | 63 | S->f[0] = (uint64_t)-1; 64 | } 65 | 66 | static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) 67 | { 68 | S->t[0] += inc; 69 | S->t[1] += ( S->t[0] < inc ); 70 | } 71 | 72 | static void blake2b_init0( blake2b_state *S ) 73 | { 74 | size_t i; 75 | memset( S, 0, sizeof( blake2b_state ) ); 76 | 77 | for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; 78 | } 79 | 80 | /* init xors IV with input parameter block */ 81 | int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) 82 | { 83 | const uint8_t *p = ( const uint8_t * )( P ); 84 | size_t i; 85 | 86 | blake2b_init0( S ); 87 | 88 | /* IV XOR ParamBlock */ 89 | for( i = 0; i < 8; ++i ) 90 | S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); 91 | 92 | S->outlen = P->digest_length; 93 | return 0; 94 | } 95 | 96 | 97 | 98 | int blake2b_init( blake2b_state *S, size_t outlen ) 99 | { 100 | blake2b_param P[1]; 101 | 102 | if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; 103 | 104 | P->digest_length = (uint8_t)outlen; 105 | P->key_length = 0; 106 | P->fanout = 1; 107 | P->depth = 1; 108 | store32( &P->leaf_length, 0 ); 109 | store32( &P->node_offset, 0 ); 110 | store32( &P->xof_length, 0 ); 111 | P->node_depth = 0; 112 | P->inner_length = 0; 113 | memset( P->reserved, 0, sizeof( P->reserved ) ); 114 | memset( P->salt, 0, sizeof( P->salt ) ); 115 | memset( P->personal, 0, sizeof( P->personal ) ); 116 | return blake2b_init_param( S, P ); 117 | } 118 | 119 | 120 | int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) 121 | { 122 | blake2b_param P[1]; 123 | 124 | if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; 125 | 126 | if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; 127 | 128 | P->digest_length = (uint8_t)outlen; 129 | P->key_length = (uint8_t)keylen; 130 | P->fanout = 1; 131 | P->depth = 1; 132 | store32( &P->leaf_length, 0 ); 133 | store32( &P->node_offset, 0 ); 134 | store32( &P->xof_length, 0 ); 135 | P->node_depth = 0; 136 | P->inner_length = 0; 137 | memset( P->reserved, 0, sizeof( P->reserved ) ); 138 | memset( P->salt, 0, sizeof( P->salt ) ); 139 | memset( P->personal, 0, sizeof( P->personal ) ); 140 | 141 | if( blake2b_init_param( S, P ) < 0 ) return -1; 142 | 143 | { 144 | uint8_t block[BLAKE2B_BLOCKBYTES]; 145 | memset( block, 0, BLAKE2B_BLOCKBYTES ); 146 | memcpy( block, key, keylen ); 147 | blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); 148 | secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ 149 | } 150 | return 0; 151 | } 152 | 153 | #define G(r,i,a,b,c,d) \ 154 | do { \ 155 | a = a + b + m[blake2b_sigma[r][2*i+0]]; \ 156 | d = rotr64(d ^ a, 32); \ 157 | c = c + d; \ 158 | b = rotr64(b ^ c, 24); \ 159 | a = a + b + m[blake2b_sigma[r][2*i+1]]; \ 160 | d = rotr64(d ^ a, 16); \ 161 | c = c + d; \ 162 | b = rotr64(b ^ c, 63); \ 163 | } while(0) 164 | 165 | #define ROUND(r) \ 166 | do { \ 167 | G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ 168 | G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ 169 | G(r,2,v[ 2],v[ 6],v[10],v[14]); \ 170 | G(r,3,v[ 3],v[ 7],v[11],v[15]); \ 171 | G(r,4,v[ 0],v[ 5],v[10],v[15]); \ 172 | G(r,5,v[ 1],v[ 6],v[11],v[12]); \ 173 | G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ 174 | G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ 175 | } while(0) 176 | 177 | static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) 178 | { 179 | uint64_t m[16]; 180 | uint64_t v[16]; 181 | size_t i; 182 | 183 | for( i = 0; i < 16; ++i ) { 184 | m[i] = load64( block + i * sizeof( m[i] ) ); 185 | } 186 | 187 | for( i = 0; i < 8; ++i ) { 188 | v[i] = S->h[i]; 189 | } 190 | 191 | v[ 8] = blake2b_IV[0]; 192 | v[ 9] = blake2b_IV[1]; 193 | v[10] = blake2b_IV[2]; 194 | v[11] = blake2b_IV[3]; 195 | v[12] = blake2b_IV[4] ^ S->t[0]; 196 | v[13] = blake2b_IV[5] ^ S->t[1]; 197 | v[14] = blake2b_IV[6] ^ S->f[0]; 198 | v[15] = blake2b_IV[7] ^ S->f[1]; 199 | 200 | ROUND( 0 ); 201 | ROUND( 1 ); 202 | ROUND( 2 ); 203 | ROUND( 3 ); 204 | ROUND( 4 ); 205 | ROUND( 5 ); 206 | ROUND( 6 ); 207 | ROUND( 7 ); 208 | ROUND( 8 ); 209 | ROUND( 9 ); 210 | ROUND( 10 ); 211 | ROUND( 11 ); 212 | 213 | for( i = 0; i < 8; ++i ) { 214 | S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; 215 | } 216 | } 217 | 218 | #undef G 219 | #undef ROUND 220 | 221 | int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) 222 | { 223 | const unsigned char * in = (const unsigned char *)pin; 224 | if( inlen > 0 ) 225 | { 226 | size_t left = S->buflen; 227 | size_t fill = BLAKE2B_BLOCKBYTES - left; 228 | if( inlen > fill ) 229 | { 230 | S->buflen = 0; 231 | memcpy( S->buf + left, in, fill ); /* Fill buffer */ 232 | blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); 233 | blake2b_compress( S, S->buf ); /* Compress */ 234 | in += fill; inlen -= fill; 235 | while(inlen > BLAKE2B_BLOCKBYTES) { 236 | blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); 237 | blake2b_compress( S, in ); 238 | in += BLAKE2B_BLOCKBYTES; 239 | inlen -= BLAKE2B_BLOCKBYTES; 240 | } 241 | } 242 | memcpy( S->buf + S->buflen, in, inlen ); 243 | S->buflen += inlen; 244 | } 245 | return 0; 246 | } 247 | 248 | int blake2b_final( blake2b_state *S, void *out, size_t outlen ) 249 | { 250 | uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; 251 | size_t i; 252 | 253 | if( out == NULL || outlen < S->outlen ) 254 | return -1; 255 | 256 | if( blake2b_is_lastblock( S ) ) 257 | return -1; 258 | 259 | blake2b_increment_counter( S, S->buflen ); 260 | blake2b_set_lastblock( S ); 261 | memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ 262 | blake2b_compress( S, S->buf ); 263 | 264 | for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ 265 | store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); 266 | 267 | memcpy( out, buffer, S->outlen ); 268 | secure_zero_memory(buffer, sizeof(buffer)); 269 | return 0; 270 | } 271 | 272 | /* inlen, at least, should be uint64_t. Others can be size_t. */ 273 | int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) 274 | { 275 | blake2b_state S[1]; 276 | 277 | /* Verify parameters */ 278 | if ( NULL == in && inlen > 0 ) return -1; 279 | 280 | if ( NULL == out ) return -1; 281 | 282 | if( NULL == key && keylen > 0 ) return -1; 283 | 284 | if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; 285 | 286 | if( keylen > BLAKE2B_KEYBYTES ) return -1; 287 | 288 | if( keylen > 0 ) 289 | { 290 | if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; 291 | } 292 | else 293 | { 294 | if( blake2b_init( S, outlen ) < 0 ) return -1; 295 | } 296 | 297 | blake2b_update( S, ( const uint8_t * )in, inlen ); 298 | blake2b_final( S, out, outlen ); 299 | return 0; 300 | } 301 | 302 | int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { 303 | return blake2b(out, outlen, in, inlen, key, keylen); 304 | } 305 | 306 | #if defined(SUPERCOP) 307 | int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) 308 | { 309 | return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); 310 | } 311 | #endif 312 | 313 | #if defined(BLAKE2B_SELFTEST) 314 | #include 315 | #include "blake2-kat.h" 316 | int main( void ) 317 | { 318 | uint8_t key[BLAKE2B_KEYBYTES]; 319 | uint8_t buf[BLAKE2_KAT_LENGTH]; 320 | size_t i, step; 321 | 322 | for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) 323 | key[i] = ( uint8_t )i; 324 | 325 | for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) 326 | buf[i] = ( uint8_t )i; 327 | 328 | /* Test simple API */ 329 | for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) 330 | { 331 | uint8_t hash[BLAKE2B_OUTBYTES]; 332 | blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); 333 | 334 | if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) 335 | { 336 | goto fail; 337 | } 338 | } 339 | 340 | /* Test streaming API */ 341 | for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { 342 | for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { 343 | uint8_t hash[BLAKE2B_OUTBYTES]; 344 | blake2b_state S; 345 | uint8_t * p = buf; 346 | size_t mlen = i; 347 | int err = 0; 348 | 349 | if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { 350 | goto fail; 351 | } 352 | 353 | while (mlen >= step) { 354 | if ( (err = blake2b_update(&S, p, step)) < 0 ) { 355 | goto fail; 356 | } 357 | mlen -= step; 358 | p += step; 359 | } 360 | if ( (err = blake2b_update(&S, p, mlen)) < 0) { 361 | goto fail; 362 | } 363 | if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { 364 | goto fail; 365 | } 366 | 367 | if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { 368 | goto fail; 369 | } 370 | } 371 | } 372 | 373 | puts( "ok" ); 374 | return 0; 375 | fail: 376 | puts("error"); 377 | return -1; 378 | } 379 | #endif 380 | -------------------------------------------------------------------------------- /src/kuku/internal/blake2xb.c: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Copyright 2016, JP Aumasson . 5 | Copyright 2016, Samuel Neves . 6 | 7 | You may use this under the terms of the CC0, the OpenSSL Licence, or 8 | the Apache Public License 2.0, at your option. The terms of these 9 | licenses can be found at: 10 | 11 | - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 12 | - OpenSSL license : https://www.openssl.org/source/license.html 13 | - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 14 | 15 | More information about the BLAKE2 hash function can be found at 16 | https://blake2.net. 17 | */ 18 | 19 | /* 20 | Minor modifications to the original file have been made and marked 21 | as `EDIT: ...`. The sole purpose of these edits is to silence misleading 22 | warnings in Visual Studio. 23 | */ 24 | 25 | #include 26 | #include 27 | #include 28 | 29 | #include "kuku/internal/blake2.h" 30 | #include "kuku/internal/blake2-impl.h" 31 | 32 | int blake2xb_init( blake2xb_state *S, const size_t outlen ) { 33 | return blake2xb_init_key(S, outlen, NULL, 0); 34 | } 35 | 36 | int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen) 37 | { 38 | if ( outlen == 0 || outlen > 0xFFFFFFFFUL ) { 39 | return -1; 40 | } 41 | 42 | if (NULL != key && keylen > BLAKE2B_KEYBYTES) { 43 | return -1; 44 | } 45 | 46 | if (NULL == key && keylen > 0) { 47 | return -1; 48 | } 49 | 50 | /* Initialize parameter block */ 51 | S->P->digest_length = BLAKE2B_OUTBYTES; 52 | 53 | /* 54 | EDIT: explicit cast to silence warnings 55 | */ 56 | S->P->key_length = (uint8_t)keylen; 57 | 58 | S->P->fanout = 1; 59 | S->P->depth = 1; 60 | store32( &S->P->leaf_length, 0 ); 61 | store32( &S->P->node_offset, 0 ); 62 | 63 | /* 64 | EDIT: explicit cast to silence warnings 65 | */ 66 | store32( &S->P->xof_length, (uint32_t)outlen ); 67 | 68 | S->P->node_depth = 0; 69 | S->P->inner_length = 0; 70 | memset( S->P->reserved, 0, sizeof( S->P->reserved ) ); 71 | memset( S->P->salt, 0, sizeof( S->P->salt ) ); 72 | memset( S->P->personal, 0, sizeof( S->P->personal ) ); 73 | 74 | if( blake2b_init_param( S->S, S->P ) < 0 ) { 75 | return -1; 76 | } 77 | 78 | if (keylen > 0) { 79 | uint8_t block[BLAKE2B_BLOCKBYTES]; 80 | memset(block, 0, BLAKE2B_BLOCKBYTES); 81 | memcpy(block, key, keylen); 82 | blake2b_update(S->S, block, BLAKE2B_BLOCKBYTES); 83 | secure_zero_memory(block, BLAKE2B_BLOCKBYTES); 84 | } 85 | return 0; 86 | } 87 | 88 | int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ) { 89 | return blake2b_update( S->S, in, inlen ); 90 | } 91 | 92 | int blake2xb_final( blake2xb_state *S, void *out, size_t outlen) { 93 | 94 | blake2b_state C[1]; 95 | blake2b_param P[1]; 96 | uint32_t xof_length = load32(&S->P->xof_length); 97 | uint8_t root[BLAKE2B_BLOCKBYTES]; 98 | size_t i; 99 | 100 | if (NULL == out) { 101 | return -1; 102 | } 103 | 104 | /* outlen must match the output size defined in xof_length, */ 105 | /* unless it was -1, in which case anything goes except 0. */ 106 | if(xof_length == 0xFFFFFFFFUL) { 107 | if(outlen == 0) { 108 | return -1; 109 | } 110 | } else { 111 | if(outlen != xof_length) { 112 | return -1; 113 | } 114 | } 115 | 116 | /* Finalize the root hash */ 117 | if (blake2b_final(S->S, root, BLAKE2B_OUTBYTES) < 0) { 118 | return -1; 119 | } 120 | 121 | /* Set common block structure values */ 122 | /* Copy values from parent instance, and only change the ones below */ 123 | memcpy(P, S->P, sizeof(blake2b_param)); 124 | P->key_length = 0; 125 | P->fanout = 0; 126 | P->depth = 0; 127 | store32(&P->leaf_length, BLAKE2B_OUTBYTES); 128 | P->inner_length = BLAKE2B_OUTBYTES; 129 | P->node_depth = 0; 130 | 131 | for (i = 0; outlen > 0; ++i) { 132 | const size_t block_size = (outlen < BLAKE2B_OUTBYTES) ? outlen : BLAKE2B_OUTBYTES; 133 | /* Initialize state */ 134 | 135 | /* 136 | EDIT: explicit cast to silence warnings. 137 | */ 138 | P->digest_length = (uint8_t)block_size; 139 | store32(&P->node_offset, (uint32_t)i); 140 | 141 | blake2b_init_param(C, P); 142 | /* Process key if needed */ 143 | blake2b_update(C, root, BLAKE2B_OUTBYTES); 144 | if (blake2b_final(C, (uint8_t *)out + i * BLAKE2B_OUTBYTES, block_size) < 0 ) { 145 | return -1; 146 | } 147 | outlen -= block_size; 148 | } 149 | secure_zero_memory(root, sizeof(root)); 150 | secure_zero_memory(P, sizeof(P)); 151 | secure_zero_memory(C, sizeof(C)); 152 | /* Put blake2xb in an invalid state? cf. blake2s_is_lastblock */ 153 | return 0; 154 | 155 | } 156 | 157 | int blake2xb(void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen) 158 | { 159 | blake2xb_state S[1]; 160 | 161 | /* Verify parameters */ 162 | if (NULL == in && inlen > 0) 163 | return -1; 164 | 165 | if (NULL == out) 166 | return -1; 167 | 168 | if (NULL == key && keylen > 0) 169 | return -1; 170 | 171 | if (keylen > BLAKE2B_KEYBYTES) 172 | return -1; 173 | 174 | if (outlen == 0) 175 | return -1; 176 | 177 | /* Initialize the root block structure */ 178 | if (blake2xb_init_key(S, outlen, key, keylen) < 0) { 179 | return -1; 180 | } 181 | 182 | /* Absorb the input message */ 183 | blake2xb_update(S, in, inlen); 184 | 185 | /* Compute the root node of the tree and the final hash using the counter construction */ 186 | return blake2xb_final(S, out, outlen); 187 | } 188 | 189 | #if defined(BLAKE2XB_SELFTEST) 190 | #include 191 | #include "blake2-kat.h" 192 | int main( void ) 193 | { 194 | uint8_t key[BLAKE2B_KEYBYTES]; 195 | uint8_t buf[BLAKE2_KAT_LENGTH]; 196 | size_t i, step, outlen; 197 | 198 | for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) { 199 | key[i] = ( uint8_t )i; 200 | } 201 | 202 | for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) { 203 | buf[i] = ( uint8_t )i; 204 | } 205 | 206 | /* Testing length of outputs rather than inputs */ 207 | /* (Test of input lengths mostly covered by blake2b tests) */ 208 | 209 | /* Test simple API */ 210 | for( outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen ) 211 | { 212 | uint8_t hash[BLAKE2_KAT_LENGTH] = {0}; 213 | if( blake2xb( hash, outlen, buf, BLAKE2_KAT_LENGTH, key, BLAKE2B_KEYBYTES ) < 0 ) { 214 | goto fail; 215 | } 216 | 217 | if( 0 != memcmp( hash, blake2xb_keyed_kat[outlen-1], outlen ) ) 218 | { 219 | goto fail; 220 | } 221 | } 222 | 223 | /* Test streaming API */ 224 | for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { 225 | for (outlen = 1; outlen <= BLAKE2_KAT_LENGTH; ++outlen) { 226 | uint8_t hash[BLAKE2_KAT_LENGTH]; 227 | blake2xb_state S; 228 | uint8_t * p = buf; 229 | size_t mlen = BLAKE2_KAT_LENGTH; 230 | int err = 0; 231 | 232 | if( (err = blake2xb_init_key(&S, outlen, key, BLAKE2B_KEYBYTES)) < 0 ) { 233 | goto fail; 234 | } 235 | 236 | while (mlen >= step) { 237 | if ( (err = blake2xb_update(&S, p, step)) < 0 ) { 238 | goto fail; 239 | } 240 | mlen -= step; 241 | p += step; 242 | } 243 | if ( (err = blake2xb_update(&S, p, mlen)) < 0) { 244 | goto fail; 245 | } 246 | if ( (err = blake2xb_final(&S, hash, outlen)) < 0) { 247 | goto fail; 248 | } 249 | 250 | if (0 != memcmp(hash, blake2xb_keyed_kat[outlen-1], outlen)) { 251 | goto fail; 252 | } 253 | } 254 | } 255 | 256 | puts( "ok" ); 257 | return 0; 258 | fail: 259 | puts("error"); 260 | return -1; 261 | } 262 | #endif 263 | -------------------------------------------------------------------------------- /src/kuku/internal/cgmanifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/component-detection-manifest.json", 3 | "Registrations": [ 4 | { 5 | "component": { 6 | "type": "git", 7 | "git": { 8 | "repositoryUrl": "https://github.com/BLAKE2/BLAKE2", 9 | "commitHash": "997fa5ba1e14b52c554fb03ce39e579e6f27b90c" 10 | } 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/kuku/internal/config.h.in: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #pragma once 5 | 6 | #define KUKU_VERSION "@Kuku_VERSION@" 7 | #define KUKU_VERSION_MAJOR @Kuku_VERSION_MAJOR@ 8 | #define KUKU_VERSION_MINOR @Kuku_VERSION_MINOR@ 9 | #define KUKU_VERSION_PATCH @Kuku_VERSION_PATCH@ 10 | #cmakedefine KUKU_DEBUG 11 | -------------------------------------------------------------------------------- /src/kuku/internal/hash.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #pragma once 5 | 6 | #include "kuku/common.h" 7 | #ifdef _MSC_VER 8 | # pragma warning(push) 9 | # pragma warning(disable: 4804) 10 | #endif 11 | #include "kuku/internal/blake2.h" 12 | #ifdef _MSC_VER 13 | # pragma warning(pop) 14 | #endif 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | namespace kuku 21 | { 22 | class HashFunc 23 | { 24 | public: 25 | HashFunc(item_type seed) 26 | { 27 | if (blake2xb( 28 | random_array_.data(), 29 | random_array_size_ * sizeof(location_type), 30 | seed.data(), 31 | sizeof(seed), 32 | nullptr, 0) != 0) 33 | { 34 | throw std::runtime_error("blake2xb failed"); 35 | } 36 | } 37 | 38 | inline location_type operator ()(item_type item) const noexcept 39 | { 40 | return 41 | random_array_[0 * block_value_count_ + static_cast(item[0])] ^ 42 | random_array_[1 * block_value_count_ + static_cast(item[1])] ^ 43 | random_array_[2 * block_value_count_ + static_cast(item[2])] ^ 44 | random_array_[3 * block_value_count_ + static_cast(item[3])] ^ 45 | random_array_[4 * block_value_count_ + static_cast(item[4])] ^ 46 | random_array_[5 * block_value_count_ + static_cast(item[5])] ^ 47 | random_array_[6 * block_value_count_ + static_cast(item[6])] ^ 48 | random_array_[7 * block_value_count_ + static_cast(item[7])] ^ 49 | random_array_[8 * block_value_count_ + static_cast(item[8])] ^ 50 | random_array_[9 * block_value_count_ + static_cast(item[9])] ^ 51 | random_array_[10 * block_value_count_ + static_cast(item[10])] ^ 52 | random_array_[11 * block_value_count_ + static_cast(item[11])] ^ 53 | random_array_[12 * block_value_count_ + static_cast(item[12])] ^ 54 | random_array_[13 * block_value_count_ + static_cast(item[13])] ^ 55 | random_array_[14 * block_value_count_ + static_cast(item[14])] ^ 56 | random_array_[15 * block_value_count_ + static_cast(item[15])]; 57 | } 58 | 59 | private: 60 | static constexpr std::size_t block_size_ = 1; 61 | 62 | static constexpr std::size_t block_count_ = sizeof(item_type); 63 | 64 | static constexpr std::size_t block_value_count_ = (std::size_t(1) << (8 * block_size_)); 65 | 66 | static constexpr std::size_t random_array_size_ = block_value_count_ * block_count_; 67 | 68 | static constexpr std::uint32_t block_mask_ = 69 | static_cast(block_value_count_ - 1); 70 | 71 | std::array random_array_; 72 | }; 73 | } 74 | -------------------------------------------------------------------------------- /src/kuku/kuku.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #include "kuku/kuku.h" 5 | 6 | using namespace std; 7 | 8 | namespace kuku 9 | { 10 | QueryResult KukuTable::query(item_type item) const 11 | { 12 | if (is_empty_item(item)) 13 | { 14 | throw invalid_argument("item cannot be the empty item"); 15 | } 16 | 17 | // Search the hash table 18 | for (uint32_t i = 0; i < loc_func_count(); i++) 19 | { 20 | auto loc = location(item, i); 21 | if (are_equal_item(table_[loc], item)) 22 | { 23 | return { loc, i }; 24 | } 25 | } 26 | 27 | // Search the stash 28 | for (location_type loc = 0; loc < stash_.size(); loc++) 29 | { 30 | if (are_equal_item(stash_[loc], item)) 31 | { 32 | return { loc, ~uint32_t(0) }; 33 | } 34 | } 35 | 36 | // Not found 37 | return { 0, max_loc_func_count }; 38 | } 39 | 40 | KukuTable::KukuTable( 41 | table_size_type table_size, table_size_type stash_size, uint32_t loc_func_count, item_type loc_func_seed, 42 | uint64_t max_probe, item_type empty_item) 43 | : table_size_(table_size), stash_size_(stash_size), loc_func_seed_(loc_func_seed), max_probe_(max_probe), 44 | empty_item_(empty_item), leftover_item_(empty_item_), inserted_items_(0), gen_(random_uint64()) 45 | { 46 | if (loc_func_count < min_loc_func_count || loc_func_count > max_loc_func_count) 47 | { 48 | throw invalid_argument("loc_func_count is out of range"); 49 | } 50 | if (table_size < min_table_size || table_size > max_table_size) 51 | { 52 | throw invalid_argument("table_size is out of range"); 53 | } 54 | if (!max_probe) 55 | { 56 | throw invalid_argument("max_probe cannot be zero"); 57 | } 58 | 59 | // Allocate the hash table 60 | table_.resize(table_size_, empty_item_); 61 | 62 | // Create the location (hash) functions 63 | generate_loc_funcs(loc_func_count, loc_func_seed_); 64 | 65 | // Set up the distribution for location function sampling 66 | u_ = std::uniform_int_distribution(0, loc_func_count - 1); 67 | } 68 | 69 | set KukuTable::all_locations(item_type item) const 70 | { 71 | if (is_empty_item(item)) 72 | { 73 | throw invalid_argument("item cannot be the empty item"); 74 | } 75 | 76 | set result; 77 | for (auto lf : loc_funcs_) 78 | { 79 | result.emplace(lf(item)); 80 | } 81 | return result; 82 | } 83 | 84 | void KukuTable::clear_table() 85 | { 86 | size_t sz = table_.size(); 87 | table_.resize(0); 88 | table_.resize(sz, empty_item_); 89 | stash_.clear(); 90 | leftover_item_ = empty_item_; 91 | inserted_items_ = 0; 92 | } 93 | 94 | void KukuTable::generate_loc_funcs(uint32_t loc_func_count, item_type seed) 95 | { 96 | loc_funcs_.clear(); 97 | while (loc_func_count--) 98 | { 99 | loc_funcs_.emplace_back(table_size_, seed); 100 | increment_item(seed); 101 | } 102 | } 103 | 104 | bool KukuTable::insert(item_type item) 105 | { 106 | // Check if the item is already inserted or is the empty item 107 | if (query(item)) 108 | { 109 | return false; 110 | } 111 | 112 | uint64_t level = max_probe_; 113 | while (level--) 114 | { 115 | // Loop over all possible locations 116 | for (uint32_t i = 0; i < loc_func_count(); i++) 117 | { 118 | location_type loc = location(item, i); 119 | if (is_empty_item(table_[loc])) 120 | { 121 | table_[loc] = item; 122 | inserted_items_++; 123 | return true; 124 | } 125 | } 126 | 127 | // Swap in the current item and in next round try the popped out item 128 | item = swap(item, location(item, static_cast(u_(gen_)))); 129 | } 130 | 131 | // level reached zero; try stash 132 | if (stash_.size() < stash_size_) 133 | { 134 | stash_.push_back(item); 135 | inserted_items_++; 136 | return true; 137 | } 138 | else 139 | { 140 | leftover_item_ = item; 141 | return false; 142 | } 143 | } 144 | } // namespace kuku 145 | -------------------------------------------------------------------------------- /src/kuku/kuku.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #pragma once 5 | 6 | #include "kuku/common.h" 7 | #include "kuku/locfunc.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace kuku 15 | { 16 | class QueryResult; 17 | 18 | /** 19 | The KukuTable class represents a cockoo hash table. It includes information about the location functions (hash 20 | functions) and holds the items inserted into the table. 21 | */ 22 | class KukuTable 23 | { 24 | public: 25 | /** 26 | Creates a new empty hash table. 27 | 28 | @param[in] table_size The size of the hash table 29 | @param[in] stash_size The size of the stash (possibly zero) 30 | @param[in] loc_func_count The number of location functions (hash functions) to use 31 | @param[in] loc_func_seed The 128-bit seed for the location functions, represented as a hash table item 32 | @param[in] max_probe The maximum number of random walk steps taken in attempting to insert an item 33 | @param[in] empty_item A hash table item that represents an empty location in the table 34 | @throws std::invalid_argument if loc_func_count is too large or too small 35 | @throws std::invalid_argument if table_size is too large or too small 36 | @throws std::invalid_argument if max_probe is zero 37 | */ 38 | KukuTable( 39 | table_size_type table_size, table_size_type stash_size, std::uint32_t loc_func_count, 40 | item_type loc_func_seed, std::uint64_t max_probe, item_type empty_item); 41 | 42 | /** 43 | Adds a single item to the hash table using random walk cuckoo hashing. The return value indicates whether 44 | the item was successfully inserted (possibly into the stash) or not. 45 | 46 | @param[in] item The hash table item to insert 47 | @throws std::invalid_argument if the given item is the empty item for this hash table 48 | */ 49 | bool insert(item_type item); 50 | 51 | /** 52 | Queries for the presence of a given item in the hash table and stash. 53 | 54 | @param[in] item The hash table item to query 55 | @throws std::invalid_argument if the given item is the empty item for this hash table 56 | */ 57 | QueryResult query(item_type item) const; 58 | 59 | /** 60 | Returns a location that a given hash table item may be placed at. 61 | 62 | @param[in] item The hash table item for which the location is to be obtained 63 | @param[in] loc_func_index The index of the location function which to use to compute the location 64 | @throws std::out_of_range if loc_func_index is out of range 65 | @throws std::invalid_argument if the given item is the empty item for this hash table 66 | */ 67 | inline location_type location(item_type item, std::uint32_t loc_func_index) const 68 | { 69 | if (loc_func_index >= loc_funcs_.size()) 70 | { 71 | throw std::out_of_range("loc_func_index is out of range"); 72 | } 73 | if (is_empty_item(item)) 74 | { 75 | throw std::invalid_argument("item cannot be the empty item"); 76 | } 77 | return loc_funcs_[loc_func_index](item); 78 | } 79 | 80 | /** 81 | Returns all hash table locations that this item may be placed at. 82 | 83 | @throws std::invalid_argument if the given item is the empty item for this hash table 84 | */ 85 | std::set all_locations(item_type item) const; 86 | 87 | /** 88 | Clears the hash table by filling every location with the empty item. 89 | */ 90 | void clear_table(); 91 | 92 | /** 93 | Returns the number of location functions used by the hash table. 94 | */ 95 | inline std::uint32_t loc_func_count() const noexcept 96 | { 97 | return static_cast(loc_funcs_.size()); 98 | } 99 | 100 | /** 101 | Returns a reference to the hash table. 102 | */ 103 | inline const std::vector &table() const noexcept 104 | { 105 | return table_; 106 | } 107 | 108 | /** 109 | Returns a reference to a specific location in the hash table. 110 | 111 | @param[in] index The index in the hash table 112 | @throws std::out_of_range if index is out of range 113 | */ 114 | inline const item_type &table(location_type index) const 115 | { 116 | if (index >= table_size_) 117 | { 118 | throw std::out_of_range("index is out of range"); 119 | } 120 | return table_[index]; 121 | } 122 | 123 | /** 124 | Returns a reference to the stash. 125 | */ 126 | inline const std::vector &stash() const noexcept 127 | { 128 | return stash_; 129 | } 130 | 131 | /** 132 | Returns a reference to a specific location in the stash. 133 | 134 | @param[in] index The index in the stash 135 | @throws std::out_of_range if index is out of range 136 | */ 137 | inline const item_type &stash(location_type index) const 138 | { 139 | if (index >= stash_size_) 140 | { 141 | throw std::out_of_range("index is out of range"); 142 | } 143 | if (index >= stash_.size() && index < stash_size_) 144 | { 145 | return empty_item_; 146 | } 147 | return stash_[index]; 148 | } 149 | 150 | /** 151 | Returns the size of the hash table. 152 | */ 153 | inline table_size_type table_size() const noexcept 154 | { 155 | return table_size_; 156 | } 157 | 158 | /** 159 | Returns the size of the stash. 160 | */ 161 | inline table_size_type stash_size() const noexcept 162 | { 163 | return stash_size_; 164 | } 165 | 166 | /** 167 | Returns the 128-bit seed used for the location functions, represented as a hash table item. 168 | */ 169 | inline item_type loc_func_seed() const noexcept 170 | { 171 | return loc_func_seed_; 172 | } 173 | 174 | /** 175 | Returns the maximum number of random walk steps taken in attempting to insert an item. 176 | */ 177 | inline std::uint64_t max_probe() const noexcept 178 | { 179 | return max_probe_; 180 | } 181 | 182 | /** 183 | Returns the hash table item that represents an empty location in the table. 184 | */ 185 | inline const item_type &empty_item() const noexcept 186 | { 187 | return empty_item_; 188 | } 189 | 190 | /** 191 | Returns whether a given location in the table is empty. 192 | 193 | @param[in] index The index in the hash table 194 | @throws std::out_of_range if index is out of range 195 | */ 196 | inline bool is_empty(location_type index) const noexcept 197 | { 198 | return is_empty_item(table(index)); 199 | } 200 | 201 | /** 202 | Returns whether a given item is the empty item for this hash table. 203 | 204 | @param[in] item The item to compare to the empty item 205 | */ 206 | inline bool is_empty_item(const item_type &item) const noexcept 207 | { 208 | return are_equal_item(item, empty_item_); 209 | } 210 | 211 | /** 212 | When the insert function fails to insert a hash table item, there is a leftover item that could not be inserted 213 | into the table. This function will return the empty item if insertion never failed, and otherwise it will return 214 | the latest leftover item. Note that due to how the random walk insertion process works, the leftover item is 215 | usually not the same one that insert was called with. 216 | */ 217 | inline item_type leftover_item() const noexcept 218 | { 219 | return leftover_item_; 220 | } 221 | 222 | /** 223 | Returns the current fill rate of the hash table and stash. 224 | */ 225 | inline double fill_rate() const noexcept 226 | { 227 | return static_cast(inserted_items_) / 228 | (static_cast(table_size()) + static_cast(stash_size_)); 229 | } 230 | 231 | private: 232 | KukuTable(const KukuTable ©) = delete; 233 | 234 | KukuTable &operator=(const KukuTable &assign) = delete; 235 | 236 | void generate_loc_funcs(std::uint32_t loc_func_count, item_type seed); 237 | 238 | /* 239 | Swap an item in the table with a given item. 240 | */ 241 | inline item_type swap(item_type item, location_type location) noexcept 242 | { 243 | item_type old_item = table_[location]; 244 | table_[location] = item; 245 | return old_item; 246 | } 247 | 248 | /* 249 | The hash table that holds all of the input data. 250 | */ 251 | std::vector table_; 252 | 253 | /* 254 | The stash. 255 | */ 256 | std::vector stash_; 257 | 258 | /* 259 | The hash functions. 260 | */ 261 | std::vector loc_funcs_; 262 | 263 | /* 264 | The size of the table. 265 | */ 266 | const table_size_type table_size_; 267 | 268 | /* 269 | The size of the stash. 270 | */ 271 | const table_size_type stash_size_; 272 | 273 | /* 274 | Seed for the hash functions 275 | */ 276 | const item_type loc_func_seed_; 277 | 278 | /* 279 | The maximum number of attempts that are made to insert an item. 280 | */ 281 | const std::uint64_t max_probe_; 282 | 283 | /* 284 | An item value that denotes an empty item. 285 | */ 286 | const item_type empty_item_; 287 | 288 | /* 289 | Storage for an item that was evicted and could not be re-inserted. This 290 | is populated when insert fails. 291 | */ 292 | item_type leftover_item_; 293 | 294 | /* 295 | The number of items that have been inserted to table or stash. 296 | */ 297 | table_size_type inserted_items_; 298 | 299 | /* 300 | Randomness source for location function sampling. 301 | */ 302 | std::mt19937_64 gen_; 303 | 304 | std::uniform_int_distribution u_; 305 | }; 306 | 307 | /** 308 | The QueryResult class represents the result of a hash table query. It includes information about whether a queried 309 | item was found in the hash table, its location in the hash table or stash (if found), and the index of the location 310 | function (hash function) that was used to insert it. QueryResult objects are returned by the KukuTable::query 311 | function. 312 | */ 313 | class QueryResult 314 | { 315 | friend class KukuTable; 316 | 317 | public: 318 | /** 319 | Creates a QueryResult object. 320 | */ 321 | QueryResult() = default; 322 | 323 | /** 324 | Returns the hash table or stash location represented by this QueryResult. 325 | */ 326 | inline location_type location() const noexcept 327 | { 328 | return location_; 329 | } 330 | 331 | /** 332 | Returns the index of the location function that was used to insert the queried item. This value is meaningless 333 | when in_stash() is true. A value equal to max_loc_func_count indicates the item was not found in the table or 334 | stash. 335 | */ 336 | inline std::uint32_t loc_func_index() const noexcept 337 | { 338 | return loc_func_index_; 339 | } 340 | 341 | /** 342 | Returns whether the queried item was found in the stash. 343 | */ 344 | inline bool in_stash() const noexcept 345 | { 346 | return !~loc_func_index_; 347 | } 348 | 349 | /** 350 | Returns whether the queried item was found in the hash table or in the stash. 351 | */ 352 | inline bool found() const noexcept 353 | { 354 | return !(loc_func_index_ & ~(max_loc_func_count - 1)) || in_stash(); 355 | } 356 | 357 | /** 358 | Returns whether the queried item was found in the hash table or in the stash. 359 | */ 360 | inline operator bool() const noexcept 361 | { 362 | return found(); 363 | } 364 | 365 | private: 366 | QueryResult(location_type location, std::uint32_t loc_func_index) 367 | : location_(location), loc_func_index_(loc_func_index) 368 | { 369 | #ifdef KUKU_DEBUG 370 | if (location >= max_table_size) 371 | { 372 | throw std::invalid_argument("invalid location"); 373 | } 374 | #endif 375 | } 376 | 377 | location_type location_ = 0; 378 | 379 | std::uint32_t loc_func_index_ = 0; 380 | }; 381 | } // namespace kuku 382 | -------------------------------------------------------------------------------- /src/kuku/locfunc.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #pragma once 5 | 6 | #include "kuku/common.h" 7 | #include "kuku/internal/hash.h" 8 | #include 9 | 10 | namespace kuku 11 | { 12 | /** 13 | An instance of the LocFunc class represents a location function (hash function) used by the KukuTable class to 14 | insert an item in the hash table. The location functions are automatically created by the KukuTable class instance 15 | from a seed. 16 | */ 17 | class LocFunc 18 | { 19 | public: 20 | /** 21 | Creates a new location function for a table of given size. The location function uses the given seed for 22 | randomness. 23 | 24 | @param[in] table_size The size of the hash table that this location function is for 25 | @param[in] seed The seed for randomness 26 | @throws std::invalid_argument if the table_size is larger or smaller than allowed 27 | */ 28 | LocFunc(table_size_type table_size, item_type seed) : table_size_(table_size), hf_(seed) 29 | { 30 | if (table_size < min_table_size || table_size > max_table_size) 31 | { 32 | throw std::invalid_argument("table_size is out of range"); 33 | } 34 | } 35 | 36 | /** 37 | Creates a copy of a given location function. 38 | 39 | @param[in] copy The location function to copy from 40 | */ 41 | LocFunc(const LocFunc ©) = default; 42 | 43 | /** 44 | Assigns this location function to be equal to a given location function. 45 | 46 | @param[in] assign The location function to assign from 47 | */ 48 | LocFunc &operator=(const LocFunc &assign) = delete; 49 | 50 | /** 51 | Returns the location for a given item. 52 | 53 | @param[in] item The hash table item for which to compute the location 54 | */ 55 | inline location_type operator()(item_type item) const noexcept 56 | { 57 | return hf_(item) % table_size_; 58 | } 59 | 60 | private: 61 | table_size_type table_size_; 62 | 63 | HashFunc hf_; 64 | }; 65 | } // namespace kuku 66 | -------------------------------------------------------------------------------- /tests/kuku/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT license. 3 | 4 | target_sources(kukutest 5 | PRIVATE 6 | ${CMAKE_CURRENT_LIST_DIR}/common.cpp 7 | ${CMAKE_CURRENT_LIST_DIR}/kuku.cpp 8 | ${CMAKE_CURRENT_LIST_DIR}/locfunc.cpp 9 | ${CMAKE_CURRENT_LIST_DIR}/testrunner.cpp 10 | ) 11 | -------------------------------------------------------------------------------- /tests/kuku/common.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #include "kuku/common.h" 5 | #include "gtest/gtest.h" 6 | #include 7 | #include 8 | 9 | using namespace kuku; 10 | using namespace std; 11 | 12 | namespace kuku_tests 13 | { 14 | TEST(CommonTests, SetItem) 15 | { 16 | item_type bl; 17 | 18 | set_item(0, 0, bl); 19 | ASSERT_EQ(0, get_low_word(bl)); 20 | ASSERT_EQ(0, get_high_word(bl)); 21 | 22 | set_item(1, 0, bl); 23 | ASSERT_EQ(1, get_low_word(bl)); 24 | ASSERT_EQ(0, get_high_word(bl)); 25 | 26 | set_item(0, 1, bl); 27 | ASSERT_EQ(0, get_low_word(bl)); 28 | ASSERT_EQ(1, get_high_word(bl)); 29 | 30 | set_item(0xF00F, 0xBABA, bl); 31 | ASSERT_EQ(0xF00F, get_low_word(bl)); 32 | ASSERT_EQ(0xBABA, get_high_word(bl)); 33 | 34 | set_item(0xF00FF00FF00FF00F, 0xBABABABABABABABA, bl); 35 | ASSERT_EQ(0xF00FF00FF00FF00F, get_low_word(bl)); 36 | ASSERT_EQ(0xBABABABABABABABA, get_high_word(bl)); 37 | 38 | unsigned char data[bytes_per_item]{ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 39 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }; 40 | set_item(data, bl); 41 | ASSERT_EQ(0x0706050403020100, get_low_word(bl)); 42 | ASSERT_EQ(0x0706050403020100, get_high_word(bl)); 43 | } 44 | 45 | TEST(CommonTests, SetZeroItem) 46 | { 47 | item_type bl; 48 | 49 | set_item(0, 0, bl); 50 | set_zero_item(bl); 51 | ASSERT_EQ(0, get_low_word(bl)); 52 | ASSERT_EQ(0, get_high_word(bl)); 53 | 54 | set_item(0xF00FF00FF00FF00F, 0xBABABABABABABABA, bl); 55 | set_zero_item(bl); 56 | ASSERT_EQ(0, get_low_word(bl)); 57 | ASSERT_EQ(0, get_high_word(bl)); 58 | } 59 | 60 | TEST(CommonTests, SetAllOnesItem) 61 | { 62 | item_type bl; 63 | 64 | set_zero_item(bl); 65 | set_all_ones_item(bl); 66 | ASSERT_EQ(0xFFFFFFFFFFFFFFFFULL, get_low_word(bl)); 67 | ASSERT_EQ(0xFFFFFFFFFFFFFFFFULL, get_high_word(bl)); 68 | 69 | set_item(0xF00FF00FF00FF00F, 0xBABABABABABABABA, bl); 70 | set_all_ones_item(bl); 71 | ASSERT_EQ(0xFFFFFFFFFFFFFFFFULL, get_low_word(bl)); 72 | ASSERT_EQ(0xFFFFFFFFFFFFFFFFULL, get_high_word(bl)); 73 | } 74 | 75 | TEST(CommonTests, IsZeroItem) 76 | { 77 | item_type bl; 78 | 79 | set_item(0, 0, bl); 80 | ASSERT_TRUE(is_zero_item(bl)); 81 | 82 | set_item(1, 0, bl); 83 | ASSERT_FALSE(is_zero_item(bl)); 84 | 85 | set_item(0, 1, bl); 86 | ASSERT_FALSE(is_zero_item(bl)); 87 | 88 | set_item(0xF00FF00FF00FF00F, 0xBABABABABABABABA, bl); 89 | ASSERT_FALSE(is_zero_item(bl)); 90 | } 91 | 92 | TEST(CommonTests, IsAllOnesItem) 93 | { 94 | item_type bl; 95 | 96 | set_all_ones_item(bl); 97 | ASSERT_TRUE(is_all_ones_item(bl)); 98 | 99 | set_item(0xFFFFFFFFFFFFFFFFULL, 0, bl); 100 | ASSERT_FALSE(is_all_ones_item(bl)); 101 | 102 | set_item(0, 0xFFFFFFFFFFFFFFFFULL, bl); 103 | ASSERT_FALSE(is_all_ones_item(bl)); 104 | 105 | set_item(0xFFFFFFFFFFFFFFFEULL, 0xFFFFFFFFFFFFFFFFULL, bl); 106 | ASSERT_FALSE(is_all_ones_item(bl)); 107 | } 108 | 109 | TEST(CommonTests, SetRandomItem) 110 | { 111 | item_type bl; 112 | 113 | set_random_item(bl); 114 | ASSERT_FALSE(is_zero_item(bl)); 115 | item_type bl2 = bl; 116 | ASSERT_TRUE(are_equal_item(bl, bl2)); 117 | set_random_item(bl); 118 | ASSERT_FALSE(is_zero_item(bl)); 119 | ASSERT_FALSE(are_equal_item(bl, bl2)); 120 | } 121 | 122 | TEST(CommonTests, ZeroItem) 123 | { 124 | item_type bl = make_random_item(); 125 | ASSERT_FALSE(is_zero_item(bl)); 126 | bl = make_zero_item(); 127 | ASSERT_TRUE(is_zero_item(bl)); 128 | } 129 | 130 | TEST(CommonTests, IncrementItem) 131 | { 132 | item_type bl = make_item(0, 0); 133 | 134 | increment_item(bl); 135 | ASSERT_EQ(1, get_low_word(bl)); 136 | ASSERT_EQ(0, get_high_word(bl)); 137 | 138 | bl = make_item(0xF00F, 0xBAAB); 139 | increment_item(bl); 140 | ASSERT_EQ(0xF010, get_low_word(bl)); 141 | ASSERT_EQ(0xBAAB, get_high_word(bl)); 142 | 143 | bl = make_item(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFE); 144 | increment_item(bl); 145 | ASSERT_EQ(0x0, get_low_word(bl)); 146 | ASSERT_EQ(0xFFFFFFFFFFFFFFFF, get_high_word(bl)); 147 | 148 | bl = make_item(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF); 149 | increment_item(bl); 150 | ASSERT_EQ(0x0, get_low_word(bl)); 151 | ASSERT_EQ(0x0, get_high_word(bl)); 152 | } 153 | } // namespace kuku_tests 154 | -------------------------------------------------------------------------------- /tests/kuku/kuku.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #include "kuku/kuku.h" 5 | #include "gtest/gtest.h" 6 | #include 7 | #include 8 | 9 | using namespace kuku; 10 | using namespace std; 11 | 12 | namespace kuku_tests 13 | { 14 | TEST(KukuTableTests, Create) 15 | { 16 | ASSERT_THROW(KukuTable(0, 0, 2, make_zero_item(), 1, make_zero_item()), invalid_argument); 17 | ASSERT_THROW(KukuTable(1, 0, 0, make_zero_item(), 1, make_zero_item()), invalid_argument); 18 | ASSERT_THROW(KukuTable(1, 0, 2, make_zero_item(), 0, make_zero_item()), invalid_argument); 19 | ASSERT_NO_THROW(KukuTable(min_table_size, 0, 2, make_zero_item(), 1, make_zero_item())); 20 | ASSERT_NO_THROW(KukuTable(1, 0, min_loc_func_count, make_zero_item(), 1, make_zero_item())); 21 | } 22 | 23 | TEST(KukuTableTests, Populate1) 24 | { 25 | { 26 | KukuTable ct(1, 0, 1, make_zero_item(), 10, make_zero_item()); 27 | ASSERT_TRUE(ct.is_empty(0)); 28 | ASSERT_TRUE(ct.insert(make_item(1, 0))); 29 | ASSERT_FALSE(ct.insert(make_item(0, 1))); 30 | ASSERT_THROW(ct.insert(ct.empty_item()), invalid_argument); 31 | ASSERT_THROW(ct.insert(make_item(0, 0)), invalid_argument); 32 | ASSERT_FALSE(ct.is_empty(0)); 33 | } 34 | { 35 | KukuTable ct(1, 0, 2, make_zero_item(), 10, make_zero_item()); 36 | ASSERT_TRUE(ct.is_empty(0)); 37 | ASSERT_TRUE(ct.insert(make_item(1, 0))); 38 | ASSERT_FALSE(ct.insert(make_item(0, 1))); 39 | ASSERT_THROW(ct.insert(ct.empty_item()), invalid_argument); 40 | ASSERT_THROW(ct.insert(make_item(0, 0)), invalid_argument); 41 | ASSERT_FALSE(ct.is_empty(0)); 42 | } 43 | { 44 | KukuTable ct(2, 0, 1, make_zero_item(), 10, make_zero_item()); 45 | ASSERT_TRUE(ct.is_empty(0)); 46 | ASSERT_TRUE(ct.insert(make_item(1, 0))); 47 | 48 | // Collision 49 | ASSERT_FALSE(ct.insert(make_item(0, 1))); 50 | 51 | // No collision 52 | ASSERT_TRUE(ct.insert(make_item(0, 2))); 53 | 54 | ASSERT_FALSE(ct.is_empty(0)); 55 | ASSERT_FALSE(ct.is_empty(1)); 56 | } 57 | } 58 | 59 | TEST(KukuTableTests, Populate2) 60 | { 61 | KukuTable ct(1 << 10, 0, 2, make_zero_item(), 10, make_zero_item()); 62 | for (location_type i = 0; i < ct.table_size(); i++) 63 | { 64 | ASSERT_TRUE(ct.is_empty(i)); 65 | } 66 | 67 | ASSERT_TRUE(ct.insert(make_item(1, 0))); 68 | ASSERT_TRUE(ct.insert(make_item(0, 1))); 69 | ASSERT_TRUE(ct.insert(make_item(1, 1))); 70 | ASSERT_TRUE(ct.insert(make_item(2, 2))); 71 | ASSERT_THROW(ct.insert(ct.empty_item()), invalid_argument); 72 | ASSERT_THROW(ct.insert(make_item(0, 0)), invalid_argument); 73 | 74 | int non_empties = 0; 75 | for (location_type i = 0; i < ct.table_size(); i++) 76 | { 77 | non_empties += ct.is_empty(i) ? 0 : 1; 78 | } 79 | ASSERT_EQ(non_empties, 4); 80 | 81 | ASSERT_TRUE(ct.query(make_item(1, 0))); 82 | ASSERT_TRUE(ct.query(make_item(0, 1))); 83 | ASSERT_TRUE(ct.query(make_item(1, 1))); 84 | ASSERT_TRUE(ct.query(make_item(2, 2))); 85 | ASSERT_FALSE(ct.query(make_item(3, 3))); 86 | } 87 | 88 | TEST(KukuTableTests, Populate3) 89 | { 90 | KukuTable ct(1 << 10, 0, 2, make_zero_item(), 10, make_random_item()); 91 | for (location_type i = 0; i < ct.table_size(); i++) 92 | { 93 | ASSERT_TRUE(ct.is_empty(i)); 94 | } 95 | 96 | ASSERT_TRUE(ct.insert(make_item(0, 0))); 97 | ASSERT_TRUE(ct.insert(make_item(1, 0))); 98 | ASSERT_TRUE(ct.insert(make_item(0, 1))); 99 | ASSERT_TRUE(ct.insert(make_item(1, 1))); 100 | ASSERT_TRUE(ct.insert(make_item(2, 2))); 101 | 102 | // Fails 103 | ASSERT_FALSE(ct.insert(make_item(2, 2))); 104 | 105 | ASSERT_THROW(ct.insert(ct.empty_item()), invalid_argument); 106 | 107 | int non_empties = 0; 108 | for (location_type i = 0; i < ct.table_size(); i++) 109 | { 110 | non_empties += ct.is_empty(i) ? 0 : 1; 111 | } 112 | ASSERT_EQ(non_empties, 5); 113 | 114 | ASSERT_TRUE(ct.query(make_item(0, 0))); 115 | ASSERT_TRUE(ct.query(make_item(1, 0))); 116 | ASSERT_TRUE(ct.query(make_item(0, 1))); 117 | ASSERT_TRUE(ct.query(make_item(1, 1))); 118 | ASSERT_TRUE(ct.query(make_item(2, 2))); 119 | ASSERT_FALSE(ct.query(make_item(3, 3))); 120 | } 121 | 122 | TEST(KukuTableTests, Fill1) 123 | { 124 | KukuTable ct(1 << 10, 0, 2, make_zero_item(), 100, make_random_item()); 125 | vector inserted_items; 126 | for (int i = 0; i < 100; i++) 127 | { 128 | inserted_items.emplace_back(make_random_item()); 129 | ASSERT_TRUE(ct.insert(inserted_items.back())); 130 | } 131 | for (auto b : inserted_items) 132 | { 133 | ASSERT_TRUE(ct.query(b)); 134 | } 135 | ASSERT_FALSE(ct.query(make_random_item())); 136 | } 137 | 138 | TEST(KukuTableTests, Fill2) 139 | { 140 | KukuTable ct((1 << 10) - 1, 0, 4, make_zero_item(), 100, make_random_item()); 141 | vector inserted_items; 142 | for (int i = 0; i < 600; i++) 143 | { 144 | inserted_items.emplace_back(make_random_item()); 145 | ASSERT_TRUE(ct.insert(inserted_items.back())); 146 | } 147 | for (auto b : inserted_items) 148 | { 149 | ASSERT_TRUE(ct.query(b)); 150 | } 151 | ASSERT_FALSE(ct.query(make_random_item())); 152 | } 153 | 154 | TEST(KukuTableTests, Fill3) 155 | { 156 | KukuTable ct((1 << 10) + 1, 4, 2, make_zero_item(), 100, make_random_item()); 157 | vector inserted_items; 158 | for (int i = 0; i < 950; i++) 159 | { 160 | inserted_items.emplace_back(make_random_item()); 161 | if (!ct.insert(inserted_items.back())) 162 | { 163 | auto it = find_if(inserted_items.cbegin(), inserted_items.cend(), [&](const item_type &item) { 164 | return are_equal_item(ct.leftover_item(), item); 165 | }); 166 | ASSERT_TRUE(it != inserted_items.cend()); 167 | ASSERT_FALSE(ct.query(ct.leftover_item())); 168 | inserted_items.erase(it); 169 | } 170 | } 171 | for (auto b : inserted_items) 172 | { 173 | ASSERT_TRUE(ct.query(b)); 174 | } 175 | } 176 | 177 | TEST(KukuTableTests, Locations) 178 | { 179 | uint8_t lfc = 2; 180 | KukuTable ct(1 << 10, 4, lfc, make_random_item(), 100, make_all_ones_item()); 181 | for (int k = 0; k < 20; k++) 182 | { 183 | auto it = make_random_item(); 184 | auto all_locs = ct.all_locations(it); 185 | 186 | bool collision_found = false; 187 | for (uint8_t i = 0; i < lfc; i++) 188 | { 189 | for (uint8_t j = 0; j < i; j++) 190 | { 191 | collision_found = collision_found || (ct.location(it, i) == ct.location(it, j)); 192 | } 193 | } 194 | 195 | ASSERT_EQ(all_locs.size() < lfc, collision_found); 196 | } 197 | } 198 | 199 | TEST(KukuTableTests, RepeatedInsert) 200 | { 201 | KukuTable ct(1 << 10, 0, 4, make_zero_item(), 10, make_zero_item()); 202 | ASSERT_TRUE(ct.insert(make_item(1, 0))); 203 | ASSERT_TRUE(ct.insert(make_item(0, 1))); 204 | ASSERT_TRUE(ct.insert(make_item(1, 1))); 205 | ASSERT_TRUE(ct.insert(make_item(2, 2))); 206 | 207 | ASSERT_FALSE(ct.insert(make_item(1, 0))); 208 | ASSERT_FALSE(ct.insert(make_item(0, 1))); 209 | ASSERT_FALSE(ct.insert(make_item(1, 1))); 210 | ASSERT_FALSE(ct.insert(make_item(2, 2))); 211 | } 212 | } // namespace kuku_tests 213 | -------------------------------------------------------------------------------- /tests/kuku/locfunc.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #include "kuku/common.h" 5 | #include "kuku/locfunc.h" 6 | #include "gtest/gtest.h" 7 | #include 8 | 9 | using namespace kuku; 10 | using namespace std; 11 | 12 | namespace kuku_tests 13 | { 14 | TEST(LocFuncTests, Create) 15 | { 16 | ASSERT_THROW(LocFunc(0, make_item(0, 0)), invalid_argument); 17 | ASSERT_THROW(LocFunc(max_table_size + 1, make_item(0, 0)), invalid_argument); 18 | ASSERT_THROW(LocFunc(0, make_item(1, 1)), invalid_argument); 19 | } 20 | 21 | TEST(LocFuncTests, Randomness) 22 | { 23 | for (table_size_type ts = min_table_size; ts < 5 * min_table_size; ts++) 24 | { 25 | for (int attempts = 0; attempts < 10; attempts++) 26 | { 27 | item_type seed = make_random_item(); 28 | LocFunc lf(ts, seed); 29 | LocFunc lf2(ts, seed); 30 | 31 | uint64_t zeros = 0; 32 | uint64_t total = 1000; 33 | for (uint64_t i = 0; i < total; i++) 34 | { 35 | item_type bl; 36 | set_random_item(bl); 37 | ASSERT_TRUE(lf(bl) == lf2(bl)); 38 | zeros += (lf(bl) == size_t(0)); 39 | } 40 | ASSERT_TRUE( 41 | abs(static_cast(zeros) / static_cast(total) - 1 / static_cast(ts)) < 0.05); 42 | } 43 | } 44 | } 45 | } // namespace kuku_tests 46 | -------------------------------------------------------------------------------- /tests/kuku/testrunner.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT license. 3 | 4 | #include "gtest/gtest.h" 5 | 6 | /** 7 | Main entry point for Google Test unit tests. 8 | */ 9 | int main(int argc, char** argv) 10 | { 11 | testing::InitGoogleTest(&argc, argv); 12 | return RUN_ALL_TESTS(); 13 | } 14 | --------------------------------------------------------------------------------