├── .gitmodules ├── docs ├── sparque │ ├── sparque_sort_all.png │ ├── sparque_push_front.png │ ├── sparque_erase_random.png │ ├── sparque_increment_each.png │ ├── sparque_insert_random.png │ └── sparque_increment_random.png ├── devector │ ├── devector_erase_random.png │ ├── devector_insert_random.png │ └── devector_erase_insert_random.png └── flat_unordered │ ├── flat_unordered_llvm17.png │ ├── flat_unordered_mingw11.png │ ├── flat_unordered_msvc17.png │ └── flat_unordered_llvm17_customhash.png ├── test ├── CMakeLists.txt ├── sparque │ └── CMakeLists.txt ├── devector │ └── CMakeLists.txt └── flat_unordered │ ├── CMakeLists.txt │ ├── test_flat_uset_main.cpp │ └── test_flat_wset_main.cpp ├── bench ├── CMakeLists.txt ├── devector │ ├── benchmark_main.cpp │ ├── CMakeLists.txt │ └── README.md ├── flat_unordered │ ├── benchmark_main.cpp │ ├── CMakeLists.txt │ └── README.md └── sparque │ ├── benchmark_main.cpp │ ├── CMakeLists.txt │ └── README.md ├── .gitignore ├── src ├── utils │ ├── debug_utils.cpp │ ├── romu_prng.h │ ├── bump_allocator.h │ ├── generators.h │ └── debug_utils.h └── indivi │ ├── detail │ ├── indivi_defines.h │ └── indivi_utils.h │ ├── flat_uset.h │ ├── flat_wset.h │ ├── hash.h │ ├── flat_umap.h │ └── flat_wmap.h ├── lib ├── segmented_tree │ ├── seq_fwd.hpp │ └── LICENSE_1_0.txt └── seq_1.1.0 │ ├── LICENSE │ ├── type_traits.hpp │ └── utils.hpp ├── CMakeLists.txt ├── README.md └── LICENSE /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/benchmark"] 2 | path = lib/benchmark 3 | url = https://github.com/google/benchmark 4 | -------------------------------------------------------------------------------- /docs/sparque/sparque_sort_all.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/sparque/sparque_sort_all.png -------------------------------------------------------------------------------- /docs/sparque/sparque_push_front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/sparque/sparque_push_front.png -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Projects 2 | add_subdirectory(devector) 3 | add_subdirectory(flat_unordered) 4 | add_subdirectory(sparque) -------------------------------------------------------------------------------- /docs/devector/devector_erase_random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/devector/devector_erase_random.png -------------------------------------------------------------------------------- /docs/sparque/sparque_erase_random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/sparque/sparque_erase_random.png -------------------------------------------------------------------------------- /docs/sparque/sparque_increment_each.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/sparque/sparque_increment_each.png -------------------------------------------------------------------------------- /docs/sparque/sparque_insert_random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/sparque/sparque_insert_random.png -------------------------------------------------------------------------------- /docs/devector/devector_insert_random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/devector/devector_insert_random.png -------------------------------------------------------------------------------- /docs/sparque/sparque_increment_random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/sparque/sparque_increment_random.png -------------------------------------------------------------------------------- /docs/devector/devector_erase_insert_random.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/devector/devector_erase_insert_random.png -------------------------------------------------------------------------------- /docs/flat_unordered/flat_unordered_llvm17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/flat_unordered/flat_unordered_llvm17.png -------------------------------------------------------------------------------- /docs/flat_unordered/flat_unordered_mingw11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/flat_unordered/flat_unordered_mingw11.png -------------------------------------------------------------------------------- /docs/flat_unordered/flat_unordered_msvc17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/flat_unordered/flat_unordered_msvc17.png -------------------------------------------------------------------------------- /docs/flat_unordered/flat_unordered_llvm17_customhash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gaujay/indivi_collection/HEAD/docs/flat_unordered/flat_unordered_llvm17_customhash.png -------------------------------------------------------------------------------- /bench/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | find_package(Threads REQUIRED) 3 | 4 | # Projects 5 | add_subdirectory(devector) 6 | add_subdirectory(flat_unordered) 7 | add_subdirectory(sparque) 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeLists.txt.user 2 | CMakeCache.txt 3 | CMakeFiles 4 | CMakeScripts 5 | Testing 6 | Makefile 7 | cmake_install.cmake 8 | install_manifest.txt 9 | compile_commands.json 10 | CMakeSettings.json 11 | CTestTestfile.cmake 12 | build/ 13 | 14 | .vs/ 15 | .vscode/ 16 | -------------------------------------------------------------------------------- /bench/devector/benchmark_main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | // Benchmark 7 | #include 8 | 9 | #include "benchmark_devector.h" 10 | 11 | 12 | // 13 | BENCHMARK_MAIN(); 14 | -------------------------------------------------------------------------------- /bench/flat_unordered/benchmark_main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | // Benchmark 7 | #include 8 | 9 | #include "benchmark_flat_map.h" 10 | 11 | // 12 | BENCHMARK_MAIN(); 13 | -------------------------------------------------------------------------------- /bench/sparque/benchmark_main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2023 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | // Benchmark 7 | #include 8 | 9 | #include "benchmark_sparque.h" 10 | 11 | 12 | // 13 | BENCHMARK_MAIN(); 14 | -------------------------------------------------------------------------------- /src/utils/debug_utils.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | #include "debug_utils.h" 7 | 8 | // Static init 9 | bool DbgClass::Dbg = false; 10 | int DbgClass::idx = 0; 11 | int DbgClass::count = 0; 12 | 13 | int dClass::count = 0; 14 | int dClass::decount = 0; 15 | uint64_t dClass::copies = 0u; 16 | uint64_t dClass::moves = 0u; 17 | bool dClass::quiet = true; 18 | -------------------------------------------------------------------------------- /bench/devector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | set(INCLUDE_FILES 3 | ${CMAKE_SOURCE_DIR}/src/indivi/devector.h 4 | benchmark_devector.h 5 | ) 6 | 7 | set(SOURCE_FILES 8 | benchmark_main.cpp 9 | ) 10 | 11 | add_executable(devector_benchmark 12 | ${INCLUDE_FILES} 13 | ${SOURCE_FILES} 14 | ) 15 | 16 | target_include_directories(devector_benchmark 17 | PUBLIC 18 | ${CMAKE_SOURCE_DIR}/src 19 | ) 20 | 21 | # 22 | target_link_libraries(devector_benchmark 23 | benchmark 24 | ${CMAKE_THREAD_LIBS_INIT} 25 | ) 26 | -------------------------------------------------------------------------------- /bench/sparque/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | set(INCLUDE_FILES 3 | benchmark_sparque.h 4 | ) 5 | 6 | set(SOURCE_FILES 7 | benchmark_main.cpp 8 | ) 9 | 10 | add_executable(sparque_benchmark 11 | ${INCLUDE_FILES} 12 | ${SOURCE_FILES} 13 | ) 14 | 15 | target_include_directories(sparque_benchmark 16 | PUBLIC 17 | ${CMAKE_SOURCE_DIR}/src 18 | ${CMAKE_SOURCE_DIR}/lib/seq_1.1.0 19 | ${CMAKE_SOURCE_DIR}/lib/segmented_tree 20 | ) 21 | 22 | # 23 | target_link_libraries(sparque_benchmark 24 | benchmark 25 | ${CMAKE_THREAD_LIBS_INIT} 26 | ) 27 | -------------------------------------------------------------------------------- /lib/segmented_tree/seq_fwd.hpp: -------------------------------------------------------------------------------- 1 | // (C) Copyright Chris Clearwater 2014-2015. Distributed under the Boost 2 | // Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy 3 | // at http://www.boost.org/LICENSE_1_0.txt) 4 | 5 | #ifndef BOOST_SEGMENTED_TREE_SEQ_FWD 6 | #define BOOST_SEGMENTED_TREE_SEQ_FWD 7 | 8 | #include 9 | 10 | namespace boost { 11 | namespace segmented_tree { 12 | template , 13 | std::size_t segment_target = 1024, std::size_t base_target = 768> 14 | class seq; 15 | } 16 | } 17 | 18 | #endif // #ifndef BOOST_SEGMENTED_TREE_SEQ_FWD 19 | -------------------------------------------------------------------------------- /bench/flat_unordered/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | set(INCLUDE_FILES 3 | benchmark_flat_map.h 4 | ) 5 | 6 | set(SOURCE_FILES 7 | benchmark_main.cpp 8 | ) 9 | 10 | add_executable(flat_unordered_benchmark 11 | ${INCLUDE_FILES} 12 | ${SOURCE_FILES} 13 | ) 14 | 15 | target_include_directories(flat_unordered_benchmark 16 | PUBLIC 17 | ${CMAKE_SOURCE_DIR}/src 18 | # ${CMAKE_SOURCE_DIR}/lib/absl_20240116.2 19 | # ${CMAKE_SOURCE_DIR}/lib/boost_1_88_0 20 | ) 21 | 22 | # 23 | target_link_libraries(flat_unordered_benchmark 24 | benchmark 25 | ${CMAKE_THREAD_LIBS_INIT} 26 | ) 27 | 28 | set_property(TARGET flat_unordered_benchmark PROPERTY CXX_STANDARD 14) 29 | -------------------------------------------------------------------------------- /test/sparque/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | include(GoogleTest) 3 | 4 | # 5 | set(SOURCE_FILES_SPARQUE 6 | test_sparque_main.cpp 7 | ${CMAKE_SOURCE_DIR}/src/utils/debug_utils.cpp 8 | ) 9 | 10 | set(gtest_force_shared_crt 11 | ON CACHE BOOL "" FORCE 12 | ) 13 | 14 | add_executable(sparque_tests 15 | ${SOURCE_FILES_SPARQUE} 16 | ) 17 | 18 | target_include_directories(sparque_tests 19 | PUBLIC 20 | ${CMAKE_SOURCE_DIR}/src 21 | ${CMAKE_SOURCE_DIR}/lib/benchmark/googletest/googletest/include 22 | ) 23 | 24 | target_link_libraries(sparque_tests 25 | PUBLIC 26 | gtest 27 | gtest_main 28 | ) 29 | 30 | # 31 | gtest_discover_tests(sparque_tests) 32 | -------------------------------------------------------------------------------- /test/devector/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | include(GoogleTest) 3 | 4 | # 5 | set(SOURCE_FILES_DEVECTOR 6 | test_devector_main.cpp 7 | ${CMAKE_SOURCE_DIR}/src/utils/debug_utils.cpp 8 | ) 9 | 10 | set(gtest_force_shared_crt 11 | ON CACHE BOOL "" FORCE 12 | ) 13 | 14 | add_executable(devector_tests 15 | ${SOURCE_FILES_DEVECTOR} 16 | ) 17 | 18 | target_include_directories(devector_tests 19 | PUBLIC 20 | ${CMAKE_SOURCE_DIR}/src 21 | ${CMAKE_SOURCE_DIR}/lib/benchmark/googletest/googletest/include 22 | ) 23 | 24 | target_link_libraries(devector_tests 25 | PUBLIC 26 | gtest 27 | gtest_main 28 | ) 29 | 30 | # 31 | gtest_discover_tests(devector_tests) 32 | -------------------------------------------------------------------------------- /bench/devector/README.md: -------------------------------------------------------------------------------- 1 | # Devector 2 | 3 | A double-ended vector, with customizable policies for reallocation and data-shift positioning. 4 | For details, see 'devector.h'. 5 | 6 | ### Benchmark results 7 | 8 | Benchmark configuration: 9 | - OS: Windows 10 64-bits 10 | - Compiler: MinGW 8.1.0 64-bits 11 | - Flags: -O2 -DNDEBUG -march=native -mtune=native 12 | - CPU: i7-10875h (L1-D 32K, L1-I 32K, L2 256K, L3 16M) 13 | - Parameters: see 'benchmark_devector.h' 14 | 15 | #### Erase random 16 | 17 | ![Erase_Random](../../docs/devector/devector_erase_random.png) 18 | 19 | #### Insert random (with realloc mode = center) 20 | 21 | ![Insert_Random](../../docs/devector/devector_insert_random.png) 22 | 23 | #### Erase-Insert random 24 | 25 | ![EraseInsert_Random](../../docs/devector/devector_erase_insert_random.png) 26 | -------------------------------------------------------------------------------- /test/flat_unordered/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | include(GoogleTest) 3 | 4 | # 5 | set(SOURCE_FILES_FLAT_UNORDERED 6 | test_flat_umap_main.cpp 7 | test_flat_uset_main.cpp 8 | test_flat_wmap_main.cpp 9 | test_flat_wset_main.cpp 10 | ${CMAKE_SOURCE_DIR}/src/utils/debug_utils.cpp 11 | ) 12 | 13 | set(gtest_force_shared_crt 14 | ON CACHE BOOL "" FORCE 15 | ) 16 | 17 | add_executable(flat_unordered_tests 18 | ${SOURCE_FILES_FLAT_UNORDERED} 19 | ) 20 | 21 | target_include_directories(flat_unordered_tests 22 | PUBLIC 23 | ${CMAKE_SOURCE_DIR}/src 24 | ${CMAKE_SOURCE_DIR}/lib/benchmark/googletest/googletest/include 25 | ) 26 | 27 | target_link_libraries(flat_unordered_tests 28 | PUBLIC 29 | gtest 30 | gtest_main 31 | ) 32 | 33 | # 34 | gtest_discover_tests(flat_unordered_tests) 35 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(indivi_collection LANGUAGES CXX C) 4 | 5 | set(CMAKE_CXX_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | 8 | if (MSVC) 9 | add_compile_options(/W3) 10 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /O2 /DNDEBUG /GL") 11 | else() 12 | add_compile_options(-Wall -Wextra) 13 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -march=native -mtune=native") 14 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -DNDEBUG -march=native -mtune=native") 15 | endif() 16 | 17 | set(BENCHMARK_ENABLE_TESTING 18 | OFF 19 | CACHE BOOL "Disable benchmark testing" FORCE 20 | ) 21 | 22 | # 23 | add_subdirectory(bench) 24 | add_subdirectory(test) 25 | 26 | add_subdirectory(lib/benchmark) 27 | add_subdirectory(lib/benchmark/googletest) 28 | -------------------------------------------------------------------------------- /bench/sparque/README.md: -------------------------------------------------------------------------------- 1 | # Sparque 2 | 3 | A sparse deque, based on a counted B+ tree with customizable chunk and nodes sizes. 4 | For details, see 'sparque.h'. 5 | 6 | ### Benchmark results 7 | 8 | Benchmark configuration: 9 | - OS: Windows 10 64-bits 10 | - Compiler: MinGW 8.1.0 64-bits 11 | - Flags: -O2 -DNDEBUG -march=native -mtune=native 12 | - CPU: i7-10875h (L1-D 32K, L1-I 32K, L2 256K, L3 16M) 13 | - Parameters: see 'benchmark_sparque.h' 14 | 15 | #### Erase random 16 | 17 | ![Erase_Random](../../docs/sparque/sparque_erase_random.png) 18 | 19 | #### Insert random 20 | 21 | ![Insert_Random](../../docs/sparque/sparque_insert_random.png) 22 | 23 | #### Increment each 24 | 25 | ![Increment_Each](../../docs/sparque/sparque_increment_each.png) 26 | 27 | #### Increment random 28 | 29 | ![Increment_Random](../../docs/sparque/sparque_increment_random.png) 30 | 31 | #### Push front 32 | 33 | ![Push_Front](../../docs/sparque/sparque_push_front.png) 34 | 35 | #### Sort 36 | 37 | ![Sort_All](../../docs/sparque/sparque_sort_all.png) 38 | -------------------------------------------------------------------------------- /lib/seq_1.1.0/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Victor Moncada 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. -------------------------------------------------------------------------------- /lib/segmented_tree/LICENSE_1_0.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/indivi/detail/indivi_defines.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | #ifndef INDIVI_DEFINES_H 7 | #define INDIVI_DEFINES_H 8 | 9 | #include 10 | 11 | // Compiler 12 | #ifdef _MSC_VER 13 | #define INDIVI_MSVC _MSC_VER 14 | #elif defined __clang__ 15 | #define INDIVI_CLANG 1 16 | #elif defined(__GNUC__) 17 | #define INDIVI_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) 18 | #endif 19 | 20 | // C++ 21 | #if ((__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) 22 | #define INDIVI_CPP17 23 | #endif 24 | #if ((__cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L)) 25 | #define INDIVI_CPP20 26 | #endif 27 | 28 | // Architecture 29 | #ifdef SIZE_MAX 30 | #if ((((SIZE_MAX >> 16) >> 16) >> 16) >> 15) != 0 31 | #define INDIVI_ARCH_64 32 | #endif 33 | #else 34 | #ifdef UINTPTR_MAX 35 | #if ((((UINTPTR_MAX >> 16) >> 16) >> 16) >> 15) != 0 36 | #define INDIVI_ARCH_64 37 | #endif 38 | #endif 39 | #endif 40 | 41 | #ifdef __ARM_ARCH_ISA_A64 42 | #define INDIVI_ARCH_ARM64 43 | #endif 44 | 45 | // SIMD 46 | #if defined(__SSE2__) || defined(_M_X64) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2) 47 | #define INDIVI_SIMD_SSE2 48 | #elif (defined(__ARM_NEON) || defined(__ARM_NEON__)) && !defined(__ARM_BIG_ENDIAN) 49 | #define INDIVI_SIMD_L_ENDIAN_NEON 50 | #endif 51 | 52 | 53 | #endif // INDIVI_DEFINES_H 54 | -------------------------------------------------------------------------------- /bench/flat_unordered/README.md: -------------------------------------------------------------------------------- 1 | # Flat_unordered 2 | 3 | Flat unordered maps/sets, based on open-addressing and SIMD instructions, with or without tombstones. 4 | For details, see `flat_umap.h`, `flat_uset.h` and `flat_wmap.h`, `flat_wset.h`. 5 | 6 | This benchmark suite is based on the great work by Jackson Allan: 7 | https://github.com/JacksonAllan/c_cpp_hash_tables_benchmark 8 | 9 | With some minor updates: 10 | https://github.com/gaujay/c_cpp_hash_tables_benchmark 11 | 12 | Change notes: 13 | - Only unordered maps are tested, but sets are expected to behave similarly 14 | - An additional benchmark was added ("re-insert after erase all") to showcase the effects of tombstones 15 | - Data type of long std::string (32-chars, no sso) replaces original c-string one 16 | - Folly `F14ValueMap` was also added for comparison, as another SIMD-based flat map without tombstones 17 | - Containers are benchmarked with their default hash function 18 | - Additional results with custom hash (murmur3/FNV-1a) for direct comparison 19 | 20 | ### Benchmark results 21 | 22 | Benchmark configuration: 23 | - OS: Windows 11 x64 24 | - Compilers: LLVM Clang 17.06, MinGW 11.2.0, MSVC 17.14.6 25 | - Flags: -O3 -DNDEBUG -static -march=native -mtune=native (or /O2 /DNDEBUG /GL) 26 | - CPU: i7-10875h (L1-D 32K, L1-I 32K, L2 256K, L3 16M) 27 | 28 | Libraries versions: 29 | - [Abseil](https://github.com/abseil/abseil-cpp) `flat_hash_map` v2024.01.16.2 30 | - [Boost](https://www.boost.org/doc/libs/1_85_0/libs/unordered/doc/html/unordered.html) `unordered_flat_map` v1.85.0 31 | - [Folly](https://github.com/facebook/folly/blob/main/folly/container/F14.md) `F14ValueMap` v2024.08.05.0 32 | - [Ankerl](https://github.com/martinus/unordered_dense) `unordered_dense` v4.1.2 33 | 34 | #### LLVM 17 35 | 36 | ![LLVM_17](../../docs/flat_unordered/flat_unordered_llvm17.png) 37 | 38 | [Detailed graphs](../../docs/flat_unordered/flat_unordered_llvm17.html) 39 | 40 | #### MinGW 11 41 | 42 | ![MinGW_11](../../docs/flat_unordered/flat_unordered_mingw11.png) 43 | 44 | [Detailed graphs](../../docs/flat_unordered/flat_unordered_mingw11.html) 45 | 46 | #### MSVC 17 47 | 48 | ![MSVC_17](../../docs/flat_unordered/flat_unordered_msvc17.png) 49 | 50 | [Detailed graphs](../../docs/flat_unordered/flat_unordered_msvc17.html) 51 | 52 | #### LLVM 17 - Custom hash 53 | 54 | ![LLVM_17_Custom](../../docs/flat_unordered/flat_unordered_llvm17_customhash.png) 55 | 56 | [Detailed graphs](../../docs/flat_unordered/flat_unordered_llvm17_customhash.html) 57 | -------------------------------------------------------------------------------- /src/utils/romu_prng.h: -------------------------------------------------------------------------------- 1 | // Romu Pseudorandom Number Generators 2 | // 3 | // Copyright 2020 Mark A. Overton 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | // 17 | // ------------------------------------------------------------------------------------------------ 18 | // 19 | // Website: romu-random.org 20 | // Paper: http://arxiv.org/abs/2002.11331 21 | // 22 | // Copy and paste the generator you want from those below. 23 | // To compile, you will need to #include and use the ROTL definition below. 24 | 25 | #include 26 | #include 27 | 28 | #define ROTL(d,lrot) ((d<<(lrot)) | (d>>(8*sizeof(d)-(lrot)))) 29 | 30 | //===== RomuDuoJr ================================================================================ 31 | // 32 | // The fastest generator using 64-bit arith., but not suited for huge jobs. 33 | // Est. capacity = 2^51 bytes. Register pressure = 4. State size = 128 bits. 34 | 35 | class RomuDuoJr 36 | { 37 | private: 38 | uint64_t xState, yState; // set to nonzero seed 39 | 40 | static uint64_t splitMix64(uint64_t& state) noexcept 41 | { 42 | uint64_t z = (state += UINT64_C(0x9e3779b97f4a7c15)); 43 | z = (z ^ (z >> 30U)) * UINT64_C(0xbf58476d1ce4e5b9); 44 | z = (z ^ (z >> 27U)) * UINT64_C(0x94d049bb133111eb); 45 | return z ^ (z >> 31U); 46 | } 47 | 48 | public: 49 | RomuDuoJr() noexcept 50 | : xState(0) 51 | , yState(0) 52 | { 53 | std::random_device rnd; 54 | std::uniform_int_distribution dist; 55 | do { 56 | xState = dist(rnd); 57 | yState = dist(rnd); 58 | } while (xState == 0u || yState == 0u); 59 | } 60 | 61 | RomuDuoJr(uint64_t seed) noexcept 62 | : xState(splitMix64(seed)) 63 | , yState(splitMix64(seed)) 64 | {} 65 | 66 | void reset(uint64_t seed) noexcept 67 | { 68 | xState = splitMix64(seed); 69 | yState = splitMix64(seed); 70 | } 71 | 72 | uint64_t operator()() noexcept 73 | { 74 | uint64_t xp = xState; 75 | xState = 15241094284759029579u * yState; 76 | yState = yState - xp; yState = ROTL(yState,27); 77 | return xp; 78 | } 79 | }; 80 | -------------------------------------------------------------------------------- /src/utils/bump_allocator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | #ifndef BUMP_ALLOCATOR_H 7 | #define BUMP_ALLOCATOR_H 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | template 19 | class bump_allocator 20 | { 21 | public: 22 | using value_type = T; 23 | 24 | using propagate_on_container_swap = std::true_type; 25 | using propagate_on_container_copy_assignment = std::false_type; 26 | using propagate_on_container_move_assignment = std::false_type; 27 | 28 | template 29 | struct rebind 30 | { 31 | typedef bump_allocator other; 32 | }; 33 | 34 | bump_allocator() = default; 35 | 36 | bump_allocator(const bump_allocator& /*other*/) noexcept 37 | {} 38 | 39 | template 40 | bump_allocator(const bump_allocator& /*other*/) noexcept 41 | {} 42 | 43 | bump_allocator(bump_allocator&& other) noexcept 44 | : mPos(other.mPos) 45 | , mChunks(std::move(other.mChunks)) 46 | , mAllocated(other.mAllocated) 47 | { 48 | other.mPos = nullptr; 49 | other.mChunks.clear(); 50 | other.mAllocated = 0u; 51 | } 52 | 53 | ~bump_allocator() 54 | { 55 | assert(mAllocated == 0); 56 | for (T* chunk : mChunks) 57 | delete[] reinterpret_cast(chunk); 58 | } 59 | 60 | bump_allocator& operator=(const bump_allocator& /*other*/) noexcept 61 | { 62 | return *this; 63 | } 64 | 65 | bump_allocator& operator=(bump_allocator&& other) noexcept 66 | { 67 | for (T* chunk : mChunks) 68 | delete[] reinterpret_cast(chunk); 69 | 70 | mPos = other.mPos; 71 | mChunks = std::move(other.mChunks); 72 | mAllocated = other.mAllocated; 73 | 74 | other.mPos = nullptr; 75 | other.mAllocated = 0; 76 | 77 | return *this; 78 | } 79 | 80 | T* allocate(std::size_t n) 81 | { 82 | assert(n <= (std::size_t)TPerChunk); 83 | if (mChunks.empty() || needNewChunk((uint32_t)n)) 84 | { 85 | mChunks.push_back(reinterpret_cast(new storage_type[TPerChunk])); 86 | mPos = mChunks.back(); 87 | } 88 | 89 | T* ptr = mPos; 90 | mPos += n; 91 | mAllocated += n; 92 | return ptr; 93 | } 94 | 95 | void deallocate(T*, std::size_t n) 96 | { 97 | // Do nothing 98 | assert(mAllocated >= n); 99 | mAllocated -= n; 100 | } 101 | 102 | bool empty() const noexcept 103 | { 104 | return mAllocated == 0u; 105 | } 106 | 107 | friend bool operator!=(const bump_allocator& lhs, const bump_allocator& rhs) noexcept 108 | { 109 | return &lhs != &rhs; 110 | } 111 | friend bool operator==(const bump_allocator& lhs, const bump_allocator& rhs) noexcept 112 | { 113 | return &lhs == &rhs; 114 | } 115 | 116 | private: 117 | using storage_type = typename std::aligned_storage::type; 118 | 119 | bool needNewChunk(uint32_t n) const 120 | { 121 | T* chunk = mChunks.back(); 122 | uint32_t taken = (uint32_t)(mPos - chunk); 123 | return taken + n > TPerChunk; 124 | } 125 | 126 | T* mPos = nullptr; 127 | std::vector mChunks; 128 | uint64_t mAllocated = 0u; // debug 129 | }; 130 | 131 | 132 | #endif // BUMP_ALLOCATOR_H 133 | -------------------------------------------------------------------------------- /src/indivi/detail/indivi_utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | #ifndef INDIVI_UTILS_H 7 | #define INDIVI_UTILS_H 8 | 9 | #include "indivi/detail/indivi_defines.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | 16 | #if defined(INDIVI_GCC) || defined(INDIVI_CLANG) 17 | #define INDIVI_PREFETCH(p) __builtin_prefetch((const char*)(p)) 18 | #elif defined(INDIVI_SIMD_SSE2) 19 | #include 20 | #define INDIVI_PREFETCH(p) _mm_prefetch((const char*)(p), _MM_HINT_T0) 21 | #else 22 | #define INDIVI_PREFETCH(p) ((void)(p)) 23 | #endif 24 | 25 | #ifdef INDIVI_MSVC 26 | #include // for BitScanForward 27 | #endif 28 | 29 | namespace indivi 30 | { 31 | namespace detail 32 | { 33 | static inline uint32_t round_up_pow2(uint32_t v) noexcept 34 | { 35 | --v; 36 | v |= v >> 1; 37 | v |= v >> 2; 38 | v |= v >> 4; 39 | v |= v >> 8; 40 | v |= v >> 16; 41 | ++v; 42 | 43 | return v; 44 | } 45 | 46 | static inline uint64_t round_up_pow2(uint64_t v) noexcept 47 | { 48 | --v; 49 | v |= v >> 1; 50 | v |= v >> 2; 51 | v |= v >> 4; 52 | v |= v >> 8; 53 | v |= v >> 16; 54 | v |= v >> 32; 55 | ++v; 56 | 57 | return v; 58 | } 59 | 60 | static inline int first_bit_index(uint32_t v) noexcept 61 | { 62 | assert(v != 0); 63 | #ifdef INDIVI_MSVC 64 | unsigned long r; 65 | _BitScanForward(&r, (unsigned long)v); 66 | return (int)r; 67 | #else 68 | return __builtin_ctz((unsigned int)v); 69 | #endif 70 | } 71 | 72 | static inline int first_bit_index(uint64_t v) noexcept 73 | { 74 | assert(v != 0); 75 | #ifdef INDIVI_MSVC 76 | unsigned long r; 77 | _BitScanForward64(&r, (unsigned __int64)v); 78 | return (int)r; 79 | #else 80 | return __builtin_ctzll((unsigned long long)v); 81 | #endif 82 | } 83 | 84 | static inline int first_bit_index(int v) noexcept 85 | { 86 | return first_bit_index((unsigned int)v); 87 | } 88 | 89 | static inline int last_bit_index(int v) noexcept 90 | { 91 | assert(v != 0); 92 | #ifdef INDIVI_MSVC 93 | unsigned long r; 94 | _BitScanReverse(&r, (unsigned long)v); 95 | return (int)r; 96 | #else 97 | return 31 ^ __builtin_clz((unsigned int)v); 98 | #endif 99 | } 100 | 101 | namespace traits 102 | { 103 | template< typename... Ts > 104 | struct make_void { typedef void type; }; 105 | 106 | template< typename... Ts > 107 | using void_t = typename make_void::type; 108 | 109 | template< class T > 110 | struct remove_cvref 111 | { 112 | using type = typename std::remove_cv< typename std::remove_reference::type >::type; 113 | }; 114 | 115 | template< class U, class V > 116 | using is_similar = std::is_same::type, typename remove_cvref::type >; 117 | 118 | using std::swap; 119 | 120 | template< class T, class = void > 121 | struct is_nothrow_swappable_impl 122 | { 123 | constexpr static bool const value = false; 124 | }; 125 | 126 | template< class T > 127 | struct is_nothrow_swappable_impl(), std::declval()))> > 129 | { 130 | constexpr static bool const value = 131 | noexcept(swap(std::declval(), std::declval())); 132 | }; 133 | 134 | template< class T > 135 | struct is_nothrow_swappable 136 | : public std::integral_constant::value> 137 | {}; 138 | 139 | } // namespace traits 140 | } // namespace detail 141 | } // namespace indivi 142 | 143 | #endif // INDIVI_UTILS_H 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Indivi-Collection 2 | 3 | A collection of std-like containers written in C++11. 4 | 5 | Includes Google Benchmark and Google Test support. 6 | 7 | ### Categories 8 | 9 | - `flat_umap`/`flat_uset` (flat unordered map/set) 10 | - an associative container that stores unordered unique key-value pairs/keys 11 | - similar to `std::unordered_map`/`std::unordered_set` 12 | - open-addressing schema, with a dynamically allocated, consolidated array of values and metadata (capacity grows based on power of 2) 13 | - each entry uses 2 additional bytes of metadata (to store hash fragments, overflow counters and distances from original buckets) 14 | - optimized for small sizes (starting at 2, container sizeof is 48 Bytes on 64-bits systems) 15 | - avoid the need for tombstone mechanism or rehashing on iterator erase (with a good hash function) 16 | - group buckets to rely on SIMD operations for speed (SSE2 or NEON are mandatory) 17 | - come with an optimized 64-bits hash function (based on [wyhash](https://github.com/wangyi-fudan/wyhash)) 18 | - search, insertion, and removal of elements have average constant time 𝓞(1) complexity 19 | 20 | - `flat_wmap`/`flat_wset` (flat unaligned unordered map/set) 21 | - same as flat_umap/uset but generally faster while using tombstones. 22 | - optimized for lookup speed, little bit slower for re-inserting and iterating. 23 | - use only 1 byte of metadata per entry (to store hash fragments). 24 | - don't group buckets but still rely on SIMD (SSE2 or NEON). 25 | - greatly minimize tombstone usage on erase. 26 | - use lower fixed max load factor (0.8 Vs 0.875 for umap/uset) 27 | - see 'bench/flat_unordered' readme for detailed comparison with others maps. 28 | 29 | - `sparque` (sparse deque) 30 | - a sequence, non-contiguous and reversible container that allows fast random insertion and deletion (with basic exception safety) 31 | - dynamically allocated and automatically adjusted storage (allocator-aware, space complexity 𝓞(n)) 32 | - similar to `std::deque`, but based on a counted B+ tree where each memory chunk behave as a double-ended vector. 33 | - options (see 'sparque.h' for more details): 34 | - chunk size (default: max(4, 1024 / sizeof(T))) 35 | - node size (default: 16) 36 | - complexity: 37 | - random access - 𝓞(log_b(n)), where b is the number of children per node 38 | - insertion or removal of elements at start/end - constant 𝓞(1) 39 | - insertion or removal of elements - amortized 𝓞(m), where m is the number of elements per chunk 40 | - iteration - contant 𝓞(n) 41 | 42 | - `devector` (double-ended vector) 43 | - a sequence, contiguous and reversible container (with basic exception safety) 44 | - dynamically allocated and automatically handled storage (supports allocator, space complexity 𝓞(n)) 45 | - similar to `std::vector` but with an additional 'offset', allowing front data manipulation 46 | - example representation: |\_|a|b|\_|\_| (with size=2, capacity=5, offset=1) 47 | - options (see 'devector.h' for more details): 48 | - reallocation position mode (start, center, end) 49 | - data shift mode (near, center, far) 50 | - growth factor 51 | - complexity: 52 | - random access - constant 𝓞(1) 53 | - remove at start/end - constant 𝓞(1) 54 | - insert at start/end - amortized constant 𝓞(1), or 𝓞(N) if size < capacity and start == startOfStorage/end == endOfStorage 55 | - insert/remove - linear in the distance to the closest between start and end 𝓞(N/2) 56 | 57 | ### Benchmark results 58 | 59 | See corresponding 'bench' sub-folders for graphs. 60 | 61 | ### Dependencies 62 | 63 | This project uses git submodule to include Google Benchmark and Google Test repositories: 64 | 65 | $ git clone https://github.com/gaujay/indivi_collection.git 66 | $ cd indivi_collection 67 | $ git submodule init && git submodule update 68 | $ git clone https://github.com/google/googletest lib/benchmark/googletest 69 | 70 | ### Building 71 | 72 | Support GCC/MinGW, Clang and MSVC (see 'CMakeLists.txt'). 73 | 74 | You can open 'CMakeLists.txt' with a compatible IDE or use command line: 75 | 76 | $ mkdir build 77 | $ cd build 78 | $ cmake .. 79 | $ make -j 80 | 81 | ### License 82 | 83 | Apache License 2.0 84 | 85 | Benchmarked third-party libraries: 86 | - [seq::tiered_vector](https://github.com/Thermadiag/seq): MIT License 87 | - [segmented_tree](https://github.com/det/segmented_tree): Boost Software License - Version 1.0 88 | 89 | Test utils third-party libraries: 90 | - [Romu Pseudorandom Number Generators](http://romu-random.org): Apache License - Version 2.0 91 | -------------------------------------------------------------------------------- /src/utils/generators.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | #ifndef GENERATORS_H 7 | #define GENERATORS_H 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | /****************************************************************************************************/ 20 | 21 | template 22 | struct Data8 { 23 | int8_t d[N]; 24 | 25 | Data8() = default; 26 | Data8(unsigned int i) : d{(int8_t)i} {} 27 | //Data8(const Data8& other) { for (size_t i=0; i 34 | using Bytes = Data8; 35 | 36 | template 37 | struct Data16 { 38 | int16_t d[N]; 39 | }; 40 | 41 | template 42 | struct Data32 { 43 | int32_t d[N]; 44 | }; 45 | 46 | template 47 | struct Data64 { 48 | int64_t d[N]; 49 | }; 50 | 51 | /****************************************************************************************************/ 52 | 53 | #define CPLXSTRUCT_SIZE 200 // Bytes 54 | struct CplxStruct { 55 | int8_t d[CPLXSTRUCT_SIZE]; 56 | 57 | CplxStruct(unsigned int i) 58 | : d{(int8_t)i} 59 | {} 60 | 61 | ~CplxStruct() 62 | {} 63 | 64 | CplxStruct(const CplxStruct& other) 65 | { 66 | this->operator=(other); 67 | } 68 | 69 | CplxStruct(CplxStruct&& other) 70 | : CplxStruct((const CplxStruct&)other) 71 | {} 72 | 73 | inline CplxStruct& operator=(const CplxStruct& other) 74 | { 75 | assign(other); 76 | return *this; 77 | } 78 | 79 | inline CplxStruct& operator=(CplxStruct&& other) 80 | { 81 | assign(other); 82 | return *this; 83 | } 84 | 85 | void assign(const CplxStruct& other) 86 | { 87 | // Time consuming computation 88 | for (size_t i = 0; i < CPLXSTRUCT_SIZE; ++i) 89 | { 90 | if (i % 2) 91 | d[i] += other.d[i] * 5; 92 | else 93 | d[i] -= other.d[i] * 7; 94 | } 95 | for (int i = 0; i < CPLXSTRUCT_SIZE; ++i) 96 | { 97 | d[i] *= d[CPLXSTRUCT_SIZE - 1 - i]; 98 | d[i] /= other.d[i] == 0 ? other.d[i] + 1 : other.d[i]; 99 | } 100 | for (int i = CPLXSTRUCT_SIZE - 1; i >= 0; --i) 101 | { 102 | d[i] *= other.d[(i*5) % CPLXSTRUCT_SIZE]; 103 | d[i] -= d[(i*10) % CPLXSTRUCT_SIZE]; 104 | } 105 | } 106 | }; 107 | 108 | /****************************************************************************************************/ 109 | 110 | template 111 | inline T* allocate(size_t N) 112 | { 113 | return static_cast(malloc(sizeof(T)*N)); 114 | } 115 | 116 | /****************************************************************************************************/ 117 | 118 | template 119 | inline T get_one() 120 | { 121 | return T(); // default constructed 122 | } 123 | 124 | template 125 | inline T get_one(unsigned int) 126 | { 127 | return T(); // default constructed 128 | } 129 | 130 | template <> 131 | inline std::string get_one(unsigned int len) 132 | { 133 | return std::string(len, (char)len); 134 | } 135 | 136 | /****************************************************************************************************/ 137 | 138 | template 139 | inline T get_from(unsigned int val, unsigned int) 140 | { 141 | return T(val); 142 | } 143 | 144 | template <> 145 | inline std::string get_from(unsigned int val, unsigned int len) 146 | { 147 | std::string res = std::to_string(val); 148 | res.resize(len, '_'); 149 | return res; 150 | } 151 | 152 | /****************************************************************************************************/ 153 | 154 | unsigned int _get_one_inc = (unsigned int)-1; 155 | 156 | void get_one_inc_reset() 157 | { 158 | _get_one_inc = (unsigned int)-1; 159 | } 160 | 161 | template 162 | inline T get_one_inc() 163 | { 164 | return (T)(++_get_one_inc); // forced cast 165 | } 166 | 167 | template <> 168 | inline std::string get_one_inc() 169 | { 170 | return std::to_string(++_get_one_inc); 171 | } 172 | 173 | template 174 | inline T get_one_inc(unsigned int) 175 | { 176 | return (T)(++_get_one_inc); // forced cast 177 | } 178 | 179 | template <> 180 | inline std::string get_one_inc(unsigned int len) 181 | { 182 | std::string res = std::to_string(++_get_one_inc); 183 | res.resize(len, '_'); 184 | return res; 185 | } 186 | 187 | /****************************************************************************************************/ 188 | 189 | inline int rand_sq() 190 | { 191 | return std::rand() * std::rand(); // RAND_MAX too limited 192 | } 193 | 194 | inline char get_rand_printable_char() 195 | { 196 | return (char)(33 + 93.f*(std::rand()/(float)RAND_MAX)); 197 | } 198 | 199 | template 200 | inline T get_rand_unit() 201 | { 202 | return (rand_sq()/(T)(RAND_MAX * RAND_MAX)); 203 | } 204 | 205 | template 206 | inline T get_rand(T min, T max) 207 | { 208 | return (T)(get_rand_unit() * (max-min) + min); 209 | } 210 | 211 | template 212 | inline T get_rand(unsigned int) 213 | { 214 | return (T)(rand_sq()); // forced cast 215 | } 216 | 217 | template <> 218 | inline bool get_rand(unsigned int) 219 | { 220 | return ((std::rand()/(float)RAND_MAX) >= 0.5f ? true : false); 221 | } 222 | 223 | template <> 224 | inline char get_rand(unsigned int) 225 | { 226 | return (char)(-128 + 255.f*(std::rand()/(float)RAND_MAX)); 227 | } 228 | 229 | template <> 230 | inline std::string get_rand(unsigned int len) 231 | { 232 | std::string res = std::to_string(rand_sq()); 233 | res.resize(len, '_'); 234 | return res; 235 | } 236 | 237 | /****************************************************************************************************/ 238 | 239 | #endif // GENERATORS_H 240 | -------------------------------------------------------------------------------- /src/indivi/flat_uset.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | #ifndef INDIVI_FLAT_USET_H 7 | #define INDIVI_FLAT_USET_H 8 | 9 | #include "indivi/hash.h" 10 | #include "indivi/detail/flat_utable.h" 11 | 12 | #include // for std::equal_to 13 | 14 | namespace indivi 15 | { 16 | /* 17 | * Flat_uset is a fast associative container that stores unordered unique keys. 18 | * Similar to `std::unordered_set` but using an open-addressing schema, 19 | * with a dynamically allocated, consolidated array of values and metadata (capacity grows based on power of 2). 20 | * It is optimized for small sizes (starting at 2, container sizeof is 48 Bytes on 64-bits). 21 | * 22 | * Each entry uses 2 additional bytes of metadata (to store hash fragments, overflow counters and distances from original buckets). 23 | * Avoiding the need for a tombstone mechanism or rehashing on iterator erase (with a good hash function). 24 | * By grouping buckets, it also relies on SIMD operations for speed (SSE2 or NEON are mandatory). 25 | * 26 | * Come with an optimized 64-bits hash function by default (see `hash.h`). 27 | * Use a fixed max load factor of 0.875. 28 | * Iterators are invalidated on usual open-addressing operations (except the end iterator), but never on erase. 29 | * Search, insertion, and removal of elements have average constant time 𝓞(1) complexity. 30 | * Best for general purpose scenarios, including erasure and iteration. 31 | */ 32 | template< 33 | class Key, 34 | class Hash = indivi::hash, 35 | class KeyEqual = std::equal_to > 36 | class flat_uset 37 | { 38 | public: 39 | using key_type = Key; 40 | using value_type = Key; 41 | using size_type = std::size_t; 42 | using difference_type = std::make_signed::type; 43 | using hasher = Hash; 44 | using key_equal = KeyEqual; 45 | using reference = value_type&; 46 | using const_reference = const value_type&; 47 | using pointer = value_type*; 48 | using const_pointer = const value_type*; 49 | 50 | private: 51 | using nc_key_type = typename std::remove_const::type; 52 | using item_type = nc_key_type; 53 | using flat_utable = detail::flat_utable; 54 | 55 | // Members 56 | flat_utable mTable; 57 | 58 | size_type group_capa() const noexcept { return mTable.group_capa(); } 59 | Hash& hash() noexcept { return mTable.hash(); } 60 | const Hash& hash() const noexcept { return mTable.hash(); } 61 | KeyEqual& equal() noexcept { return mTable.equal(); } 62 | const KeyEqual& equal() const noexcept { return mTable.equal(); } 63 | 64 | public: 65 | using iterator = typename flat_utable::iterator; 66 | using const_iterator = typename flat_utable::const_iterator; 67 | 68 | // Ctr/Dtr 69 | flat_uset() : flat_uset(0) 70 | {} 71 | 72 | explicit flat_uset(size_type bucket_count, const Hash& hash = Hash(), const key_equal& equal = key_equal()) 73 | : mTable(bucket_count, hash, equal) 74 | {} 75 | 76 | template< class InputIt > 77 | flat_uset(InputIt first, InputIt last, size_type bucket_count = 0, const Hash& hash = Hash(), const key_equal& equal = key_equal()) 78 | : mTable(first, last, bucket_count, hash, equal) 79 | {} 80 | 81 | flat_uset(std::initializer_list init, size_type bucket_count = 0, const Hash& hash = Hash(), const key_equal& equal = key_equal()) 82 | : mTable(init.begin(), init.end(), bucket_count, hash, equal) 83 | {} 84 | 85 | flat_uset(const flat_uset& other) 86 | : mTable(other.mTable) 87 | {} 88 | 89 | flat_uset(flat_uset&& other) 90 | noexcept(std::is_nothrow_move_constructible::value) 91 | : mTable(std::move(other.mTable)) 92 | {} 93 | 94 | ~flat_uset() = default; 95 | 96 | // Assignment 97 | flat_uset& operator=(const flat_uset& other) 98 | { 99 | mTable = other.mTable; 100 | return *this; 101 | } 102 | flat_uset& operator=(flat_uset&& other) noexcept(std::is_nothrow_move_assignable::value) 103 | { 104 | mTable = std::move(other.mTable); 105 | return *this; 106 | } 107 | flat_uset& operator=(std::initializer_list ilist) 108 | { 109 | clear(); 110 | insert(ilist.begin(), ilist.end()); 111 | return *this; 112 | } 113 | 114 | // Iterators 115 | iterator begin() noexcept { return mTable.begin(); } 116 | const_iterator begin() const noexcept { return mTable.begin(); } 117 | const_iterator cbegin() const noexcept { return mTable.cbegin(); } 118 | 119 | iterator end() noexcept { return mTable.end(); } 120 | const_iterator end() const noexcept { return mTable.cend(); } 121 | const_iterator cend() const noexcept { return mTable.cend(); } 122 | 123 | // Capacity 124 | bool empty() const noexcept { return mTable.empty(); } 125 | size_type size() const noexcept { return mTable.size(); } 126 | size_type max_size() const noexcept { return mTable.max_size(); } 127 | 128 | // Bucket interface 129 | size_type bucket_count() const noexcept { return mTable.bucket_count(); } 130 | size_type max_bucket_count() const noexcept { return mTable.max_bucket_count(); } 131 | 132 | // Hash policy 133 | float load_factor() const noexcept { return mTable.load_factor(); } 134 | float max_load_factor() const noexcept { return mTable.max_load_factor(); } 135 | void max_load_factor(float) noexcept { /*for compatibility*/ } 136 | 137 | void rehash(size_type count) { mTable.rehash(count); } 138 | void reserve(size_type count) { mTable.reserve(count); } 139 | 140 | // Observers 141 | hasher hash_function() const { return mTable.hash_function(); } 142 | key_equal key_eq() const { return mTable.key_eq(); } 143 | 144 | // Lookup 145 | size_type count(const Key& key) const { return mTable.count(key); } 146 | bool contains(const Key& key) const { return mTable.contains(key); } 147 | 148 | iterator find(const Key& key) { return mTable.find(key); } 149 | const_iterator find(const Key& key) const { return mTable.find(key); } 150 | 151 | // Modifiers 152 | void clear() noexcept { mTable.clear(); } 153 | 154 | template< class P > 155 | std::pair insert(P&& value) { return mTable.insert(std::forward

(value)); } 156 | 157 | std::pair insert(Key&& value) { return mTable.insert(std::move(value)); } 158 | 159 | template< class InputIt > 160 | void insert(InputIt first, InputIt last) { mTable.insert(first, last); } 161 | 162 | void insert(std::initializer_list ilist) { mTable.insert_list(ilist); } 163 | 164 | template< class... Args > 165 | std::pair emplace(Args&&... args) { return mTable.emplace(std::forward(args)...); } 166 | 167 | iterator erase_(iterator pos) { return mTable.erase_(pos); } 168 | iterator erase_(const_iterator pos) { return mTable.erase_(pos); } 169 | // non-standard, see `erase_()` 170 | void erase(iterator pos) { mTable.erase(pos); } 171 | void erase(const_iterator pos) { mTable.erase(pos); } 172 | 173 | size_type erase(const Key& key) { return mTable.erase(key); } 174 | 175 | void swap(flat_uset& other) noexcept(noexcept(mTable.swap(other.mTable))) { mTable.swap(other.mTable); } 176 | 177 | // Non-member 178 | friend void swap(flat_uset& lhs, flat_uset& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } 179 | 180 | friend bool operator==(const flat_uset& lhs, const flat_uset& rhs) { return lhs.mTable.equal(rhs.mTable); } 181 | 182 | friend bool operator!=(const flat_uset& lhs, const flat_uset& rhs) { return !(lhs.mTable.equal(rhs.mTable)); } 183 | 184 | template< class Pred > 185 | friend size_type erase_if(flat_uset& set, Pred pred) { return set.mTable.erase_if(pred); } 186 | 187 | #ifdef INDIVI_FLAT_U_DEBUG 188 | bool is_cleared() const noexcept { return mTable.is_cleared(); } 189 | #endif 190 | #ifdef INDIVI_FLAT_U_STATS 191 | typename flat_utable::GroupStats get_group_stats() const noexcept { return mTable.get_group_stats(); } 192 | typename flat_utable::FindStats get_find_stats() const noexcept { return mTable.get_find_stats(); } 193 | void reset_find_stats() noexcept { return mTable.reset_find_stats(); } 194 | #endif 195 | }; 196 | 197 | } // namespace indivi 198 | 199 | #endif // INDIVI_FLAT_USET_H 200 | -------------------------------------------------------------------------------- /src/indivi/flat_wset.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | #ifndef INDIVI_FLAT_WSET_H 7 | #define INDIVI_FLAT_WSET_H 8 | 9 | #include "indivi/hash.h" 10 | #include "indivi/detail/flat_wtable.h" 11 | 12 | #include // for std::equal_to 13 | 14 | namespace indivi 15 | { 16 | /* 17 | * Flat_wset is a fast associative container that stores (unaligned) unordered unique keys. 18 | * Similar to `std::unordered_set` but using an open-addressing schema, 19 | * with a dynamically allocated, consolidated array of values and metadata (capacity grows based on power of 2). 20 | * It is optimized for small sizes (starting at 2, container sizeof is 48 Bytes on 64-bits). 21 | * 22 | * Each entry uses 1 additional byte of metadata (to store hash fragments or empty/tombstone markers). 23 | * While trying to greatly minimize tombstone usage on erase. 24 | * It doesn't group buckets but still relies on SIMD operations for speed (SSE2 or NEON are mandatory). 25 | * 26 | * Come with an optimized 64-bits hash function by default (see `hash.h`). 27 | * Use a fixed max load factor of 0.8 (a bit lower, to keep find-miss fast on high loads). 28 | * Iterators are invalidated on usual open-addressing operations (except the end iterator), but never on erase. 29 | * Search, insertion, and removal of elements have average constant time 𝓞(1) complexity. 30 | * Best for find hit/miss scenarios, a bit slower for re-inserting and iterating. 31 | */ 32 | template< 33 | class Key, 34 | class Hash = indivi::hash, 35 | class KeyEqual = std::equal_to > 36 | class flat_wset 37 | { 38 | public: 39 | using key_type = Key; 40 | using value_type = Key; 41 | using size_type = std::size_t; 42 | using difference_type = std::make_signed::type; 43 | using hasher = Hash; 44 | using key_equal = KeyEqual; 45 | using reference = value_type&; 46 | using const_reference = const value_type&; 47 | using pointer = value_type*; 48 | using const_pointer = const value_type*; 49 | 50 | private: 51 | using nc_key_type = typename std::remove_const::type; 52 | using item_type = nc_key_type; 53 | using flat_wtable = detail::flat_wtable; 54 | 55 | // Members 56 | flat_wtable mTable; 57 | 58 | size_type group_capa() const noexcept { return mTable.group_capa(); } 59 | Hash& hash() noexcept { return mTable.hash(); } 60 | const Hash& hash() const noexcept { return mTable.hash(); } 61 | KeyEqual& equal() noexcept { return mTable.equal(); } 62 | const KeyEqual& equal() const noexcept { return mTable.equal(); } 63 | 64 | public: 65 | using iterator = typename flat_wtable::iterator; 66 | using const_iterator = typename flat_wtable::const_iterator; 67 | 68 | // Ctr/Dtr 69 | flat_wset() : flat_wset(0) 70 | {} 71 | 72 | explicit flat_wset(size_type bucket_count, const Hash& hash = Hash(), const key_equal& equal = key_equal()) 73 | : mTable(bucket_count, hash, equal) 74 | {} 75 | 76 | template< class InputIt > 77 | flat_wset(InputIt first, InputIt last, size_type bucket_count = 0, const Hash& hash = Hash(), const key_equal& equal = key_equal()) 78 | : mTable(first, last, bucket_count, hash, equal) 79 | {} 80 | 81 | flat_wset(std::initializer_list init, size_type bucket_count = 0, const Hash& hash = Hash(), const key_equal& equal = key_equal()) 82 | : mTable(init.begin(), init.end(), bucket_count, hash, equal) 83 | {} 84 | 85 | flat_wset(const flat_wset& other) 86 | : mTable(other.mTable) 87 | {} 88 | 89 | flat_wset(flat_wset&& other) 90 | noexcept(std::is_nothrow_move_constructible::value) 91 | : mTable(std::move(other.mTable)) 92 | {} 93 | 94 | ~flat_wset() = default; 95 | 96 | // Assignment 97 | flat_wset& operator=(const flat_wset& other) 98 | { 99 | mTable = other.mTable; 100 | return *this; 101 | } 102 | flat_wset& operator=(flat_wset&& other) noexcept(std::is_nothrow_move_assignable::value) 103 | { 104 | mTable = std::move(other.mTable); 105 | return *this; 106 | } 107 | flat_wset& operator=(std::initializer_list ilist) 108 | { 109 | clear(); 110 | insert(ilist.begin(), ilist.end()); 111 | return *this; 112 | } 113 | 114 | // Iterators 115 | iterator begin() noexcept { return mTable.begin(); } 116 | const_iterator begin() const noexcept { return mTable.begin(); } 117 | const_iterator cbegin() const noexcept { return mTable.cbegin(); } 118 | 119 | iterator end() noexcept { return mTable.end(); } 120 | const_iterator end() const noexcept { return mTable.cend(); } 121 | const_iterator cend() const noexcept { return mTable.cend(); } 122 | 123 | // Capacity 124 | bool empty() const noexcept { return mTable.empty(); } 125 | size_type size() const noexcept { return mTable.size(); } 126 | size_type max_size() const noexcept { return mTable.max_size(); } 127 | 128 | // Bucket interface 129 | size_type bucket_count() const noexcept { return mTable.bucket_count(); } 130 | size_type max_bucket_count() const noexcept { return mTable.max_bucket_count(); } 131 | 132 | // Hash policy 133 | float load_factor() const noexcept { return mTable.load_factor(); } 134 | float max_load_factor() const noexcept { return mTable.max_load_factor(); } 135 | void max_load_factor(float) noexcept { /*for compatibility*/ } 136 | 137 | void rehash(size_type count) { mTable.rehash(count); } 138 | void reserve(size_type count) { mTable.reserve(count); } 139 | 140 | // Observers 141 | hasher hash_function() const { return mTable.hash_function(); } 142 | key_equal key_eq() const { return mTable.key_eq(); } 143 | 144 | // Lookup 145 | size_type count(const Key& key) const { return mTable.count(key); } 146 | bool contains(const Key& key) const { return mTable.contains(key); } 147 | 148 | iterator find(const Key& key) { return mTable.find(key); } 149 | const_iterator find(const Key& key) const { return mTable.find(key); } 150 | 151 | // Modifiers 152 | void clear() noexcept { mTable.clear(); } 153 | 154 | template< class P > 155 | std::pair insert(P&& value) { return mTable.insert(std::forward

(value)); } 156 | 157 | std::pair insert(Key&& value) { return mTable.insert(std::move(value)); } 158 | 159 | template< class InputIt > 160 | void insert(InputIt first, InputIt last) { mTable.insert(first, last); } 161 | 162 | void insert(std::initializer_list ilist) { mTable.insert_list(ilist); } 163 | 164 | template< class... Args > 165 | std::pair emplace(Args&&... args) { return mTable.emplace(std::forward(args)...); } 166 | 167 | iterator erase_(iterator pos) { return mTable.erase_(pos); } 168 | iterator erase_(const_iterator pos) { return mTable.erase_(pos); } 169 | // non-standard, see `erase_()` 170 | void erase(iterator pos) { mTable.erase(pos); } 171 | void erase(const_iterator pos) { mTable.erase(pos); } 172 | 173 | size_type erase(const Key& key) { return mTable.erase(key); } 174 | 175 | void swap(flat_wset& other) noexcept(noexcept(mTable.swap(other.mTable))) { mTable.swap(other.mTable); } 176 | 177 | // Non-member 178 | friend void swap(flat_wset& lhs, flat_wset& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } 179 | 180 | friend bool operator==(const flat_wset& lhs, const flat_wset& rhs) { return lhs.mTable.equal(rhs.mTable); } 181 | 182 | friend bool operator!=(const flat_wset& lhs, const flat_wset& rhs) { return !(lhs.mTable.equal(rhs.mTable)); } 183 | 184 | template< class Pred > 185 | friend size_type erase_if(flat_wset& set, Pred pred) { return set.mTable.erase_if(pred); } 186 | 187 | #ifdef INDIVI_FLAT_W_DEBUG 188 | bool is_cleared() const noexcept { return mTable.is_cleared(); } 189 | #endif 190 | #ifdef INDIVI_FLAT_W_STATS 191 | typename flat_wtable::GroupStats get_group_stats() const noexcept { return mTable.get_group_stats(); } 192 | typename flat_wtable::FindStats get_find_stats() const noexcept { return mTable.get_find_stats(); } 193 | void reset_find_stats() noexcept { return mTable.reset_find_stats(); } 194 | #endif 195 | }; 196 | 197 | } // namespace indivi 198 | 199 | #endif // INDIVI_FLAT_WSET_H 200 | -------------------------------------------------------------------------------- /src/indivi/hash.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | // Based on wyhash, identity for basic types and fallback on std::hash 7 | // Only supports uint64_t version currently 8 | // https://github.com/wangyi-fudan/wyhash 9 | // Free and unencumbered software released into the public domain. 10 | 11 | #ifndef INDIVI_HASH_H 12 | #define INDIVI_HASH_H 13 | 14 | #include "indivi/detail/indivi_defines.h" 15 | #include "indivi/detail/indivi_utils.h" 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | 25 | #ifdef INDIVI_MSVC 26 | #include // for _umul128 27 | #endif 28 | #ifdef INDIVI_CPP17 29 | #include 30 | #include 31 | #endif 32 | 33 | #ifdef INDIVI_ARCH_64 34 | #define INDIVI_PTR_SHIFT 3 35 | #else 36 | #define INDIVI_PTR_SHIFT 2 37 | #endif 38 | 39 | namespace indivi 40 | { 41 | namespace detail 42 | { 43 | namespace wyhash 44 | { 45 | inline void mum(uint64_t* a, uint64_t* b) 46 | { 47 | #if defined(__SIZEOF_INT128__) 48 | __uint128_t r = *a; 49 | r *= *b; 50 | *a = static_cast(r); 51 | *b = static_cast(r >> 64U); 52 | #elif defined(INDIVI_MSVC) && defined(_M_X64) 53 | *a = _umul128(*a, *b, b); 54 | #else 55 | uint64_t ha = *a >> 32U; 56 | uint64_t hb = *b >> 32U; 57 | uint64_t la = static_cast(*a); 58 | uint64_t lb = static_cast(*b); 59 | uint64_t hi{}; 60 | uint64_t lo{}; 61 | uint64_t rh = ha * hb; 62 | uint64_t rm0 = ha * lb; 63 | uint64_t rm1 = hb * la; 64 | uint64_t rl = la * lb; 65 | uint64_t t = rl + (rm0 << 32U); 66 | auto c = static_cast(t < rl); 67 | lo = t + (rm1 << 32U); 68 | c += static_cast(lo < t); 69 | hi = rh + (rm0 >> 32U) + (rm1 >> 32U) + c; 70 | *a = lo; 71 | *b = hi; 72 | #endif 73 | } 74 | 75 | inline uint64_t mix(uint64_t a, uint64_t b) 76 | { 77 | mum(&a, &b); 78 | return a ^ b; 79 | } 80 | 81 | inline uint32_t mix32(uint32_t a, uint32_t b) 82 | { 83 | uint64_t r = (uint64_t)a * (uint64_t)b; 84 | return (uint32_t)r ^ (uint32_t)(r >> 32); 85 | } 86 | 87 | // read functions (ignoring endianness = hash not cross platform) 88 | inline uint64_t r8(const uint8_t* p) 89 | { 90 | uint64_t v{}; 91 | std::memcpy(&v, p, 8U); 92 | return v; 93 | } 94 | 95 | inline uint64_t r4(const uint8_t* p) 96 | { 97 | uint32_t v{}; 98 | std::memcpy(&v, p, 4); 99 | return v; 100 | } 101 | 102 | inline uint64_t r3(const uint8_t* p, size_t k) 103 | { 104 | return (static_cast(p[0]) << 16U) 105 | | (static_cast(p[k >> 1U]) << 8U) | p[k - 1]; 106 | } 107 | 108 | inline uint64_t hash(const void* key, size_t len) 109 | { 110 | static constexpr uint64_t secret[] { UINT64_C(0x2d358dccaa6c78a5), 111 | UINT64_C(0x8bb84b93962eacc9), 112 | UINT64_C(0x4b33a62ed433d4a3), 113 | UINT64_C(0x4d5a2da51de1aa47) }; 114 | 115 | const uint8_t* p = static_cast(key); 116 | uint64_t seed = secret[0]; 117 | uint64_t a{}; 118 | uint64_t b{}; 119 | 120 | if (len <= 16) 121 | { 122 | if (len >= 4) 123 | { 124 | a = (r4(p) << 32U) | r4(p + ((len >> 3U) << 2U)); 125 | b = (r4(p + len - 4) << 32U) | r4(p + len - 4 - ((len >> 3U) << 2U)); 126 | } 127 | else if (len > 0) 128 | { 129 | a = r3(p, len); 130 | b = 0; 131 | } 132 | else 133 | { 134 | a = 0; 135 | b = 0; 136 | } 137 | } 138 | else 139 | { 140 | size_t i = len; 141 | if (i > 48) 142 | { 143 | uint64_t see1 = seed; 144 | uint64_t see2 = seed; 145 | do { 146 | seed = mix(r8(p ) ^ secret[1], r8(p + 8) ^ seed); 147 | see1 = mix(r8(p + 16) ^ secret[2], r8(p + 24) ^ see1); 148 | see2 = mix(r8(p + 32) ^ secret[3], r8(p + 40) ^ see2); 149 | p += 48; 150 | i -= 48; 151 | } 152 | while (i > 48); 153 | 154 | seed ^= see1 ^ see2; 155 | } 156 | 157 | while (i > 16) 158 | { 159 | seed = mix(r8(p) ^ secret[1], r8(p + 8) ^ seed); 160 | i -= 16; 161 | p += 16; 162 | } 163 | a = r8(p + i - 16); 164 | b = r8(p + i - 8); 165 | } 166 | 167 | return mix(secret[1] ^ len, mix(a ^ secret[1], b ^ seed)); 168 | } 169 | 170 | } // namespace wyhash 171 | 172 | // Abstraction for optional hash mixer 173 | struct no_mix 174 | { 175 | template< typename H, typename V > 176 | static inline std::size_t mix(const H& hasher, const V& v) 177 | { 178 | return hasher(v); 179 | } 180 | }; 181 | 182 | struct bit_mix 183 | { 184 | template< typename H, typename V > 185 | static inline std::size_t mix(const H& hasher, const V& v) 186 | { 187 | std::size_t hash = hasher(v); 188 | #ifdef INDIVI_ARCH_64 189 | constexpr uint64_t phi = UINT64_C(0x9E3779B97F4A7C15); 190 | return wyhash::mix(hash, phi); 191 | #else // 32-bits assumed 192 | // from https://arxiv.org/abs/2001.05304 193 | constexpr uint32_t multiplier = UINT32_C(0xE817FB2D); 194 | return wyhash::mix32(hash, multiplier); 195 | #endif 196 | } 197 | }; 198 | 199 | // Detect if Hash has avalanching trait 200 | // i.e. if 'Hash::is_avalanching' type is present 201 | // Default is false (triggers additional bit mixing) 202 | template< typename Hash, typename = void > 203 | struct hash_is_avalanching_impl 204 | : std::false_type {}; 205 | 206 | template< typename Hash > 207 | struct hash_is_avalanching_impl> 208 | : std::true_type {}; 209 | 210 | template< typename Hash > 211 | struct hash_is_avalanching : hash_is_avalanching_impl::type {}; 212 | 213 | } // namespace detail 214 | 215 | 216 | // Fallback (non avalanching) 217 | template< typename T, typename Enable = void > 218 | struct hash 219 | { 220 | uint64_t operator()(const T& obj) const 221 | noexcept(noexcept(std::declval>().operator()(std::declval()))) 222 | { 223 | return std::hash{}(obj); 224 | } 225 | }; 226 | 227 | // Specializations 228 | template< typename CharT > 229 | struct hash> 230 | { 231 | using is_avalanching = void; 232 | uint64_t operator()(const std::basic_string& str) const noexcept 233 | { 234 | return detail::wyhash::hash(str.data(), sizeof(CharT) * str.size()); 235 | } 236 | }; 237 | 238 | #ifdef INDIVI_CPP17 239 | template< typename CharT > 240 | struct hash> 241 | { 242 | using is_avalanching = void; 243 | uint64_t operator()(const std::basic_string_view& sv) const noexcept 244 | { 245 | return detail::wyhash::hash(sv.data(), sizeof(CharT) * sv.size()); 246 | } 247 | }; 248 | #endif 249 | 250 | template< class T > 251 | struct hash 252 | { 253 | uint64_t operator()(T* ptr) const noexcept 254 | { 255 | uint64_t raw = (uint64_t)ptr; 256 | return (uint64_t)(raw + (raw >> INDIVI_PTR_SHIFT)); 257 | } 258 | }; 259 | 260 | template< class T > 261 | struct hash> 262 | { 263 | uint64_t operator()(const std::unique_ptr& ptr) const noexcept 264 | { 265 | uint64_t raw = (uint64_t)ptr.get(); 266 | return (uint64_t)(raw + (raw >> INDIVI_PTR_SHIFT)); 267 | } 268 | }; 269 | 270 | template< class T > 271 | struct hash> 272 | { 273 | uint64_t operator()(const std::shared_ptr& ptr) const noexcept 274 | { 275 | uint64_t raw = (uint64_t)ptr.get(); 276 | return (uint64_t)(raw + (raw >> INDIVI_PTR_SHIFT)); 277 | } 278 | }; 279 | 280 | template< typename Enum > 281 | struct hash::value>::type> 282 | { 283 | uint64_t operator()(Enum e) const noexcept 284 | { 285 | return (uint64_t)e; 286 | } 287 | }; 288 | 289 | // Identity (non avalanching) 290 | #define INDIVI_HASH_IMPL(T) \ 291 | template<> \ 292 | struct hash { \ 293 | uint64_t operator()(const T& obj) const noexcept { \ 294 | return (uint64_t)obj; \ 295 | } \ 296 | } 297 | 298 | #ifdef INDIVI_CPP17 299 | INDIVI_HASH_IMPL(std::byte); 300 | #endif 301 | INDIVI_HASH_IMPL(bool); 302 | INDIVI_HASH_IMPL(char); 303 | INDIVI_HASH_IMPL(signed char); 304 | INDIVI_HASH_IMPL(unsigned char); 305 | INDIVI_HASH_IMPL(char16_t); 306 | INDIVI_HASH_IMPL(char32_t); 307 | INDIVI_HASH_IMPL(wchar_t); 308 | INDIVI_HASH_IMPL(short); 309 | INDIVI_HASH_IMPL(unsigned short); 310 | INDIVI_HASH_IMPL(int); 311 | INDIVI_HASH_IMPL(unsigned int); 312 | INDIVI_HASH_IMPL(long); 313 | INDIVI_HASH_IMPL(unsigned long); 314 | INDIVI_HASH_IMPL(long long); 315 | INDIVI_HASH_IMPL(unsigned long long); 316 | 317 | } // namespace indivi 318 | 319 | #undef INDIVI_HASH_IMPL 320 | #undef INDIVI_PTR_SHIFT 321 | 322 | #endif // INDIVI_HASH_H 323 | -------------------------------------------------------------------------------- /src/indivi/flat_umap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | #ifndef INDIVI_FLAT_UMAP_H 7 | #define INDIVI_FLAT_UMAP_H 8 | 9 | #include "indivi/hash.h" 10 | #include "indivi/detail/flat_utable.h" 11 | 12 | #include // for std::equal_to 13 | 14 | namespace indivi 15 | { 16 | /* 17 | * Flat_umap is a fast associative container that stores unordered unique key-value pairs. 18 | * Similar to `std::unordered_map` but using an open-addressing schema, 19 | * with a dynamically allocated, consolidated array of values and metadata (capacity grows based on power of 2). 20 | * It is optimized for small sizes (starting at 2, container sizeof is 48 Bytes on 64-bits). 21 | * 22 | * Each entry uses 2 additional bytes of metadata (to store hash fragments, overflow counters and distances from original buckets). 23 | * Avoiding the need for a tombstone mechanism or rehashing on iterator erase (with a good hash function). 24 | * By grouping buckets, it also relies on SIMD operations for speed (SSE2 or NEON are mandatory). 25 | * 26 | * Come with an optimized 64-bits hash function by default (see `hash.h`). 27 | * Use a fixed max load factor of 0.875. 28 | * Iterators are invalidated on usual open-addressing operations (except the end iterator), but never on erase. 29 | * Search, insertion, and removal of elements have average constant time 𝓞(1) complexity. 30 | * Best for general purpose scenarios, including erasure and iteration. 31 | */ 32 | template< 33 | class Key, 34 | class T, 35 | class Hash = indivi::hash, 36 | class KeyEqual = std::equal_to > 37 | class flat_umap 38 | { 39 | public: 40 | using key_type = Key; 41 | using mapped_type = T; 42 | using value_type = std::pair; 43 | using size_type = std::size_t; 44 | using difference_type = std::make_signed::type; 45 | using hasher = Hash; 46 | using key_equal = KeyEqual; 47 | using reference = value_type&; 48 | using const_reference = const value_type&; 49 | using pointer = value_type*; 50 | using const_pointer = const value_type*; 51 | 52 | private: 53 | using nc_key_type = typename std::remove_const::type; 54 | using nc_mapped_type = typename std::remove_const::type; 55 | using item_type = std::pair; 56 | using init_type = item_type; 57 | using flat_utable = detail::flat_utable; 58 | 59 | // Members 60 | flat_utable mTable; 61 | 62 | size_type group_capa() const noexcept { return mTable.group_capa(); } 63 | Hash& hash() noexcept { return mTable.hash(); } 64 | const Hash& hash() const noexcept { return mTable.hash(); } 65 | KeyEqual& equal() noexcept { return mTable.equal(); } 66 | const KeyEqual& equal() const noexcept { return mTable.equal(); } 67 | 68 | public: 69 | using iterator = typename flat_utable::iterator; 70 | using const_iterator = typename flat_utable::const_iterator; 71 | 72 | // Ctr/Dtr 73 | flat_umap() : flat_umap(0) 74 | {} 75 | 76 | explicit flat_umap(size_type bucket_count, const Hash& hash = Hash(), const key_equal& equal = key_equal()) 77 | : mTable(bucket_count, hash, equal) 78 | {} 79 | 80 | template< class InputIt > 81 | flat_umap(InputIt first, InputIt last, size_type bucket_count = 0, const Hash& hash = Hash(), const key_equal& equal = key_equal()) 82 | : mTable(first, last, bucket_count, hash, equal) 83 | {} 84 | 85 | flat_umap(std::initializer_list init, size_type bucket_count = 0, const Hash& hash = Hash(), const key_equal& equal = key_equal()) 86 | : mTable(init.begin(), init.end(), bucket_count, hash, equal) 87 | {} 88 | 89 | flat_umap(const flat_umap& other) 90 | : mTable(other.mTable) 91 | {} 92 | 93 | flat_umap(flat_umap&& other) 94 | noexcept(std::is_nothrow_move_constructible::value) 95 | : mTable(std::move(other.mTable)) 96 | {} 97 | 98 | ~flat_umap() = default; 99 | 100 | // Assignment 101 | flat_umap& operator=(const flat_umap& other) 102 | { 103 | mTable = other.mTable; 104 | return *this; 105 | } 106 | flat_umap& operator=(flat_umap&& other) noexcept(std::is_nothrow_move_assignable::value) 107 | { 108 | mTable = std::move(other.mTable); 109 | return *this; 110 | } 111 | flat_umap& operator=(std::initializer_list ilist) 112 | { 113 | clear(); 114 | insert(ilist.begin(), ilist.end()); 115 | return *this; 116 | } 117 | 118 | // Iterators 119 | iterator begin() noexcept { return mTable.begin(); } 120 | const_iterator begin() const noexcept { return mTable.begin(); } 121 | const_iterator cbegin() const noexcept { return mTable.cbegin(); } 122 | 123 | iterator end() noexcept { return mTable.end(); } 124 | const_iterator end() const noexcept { return mTable.cend(); } 125 | const_iterator cend() const noexcept { return mTable.cend(); } 126 | 127 | // Capacity 128 | bool empty() const noexcept { return mTable.empty(); } 129 | size_type size() const noexcept { return mTable.size(); } 130 | size_type max_size() const noexcept { return mTable.max_size(); } 131 | 132 | // Bucket interface 133 | size_type bucket_count() const noexcept { return mTable.bucket_count(); } 134 | size_type max_bucket_count() const noexcept { return mTable.max_bucket_count(); } 135 | 136 | // Hash policy 137 | float load_factor() const noexcept { return mTable.load_factor(); } 138 | float max_load_factor() const noexcept { return mTable.max_load_factor(); } 139 | void max_load_factor(float) noexcept { /*for compatibility*/ } 140 | 141 | void rehash(size_type count) { mTable.rehash(count); } 142 | void reserve(size_type count) { mTable.reserve(count); } 143 | 144 | // Observers 145 | hasher hash_function() const { return mTable.hash_function(); } 146 | key_equal key_eq() const { return mTable.key_eq(); } 147 | 148 | // Lookup 149 | T& at(const Key& key) { return mTable.at(key); } 150 | const T& at(const Key& key) const { return mTable.at(key); } 151 | 152 | T& operator[](const Key& key) { return mTable[key]; } 153 | T& operator[](Key&& key) { return mTable[std::move(key)]; } 154 | 155 | size_type count(const Key& key) const { return mTable.count(key); } 156 | bool contains(const Key& key) const { return mTable.contains(key); } 157 | 158 | iterator find(const Key& key) { return mTable.find(key); } 159 | const_iterator find(const Key& key) const { return mTable.find(key); } 160 | 161 | // Modifiers 162 | void clear() noexcept { mTable.clear(); } 163 | 164 | template< class P > 165 | std::pair insert(P&& value) { return mTable.insert(std::forward

(value)); } 166 | 167 | std::pair insert(init_type&& value) { return mTable.insert(std::move(value)); } 168 | 169 | template< class InputIt > 170 | void insert(InputIt first, InputIt last) { mTable.insert(first, last); } 171 | 172 | template< typename = void > // resolve ambiguities with item_type 173 | void insert(std::initializer_list ilist) { mTable.insert_list(ilist); } 174 | void insert(std::initializer_list ilist) { mTable.insert_list(ilist); } 175 | 176 | template< class M > 177 | std::pair insert_or_assign(const Key& key, M&& obj) { return mTable.insert_or_assign(key, std::forward(obj)); } 178 | 179 | template< class M > 180 | std::pair insert_or_assign(Key&& key, M&& obj) { return mTable.insert_or_assign(std::move(key), std::forward(obj)); } 181 | 182 | template< class U1, class U2 > 183 | std::pair emplace(U1&& key, U2&& obj) { return mTable.try_emplace(std::forward(key), std::forward(obj)); } 184 | 185 | template< class... Args > 186 | std::pair emplace(Args&&... args) { return mTable.emplace(std::forward(args)...); } 187 | 188 | template< class... Args > 189 | std::pair try_emplace(const Key& key, Args&&... args) { return mTable.try_emplace(key, std::forward(args)...); } 190 | 191 | template< class... Args > 192 | std::pair try_emplace(Key&& key, Args&&... args) { return mTable.try_emplace(std::move(key), std::forward(args)...); } 193 | 194 | iterator erase_(iterator pos) { return mTable.erase_(pos); } 195 | iterator erase_(const_iterator pos) { return mTable.erase_(pos); } 196 | // non-standard, see `erase_()` 197 | void erase(iterator pos) { mTable.erase(pos); } 198 | void erase(const_iterator pos) { mTable.erase(pos); } 199 | 200 | size_type erase(const Key& key) { return mTable.erase(key); } 201 | 202 | void swap(flat_umap& other) noexcept(noexcept(mTable.swap(other.mTable))) { mTable.swap(other.mTable); } 203 | 204 | // Non-member 205 | friend void swap(flat_umap& lhs, flat_umap& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } 206 | 207 | friend bool operator==(const flat_umap& lhs, const flat_umap& rhs) { return lhs.mTable.equal(rhs.mTable); } 208 | 209 | friend bool operator!=(const flat_umap& lhs, const flat_umap& rhs) { return !(lhs.mTable.equal(rhs.mTable)); } 210 | 211 | template< class Pred > 212 | friend size_type erase_if(flat_umap& map, Pred pred) { return map.mTable.erase_if(pred); } 213 | 214 | #ifdef INDIVI_FLAT_U_DEBUG 215 | bool is_cleared() const noexcept { return mTable.is_cleared(); } 216 | #endif 217 | #ifdef INDIVI_FLAT_U_STATS 218 | typename flat_utable::GroupStats get_group_stats() const noexcept { return mTable.get_group_stats(); } 219 | typename flat_utable::FindStats get_find_stats() const noexcept { return mTable.get_find_stats(); } 220 | void reset_find_stats() noexcept { return mTable.reset_find_stats(); } 221 | #endif 222 | }; 223 | 224 | } // namespace indivi 225 | 226 | #endif // INDIVI_FLAT_UMAP_H 227 | -------------------------------------------------------------------------------- /src/indivi/flat_wmap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | #ifndef INDIVI_FLAT_WMAP_H 7 | #define INDIVI_FLAT_WMAP_H 8 | 9 | #include "indivi/hash.h" 10 | #include "indivi/detail/flat_wtable.h" 11 | 12 | #include // for std::equal_to 13 | 14 | namespace indivi 15 | { 16 | /* 17 | * Flat_wmap is a fast associative container that stores (unaligned) unordered unique key-value pairs. 18 | * Similar to `std::unordered_map` but using an open-addressing schema, 19 | * with a dynamically allocated, consolidated array of values and metadata (capacity grows based on power of 2). 20 | * It is optimized for small sizes (starting at 2, container sizeof is 48 Bytes on 64-bits). 21 | * 22 | * Each entry uses 1 additional byte of metadata (to store hash fragments or empty/tombstone markers). 23 | * While trying to greatly minimize tombstone usage on erase. 24 | * It doesn't group buckets but still relies on SIMD operations for speed (SSE2 or NEON are mandatory). 25 | * 26 | * Come with an optimized 64-bits hash function by default (see `hash.h`). 27 | * Use a fixed max load factor of 0.8 (a bit lower, to keep find-miss fast on high loads). 28 | * Iterators are invalidated on usual open-addressing operations (except the end iterator), but never on erase. 29 | * Search, insertion, and removal of elements have average constant time 𝓞(1) complexity. 30 | * Best for find hit/miss scenarios, a bit slower for re-inserting and iterating. 31 | */ 32 | template< 33 | class Key, 34 | class T, 35 | class Hash = indivi::hash, 36 | class KeyEqual = std::equal_to > 37 | class flat_wmap 38 | { 39 | public: 40 | using key_type = Key; 41 | using mapped_type = T; 42 | using value_type = std::pair; 43 | using size_type = std::size_t; 44 | using difference_type = std::make_signed::type; 45 | using hasher = Hash; 46 | using key_equal = KeyEqual; 47 | using reference = value_type&; 48 | using const_reference = const value_type&; 49 | using pointer = value_type*; 50 | using const_pointer = const value_type*; 51 | 52 | private: 53 | using nc_key_type = typename std::remove_const::type; 54 | using nc_mapped_type = typename std::remove_const::type; 55 | using item_type = std::pair; 56 | using init_type = item_type; 57 | using flat_wtable = detail::flat_wtable; 58 | 59 | // Members 60 | flat_wtable mTable; 61 | 62 | size_type group_capa() const noexcept { return mTable.group_capa(); } 63 | Hash& hash() noexcept { return mTable.hash(); } 64 | const Hash& hash() const noexcept { return mTable.hash(); } 65 | KeyEqual& equal() noexcept { return mTable.equal(); } 66 | const KeyEqual& equal() const noexcept { return mTable.equal(); } 67 | 68 | public: 69 | using iterator = typename flat_wtable::iterator; 70 | using const_iterator = typename flat_wtable::const_iterator; 71 | 72 | // Ctr/Dtr 73 | flat_wmap() : flat_wmap(0) 74 | {} 75 | 76 | explicit flat_wmap(size_type bucket_count, const Hash& hash = Hash(), const key_equal& equal = key_equal()) 77 | : mTable(bucket_count, hash, equal) 78 | {} 79 | 80 | template< class InputIt > 81 | flat_wmap(InputIt first, InputIt last, size_type bucket_count = 0, const Hash& hash = Hash(), const key_equal& equal = key_equal()) 82 | : mTable(first, last, bucket_count, hash, equal) 83 | {} 84 | 85 | flat_wmap(std::initializer_list init, size_type bucket_count = 0, const Hash& hash = Hash(), const key_equal& equal = key_equal()) 86 | : mTable(init.begin(), init.end(), bucket_count, hash, equal) 87 | {} 88 | 89 | flat_wmap(const flat_wmap& other) 90 | : mTable(other.mTable) 91 | {} 92 | 93 | flat_wmap(flat_wmap&& other) 94 | noexcept(std::is_nothrow_move_constructible::value) 95 | : mTable(std::move(other.mTable)) 96 | {} 97 | 98 | ~flat_wmap() = default; 99 | 100 | // Assignment 101 | flat_wmap& operator=(const flat_wmap& other) 102 | { 103 | mTable = other.mTable; 104 | return *this; 105 | } 106 | flat_wmap& operator=(flat_wmap&& other) noexcept(std::is_nothrow_move_assignable::value) 107 | { 108 | mTable = std::move(other.mTable); 109 | return *this; 110 | } 111 | flat_wmap& operator=(std::initializer_list ilist) 112 | { 113 | clear(); 114 | insert(ilist.begin(), ilist.end()); 115 | return *this; 116 | } 117 | 118 | // Iterators 119 | iterator begin() noexcept { return mTable.begin(); } 120 | const_iterator begin() const noexcept { return mTable.begin(); } 121 | const_iterator cbegin() const noexcept { return mTable.cbegin(); } 122 | 123 | iterator end() noexcept { return mTable.end(); } 124 | const_iterator end() const noexcept { return mTable.cend(); } 125 | const_iterator cend() const noexcept { return mTable.cend(); } 126 | 127 | // Capacity 128 | bool empty() const noexcept { return mTable.empty(); } 129 | size_type size() const noexcept { return mTable.size(); } 130 | size_type max_size() const noexcept { return mTable.max_size(); } 131 | 132 | // Bucket interface 133 | size_type bucket_count() const noexcept { return mTable.bucket_count(); } 134 | size_type max_bucket_count() const noexcept { return mTable.max_bucket_count(); } 135 | 136 | // Hash policy 137 | float load_factor() const noexcept { return mTable.load_factor(); } 138 | float max_load_factor() const noexcept { return mTable.max_load_factor(); } 139 | void max_load_factor(float) noexcept { /*for compatibility*/ } 140 | 141 | void rehash(size_type count) { mTable.rehash(count); } 142 | void reserve(size_type count) { mTable.reserve(count); } 143 | 144 | // Observers 145 | hasher hash_function() const { return mTable.hash_function(); } 146 | key_equal key_eq() const { return mTable.key_eq(); } 147 | 148 | // Lookup 149 | T& at(const Key& key) { return mTable.at(key); } 150 | const T& at(const Key& key) const { return mTable.at(key); } 151 | 152 | T& operator[](const Key& key) { return mTable[key]; } 153 | T& operator[](Key&& key) { return mTable[std::move(key)]; } 154 | 155 | size_type count(const Key& key) const { return mTable.count(key); } 156 | bool contains(const Key& key) const { return mTable.contains(key); } 157 | 158 | iterator find(const Key& key) { return mTable.find(key); } 159 | const_iterator find(const Key& key) const { return mTable.find(key); } 160 | 161 | // Modifiers 162 | void clear() noexcept { mTable.clear(); } 163 | 164 | template< class P > 165 | std::pair insert(P&& value) { return mTable.insert(std::forward

(value)); } 166 | 167 | std::pair insert(init_type&& value) { return mTable.insert(std::move(value)); } 168 | 169 | template< class InputIt > 170 | void insert(InputIt first, InputIt last) { mTable.insert(first, last); } 171 | 172 | template< typename = void > // resolve ambiguities with item_type 173 | void insert(std::initializer_list ilist) { mTable.insert_list(ilist); } 174 | void insert(std::initializer_list ilist) { mTable.insert_list(ilist); } 175 | 176 | template< class M > 177 | std::pair insert_or_assign(const Key& key, M&& obj) { return mTable.insert_or_assign(key, std::forward(obj)); } 178 | 179 | template< class M > 180 | std::pair insert_or_assign(Key&& key, M&& obj) { return mTable.insert_or_assign(std::move(key), std::forward(obj)); } 181 | 182 | template< class U1, class U2 > 183 | std::pair emplace(U1&& key, U2&& obj) { return mTable.try_emplace(std::forward(key), std::forward(obj)); } 184 | 185 | template< class... Args > 186 | std::pair emplace(Args&&... args) { return mTable.emplace(std::forward(args)...); } 187 | 188 | template< class... Args > 189 | std::pair try_emplace(const Key& key, Args&&... args) { return mTable.try_emplace(key, std::forward(args)...); } 190 | 191 | template< class... Args > 192 | std::pair try_emplace(Key&& key, Args&&... args) { return mTable.try_emplace(std::move(key), std::forward(args)...); } 193 | 194 | iterator erase_(iterator pos) { return mTable.erase_(pos); } 195 | iterator erase_(const_iterator pos) { return mTable.erase_(pos); } 196 | // non-standard, see `erase_()` 197 | void erase(iterator pos) { mTable.erase(pos); } 198 | void erase(const_iterator pos) { mTable.erase(pos); } 199 | 200 | size_type erase(const Key& key) { return mTable.erase(key); } 201 | 202 | void swap(flat_wmap& other) noexcept(noexcept(mTable.swap(other.mTable))) { mTable.swap(other.mTable); } 203 | 204 | // Non-member 205 | friend void swap(flat_wmap& lhs, flat_wmap& rhs) noexcept(noexcept(lhs.swap(rhs))) { lhs.swap(rhs); } 206 | 207 | friend bool operator==(const flat_wmap& lhs, const flat_wmap& rhs) { return lhs.mTable.equal(rhs.mTable); } 208 | 209 | friend bool operator!=(const flat_wmap& lhs, const flat_wmap& rhs) { return !(lhs.mTable.equal(rhs.mTable)); } 210 | 211 | template< class Pred > 212 | friend size_type erase_if(flat_wmap& map, Pred pred) { return map.mTable.erase_if(pred); } 213 | 214 | #ifdef INDIVI_FLAT_W_DEBUG 215 | bool is_cleared() const noexcept { return mTable.is_cleared(); } 216 | #endif 217 | #ifdef INDIVI_FLAT_W_STATS 218 | typename flat_wtable::GroupStats get_group_stats() const noexcept { return mTable.get_group_stats(); } 219 | typename flat_wtable::FindStats get_find_stats() const noexcept { return mTable.get_find_stats(); } 220 | void reset_find_stats() noexcept { return mTable.reset_find_stats(); } 221 | #endif 222 | }; 223 | 224 | } // namespace indivi 225 | 226 | #endif // INDIVI_FLAT_WMAP_H 227 | -------------------------------------------------------------------------------- /lib/seq_1.1.0/type_traits.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Victor Moncada 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef SEQ_TYPE_TRAITS_HPP 26 | #define SEQ_TYPE_TRAITS_HPP 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | 35 | 36 | // namespace std 37 | // { 38 | // #if defined(__GNUG__) && (__GNUC__ < 5) 39 | 40 | // // Reimplement the wheel for older gcc 41 | 42 | // template 43 | // struct is_trivially_copyable 44 | // { 45 | // static constexpr bool value = __has_trivial_copy(T); 46 | // }; 47 | 48 | // template< class T> 49 | // struct is_trivially_move_assignable : is_trivially_copyable {}; 50 | // : std::is_trivially_assignable< typename std::add_lvalue_reference::type, 51 | // typename std::add_rvalue_reference::type> {}; 52 | 53 | 54 | // #endif 55 | // } 56 | 57 | namespace seq 58 | { 59 | 60 | /// @brief Compute integer type maximum value at compile time 61 | template ::value> 62 | struct integer_max 63 | { 64 | static constexpr T value = std::numeric_limits::max();//static_cast( ~(static_cast < T>(1) << (static_cast(sizeof(T) * 8) - static_cast < T>(1))) ); 65 | }; 66 | template 67 | struct integer_max 68 | { 69 | static constexpr T value = static_cast(-1); 70 | }; 71 | 72 | /// @brief Compute integer type minimum value at compile time 73 | template ::value> 74 | struct integer_min 75 | { 76 | static constexpr T value = (-integer_max::value) - static_cast < T>(1) ; 77 | }; 78 | template 79 | struct integer_min 80 | { 81 | static constexpr T value = static_cast < T>(0); 82 | }; 83 | 84 | /// @brief Define the return type of seq::negate_if_signed and seq::abs 85 | template::value, size_t Size = sizeof(T)> 86 | struct integer_abs_return 87 | { 88 | using type = T; 89 | }; 90 | template 91 | struct integer_abs_return 92 | { 93 | using type = std::uint16_t; 94 | }; 95 | template 96 | struct integer_abs_return 97 | { 98 | using type = std::uint32_t; 99 | }; 100 | template 101 | struct integer_abs_return 102 | { 103 | using type = std::uint64_t; 104 | }; 105 | template 106 | struct integer_abs_return 107 | { 108 | using type = std::uint64_t; 109 | }; 110 | 111 | namespace detail 112 | { 113 | template::value> 114 | struct IntegerAbs 115 | { 116 | using type = typename integer_abs_return::type; 117 | static inline auto neg_if_signed(T v) -> type { return static_cast(-v); } 118 | static inline auto abs(T v) -> type { return static_cast(v < 0 ? -v : v); } 119 | }; 120 | template 121 | struct IntegerAbs 122 | { 123 | using type = T; 124 | static inline auto neg_if_signed(T v) -> T { return v; } 125 | static inline auto abs(T v) -> T { return v; } 126 | }; 127 | } 128 | 129 | /// @brief Returns -v if v is signed, v otherwise. 130 | template 131 | auto negate_if_signed(T v) -> typename integer_abs_return::type { return detail::IntegerAbs::neg_if_signed(v); } 132 | /// @brief Returns absolute value of v. 133 | template 134 | auto abs(T v) -> typename integer_abs_return::type { return detail::IntegerAbs::abs(v); } 135 | 136 | 137 | 138 | 139 | namespace detail 140 | { 141 | template 142 | struct unique_ptr_traits {}; 143 | 144 | template 145 | struct unique_ptr_traits > 146 | { 147 | using value_type = T; 148 | using pointer = T*; 149 | using const_pointer = const T*; 150 | using reference = T&; 151 | using const_reference = const T&; 152 | }; 153 | template 154 | struct unique_ptr_traits > 155 | { 156 | using value_type = T; 157 | using pointer = const T*; 158 | using const_pointer = const T*; 159 | using reference = const T&; 160 | using const_reference = const T&; 161 | }; 162 | } 163 | 164 | /// @brief Inherits std::true_type is T is of type std::unique_ptr<...>, false otherwise 165 | template 166 | struct is_unique_ptr : std::false_type {}; 167 | 168 | template 169 | struct is_unique_ptr > : std::true_type {}; 170 | 171 | /// @brief Type trait telling if a class is relocatable or not. 172 | /// 173 | /// A type is considered relocatable if these consecutive calls 174 | /// \code{.cpp} 175 | /// new(new_place) T(std::move(old_place)); 176 | /// old_place.~T(); 177 | /// \endcode 178 | /// can be replaced by 179 | /// \code{.cpp} 180 | /// memcpy(&new_place, &old_place, sizeof(T)); 181 | /// \endcode 182 | /// 183 | /// This property is used to optimize containers like seq::devector, seq::tiered_vector, seq::flat_(map/set/multimap/multiset). 184 | /// 185 | template 186 | struct is_relocatable { 187 | static constexpr bool value = std::is_trivially_copyable::value && std::is_trivially_destructible::value; 188 | }; 189 | 190 | // Specilizations for unique_ptr, shared_ptr and pair 191 | 192 | template 193 | struct is_relocatable > { 194 | static constexpr bool value = is_relocatable::value; 195 | }; 196 | template 197 | struct is_relocatable > { 198 | static constexpr bool value = true; 199 | }; 200 | template 201 | struct is_relocatable > { 202 | static constexpr bool value = is_relocatable::value && is_relocatable::value; 203 | }; 204 | template 205 | struct is_relocatable > { 206 | static constexpr bool value = true; 207 | }; 208 | 209 | 210 | 211 | // On msvc, std::string is relocatable, as opposed to gcc implementation that stores a pointer to its internal data 212 | /*#if defined( _MSC_VER) 213 | template 214 | struct is_relocatable > { 215 | static constexpr bool value = true; 216 | }; 217 | #endif*/ 218 | 219 | /// @brief Tells if given type is hashable with std::hash. 220 | /// True by default, optimistically assume that all types are hashable. 221 | /// Used by seq::hold_any. 222 | template 223 | struct is_hashable : std::true_type {}; 224 | 225 | /// @brief Tells if given type can be streamed to a std::ostream object 226 | template 227 | class is_ostreamable 228 | { 229 | template 230 | static auto test(int) 231 | -> decltype(std::declval() << std::declval(), std::true_type()); 232 | 233 | template 234 | static auto test(...)->std::false_type; 235 | 236 | public: 237 | static constexpr bool value = decltype(test(0))::value; 238 | }; 239 | 240 | /// @brief Tells if given type can be read from a std::istream object 241 | template 242 | class is_istreamable 243 | { 244 | template 245 | static auto test(int) 246 | -> decltype(std::declval() >> std::declval(), std::true_type()); 247 | 248 | template 249 | static auto test(...)->std::false_type; 250 | 251 | public: 252 | static constexpr bool value = decltype(test(0))::value; 253 | }; 254 | 255 | /// @brief Tells if given type supports equality comparison with operator == 256 | template 257 | class is_equal_comparable 258 | { 259 | template 260 | static auto test(int) 261 | -> decltype(std::declval() == std::declval(), std::true_type()); 262 | 263 | template 264 | static auto test(...)->std::false_type; 265 | 266 | public: 267 | static constexpr bool value = decltype(test(0))::value; 268 | }; 269 | 270 | /// @brief Tells if given type supports comparison with operator < 271 | template 272 | class is_less_comparable 273 | { 274 | template 275 | static auto test(int) 276 | -> decltype(std::declval() < std::declval(), std::true_type()); 277 | 278 | template 279 | static auto test(...)->std::false_type; 280 | 281 | public: 282 | static constexpr bool value = decltype(test(0))::value; 283 | }; 284 | 285 | /// @brief Tells if given type supports invocation with signature void(Args ...) 286 | /// Equivalent to C++17 std::is_invocable 287 | template 288 | struct is_invocable : 289 | std::is_constructible< 290 | std::function, 291 | std::reference_wrapper::type> 292 | > 293 | { 294 | }; 295 | 296 | /// @brief Tells if given type supports invocation with signature R(Args ...) 297 | /// Equivalent to C++17 std::is_invocable_r 298 | template 299 | struct is_invocable_r : 300 | std::is_constructible< 301 | std::function, 302 | std::reference_wrapper::type> 303 | > 304 | { 305 | }; 306 | 307 | 308 | 309 | namespace detail 310 | { 311 | template 312 | struct make_void { 313 | using type = void; 314 | }; 315 | 316 | template 317 | struct has_iterator : std::false_type {}; 318 | 319 | template 320 | struct has_iterator::type> 322 | : std::true_type {}; 323 | 324 | template 325 | struct has_value_type : std::false_type {}; 326 | 327 | template 328 | struct has_value_type::type> 330 | : std::true_type {}; 331 | } 332 | 333 | template 334 | struct is_iterable 335 | { 336 | static constexpr bool value = detail::has_iterator::value && detail::has_value_type::value; 337 | }; 338 | } 339 | #endif 340 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/utils/debug_utils.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2021 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | #ifndef DEBUG_UTILS_H 7 | #define DEBUG_UTILS_H 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | // arbitrary 16 | #define INIT_CODE (9876) // _init after ctr 17 | #define MOVE_CODE (7689) // _init after move 18 | #define DTR_ID_CODE (-1234) // id after dtr 19 | #define DTR_VAL_CODE (-4321) // val after dtr 20 | //#define MVE_ID_CODE (2143) // id after move 21 | #define MVE_VAL_CODE (3412) // val after move 22 | 23 | 24 | // Debug Class 1 25 | struct DbgClass 26 | { 27 | static bool Dbg; // need init in translation unit 28 | static int idx; 29 | static int count; 30 | int id; // 0 = destroyed 31 | 32 | DbgClass() : id(++idx) { ++count; if (Dbg) std::cout << "Ctr: " << id << "\n"; } 33 | DbgClass(int id_) : id(id_) { assert(id > 0); ++count; if (Dbg) std::cout << "Ctr: " << id << "\n"; } 34 | DbgClass(const DbgClass& other) : id(other.id) { assert(id > 0); ++count; if (Dbg) std::cout << "Ctr(const&): " << id << "\n"; } 35 | DbgClass(DbgClass&& other) noexcept : id(other.id) { assert(id > 0); ++count; other.id = -other.id; if (Dbg) std::cout << "Ctr(&&): " << id << "\n"; } 36 | ~DbgClass() { assert(id != 0); --count; if (Dbg) std::cout << "Dtr: " << id << "\n"; id = 0; } 37 | 38 | DbgClass& operator=(const DbgClass& other) { id = other.id; if (Dbg) std::cout << "Op=&: " << id << "\n"; return *this; } 39 | DbgClass& operator=(DbgClass&& other) noexcept { id = other.id; other.id = -other.id; if (Dbg) std::cout << "Op=&&: " << id << "\n"; return *this; } 40 | 41 | bool operator==(const DbgClass& other) const { return id == other.id; } 42 | bool operator!=(const DbgClass& other) const { return id != other.id; } 43 | }; 44 | 45 | // MurmurHash3’s 64-bit finalizer 46 | // by Austin Appleby, public domain 47 | static uint64_t hash_key(uint64_t key) 48 | { 49 | uint64_t result = key; 50 | result ^= result >> 33; 51 | result *= 0xff51afd7ed558ccdull; 52 | result ^= result >> 33; 53 | result *= 0xc4ceb9fe1a85ec53ull; 54 | result ^= result >> 33; 55 | return result; 56 | } 57 | 58 | namespace std { 59 | template <> struct hash { 60 | size_t operator()(const DbgClass& x) const { return hash_key(x.id); } 61 | }; 62 | } 63 | 64 | // Debug Class 2 65 | class dClass 66 | { 67 | public: 68 | dClass() : val(-1), id(++count), _init(INIT_CODE) { 69 | if (!quiet) std::cout << "Ctr0: " << id << " (val: " << val << ")" << std::endl; 70 | } 71 | dClass(int v) : val(v), id(++count), _init(INIT_CODE) { 72 | if (!quiet) std::cout << "Ctr1: " << id << " (val: " << val << ")" << std::endl; 73 | } 74 | dClass(const dClass& d) : val(d.val), id(++count), _init(INIT_CODE) { 75 | assert(id != d.id); 76 | assert(d._init == INIT_CODE); 77 | ++copies; 78 | if (!quiet) std::cout << "CtrCpy: " << id << " from " << d.id << " (val: " << val << ")" << std::endl; 79 | } 80 | dClass(dClass&& d) noexcept : val(d.val), id(++count), _init(INIT_CODE) { 81 | assert(id != d.id); 82 | assert(d._init == INIT_CODE); 83 | ++moves; 84 | if (!quiet) std::cout << "CtrMve: " << id << " from " << d.id << " (val: " << val << ")" << std::endl; 85 | d._init = MOVE_CODE; 86 | d.val = MVE_VAL_CODE; 87 | } 88 | 89 | ~dClass() { 90 | assert((id != DTR_ID_CODE || val != DTR_VAL_CODE || _init != -INIT_CODE) && "Error: double destruct"); 91 | assert(id > 0 && id <= count && (_init == INIT_CODE || _init == MOVE_CODE) && "Error: garbage destruct"); 92 | if (!quiet) std::cout << "Dtr: " << id << " (val: " << val << ")" << std::endl; 93 | id = DTR_ID_CODE; 94 | val = DTR_VAL_CODE; 95 | _init = -INIT_CODE; 96 | ++decount; 97 | } 98 | 99 | dClass& operator=(const dClass& d) 100 | { 101 | // assert(id != d.id); 102 | assert( _init == INIT_CODE || _init == MOVE_CODE); 103 | assert(d._init == INIT_CODE); 104 | ++copies; 105 | if (!quiet) 106 | std::cout << "AssignCpy: " << d.id << " overrides " << id 107 | << " (val: " << d.val << " overrides " << val << ")" << std::endl; 108 | val = d.val; 109 | _init = INIT_CODE; 110 | return *this; 111 | } 112 | dClass& operator=(dClass&& d) noexcept 113 | { 114 | // assert(id != d.id); 115 | assert( _init == INIT_CODE || _init == MOVE_CODE); 116 | assert(d._init == INIT_CODE); 117 | ++moves; 118 | if (!quiet) 119 | std::cout << "AssignMve: " << d.id << " overrides " << id 120 | << " (val: " << d.val << " overrides " << val << ")" << std::endl; 121 | val = d.val; 122 | _init = INIT_CODE; 123 | 124 | d._init = MOVE_CODE; 125 | d.val = MVE_VAL_CODE; 126 | return *this; 127 | } 128 | 129 | std::string toString() const { return std::to_string(id) + " (val: " + std::to_string(val) + ")\n"; } 130 | 131 | // Non-member functions 132 | friend bool operator<(const dClass& lhs, const dClass& rhs) { return lhs.val < rhs.val; } 133 | friend std::ostream& operator<<(std::ostream &os, const dClass& d) { return os << d.val; } 134 | 135 | friend bool operator==(const dClass& lhs, const dClass& rhs) { return lhs.val == rhs.val; } 136 | friend bool operator!=(const dClass& lhs, const dClass& rhs) { return !(lhs.val == rhs.val); } 137 | 138 | // Members 139 | int val; 140 | int id; 141 | int _init; 142 | static int count; 143 | static int decount; 144 | static uint64_t copies; 145 | static uint64_t moves; 146 | static bool quiet; 147 | 148 | static void resetStats() noexcept 149 | { 150 | count = 0; 151 | decount = 0; 152 | copies = 0u; 153 | moves = 0u; 154 | quiet = true; 155 | } 156 | }; 157 | 158 | 159 | // Error Class, throws on: 160 | template 164 | class eClass 165 | { 166 | public: 167 | eClass() : val(-1), id(++count) { 168 | if (Ctr0 && WaitCount < ++countCtr0) { 169 | --count; 170 | throw std::runtime_error("Ctr0: " + std::to_string(id) + " (val: " + std::to_string(val) + ")"); 171 | } 172 | if (!quiet) std::cout << "Ctr0: " << id << " (val: " << val << ")" << std::endl; 173 | } 174 | eClass(int v) : val(v), id(++count) { 175 | bool failure = Ctr1 && WaitCount < ++countCtr1; 176 | if (failure) { 177 | --count; 178 | throw std::runtime_error("Ctr1: " + std::to_string(id) + " (val: " + std::to_string(val) + ")"); 179 | } 180 | if (!quiet) std::cout << "Ctr1: " << id << " (val: " << val << ")" << std::endl; 181 | } 182 | eClass(const eClass& d) : val(d.val), id(++count) { 183 | assert(id != d.id); 184 | bool failure = CtrCpy && WaitCount < ++countCtrCpy; 185 | if (failure) { 186 | --count; 187 | throw std::runtime_error("CtrCpy: " + std::to_string(id) + " from " + std::to_string(d.id) + " (val: " + std::to_string(val) + ")"); 188 | } 189 | if (!quiet) std::cout << "CtrCpy: " << id << " from " << d.id << " (val: " << val << ")" << std::endl; 190 | } 191 | eClass(eClass&& d) : val(d.val), id(++count) { 192 | assert(id != d.id); 193 | bool failure = CtrMve && WaitCount < ++countCtrMve; 194 | if (failure) { 195 | --count; 196 | throw std::runtime_error("CtrMve: " + std::to_string(id) + " from " + std::to_string(d.id) + " (val: " + std::to_string(val) + ")"); 197 | } 198 | if (!quiet) std::cout << "CtrMve: " << id << " from " << d.id << " (val: " << val << ")" << std::endl; 199 | } 200 | 201 | ~eClass() { 202 | assert((id != DTR_ID_CODE || val != DTR_VAL_CODE) && "Error: double destruct"); 203 | assert(id > 0 && id <= count && "Error: garbage destruct"); 204 | if (!quiet) std::cout << "Dtr: " << id << " (val: " << val << ")" << std::endl; 205 | id = DTR_ID_CODE; 206 | val = DTR_VAL_CODE; 207 | ++decount; 208 | } 209 | 210 | eClass& operator=(const eClass& d) 211 | { 212 | assert(id != d.id); 213 | bool failure = AsgCpy && WaitCount < ++countAsgCpy; 214 | if (failure) 215 | throw std::runtime_error("AsgCpy: " + std::to_string(d.id) + " overrides " + std::to_string(id) 216 | + " (val: " + std::to_string(d.val) + " overrides " + std::to_string(val) + ")"); 217 | if (!quiet) 218 | std::cout << "AsgCpy: " << d.id << " overrides " << id 219 | << " (val: " << d.val << " overrides " << val << ")" << std::endl; 220 | val = d.val; 221 | return *this; 222 | } 223 | eClass& operator=(eClass&& d) 224 | { 225 | assert(id != d.id); 226 | bool failure = AsgMve && WaitCount < ++countAsgMve; 227 | if (failure) 228 | throw std::runtime_error("AsgMve: " + std::to_string(d.id) + " overrides " + std::to_string(id) 229 | + " (val: " + std::to_string(d.val) + " overrides " + std::to_string(val) + ")"); 230 | if (!quiet) 231 | std::cout << "AsgMve: " << d.id << " overrides " << id 232 | << " (val: " << d.val << " overrides " << val << ")" << std::endl; 233 | val = d.val; 234 | return *this; 235 | } 236 | 237 | std::string toString() const { return std::to_string(id) + " (val: " + std::to_string(val) + ")\n"; } 238 | 239 | static void resetOpCounters() { countCtr0 = countCtr1 = countCtrCpy = countCtrMve = countAsgCpy = countAsgMve = 0; } 240 | 241 | 242 | // Members 243 | int val; 244 | int id; 245 | static int countCtr0, countCtr1, countCtrCpy, countCtrMve, countAsgCpy, countAsgMve; 246 | static int count; 247 | static int decount; 248 | static bool quiet; 249 | }; 250 | 251 | // Init 252 | template 253 | int eClass::countCtr0 = 0; 254 | template 255 | int eClass::countCtr1 = 0; 256 | template 257 | int eClass::countCtrCpy = 0; 258 | template 259 | int eClass::countCtrMve = 0; 260 | template 261 | int eClass::countAsgCpy = 0; 262 | template 263 | int eClass::countAsgMve = 0; 264 | template 265 | int eClass::count = 0; 266 | template 267 | int eClass::decount = 0; 268 | template 269 | bool eClass::quiet = true; 270 | 271 | // Helpers (throw on 2nd operation) 272 | typedef eClass eClassCtr0; 273 | typedef eClass eClassCtr1; 274 | typedef eClass eClassCtrCpy; 275 | typedef eClass eClassCtrMve; 276 | typedef eClass eClassAsgCpy; 277 | typedef eClass eClassAsgMve; 278 | 279 | 280 | #endif // DEBUG_UTILS_H 281 | -------------------------------------------------------------------------------- /lib/seq_1.1.0/utils.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * MIT License 3 | * 4 | * Copyright (c) 2022 Victor Moncada 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | * SOFTWARE. 23 | */ 24 | 25 | #ifndef SEQ_UTILS_HPP 26 | #define SEQ_UTILS_HPP 27 | 28 | 29 | 30 | /** @file */ 31 | 32 | #include 33 | 34 | #include "bits.hpp" 35 | #include "type_traits.hpp" 36 | 37 | 38 | namespace seq 39 | { 40 | 41 | /// @brief Memory Layout Management for containers like seq::sequence or seq::tiered_vector 42 | enum LayoutManagement 43 | { 44 | OptimizeForSpeed, //! Use more memory to favor speed 45 | OptimizeForMemory //! Use as few memory as possible 46 | }; 47 | 48 | 49 | /// @brief Convenient random access iterator on a constant value 50 | template 51 | class cvalue_iterator 52 | { 53 | using alloc_traits = std::allocator_traits >; 54 | public: 55 | using iterator_category = std::random_access_iterator_tag; 56 | using value_type = T; 57 | using difference_type = typename alloc_traits::difference_type; 58 | using size_type = typename alloc_traits::size_type; 59 | using pointer = typename alloc_traits::const_pointer; 60 | using reference = const value_type&; 61 | 62 | explicit cvalue_iterator(size_type _pos) : pos(_pos) {} 63 | cvalue_iterator(size_type _pos, const T& _value) : value(_value), pos(_pos) {} 64 | 65 | auto operator*() const noexcept -> reference { 66 | return value; 67 | } 68 | auto operator->() const noexcept -> pointer { 69 | return std::pointer_traits::pointer_to(**this); 70 | } 71 | auto operator++() noexcept -> cvalue_iterator& { 72 | ++pos; 73 | return *this; 74 | } 75 | auto operator++(int) noexcept -> cvalue_iterator { 76 | cvalue_iterator _Tmp = *this; 77 | ++(*this); 78 | return _Tmp; 79 | } 80 | auto operator--() noexcept -> cvalue_iterator& { 81 | // TODO(VM213788): check decrement 82 | --pos; 83 | return *this; 84 | } 85 | auto operator--(int) noexcept -> cvalue_iterator { 86 | cvalue_iterator _Tmp = *this; 87 | --(*this); 88 | return _Tmp; 89 | } 90 | auto operator==(const cvalue_iterator& other) const noexcept -> bool { 91 | return pos == other.pos; 92 | } 93 | auto operator!=(const cvalue_iterator& other) const noexcept -> bool { 94 | return pos != other.pos; 95 | } 96 | auto operator+=(difference_type diff)noexcept -> cvalue_iterator& { 97 | pos += diff; 98 | return *this; 99 | } 100 | auto operator-=(difference_type diff)noexcept -> cvalue_iterator& { 101 | pos -= diff; 102 | return *this; 103 | } 104 | auto operator[](difference_type /*diff*/) const noexcept -> const value_type& { 105 | return value; 106 | } 107 | 108 | T value; 109 | size_type pos; 110 | }; 111 | 112 | template < class T> 113 | auto operator+(const cvalue_iterator< T>& it, typename cvalue_iterator< T>::difference_type diff)noexcept -> cvalue_iterator< T> { 114 | cvalue_iterator< T> res = it; 115 | return res += diff; 116 | } 117 | template < class T> 118 | auto operator-(const cvalue_iterator< T>& it, typename cvalue_iterator< T>::difference_type diff)noexcept -> cvalue_iterator< T> { 119 | cvalue_iterator< T> res = it; 120 | return res -= diff; 121 | } 122 | template < class T> 123 | auto operator-(const cvalue_iterator< T>& it1, const cvalue_iterator< T>& it2)noexcept -> typename cvalue_iterator< T>::difference_type { 124 | return it1.pos - it2.pos; 125 | } 126 | template < class T> 127 | auto operator<(const cvalue_iterator< T>& it1, const cvalue_iterator< T>& it2)noexcept -> bool { 128 | return it1.pos < it2.pos; 129 | } 130 | template < class T> 131 | auto operator>(const cvalue_iterator< T>& it1, const cvalue_iterator< T>& it2) noexcept -> bool { 132 | return it1.pos > it2.pos; 133 | } 134 | template < class T> 135 | auto operator<=(const cvalue_iterator< T>& it1, const cvalue_iterator< T>& it2)noexcept -> bool { 136 | return it1.pos <= it2.pos; 137 | } 138 | template < class T> 139 | auto operator>=(const cvalue_iterator< T>& it1, const cvalue_iterator< T>& it2)noexcept -> bool { 140 | return it1.pos >= it2.pos; 141 | } 142 | 143 | /// @brief Simply call p->~T(), used as a replacement to std::allocator::destroy() which was removed in C++20 144 | template 145 | SEQ_ALWAYS_INLINE void destroy_ptr(T* p) 146 | { 147 | p->~T(); 148 | } 149 | 150 | /// @brief Simply call new (p) T(...), used as a replacement to std::allocator::construct() which was removed in C++20 151 | template 152 | SEQ_ALWAYS_INLINE void construct_ptr(T* p, Args&&... args) 153 | { 154 | new (p) T(std::forward(args)...); 155 | } 156 | 157 | 158 | namespace detail 159 | { 160 | // Extract the key on a std::pair or a Key value 161 | template 162 | struct ExtractKey 163 | { 164 | using key_type = Key; 165 | using value_type = T; 166 | using mapped_type = typename T::second_type; 167 | SEQ_ALWAYS_INLINE static auto key(const value_type& value) noexcept -> const key_type& { return value.first; } 168 | template 169 | SEQ_ALWAYS_INLINE static auto key(const std::pair& value) noexcept -> const U& { return value.first; } 170 | template 171 | SEQ_ALWAYS_INLINE static auto key(const U& value) noexcept -> const U& { return value; } 172 | }; 173 | template 174 | struct ExtractKey 175 | { 176 | using key_type = T; 177 | using value_type = T; 178 | using mapped_type = T; 179 | SEQ_ALWAYS_INLINE static auto key(const T& value) noexcept -> const T& { return value; } 180 | template 181 | SEQ_ALWAYS_INLINE static auto key(const U& value) noexcept -> const U& { return value; } 182 | }; 183 | 184 | // Build a std::pair or Key value from forwarded arguments 185 | template 186 | struct BuildValue 187 | { 188 | using type = T; 189 | SEQ_ALWAYS_INLINE static T build(Args&&... args) { return T(std::forward(args)...); } 190 | }; 191 | template 192 | struct BuildValue 193 | { 194 | using type = const T&; 195 | SEQ_ALWAYS_INLINE static const T& build(const T& v) { return v; } 196 | }; 197 | template 198 | struct BuildValue 199 | { 200 | using type = const T&; 201 | SEQ_ALWAYS_INLINE static const T& build(const T& v) { return v; } 202 | }; 203 | template 204 | struct BuildValue 205 | { 206 | using type = T&&; 207 | SEQ_ALWAYS_INLINE static T&& build(T&& v) { return std::move(v); } 208 | }; 209 | template 210 | struct BuildValue 211 | { 212 | using type = const T&; 213 | SEQ_ALWAYS_INLINE static const T& build(const T& v) { return v; } 214 | }; 215 | 216 | 217 | // Helper class to detect is_transparent typedef in hash functor or comparison functor 218 | 219 | 220 | template 221 | struct has_is_transparent : std::false_type {}; 222 | 223 | template 224 | struct has_is_transparent::type> 226 | : std::true_type {}; 227 | 228 | 229 | template 230 | struct has_is_always_equal : std::false_type {}; 231 | 232 | template 233 | struct has_is_always_equal::type> 235 | : std::true_type {}; 236 | 237 | /// Provide a is_always_equal type traits for allocators in case current compiler 238 | /// std::allocator_traits::is_always_equal is not present. 239 | template::value> 240 | struct is_always_equal 241 | { 242 | static constexpr bool value = Alloc::is_always_equal::value; 243 | }; 244 | template 245 | struct is_always_equal 246 | { 247 | static constexpr bool value = std::is_empty::value; 248 | }; 249 | 250 | 251 | // Returns distance between 2 iterators, or 0 for non random access iterators 252 | template 253 | auto iter_distance(const Iter& , const Iter& , Cat /*unused*/) noexcept -> size_t { return 0; } 254 | template 255 | auto iter_distance(const Iter& first, const Iter& last, std::random_access_iterator_tag /*unused*/) noexcept -> size_t { 256 | return (last > first) ? static_cast(last - first) : 0; 257 | } 258 | 259 | } 260 | 261 | /// @brief Returns the distance between first and last iterators for random access iterator category, 0 otherwise. 262 | template 263 | auto distance(const Iter& first, const Iter& last) noexcept -> size_t { 264 | return detail::iter_distance(first, last, typename std::iterator_traits::iterator_category()); 265 | } 266 | 267 | 268 | namespace detail 269 | { 270 | 271 | template 272 | struct CallLambda 273 | { 274 | template 275 | using return_type = decltype(std::declval()(std::declval()...)); 276 | 277 | template 278 | auto operator()(const L1& l1, const L2& /*unused*/, Args&&... args) const -> return_type 279 | { 280 | return l1(std::forward(args)...); 281 | } 282 | }; 283 | template 284 | struct CallLambda 285 | { 286 | template 287 | using return_type = decltype(std::declval()(std::declval()...)); 288 | 289 | template 290 | auto operator()(const L1& /*unused*/, const L2& l2, Args&&... args) const -> return_type 291 | { 292 | return l2(std::forward(args)...); 293 | } 294 | }; 295 | } 296 | 297 | /// @brief Simulation of C++17 if constexpr in C++14 298 | template 299 | auto constexpr_if( const L1& l1, const L2& l2, Args&&... args) 300 | -> decltype(std::declval&>()(std::declval(), std::declval(),std::declval()...)) 301 | { 302 | return detail::CallLambda{}(l1,l2, std::forward(args)...); 303 | } 304 | 305 | // C++11 equal_to 306 | template 307 | struct equal_to { 308 | typedef _Ty first_argument_type; 309 | typedef _Ty second_argument_type; 310 | typedef bool result_type; 311 | constexpr bool operator()(const _Ty& left, const _Ty& right) const { 312 | return left == right; 313 | } 314 | }; 315 | template <> 316 | struct equal_to { 317 | template 318 | constexpr auto operator()(_Ty1&& left, _Ty2&& right) const 319 | noexcept(noexcept(static_cast<_Ty1&&>(left) == static_cast<_Ty2&&>(right))) 320 | -> decltype(static_cast<_Ty1&&>(left) == static_cast<_Ty2&&>(right)) { 321 | return static_cast<_Ty1&&>(left) == static_cast<_Ty2&&>(right); 322 | } 323 | 324 | using is_transparent = int; 325 | }; 326 | 327 | // C++11 less 328 | template 329 | struct less { 330 | typedef _Ty first_argument_type; 331 | typedef _Ty second_argument_type; 332 | typedef bool result_type; 333 | constexpr bool operator()(const _Ty& left, const _Ty& right) const { 334 | return left < right; 335 | } 336 | }; 337 | template <> 338 | struct less { 339 | template 340 | constexpr auto operator()(_Ty1&& left, _Ty2&& right) const 341 | noexcept(noexcept(static_cast<_Ty1&&>(left) < static_cast<_Ty2&&>(right))) 342 | -> decltype(static_cast<_Ty1&&>(left) < static_cast<_Ty2&&>(right)) { 343 | return static_cast<_Ty1&&>(left) < static_cast<_Ty2&&>(right); 344 | } 345 | 346 | using is_transparent = int; 347 | }; 348 | 349 | // C++11 greater 350 | template 351 | struct greater { 352 | typedef _Ty first_argument_type; 353 | typedef _Ty second_argument_type; 354 | typedef bool result_type; 355 | constexpr bool operator()(const _Ty& left, const _Ty& right) const { 356 | return left > right; 357 | } 358 | }; 359 | template <> 360 | struct greater { 361 | template 362 | constexpr auto operator()(_Ty1&& left, _Ty2&& right) const 363 | noexcept(noexcept(static_cast<_Ty1&&>(left) > static_cast<_Ty2&&>(right))) 364 | -> decltype(static_cast<_Ty1&&>(left) > static_cast<_Ty2&&>(right)) { 365 | return static_cast<_Ty1&&>(left) > static_cast<_Ty2&&>(right); 366 | } 367 | 368 | using is_transparent = int; 369 | }; 370 | 371 | /// @brief Copy allocator for container copy constructor 372 | template 373 | auto copy_allocator(const Allocator& alloc) -> Allocator 374 | { 375 | return std::allocator_traits< Allocator>::select_on_container_copy_construction(alloc); 376 | } 377 | 378 | /// @brief Swap allocators for container.swap member 379 | template 380 | void swap_allocator(Allocator& left, Allocator& right) noexcept { 381 | if SEQ_CONSTEXPR (std::allocator_traits::propagate_on_container_swap::value) { 382 | std::swap(left, right); 383 | } 384 | else { 385 | SEQ_ASSERT_DEBUG(left == right, "containers incompatible for swap"); 386 | } 387 | } 388 | 389 | /// @brief Assign allocator for container copy operator 390 | template 391 | void assign_allocator(Allocator& left, const Allocator& right) noexcept 392 | { 393 | if SEQ_CONSTEXPR (std::allocator_traits::propagate_on_container_copy_assignment::value) { 394 | left = right; 395 | } 396 | } 397 | 398 | /// @brief Move allocator for container move assignment 399 | template 400 | void move_allocator(Allocator& left, Allocator& right) noexcept 401 | { 402 | // (maybe) propagate on container move assignment 403 | if SEQ_CONSTEXPR (std::allocator_traits::propagate_on_container_move_assignment::value) { 404 | left = std::move(right); 405 | } 406 | } 407 | 408 | // Returns whether an attempt to propagate allocators is necessary in copy assignment operations. 409 | // Note that even when false_type, callers should call assign_allocator as we want to assign allocators even when equal. 410 | template 411 | struct assign_alloc 412 | { 413 | static constexpr bool value = std::allocator_traits::propagate_on_container_copy_assignment::value 414 | && !detail::is_always_equal::value; 415 | }; 416 | 417 | template 418 | struct move_alloc 419 | { 420 | static constexpr bool value = std::allocator_traits::propagate_on_container_move_assignment::type 421 | && !detail::is_always_equal::value; 422 | }; 423 | 424 | 425 | }//end namespace seq 426 | 427 | #endif 428 | -------------------------------------------------------------------------------- /test/flat_unordered/test_flat_uset_main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | #include "gtest/gtest.h" 7 | 8 | #define INDIVI_FLAT_U_DEBUG 9 | #include "indivi/flat_uset.h" 10 | #include "utils/debug_utils.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | using namespace indivi; 24 | 25 | 26 | TEST(FlatUSetTest, Constructor) 27 | { 28 | { 29 | flat_uset fus; 30 | EXPECT_FALSE(fus.contains(1)); 31 | } 32 | { 33 | enum EN { AA, BB }; 34 | 35 | flat_uset fus; 36 | EXPECT_FALSE(fus.contains(AA)); 37 | } 38 | { 39 | flat_uset> fus; 40 | EXPECT_FALSE(fus.contains(nullptr)); 41 | } 42 | { 43 | flat_uset> fus; 44 | EXPECT_FALSE(fus.contains(nullptr)); 45 | } 46 | { 47 | flat_uset fus; 48 | EXPECT_FALSE(fus.contains(nullptr)); 49 | } 50 | { 51 | flat_uset fus; 52 | EXPECT_FALSE(fus.contains(0)); 53 | } 54 | { 55 | flat_uset fus; 56 | EXPECT_FALSE(fus.contains("")); 57 | } 58 | { 59 | flat_uset fus; 60 | EXPECT_FALSE(fus.contains("")); 61 | } 62 | // No object leak 63 | EXPECT_EQ(DbgClass::count, 0); 64 | } 65 | 66 | TEST(FlatUSetTest, Constructor2) 67 | { 68 | { 69 | flat_uset fus(10); 70 | EXPECT_GE(fus.bucket_count(), 10u); 71 | EXPECT_FALSE(fus.contains(1)); 72 | } 73 | { 74 | flat_uset> fus(0, std::hash()); 75 | EXPECT_FALSE(fus.contains(1)); 76 | } 77 | { 78 | flat_uset, std::equal_to> fus(0, std::hash(), std::equal_to()); 79 | EXPECT_FALSE(fus.contains(1)); 80 | } 81 | { 82 | std::vector vec{1, 3}; 83 | flat_uset fus(vec.begin(), vec.end()); 84 | EXPECT_EQ(fus.size(), 2u); 85 | EXPECT_TRUE(fus.contains(1)); 86 | EXPECT_TRUE(fus.contains(3)); 87 | } 88 | { 89 | flat_uset fus{1}; 90 | EXPECT_EQ(fus.size(), 1u); 91 | EXPECT_TRUE(fus.contains(1)); 92 | EXPECT_FALSE(fus.contains(2)); 93 | } 94 | // No object leak 95 | EXPECT_EQ(DbgClass::count, 0); 96 | } 97 | 98 | TEST(FlatUSetTest, Constructor3) 99 | { 100 | { 101 | flat_uset fus1; 102 | flat_uset fus2 = fus1; 103 | EXPECT_EQ(fus1.size(), 0u); 104 | EXPECT_EQ(fus2.size(), 0u); 105 | } 106 | { 107 | flat_uset fus1{1}; 108 | flat_uset fus2 = fus1; 109 | EXPECT_TRUE(fus1.contains(1)); 110 | EXPECT_TRUE(fus2.contains(1)); 111 | } 112 | { 113 | flat_uset fus1; 114 | flat_uset fus2 = std::move(fus1); 115 | EXPECT_EQ(fus1.size(), 0u); 116 | EXPECT_EQ(fus2.size(), 0u); 117 | } 118 | { 119 | flat_uset fus1{1}; 120 | flat_uset fus2 = std::move(fus1); 121 | EXPECT_FALSE(fus1.contains(1)); 122 | EXPECT_TRUE(fus2.contains(1)); 123 | } 124 | // No object leak 125 | EXPECT_EQ(DbgClass::count, 0); 126 | } 127 | 128 | TEST(FlatUSetTest, Assignment) 129 | { 130 | { 131 | flat_uset fus1; 132 | flat_uset fus2; 133 | fus2 = fus1; 134 | EXPECT_TRUE(fus1.empty()); 135 | EXPECT_TRUE(fus2.empty()); 136 | } 137 | { 138 | flat_uset fus1{1}; 139 | flat_uset fus2; 140 | fus2 = fus1; 141 | EXPECT_TRUE(fus1.contains(1)); 142 | EXPECT_TRUE(fus2.contains(1)); 143 | } 144 | { 145 | flat_uset fus1; 146 | flat_uset fus2{1}; 147 | fus2 = fus1; 148 | EXPECT_FALSE(fus1.contains(1)); 149 | EXPECT_FALSE(fus2.contains(1)); 150 | } 151 | { 152 | flat_uset fus1{1}; 153 | flat_uset fus2{{2, 4}}; 154 | EXPECT_FALSE(fus2.contains(1)); 155 | EXPECT_TRUE(fus2.contains(2)); 156 | fus2 = fus1; 157 | EXPECT_TRUE(fus1.contains(1)); 158 | EXPECT_TRUE(fus2.contains(1)); 159 | EXPECT_FALSE(fus2.contains(2)); 160 | } 161 | { 162 | flat_uset fus; 163 | EXPECT_TRUE(fus.empty()); 164 | fus = {1, 3}; 165 | EXPECT_TRUE(fus.contains(1)); 166 | EXPECT_TRUE(fus.contains(3)); 167 | fus = {}; 168 | EXPECT_FALSE(fus.contains(1)); 169 | EXPECT_FALSE(fus.contains(3)); 170 | } 171 | { 172 | flat_uset fus{{1, 4}}; 173 | fus = {1, 3}; 174 | EXPECT_TRUE(fus.contains(1)); 175 | EXPECT_TRUE(fus.contains(3)); 176 | fus = {}; 177 | EXPECT_FALSE(fus.contains(1)); 178 | EXPECT_FALSE(fus.contains(3)); 179 | EXPECT_TRUE(fus.empty()); 180 | } 181 | // No object leak 182 | EXPECT_EQ(DbgClass::count, 0); 183 | } 184 | 185 | TEST(FlatUSetTest, Assignment2) 186 | { 187 | { 188 | flat_uset fus1; 189 | flat_uset fus2; 190 | fus2 = std::move(fus1); 191 | EXPECT_TRUE(fus1.empty()); 192 | EXPECT_TRUE(fus2.empty()); 193 | } 194 | { 195 | flat_uset fus1{1}; 196 | flat_uset fus2; 197 | fus2 = std::move(fus1); 198 | EXPECT_FALSE(fus1.contains(1)); 199 | EXPECT_TRUE(fus2.contains(1)); 200 | } 201 | { 202 | flat_uset fus1; 203 | flat_uset fus2{1}; 204 | fus2 = std::move(fus1); 205 | EXPECT_FALSE(fus1.contains(1)); 206 | EXPECT_FALSE(fus2.contains(1)); 207 | } 208 | { 209 | flat_uset fus1{1}; 210 | flat_uset fus2{1, 4}; 211 | fus2 = std::move(fus1); 212 | EXPECT_FALSE(fus1.contains(1)); 213 | EXPECT_TRUE(fus2.contains(1)); 214 | EXPECT_FALSE(fus2.contains(4)); 215 | } 216 | { 217 | flat_uset fus1{1, 6}; 218 | flat_uset fus2{1, 4}; 219 | fus2 = fus1; 220 | EXPECT_TRUE(fus1.contains(1)); 221 | EXPECT_TRUE(fus2.contains(1)); 222 | EXPECT_FALSE(fus2.contains(4)); 223 | EXPECT_TRUE(fus2.contains(6)); 224 | } 225 | // No object leak 226 | EXPECT_EQ(DbgClass::count, 0); 227 | } 228 | 229 | TEST(FlatUSetTest, Capacity) 230 | { 231 | { 232 | flat_uset fus; 233 | EXPECT_TRUE(fus.empty()); 234 | EXPECT_EQ(fus.size(), 0u); 235 | EXPECT_EQ(fus.bucket_count(), 0u); 236 | EXPECT_EQ(fus.load_factor(), 0.f); 237 | 238 | auto max_size = (flat_uset::size_type)(fus.max_bucket_count() * fus.max_load_factor()); 239 | EXPECT_EQ(fus.max_load_factor(), 0.875f); 240 | EXPECT_EQ(fus.max_size(), max_size); 241 | EXPECT_GT(fus.max_bucket_count(), 0u); 242 | fus.max_load_factor(0.f); // no-op 243 | EXPECT_GT(fus.max_bucket_count(), 0u); 244 | 245 | fus = {1, 3}; 246 | EXPECT_EQ(fus.size(), 2u); 247 | EXPECT_EQ(fus.bucket_count(), 2u); 248 | EXPECT_EQ(fus.load_factor(), 1.f); 249 | } 250 | { 251 | flat_uset fus{{1, 1}}; 252 | EXPECT_FALSE(fus.empty()); 253 | EXPECT_EQ(fus.size(), 1u); 254 | EXPECT_EQ(fus.bucket_count(), 2u); 255 | EXPECT_EQ(fus.load_factor(), 0.5f); 256 | 257 | fus.insert(2); 258 | EXPECT_EQ(fus.size(), 2u); 259 | EXPECT_EQ(fus.bucket_count(), 2u); 260 | EXPECT_EQ(fus.load_factor(), 1.f); 261 | 262 | fus.insert(3); 263 | EXPECT_EQ(fus.size(), 3u); 264 | EXPECT_EQ(fus.bucket_count(), 4u); 265 | EXPECT_EQ(fus.load_factor(), 0.75f); 266 | } 267 | // No object leak 268 | EXPECT_EQ(DbgClass::count, 0); 269 | } 270 | 271 | TEST(FlatUSetTest, Capacity2) 272 | { 273 | { 274 | flat_uset fus; 275 | fus.reserve(0); 276 | EXPECT_EQ(fus.size(), 0u); 277 | EXPECT_EQ(fus.bucket_count(), 0u); 278 | EXPECT_EQ(fus.load_factor(), 0.f); 279 | } 280 | { 281 | flat_uset fus; 282 | fus.reserve(7); 283 | EXPECT_EQ(fus.size(), 0u); 284 | EXPECT_EQ(fus.bucket_count(), 8u); 285 | EXPECT_EQ(fus.load_factor(), 0.f); 286 | } 287 | { 288 | flat_uset fus; 289 | fus.reserve(31); 290 | EXPECT_EQ(fus.size(), 0u); 291 | EXPECT_EQ(fus.bucket_count(), 64u); 292 | EXPECT_EQ(fus.load_factor(), 0.f); 293 | } 294 | { 295 | flat_uset fus; 296 | fus.rehash(12); 297 | EXPECT_EQ(fus.size(), 0u); 298 | EXPECT_EQ(fus.bucket_count(), 16u); 299 | EXPECT_EQ(fus.load_factor(), 0.f); 300 | } 301 | { 302 | flat_uset fus; 303 | fus.rehash(31); 304 | EXPECT_EQ(fus.size(), 0u); 305 | EXPECT_EQ(fus.bucket_count(), 32u); 306 | EXPECT_EQ(fus.load_factor(), 0.f); 307 | } 308 | { 309 | flat_uset fus(3); 310 | fus.insert(1); 311 | EXPECT_EQ(fus.size(), 1u); 312 | EXPECT_EQ(fus.bucket_count(), 4u); 313 | 314 | fus.rehash(0); 315 | EXPECT_EQ(fus.size(), 1u); 316 | EXPECT_EQ(fus.bucket_count(), 2u); 317 | 318 | fus.clear(); 319 | fus.rehash(0); 320 | EXPECT_EQ(fus.size(), 0u); 321 | EXPECT_EQ(fus.bucket_count(), 0u); 322 | 323 | fus.insert(3); 324 | EXPECT_TRUE(fus.contains(3)); 325 | EXPECT_EQ(fus.size(), 1u); 326 | EXPECT_EQ(fus.bucket_count(), 2u); 327 | fus.rehash(4); 328 | EXPECT_TRUE(fus.contains(3)); 329 | EXPECT_EQ(fus.size(), 1u); 330 | EXPECT_EQ(fus.bucket_count(), 4u); 331 | 332 | fus.reserve(4); 333 | EXPECT_TRUE(fus.contains(3)); 334 | EXPECT_EQ(fus.size(), 1u); 335 | EXPECT_EQ(fus.bucket_count(), 4u); 336 | 337 | fus.reserve(5); 338 | EXPECT_TRUE(fus.contains(3)); 339 | EXPECT_EQ(fus.size(), 1u); 340 | EXPECT_EQ(fus.bucket_count(), 8u); 341 | } 342 | // No object leak 343 | EXPECT_EQ(DbgClass::count, 0); 344 | } 345 | 346 | TEST(FlatUSetTest, Observers) 347 | { 348 | { 349 | flat_uset fus; 350 | auto hash = fus.hash_function(); 351 | auto keq = fus.key_eq(); 352 | 353 | EXPECT_NE(hash(1), 1); 354 | EXPECT_TRUE(keq(1, 1)); 355 | } 356 | // No object leak 357 | EXPECT_EQ(DbgClass::count, 0); 358 | } 359 | 360 | TEST(FlatUSetTest, Iterator) 361 | { 362 | { 363 | flat_uset fus; 364 | EXPECT_EQ(fus.begin(), fus.end()); 365 | EXPECT_EQ(fus.cbegin(), fus.cend()); 366 | EXPECT_EQ(fus.cbegin(), fus.end()); 367 | EXPECT_EQ(fus.begin(), fus.cend()); 368 | EXPECT_EQ(fus.begin(), fus.cbegin()); 369 | EXPECT_EQ(fus.end(), fus.cend()); 370 | } 371 | { 372 | flat_uset fus{1}; 373 | EXPECT_NE(fus.begin(), fus.end()); 374 | EXPECT_NE(fus.cbegin(), fus.cend()); 375 | EXPECT_EQ(++fus.begin(), fus.end()); 376 | EXPECT_EQ(++fus.cbegin(), fus.cend()); 377 | 378 | auto it = fus.begin(); 379 | EXPECT_EQ(*it, 1); 380 | 381 | fus.insert(3); 382 | it = fus.begin(); 383 | EXPECT_TRUE(*it == 3 || *it == 1); 384 | } 385 | { 386 | flat_uset fus{1, 3}; 387 | auto it1 = fus.find(1); 388 | EXPECT_NE(it1, fus.end()); 389 | auto it2 = fus.find(3); 390 | EXPECT_NE(it2, fus.end()); 391 | EXPECT_NE(it2, it1); 392 | auto it3 = fus.find(5); 393 | EXPECT_EQ(it3, fus.end()); 394 | EXPECT_NE(it3, it1); 395 | EXPECT_NE(it3, it2); 396 | } 397 | { 398 | flat_uset fus; 399 | for (int i = 1; i <= 111; ++i) 400 | fus.insert(i); 401 | 402 | int count = 0; 403 | for (const auto& val : fus) 404 | { 405 | EXPECT_TRUE(fus.contains(val)); 406 | ++count; 407 | } 408 | EXPECT_EQ(count, (int)fus.size()); 409 | } 410 | // No object leak 411 | EXPECT_EQ(DbgClass::count, 0); 412 | } 413 | 414 | TEST(FlatUSetTest, ElementAccess) 415 | { 416 | { 417 | flat_uset fus{1, 3, 5}; 418 | EXPECT_TRUE(fus.contains(5)); 419 | EXPECT_FALSE(fus.contains(6)); 420 | EXPECT_EQ(fus.count(1), 1u); 421 | EXPECT_EQ(fus.count(2), 0u); 422 | 423 | auto it = fus.find(3); 424 | ASSERT_NE(it, fus.end()); 425 | EXPECT_EQ(*it, 3); 426 | 427 | it = fus.find(2); 428 | EXPECT_EQ(it, fus.end()); 429 | } 430 | { 431 | flat_uset fus{"1", "", "3"}; 432 | EXPECT_TRUE(fus.contains("1")); 433 | EXPECT_TRUE(fus.contains("3")); 434 | EXPECT_TRUE(fus.contains("")); 435 | EXPECT_FALSE(fus.contains("2")); 436 | } 437 | // No object leak 438 | EXPECT_EQ(DbgClass::count, 0); 439 | } 440 | 441 | TEST(FlatUSetTest, ElementAccess2) 442 | { 443 | { 444 | flat_uset fus; 445 | DbgClass k(1); 446 | 447 | fus.emplace(k); 448 | EXPECT_EQ(k, 1); 449 | EXPECT_EQ(fus.count(k), 1u); 450 | EXPECT_EQ(fus.size(), 1u); 451 | 452 | fus.insert(std::move(k)); 453 | EXPECT_EQ(k, 1); 454 | EXPECT_EQ(fus.count(1), 1u); 455 | EXPECT_EQ(fus.size(), 1u); 456 | 457 | DbgClass k2(2); 458 | fus.insert(std::move(k2)); 459 | EXPECT_NE(k2, 2); 460 | EXPECT_EQ(fus.count(2), 1u); 461 | EXPECT_EQ(fus.size(), 2u); 462 | } 463 | { 464 | flat_uset fus; 465 | auto p1 = fus.insert("a"); 466 | EXPECT_EQ(*p1.first, "a"); 467 | EXPECT_TRUE(p1.second); 468 | EXPECT_EQ(fus.size(), 1u); 469 | 470 | auto p2 = fus.insert("a"); 471 | EXPECT_EQ(*p2.first, "a"); 472 | EXPECT_FALSE(p2.second); 473 | EXPECT_EQ(fus.size(), 1u); 474 | } 475 | // No object leak 476 | EXPECT_EQ(DbgClass::count, 0); 477 | } 478 | 479 | TEST(FlatUSetTest, Clear) 480 | { 481 | { 482 | flat_uset fus; 483 | EXPECT_EQ(fus.size(), 0u); 484 | EXPECT_EQ(fus.bucket_count(), 0u); 485 | 486 | fus.clear(); 487 | EXPECT_EQ(fus.size(), 0u); 488 | EXPECT_EQ(fus.bucket_count(), 0u); 489 | } 490 | { 491 | flat_uset fus{1, 3, 5}; 492 | EXPECT_EQ(fus.size(), 3u); 493 | EXPECT_EQ(fus.bucket_count(), 4u); 494 | 495 | fus.clear(); 496 | EXPECT_EQ(fus.size(), 0u); 497 | EXPECT_EQ(fus.bucket_count(), 4u); 498 | fus.clear(); 499 | EXPECT_EQ(fus.size(), 0u); 500 | EXPECT_EQ(fus.bucket_count(), 4u); 501 | } 502 | // No object leak 503 | EXPECT_EQ(DbgClass::count, 0); 504 | } 505 | 506 | TEST(FlatUSetTest, Insert) 507 | { 508 | { 509 | flat_uset fus; 510 | EXPECT_FALSE(fus.contains(1)); 511 | auto it = fus.insert(1); 512 | ASSERT_NE(it.first, fus.end()); 513 | EXPECT_EQ(*it.first, 1); 514 | EXPECT_TRUE(it.second); 515 | EXPECT_TRUE(fus.contains(1)); 516 | 517 | fus.insert(3); 518 | EXPECT_TRUE(fus.contains(3)); 519 | 520 | it = fus.insert(3); 521 | EXPECT_TRUE(fus.contains(3)); 522 | ASSERT_NE(it.first, fus.end()); 523 | EXPECT_EQ(*it.first, 3); 524 | EXPECT_FALSE(it.second); 525 | } 526 | { 527 | flat_uset fus; 528 | DbgClass v(10); 529 | 530 | fus.insert(v); 531 | EXPECT_TRUE(fus.contains(10)); 532 | EXPECT_EQ(v, 10); 533 | } 534 | { 535 | flat_uset fus; 536 | const DbgClass v(10); 537 | 538 | fus.insert(v); 539 | EXPECT_TRUE(fus.contains(10)); 540 | EXPECT_EQ(v, 10); 541 | } 542 | { 543 | flat_uset fus; 544 | DbgClass v(10); 545 | 546 | fus.insert(std::move(v)); 547 | EXPECT_TRUE(fus.contains(10)); 548 | EXPECT_NE(v, 10); 549 | } 550 | { 551 | flat_uset fus; 552 | const DbgClass v(10); 553 | 554 | fus.insert(std::move(v)); 555 | EXPECT_TRUE(fus.contains(10)); 556 | EXPECT_EQ(v, 10); 557 | } 558 | // No object leak 559 | EXPECT_EQ(DbgClass::count, 0); 560 | } 561 | 562 | TEST(FlatUSetTest, Insert2) 563 | { 564 | { 565 | std::vector vec{1, 1}; 566 | flat_uset fus; 567 | fus.insert(vec.begin(), vec.end()); 568 | EXPECT_EQ(fus.size(), 1u); 569 | EXPECT_EQ(vec.size(), 2u); 570 | EXPECT_EQ(vec[0], 1); 571 | } 572 | { 573 | std::vector vec{1, 1}; 574 | flat_uset fus; 575 | fus.insert(std::make_move_iterator(vec.begin()), std::make_move_iterator(vec.end())); 576 | 577 | EXPECT_TRUE(fus.contains(1)); 578 | EXPECT_EQ(fus.size(), 1u); 579 | EXPECT_EQ(vec.size(), 2u); 580 | EXPECT_NE(vec[0], 1); 581 | EXPECT_EQ(vec[1], 1); 582 | } 583 | { 584 | flat_uset fus; 585 | std::initializer_list ilist{1, 1}; 586 | fus.insert(ilist); 587 | 588 | EXPECT_TRUE(fus.contains(1)); 589 | EXPECT_EQ(fus.size(), 1u); 590 | EXPECT_EQ(ilist.size(), 2u); 591 | EXPECT_EQ(*ilist.begin(), 1); 592 | } 593 | { 594 | flat_uset fus; 595 | fus.insert({1, 1}); 596 | 597 | EXPECT_TRUE(fus.contains(1)); 598 | EXPECT_EQ(fus.size(), 1u); 599 | } 600 | // No object leak 601 | EXPECT_EQ(DbgClass::count, 0); 602 | } 603 | 604 | TEST(FlatUSetTest, Emplace) 605 | { 606 | { 607 | flat_uset fus; 608 | auto it = fus.emplace(1); 609 | ASSERT_NE(it.first, fus.end()); 610 | EXPECT_EQ(*it.first, 1); 611 | EXPECT_TRUE(it.second); 612 | EXPECT_TRUE(fus.contains(1)); 613 | EXPECT_EQ(fus.size(), 1u); 614 | 615 | it = fus.emplace(1); 616 | ASSERT_NE(it.first, fus.end()); 617 | EXPECT_EQ(*it.first, 1); 618 | EXPECT_FALSE(it.second); 619 | EXPECT_TRUE(fus.contains(1)); 620 | EXPECT_EQ(fus.size(), 1u); 621 | 622 | it = fus.emplace(2); 623 | ASSERT_NE(it.first, fus.end()); 624 | EXPECT_TRUE(it.second); 625 | EXPECT_TRUE(fus.contains(2)); 626 | EXPECT_EQ(fus.size(), 2u); 627 | } 628 | { 629 | flat_uset fus; 630 | DbgClass k(1); 631 | 632 | auto it = fus.emplace(k); 633 | ASSERT_NE(it.first, fus.end()); 634 | EXPECT_EQ(*it.first, 1); 635 | EXPECT_TRUE(it.second); 636 | EXPECT_TRUE(fus.contains(1)); 637 | EXPECT_EQ(fus.size(), 1u); 638 | EXPECT_EQ(k, 1); 639 | } 640 | { 641 | flat_uset fus; 642 | DbgClass k(1); 643 | 644 | auto it = fus.emplace(std::move(k)); 645 | ASSERT_NE(it.first, fus.end()); 646 | EXPECT_EQ(*it.first, 1); 647 | EXPECT_TRUE(it.second); 648 | EXPECT_TRUE(fus.contains(1)); 649 | EXPECT_EQ(fus.size(), 1u); 650 | EXPECT_NE(k, 1); 651 | } 652 | { 653 | flat_uset fus; 654 | const char* k = "1"; 655 | 656 | auto it = fus.emplace(k); 657 | ASSERT_NE(it.first, fus.end()); 658 | EXPECT_EQ(*it.first, k); 659 | EXPECT_TRUE(it.second); 660 | EXPECT_TRUE(fus.contains(k)); 661 | EXPECT_EQ(fus.size(), 1u); 662 | } 663 | // No object leak 664 | EXPECT_EQ(DbgClass::count, 0); 665 | } 666 | 667 | TEST(FlatUSetTest, Erase) 668 | { 669 | { 670 | flat_uset fus{1, 3}; 671 | EXPECT_EQ(fus.erase(1), 1u); 672 | EXPECT_FALSE(fus.contains(1)); 673 | EXPECT_TRUE(fus.contains(3)); 674 | EXPECT_EQ(fus.size(), 1u); 675 | EXPECT_EQ(fus.bucket_count(), 2u); 676 | 677 | EXPECT_EQ(fus.erase(1), 0u); 678 | EXPECT_FALSE(fus.contains(1)); 679 | EXPECT_TRUE(fus.contains(3)); 680 | EXPECT_EQ(fus.size(), 1u); 681 | EXPECT_EQ(fus.bucket_count(), 2u); 682 | 683 | EXPECT_EQ(fus.erase(3), 1u); 684 | EXPECT_FALSE(fus.contains(1)); 685 | EXPECT_FALSE(fus.contains(3)); 686 | EXPECT_EQ(fus.size(), 0u); 687 | EXPECT_EQ(fus.bucket_count(), 2u); 688 | } 689 | { 690 | flat_uset fus; 691 | EXPECT_EQ(fus.erase(1), 0u); 692 | EXPECT_FALSE(fus.contains(1)); 693 | EXPECT_EQ(fus.size(), 0u); 694 | EXPECT_EQ(fus.bucket_count(), 0u); 695 | 696 | fus.emplace(2); 697 | EXPECT_EQ(fus.erase(1), 0u); 698 | EXPECT_TRUE(fus.contains(2)); 699 | EXPECT_EQ(fus.size(), 1u); 700 | EXPECT_EQ(fus.bucket_count(), 2u); 701 | } 702 | // No object leak 703 | EXPECT_EQ(DbgClass::count, 0); 704 | } 705 | 706 | TEST(FlatUSetTest, EraseIter) 707 | { 708 | { 709 | flat_uset fus{1, 3}; 710 | auto first = fus.begin(); 711 | bool is1 = *first == 1; 712 | 713 | fus.erase(first); 714 | EXPECT_EQ(fus.contains(1), !is1); 715 | EXPECT_EQ(fus.contains(3), is1); 716 | EXPECT_EQ(fus.size(), 1u); 717 | 718 | first = fus.begin(); 719 | fus.erase(first); 720 | EXPECT_FALSE(fus.contains(1)); 721 | EXPECT_FALSE(fus.contains(3)); 722 | EXPECT_EQ(fus.size(), 0u); 723 | } 724 | { 725 | flat_uset fus{1, 3}; 726 | auto first = fus.begin(); 727 | bool is1 = *first == 1; 728 | 729 | auto it = fus.erase_(first); 730 | ASSERT_NE(it, fus.end()); 731 | EXPECT_EQ(*it, is1 ? 3 : 1); 732 | EXPECT_EQ(fus.contains(1), !is1); 733 | EXPECT_EQ(fus.contains(3), is1); 734 | EXPECT_EQ(fus.size(), 1u); 735 | 736 | first = fus.begin(); 737 | it = fus.erase_(first); 738 | ASSERT_EQ(it, fus.end()); 739 | EXPECT_FALSE(fus.contains(1)); 740 | EXPECT_FALSE(fus.contains(3)); 741 | EXPECT_EQ(fus.size(), 0u); 742 | } 743 | { 744 | flat_uset fus; 745 | for (int i = 1; i <= 100; ++i) 746 | fus.emplace(i); 747 | EXPECT_EQ(fus.size(), 100u); 748 | 749 | auto it = fus.begin(); 750 | while (!fus.empty()) 751 | it = fus.erase_(it); 752 | EXPECT_EQ(fus.size(), 0u); 753 | } 754 | // No object leak 755 | EXPECT_EQ(DbgClass::count, 0); 756 | } 757 | 758 | TEST(FlatUSetTest, EraseIf) 759 | { 760 | { 761 | flat_uset fus; 762 | 763 | erase_if(fus, [](const auto& item){ return (item.id % 2); }); 764 | EXPECT_EQ(fus.size(), 0u); 765 | } 766 | { 767 | flat_uset fus{1, 3}; 768 | 769 | erase_if(fus, [](const auto& item){ return (item.id % 2); }); 770 | EXPECT_EQ(fus.size(), 0u); 771 | } 772 | { 773 | flat_uset fus{1, 2, 3}; 774 | 775 | erase_if(fus, [](const auto& item){ return (item.id % 2); }); 776 | EXPECT_EQ(fus.size(), 1u); 777 | EXPECT_FALSE(fus.contains(1)); 778 | EXPECT_TRUE(fus.contains(2)); 779 | EXPECT_FALSE(fus.contains(3)); 780 | } 781 | { 782 | flat_uset fus{2, 4}; 783 | 784 | erase_if(fus, [](const auto& item){ return (item.id % 2); }); 785 | EXPECT_EQ(fus.size(), 2u); 786 | EXPECT_TRUE(fus.contains(2)); 787 | EXPECT_TRUE(fus.contains(4)); 788 | } 789 | // No object leak 790 | EXPECT_EQ(DbgClass::count, 0); 791 | } 792 | 793 | TEST(FlatUSetTest, Swap) 794 | { 795 | { 796 | flat_uset fus1; 797 | flat_uset fus2; 798 | 799 | fus2.swap(fus1); 800 | EXPECT_EQ(fus1.size(), 0u); 801 | EXPECT_EQ(fus2.size(), 0u); 802 | 803 | fus2.swap(fus1); 804 | EXPECT_EQ(fus1.size(), 0u); 805 | EXPECT_EQ(fus2.size(), 0u); 806 | } 807 | { 808 | flat_uset fus1{1, 3, 5}; 809 | flat_uset fus2; 810 | 811 | fus2.swap(fus1); 812 | EXPECT_EQ(fus1.size(), 0u); 813 | EXPECT_EQ(fus2.size(), 3u); 814 | EXPECT_FALSE(fus1.contains(3)); 815 | EXPECT_TRUE(fus2.contains(3)); 816 | 817 | fus2.swap(fus1); 818 | EXPECT_EQ(fus1.size(), 3u); 819 | EXPECT_EQ(fus2.size(), 0u); 820 | EXPECT_TRUE(fus1.contains(3)); 821 | EXPECT_FALSE(fus2.contains(3)); 822 | } 823 | { 824 | flat_uset fus1{1, 3}; 825 | flat_uset fus2{5}; 826 | 827 | fus2.swap(fus1); 828 | EXPECT_EQ(fus1.size(), 1u); 829 | EXPECT_EQ(fus2.size(), 2u); 830 | EXPECT_TRUE(fus1.contains(5)); 831 | EXPECT_FALSE(fus1.contains(1)); 832 | EXPECT_TRUE(fus2.contains(1)); 833 | EXPECT_FALSE(fus2.contains(5)); 834 | 835 | fus2.swap(fus1); 836 | EXPECT_EQ(fus1.size(), 2u); 837 | EXPECT_EQ(fus2.size(), 1u); 838 | EXPECT_TRUE(fus1.contains(3)); 839 | EXPECT_FALSE(fus1.contains(5)); 840 | EXPECT_TRUE(fus2.contains(5)); 841 | EXPECT_FALSE(fus2.contains(3)); 842 | } 843 | { 844 | using std::swap; 845 | 846 | flat_uset fus1{1, 3}; 847 | flat_uset fus2{5}; 848 | 849 | swap(fus1, fus2); 850 | EXPECT_EQ(fus1.size(), 1u); 851 | EXPECT_EQ(fus2.size(), 2u); 852 | EXPECT_TRUE(fus1.contains(5)); 853 | EXPECT_FALSE(fus1.contains(1)); 854 | EXPECT_TRUE(fus2.contains(1)); 855 | EXPECT_FALSE(fus2.contains(5)); 856 | 857 | swap(fus1, fus2); 858 | EXPECT_EQ(fus1.size(), 2u); 859 | EXPECT_EQ(fus2.size(), 1u); 860 | EXPECT_TRUE(fus1.contains(3)); 861 | EXPECT_FALSE(fus1.contains(5)); 862 | EXPECT_TRUE(fus2.contains(5)); 863 | EXPECT_FALSE(fus2.contains(3)); 864 | } 865 | { 866 | using std::swap; 867 | 868 | flat_uset fus1{1}; 869 | flat_uset fus2{5, 4}; 870 | 871 | swap(fus1, fus1); 872 | EXPECT_EQ(fus1.size(), 1u); 873 | EXPECT_TRUE(fus1.contains(1)); 874 | 875 | swap(fus2, fus1); 876 | fus1.swap(fus2); 877 | EXPECT_EQ(fus1.size(), 1u); 878 | EXPECT_EQ(fus2.size(), 2u); 879 | EXPECT_TRUE(fus1.contains(1)); 880 | EXPECT_FALSE(fus1.contains(5)); 881 | EXPECT_TRUE(fus2.contains(5)); 882 | EXPECT_FALSE(fus2.contains(1)); 883 | } 884 | // No object leak 885 | EXPECT_EQ(DbgClass::count, 0); 886 | } 887 | 888 | TEST(FlatUSetTest, Equality) 889 | { 890 | { 891 | flat_uset fus1; 892 | flat_uset fus2; 893 | EXPECT_EQ(fus1, fus2); 894 | EXPECT_EQ(fus2, fus1); 895 | } 896 | { 897 | flat_uset fus1{1}; 898 | flat_uset fus2{1}; 899 | EXPECT_EQ(fus1, fus2); 900 | EXPECT_EQ(fus2, fus1); 901 | } 902 | { 903 | flat_uset fus1{1}; 904 | flat_uset fus2{5}; 905 | EXPECT_NE(fus1, fus2); 906 | EXPECT_NE(fus2, fus1); 907 | } 908 | { 909 | flat_uset fus1{}; 910 | flat_uset fus2{1}; 911 | EXPECT_NE(fus1, fus2); 912 | EXPECT_NE(fus2, fus1); 913 | } 914 | { 915 | flat_uset fus1{1}; 916 | flat_uset fus2{1, 3}; 917 | EXPECT_NE(fus1, fus2); 918 | EXPECT_NE(fus2, fus1); 919 | } 920 | // No object leak 921 | EXPECT_EQ(DbgClass::count, 0); 922 | } 923 | 924 | TEST(FlatUSetTest, BadHash) 925 | { 926 | struct bad_hash { 927 | size_t operator()(const int& x) const { return x; } 928 | }; 929 | 930 | flat_uset fus; 931 | std::unordered_set set; 932 | for (int i = 0; i < 100000; ++i) 933 | { 934 | fus.emplace(i); 935 | set.emplace(i); 936 | } 937 | EXPECT_EQ(fus.size(), set.size()); 938 | 939 | for (const auto& item : set) 940 | { 941 | auto itF = fus.find(item); 942 | ASSERT_NE(itF, fus.end()); 943 | } 944 | } 945 | 946 | TEST(FlatUSetTest, Stress) 947 | { 948 | { 949 | auto seed = time(NULL); 950 | auto get_rand = []() { 951 | int r = rand() + 1; 952 | return (RAND_MAX > 32768) ? r : r * rand() + 1; 953 | }; 954 | 955 | std::cout << "Stress seed: " << seed << "\n"; 956 | srand((unsigned int)seed); 957 | 958 | flat_uset fus; 959 | std::unordered_set set; 960 | std::vector vec; 961 | 962 | int addCount = 0; 963 | int findCount = 0; 964 | int removeCount = 0; 965 | int rehashCount = 0; 966 | 967 | for (int i = 0; i < 1000000; ++i) 968 | { 969 | int op = rand() % 8; 970 | switch(op) 971 | { 972 | case 0: // insert 973 | { 974 | int k = get_rand(); 975 | fus.insert(k); 976 | set.insert(k); 977 | vec.push_back(k); 978 | ++addCount; 979 | EXPECT_EQ(fus.size(), set.size()) << "insert"; 980 | break; 981 | } 982 | case 1: // emplace 983 | case 2: 984 | { 985 | int k = get_rand(); 986 | fus.emplace(k); 987 | set.emplace(k); 988 | vec.push_back(k); 989 | ++addCount; 990 | EXPECT_EQ(fus.size(), set.size()) << "emplace"; 991 | break; 992 | } 993 | case 3: // count 994 | { 995 | if (!vec.empty()) 996 | { 997 | auto k = vec[rand() % vec.size()]; 998 | EXPECT_EQ(fus.count(k), set.count(k)); 999 | ++findCount; 1000 | } 1001 | EXPECT_EQ(fus.size(), set.size()) << "count"; 1002 | break; 1003 | } 1004 | case 4: // erase 1005 | { 1006 | if (!vec.empty()) 1007 | { 1008 | auto k = vec[rand() % vec.size()]; 1009 | auto countF = fus.erase(k); 1010 | auto countS = set.erase(k); 1011 | ++removeCount; 1012 | EXPECT_EQ(countF, countS); 1013 | } 1014 | EXPECT_EQ(fus.size(), set.size()) << "erase"; 1015 | break; 1016 | } 1017 | case 5: // erase first 1018 | { 1019 | if (!set.empty()) 1020 | { 1021 | auto itS = set.begin(); 1022 | auto itF = fus.find(*itS); 1023 | ASSERT_NE(itF, fus.end()); 1024 | ASSERT_NE(itS, set.end()); 1025 | fus.erase_(itF); 1026 | set.erase(itS); 1027 | ++removeCount; 1028 | } 1029 | EXPECT_EQ(fus.size(), set.size()) << "erase first"; 1030 | break; 1031 | } 1032 | case 6: // find 1033 | { 1034 | if (!vec.empty()) 1035 | { 1036 | auto k = vec[rand() % vec.size()]; 1037 | auto itF = fus.find(k); 1038 | auto itS = set.find(k); 1039 | if (itF != fus.end()) 1040 | { 1041 | EXPECT_NE(itS, set.end()); 1042 | EXPECT_EQ(*itF, *itS) << "find"; 1043 | } 1044 | else 1045 | { 1046 | EXPECT_EQ(itS, set.end()) << "find none"; 1047 | } 1048 | ++findCount; 1049 | } 1050 | break; 1051 | } 1052 | case 7: // rehash 1053 | { 1054 | if (rand() < RAND_MAX / 50) 1055 | { 1056 | auto j = vec.size() ? rand() % vec.size() : 0; 1057 | fus.rehash(j); 1058 | ++rehashCount; 1059 | } 1060 | EXPECT_EQ(fus.size(), set.size()) << "rehash"; 1061 | break; 1062 | } 1063 | default: 1064 | assert(false); 1065 | } 1066 | } 1067 | std::cout << "Stress final size: " << set.size() 1068 | << ", add: " << addCount 1069 | << ", find: " << findCount 1070 | << ", remove: " << removeCount 1071 | << ", rehash: " << rehashCount << "\n"; 1072 | EXPECT_EQ(fus.size(), set.size()) << "final"; 1073 | 1074 | for (const auto& item : set) 1075 | { 1076 | auto itF = fus.find(item); 1077 | ASSERT_NE(itF, fus.end()); 1078 | } 1079 | } 1080 | // No object leak 1081 | EXPECT_EQ(DbgClass::count, 0); 1082 | } 1083 | -------------------------------------------------------------------------------- /test/flat_unordered/test_flat_wset_main.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2025 Guillaume AUJAY. All rights reserved. 3 | * Distributed under the Apache License Version 2.0 4 | */ 5 | 6 | #include "gtest/gtest.h" 7 | 8 | #define INDIVI_FLAT_W_DEBUG 9 | #include "indivi/flat_wset.h" 10 | #include "utils/debug_utils.h" 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | 23 | using namespace indivi; 24 | 25 | TEST(FlatWSetTest, Constructor) 26 | { 27 | { 28 | flat_wset fws; 29 | EXPECT_FALSE(fws.contains(1)); 30 | } 31 | { 32 | enum EN { AA, BB }; 33 | 34 | flat_wset fws; 35 | EXPECT_FALSE(fws.contains(AA)); 36 | } 37 | { 38 | flat_wset> fws; 39 | EXPECT_FALSE(fws.contains(nullptr)); 40 | } 41 | { 42 | flat_wset> fws; 43 | EXPECT_FALSE(fws.contains(nullptr)); 44 | } 45 | { 46 | flat_wset fws; 47 | EXPECT_FALSE(fws.contains(nullptr)); 48 | } 49 | { 50 | flat_wset fws; 51 | EXPECT_FALSE(fws.contains(0)); 52 | } 53 | { 54 | flat_wset fws; 55 | EXPECT_FALSE(fws.contains("")); 56 | } 57 | { 58 | flat_wset fws; 59 | EXPECT_FALSE(fws.contains("")); 60 | } 61 | // No object leak 62 | EXPECT_EQ(DbgClass::count, 0); 63 | } 64 | 65 | TEST(FlatWSetTest, Constructor2) 66 | { 67 | { 68 | flat_wset fws(10); 69 | EXPECT_GE(fws.bucket_count(), 10u); 70 | EXPECT_FALSE(fws.contains(1)); 71 | } 72 | { 73 | flat_wset> fws(0, std::hash()); 74 | EXPECT_FALSE(fws.contains(1)); 75 | } 76 | { 77 | flat_wset, std::equal_to> fws(0, std::hash(), std::equal_to()); 78 | EXPECT_FALSE(fws.contains(1)); 79 | } 80 | { 81 | std::vector vec{1, 3}; 82 | flat_wset fws(vec.begin(), vec.end()); 83 | EXPECT_EQ(fws.size(), 2u); 84 | EXPECT_TRUE(fws.contains(1)); 85 | EXPECT_TRUE(fws.contains(3)); 86 | } 87 | { 88 | flat_wset fws{1}; 89 | EXPECT_EQ(fws.size(), 1u); 90 | EXPECT_TRUE(fws.contains(1)); 91 | EXPECT_FALSE(fws.contains(2)); 92 | } 93 | // No object leak 94 | EXPECT_EQ(DbgClass::count, 0); 95 | } 96 | 97 | TEST(FlatWSetTest, Constructor3) 98 | { 99 | { 100 | flat_wset fws1; 101 | flat_wset fws2 = fws1; 102 | EXPECT_EQ(fws1.size(), 0u); 103 | EXPECT_EQ(fws2.size(), 0u); 104 | } 105 | { 106 | flat_wset fws1{1}; 107 | flat_wset fws2 = fws1; 108 | EXPECT_TRUE(fws1.contains(1)); 109 | EXPECT_TRUE(fws2.contains(1)); 110 | } 111 | { 112 | flat_wset fws1; 113 | flat_wset fws2 = std::move(fws1); 114 | EXPECT_EQ(fws1.size(), 0u); 115 | EXPECT_EQ(fws2.size(), 0u); 116 | } 117 | { 118 | flat_wset fws1{1}; 119 | flat_wset fws2 = std::move(fws1); 120 | EXPECT_FALSE(fws1.contains(1)); 121 | EXPECT_TRUE(fws2.contains(1)); 122 | } 123 | // No object leak 124 | EXPECT_EQ(DbgClass::count, 0); 125 | } 126 | 127 | TEST(FlatWSetTest, Assignment) 128 | { 129 | { 130 | flat_wset fws1; 131 | flat_wset fws2; 132 | fws2 = fws1; 133 | EXPECT_TRUE(fws1.empty()); 134 | EXPECT_TRUE(fws2.empty()); 135 | } 136 | { 137 | flat_wset fws1{1}; 138 | flat_wset fws2; 139 | fws2 = fws1; 140 | EXPECT_TRUE(fws1.contains(1)); 141 | EXPECT_TRUE(fws2.contains(1)); 142 | } 143 | { 144 | flat_wset fws1; 145 | flat_wset fws2{1}; 146 | fws2 = fws1; 147 | EXPECT_FALSE(fws1.contains(1)); 148 | EXPECT_FALSE(fws2.contains(1)); 149 | } 150 | { 151 | flat_wset fws1{1}; 152 | flat_wset fws2{{2, 4}}; 153 | EXPECT_FALSE(fws2.contains(1)); 154 | EXPECT_TRUE(fws2.contains(2)); 155 | fws2 = fws1; 156 | EXPECT_TRUE(fws1.contains(1)); 157 | EXPECT_TRUE(fws2.contains(1)); 158 | EXPECT_FALSE(fws2.contains(2)); 159 | } 160 | { 161 | flat_wset fws; 162 | EXPECT_TRUE(fws.empty()); 163 | fws = {1, 3}; 164 | EXPECT_TRUE(fws.contains(1)); 165 | EXPECT_TRUE(fws.contains(3)); 166 | fws = {}; 167 | EXPECT_FALSE(fws.contains(1)); 168 | EXPECT_FALSE(fws.contains(3)); 169 | } 170 | { 171 | flat_wset fws{{1, 4}}; 172 | fws = {1, 3}; 173 | EXPECT_TRUE(fws.contains(1)); 174 | EXPECT_TRUE(fws.contains(3)); 175 | fws = {}; 176 | EXPECT_FALSE(fws.contains(1)); 177 | EXPECT_FALSE(fws.contains(3)); 178 | EXPECT_TRUE(fws.empty()); 179 | } 180 | // No object leak 181 | EXPECT_EQ(DbgClass::count, 0); 182 | } 183 | 184 | TEST(FlatWSetTest, Assignment2) 185 | { 186 | { 187 | flat_wset fws1; 188 | flat_wset fws2; 189 | fws2 = std::move(fws1); 190 | EXPECT_TRUE(fws1.empty()); 191 | EXPECT_TRUE(fws2.empty()); 192 | } 193 | { 194 | flat_wset fws1{1}; 195 | flat_wset fws2; 196 | fws2 = std::move(fws1); 197 | EXPECT_FALSE(fws1.contains(1)); 198 | EXPECT_TRUE(fws2.contains(1)); 199 | } 200 | { 201 | flat_wset fws1; 202 | flat_wset fws2{1}; 203 | fws2 = std::move(fws1); 204 | EXPECT_FALSE(fws1.contains(1)); 205 | EXPECT_FALSE(fws2.contains(1)); 206 | } 207 | { 208 | flat_wset fws1{1}; 209 | flat_wset fws2{1, 4}; 210 | fws2 = std::move(fws1); 211 | EXPECT_FALSE(fws1.contains(1)); 212 | EXPECT_TRUE(fws2.contains(1)); 213 | EXPECT_FALSE(fws2.contains(4)); 214 | } 215 | { 216 | flat_wset fws1{1, 6}; 217 | flat_wset fws2{1, 4}; 218 | fws2 = fws1; 219 | EXPECT_TRUE(fws1.contains(1)); 220 | EXPECT_TRUE(fws2.contains(1)); 221 | EXPECT_FALSE(fws2.contains(4)); 222 | EXPECT_TRUE(fws2.contains(6)); 223 | } 224 | // No object leak 225 | EXPECT_EQ(DbgClass::count, 0); 226 | } 227 | 228 | TEST(FlatWSetTest, Capacity) 229 | { 230 | { 231 | flat_wset fws; 232 | EXPECT_TRUE(fws.empty()); 233 | EXPECT_EQ(fws.size(), 0u); 234 | EXPECT_EQ(fws.bucket_count(), 0u); 235 | EXPECT_EQ(fws.load_factor(), 0.f); 236 | 237 | auto max_size = (flat_wset::size_type)(fws.max_bucket_count() * fws.max_load_factor()); 238 | EXPECT_EQ(fws.max_load_factor(), 0.8f); 239 | EXPECT_EQ(fws.max_size(), max_size); 240 | EXPECT_GT(fws.max_bucket_count(), 0u); 241 | fws.max_load_factor(0.f); // no-op 242 | EXPECT_GT(fws.max_bucket_count(), 0u); 243 | 244 | fws = {1, 3}; 245 | EXPECT_EQ(fws.size(), 2u); 246 | EXPECT_EQ(fws.bucket_count(), 2u); 247 | EXPECT_EQ(fws.load_factor(), 1.f); 248 | } 249 | { 250 | flat_wset fws{{1, 1}}; 251 | EXPECT_FALSE(fws.empty()); 252 | EXPECT_EQ(fws.size(), 1u); 253 | EXPECT_EQ(fws.bucket_count(), 2u); 254 | EXPECT_EQ(fws.load_factor(), 0.5f); 255 | 256 | fws.insert(2); 257 | EXPECT_EQ(fws.size(), 2u); 258 | EXPECT_EQ(fws.bucket_count(), 2u); 259 | EXPECT_EQ(fws.load_factor(), 1.f); 260 | 261 | fws.insert(3); 262 | EXPECT_EQ(fws.size(), 3u); 263 | EXPECT_EQ(fws.bucket_count(), 4u); 264 | EXPECT_EQ(fws.load_factor(), 0.75f); 265 | } 266 | // No object leak 267 | EXPECT_EQ(DbgClass::count, 0); 268 | } 269 | 270 | TEST(FlatWSetTest, Capacity2) 271 | { 272 | { 273 | flat_wset fws; 274 | fws.reserve(0); 275 | EXPECT_EQ(fws.size(), 0u); 276 | EXPECT_EQ(fws.bucket_count(), 0u); 277 | EXPECT_EQ(fws.load_factor(), 0.f); 278 | } 279 | { 280 | flat_wset fws; 281 | fws.reserve(7); 282 | EXPECT_EQ(fws.size(), 0u); 283 | EXPECT_EQ(fws.bucket_count(), 8u); 284 | EXPECT_EQ(fws.load_factor(), 0.f); 285 | } 286 | { 287 | flat_wset fws; 288 | fws.reserve(31); 289 | EXPECT_EQ(fws.size(), 0u); 290 | EXPECT_EQ(fws.bucket_count(), 64u); 291 | EXPECT_EQ(fws.load_factor(), 0.f); 292 | } 293 | { 294 | flat_wset fws; 295 | fws.rehash(12); 296 | EXPECT_EQ(fws.size(), 0u); 297 | EXPECT_EQ(fws.bucket_count(), 16u); 298 | EXPECT_EQ(fws.load_factor(), 0.f); 299 | } 300 | { 301 | flat_wset fws; 302 | fws.rehash(31); 303 | EXPECT_EQ(fws.size(), 0u); 304 | EXPECT_EQ(fws.bucket_count(), 32u); 305 | EXPECT_EQ(fws.load_factor(), 0.f); 306 | } 307 | { 308 | flat_wset fws(3); 309 | fws.insert(1); 310 | EXPECT_EQ(fws.size(), 1u); 311 | EXPECT_EQ(fws.bucket_count(), 4u); 312 | 313 | fws.rehash(0); 314 | EXPECT_EQ(fws.size(), 1u); 315 | EXPECT_EQ(fws.bucket_count(), 2u); 316 | 317 | fws.clear(); 318 | fws.rehash(0); 319 | EXPECT_EQ(fws.size(), 0u); 320 | EXPECT_EQ(fws.bucket_count(), 0u); 321 | 322 | fws.insert(3); 323 | EXPECT_TRUE(fws.contains(3)); 324 | EXPECT_EQ(fws.size(), 1u); 325 | EXPECT_EQ(fws.bucket_count(), 2u); 326 | fws.rehash(4); 327 | EXPECT_TRUE(fws.contains(3)); 328 | EXPECT_EQ(fws.size(), 1u); 329 | EXPECT_EQ(fws.bucket_count(), 4u); 330 | 331 | fws.reserve(4); 332 | EXPECT_TRUE(fws.contains(3)); 333 | EXPECT_EQ(fws.size(), 1u); 334 | EXPECT_EQ(fws.bucket_count(), 4u); 335 | 336 | fws.reserve(5); 337 | EXPECT_TRUE(fws.contains(3)); 338 | EXPECT_EQ(fws.size(), 1u); 339 | EXPECT_EQ(fws.bucket_count(), 8u); 340 | } 341 | // No object leak 342 | EXPECT_EQ(DbgClass::count, 0); 343 | } 344 | 345 | TEST(FlatWSetTest, Observers) 346 | { 347 | { 348 | flat_wset fws; 349 | auto hash = fws.hash_function(); 350 | auto keq = fws.key_eq(); 351 | 352 | EXPECT_NE(hash(1), 1); 353 | EXPECT_TRUE(keq(1, 1)); 354 | } 355 | // No object leak 356 | EXPECT_EQ(DbgClass::count, 0); 357 | } 358 | 359 | TEST(FlatWSetTest, Iterator) 360 | { 361 | { 362 | flat_wset fws; 363 | EXPECT_EQ(fws.begin(), fws.end()); 364 | EXPECT_EQ(fws.cbegin(), fws.cend()); 365 | EXPECT_EQ(fws.cbegin(), fws.end()); 366 | EXPECT_EQ(fws.begin(), fws.cend()); 367 | EXPECT_EQ(fws.begin(), fws.cbegin()); 368 | EXPECT_EQ(fws.end(), fws.cend()); 369 | } 370 | { 371 | flat_wset fws{1}; 372 | EXPECT_NE(fws.begin(), fws.end()); 373 | EXPECT_NE(fws.cbegin(), fws.cend()); 374 | EXPECT_EQ(++fws.begin(), fws.end()); 375 | EXPECT_EQ(++fws.cbegin(), fws.cend()); 376 | 377 | auto it = fws.begin(); 378 | EXPECT_EQ(*it, 1); 379 | 380 | fws.insert(3); 381 | it = fws.begin(); 382 | EXPECT_TRUE(*it == 3 || *it == 1); 383 | } 384 | { 385 | flat_wset fws{1, 3}; 386 | auto it1 = fws.find(1); 387 | EXPECT_NE(it1, fws.end()); 388 | auto it2 = fws.find(3); 389 | EXPECT_NE(it2, fws.end()); 390 | EXPECT_NE(it2, it1); 391 | auto it3 = fws.find(5); 392 | EXPECT_EQ(it3, fws.end()); 393 | EXPECT_NE(it3, it1); 394 | EXPECT_NE(it3, it2); 395 | } 396 | { 397 | flat_wset fws; 398 | for (int i = 1; i <= 111; ++i) 399 | fws.insert(i); 400 | 401 | int count = 0; 402 | for (const auto& val : fws) 403 | { 404 | EXPECT_TRUE(fws.contains(val)); 405 | ++count; 406 | } 407 | EXPECT_EQ(count, (int)fws.size()); 408 | } 409 | // No object leak 410 | EXPECT_EQ(DbgClass::count, 0); 411 | } 412 | 413 | TEST(FlatWSetTest, ElementAccess) 414 | { 415 | { 416 | flat_wset fws{1, 3, 5}; 417 | EXPECT_TRUE(fws.contains(5)); 418 | EXPECT_FALSE(fws.contains(6)); 419 | EXPECT_EQ(fws.count(1), 1u); 420 | EXPECT_EQ(fws.count(2), 0u); 421 | 422 | auto it = fws.find(3); 423 | ASSERT_NE(it, fws.end()); 424 | EXPECT_EQ(*it, 3); 425 | 426 | it = fws.find(2); 427 | EXPECT_EQ(it, fws.end()); 428 | } 429 | { 430 | flat_wset fws{"1", "", "3"}; 431 | EXPECT_TRUE(fws.contains("1")); 432 | EXPECT_TRUE(fws.contains("3")); 433 | EXPECT_TRUE(fws.contains("")); 434 | EXPECT_FALSE(fws.contains("2")); 435 | } 436 | // No object leak 437 | EXPECT_EQ(DbgClass::count, 0); 438 | } 439 | 440 | // TEST(FlatWSetTest, ElementAccess2) 441 | // { 442 | // { 443 | // flat_wset fws; 444 | // DbgClass k(1); 445 | 446 | // fws.emplace(k); 447 | // EXPECT_EQ(k, 1); 448 | // EXPECT_EQ(fws.count(k), 1u); 449 | // EXPECT_EQ(fws.size(), 1u); 450 | 451 | // fws.insert(std::move(k)); 452 | // EXPECT_EQ(k, 1); 453 | // EXPECT_EQ(fws.count(1), 1u); 454 | // EXPECT_EQ(fws.size(), 1u); 455 | 456 | // DbgClass k2(2); 457 | // fws.insert(std::move(k2)); 458 | // EXPECT_NE(k2, 2); 459 | // EXPECT_EQ(fws.count(2), 1u); 460 | // EXPECT_EQ(fws.size(), 2u); 461 | // } 462 | // { 463 | // flat_wset fws; 464 | // auto p1 = fws.insert("a"); 465 | // EXPECT_EQ(*p1.first, "a"); 466 | // EXPECT_TRUE(p1.second); 467 | // EXPECT_EQ(fws.size(), 1u); 468 | 469 | // auto p2 = fws.insert("a"); 470 | // EXPECT_EQ(*p2.first, "a"); 471 | // EXPECT_FALSE(p2.second); 472 | // EXPECT_EQ(fws.size(), 1u); 473 | // } 474 | // // No object leak 475 | // EXPECT_EQ(DbgClass::count, 0); 476 | // } 477 | 478 | TEST(FlatWSetTest, Clear) 479 | { 480 | { 481 | flat_wset fws; 482 | EXPECT_EQ(fws.size(), 0u); 483 | EXPECT_EQ(fws.bucket_count(), 0u); 484 | 485 | fws.clear(); 486 | EXPECT_EQ(fws.size(), 0u); 487 | EXPECT_EQ(fws.bucket_count(), 0u); 488 | } 489 | { 490 | flat_wset fws{1, 3, 5}; 491 | EXPECT_EQ(fws.size(), 3u); 492 | EXPECT_EQ(fws.bucket_count(), 4u); 493 | 494 | fws.clear(); 495 | EXPECT_EQ(fws.size(), 0u); 496 | EXPECT_EQ(fws.bucket_count(), 4u); 497 | fws.clear(); 498 | EXPECT_EQ(fws.size(), 0u); 499 | EXPECT_EQ(fws.bucket_count(), 4u); 500 | } 501 | // No object leak 502 | EXPECT_EQ(DbgClass::count, 0); 503 | } 504 | 505 | TEST(FlatWSetTest, Insert) 506 | { 507 | { 508 | flat_wset fws; 509 | EXPECT_FALSE(fws.contains(1)); 510 | auto it = fws.insert(1); 511 | ASSERT_NE(it.first, fws.end()); 512 | EXPECT_EQ(*it.first, 1); 513 | EXPECT_TRUE(it.second); 514 | EXPECT_TRUE(fws.contains(1)); 515 | 516 | fws.insert(3); 517 | EXPECT_TRUE(fws.contains(3)); 518 | 519 | it = fws.insert(3); 520 | EXPECT_TRUE(fws.contains(3)); 521 | ASSERT_NE(it.first, fws.end()); 522 | EXPECT_EQ(*it.first, 3); 523 | EXPECT_FALSE(it.second); 524 | } 525 | { 526 | flat_wset fws; 527 | DbgClass v(10); 528 | 529 | fws.insert(v); 530 | EXPECT_TRUE(fws.contains(10)); 531 | EXPECT_EQ(v, 10); 532 | } 533 | { 534 | flat_wset fws; 535 | const DbgClass v(10); 536 | 537 | fws.insert(v); 538 | EXPECT_TRUE(fws.contains(10)); 539 | EXPECT_EQ(v, 10); 540 | } 541 | { 542 | flat_wset fws; 543 | DbgClass v(10); 544 | 545 | fws.insert(std::move(v)); 546 | EXPECT_TRUE(fws.contains(10)); 547 | EXPECT_NE(v, 10); 548 | } 549 | { 550 | flat_wset fws; 551 | const DbgClass v(10); 552 | 553 | fws.insert(std::move(v)); 554 | EXPECT_TRUE(fws.contains(10)); 555 | EXPECT_EQ(v, 10); 556 | } 557 | // No object leak 558 | EXPECT_EQ(DbgClass::count, 0); 559 | } 560 | 561 | TEST(FlatWSetTest, Insert2) 562 | { 563 | { 564 | std::vector vec{1, 1}; 565 | flat_wset fws; 566 | fws.insert(vec.begin(), vec.end()); 567 | EXPECT_EQ(fws.size(), 1u); 568 | EXPECT_EQ(vec.size(), 2u); 569 | EXPECT_EQ(vec[0], 1); 570 | } 571 | { 572 | std::vector vec{1, 1}; 573 | flat_wset fws; 574 | fws.insert(std::make_move_iterator(vec.begin()), std::make_move_iterator(vec.end())); 575 | 576 | EXPECT_TRUE(fws.contains(1)); 577 | EXPECT_EQ(fws.size(), 1u); 578 | EXPECT_EQ(vec.size(), 2u); 579 | EXPECT_NE(vec[0], 1); 580 | EXPECT_EQ(vec[1], 1); 581 | } 582 | { 583 | flat_wset fws; 584 | std::initializer_list ilist{1, 1}; 585 | fws.insert(ilist); 586 | 587 | EXPECT_TRUE(fws.contains(1)); 588 | EXPECT_EQ(fws.size(), 1u); 589 | EXPECT_EQ(ilist.size(), 2u); 590 | EXPECT_EQ(*ilist.begin(), 1); 591 | } 592 | { 593 | flat_wset fws; 594 | fws.insert({1, 1}); 595 | 596 | EXPECT_TRUE(fws.contains(1)); 597 | EXPECT_EQ(fws.size(), 1u); 598 | } 599 | // No object leak 600 | EXPECT_EQ(DbgClass::count, 0); 601 | } 602 | 603 | TEST(FlatWSetTest, Emplace) 604 | { 605 | { 606 | flat_wset fws; 607 | auto it = fws.emplace(1); 608 | ASSERT_NE(it.first, fws.end()); 609 | EXPECT_EQ(*it.first, 1); 610 | EXPECT_TRUE(it.second); 611 | EXPECT_TRUE(fws.contains(1)); 612 | EXPECT_EQ(fws.size(), 1u); 613 | 614 | it = fws.emplace(1); 615 | ASSERT_NE(it.first, fws.end()); 616 | EXPECT_EQ(*it.first, 1); 617 | EXPECT_FALSE(it.second); 618 | EXPECT_TRUE(fws.contains(1)); 619 | EXPECT_EQ(fws.size(), 1u); 620 | 621 | it = fws.emplace(2); 622 | ASSERT_NE(it.first, fws.end()); 623 | EXPECT_TRUE(it.second); 624 | EXPECT_TRUE(fws.contains(2)); 625 | EXPECT_EQ(fws.size(), 2u); 626 | } 627 | { 628 | flat_wset fws; 629 | DbgClass k(1); 630 | 631 | auto it = fws.emplace(k); 632 | ASSERT_NE(it.first, fws.end()); 633 | EXPECT_EQ(*it.first, 1); 634 | EXPECT_TRUE(it.second); 635 | EXPECT_TRUE(fws.contains(1)); 636 | EXPECT_EQ(fws.size(), 1u); 637 | EXPECT_EQ(k, 1); 638 | } 639 | { 640 | flat_wset fws; 641 | DbgClass k(1); 642 | 643 | auto it = fws.emplace(std::move(k)); 644 | ASSERT_NE(it.first, fws.end()); 645 | EXPECT_EQ(*it.first, 1); 646 | EXPECT_TRUE(it.second); 647 | EXPECT_TRUE(fws.contains(1)); 648 | EXPECT_EQ(fws.size(), 1u); 649 | EXPECT_NE(k, 1); 650 | } 651 | { 652 | flat_wset fws; 653 | const char* k = "1"; 654 | 655 | auto it = fws.emplace(k); 656 | ASSERT_NE(it.first, fws.end()); 657 | EXPECT_EQ(*it.first, k); 658 | EXPECT_TRUE(it.second); 659 | EXPECT_TRUE(fws.contains(k)); 660 | EXPECT_EQ(fws.size(), 1u); 661 | } 662 | // No object leak 663 | EXPECT_EQ(DbgClass::count, 0); 664 | } 665 | 666 | TEST(FlatWSetTest, Erase) 667 | { 668 | { 669 | flat_wset fws{1, 3}; 670 | EXPECT_EQ(fws.erase(1), 1u); 671 | EXPECT_FALSE(fws.contains(1)); 672 | EXPECT_TRUE(fws.contains(3)); 673 | EXPECT_EQ(fws.size(), 1u); 674 | EXPECT_EQ(fws.bucket_count(), 2u); 675 | 676 | EXPECT_EQ(fws.erase(1), 0u); 677 | EXPECT_FALSE(fws.contains(1)); 678 | EXPECT_TRUE(fws.contains(3)); 679 | EXPECT_EQ(fws.size(), 1u); 680 | EXPECT_EQ(fws.bucket_count(), 2u); 681 | 682 | EXPECT_EQ(fws.erase(3), 1u); 683 | EXPECT_FALSE(fws.contains(1)); 684 | EXPECT_FALSE(fws.contains(3)); 685 | EXPECT_EQ(fws.size(), 0u); 686 | EXPECT_EQ(fws.bucket_count(), 2u); 687 | } 688 | { 689 | flat_wset fws; 690 | EXPECT_EQ(fws.erase(1), 0u); 691 | EXPECT_FALSE(fws.contains(1)); 692 | EXPECT_EQ(fws.size(), 0u); 693 | EXPECT_EQ(fws.bucket_count(), 0u); 694 | 695 | fws.emplace(2); 696 | EXPECT_EQ(fws.erase(1), 0u); 697 | EXPECT_TRUE(fws.contains(2)); 698 | EXPECT_EQ(fws.size(), 1u); 699 | EXPECT_EQ(fws.bucket_count(), 2u); 700 | } 701 | // No object leak 702 | EXPECT_EQ(DbgClass::count, 0); 703 | } 704 | 705 | TEST(FlatWSetTest, EraseIter) 706 | { 707 | { 708 | flat_wset fws{1, 3}; 709 | auto first = fws.begin(); 710 | bool is1 = *first == 1; 711 | 712 | fws.erase(first); 713 | EXPECT_EQ(fws.contains(1), !is1); 714 | EXPECT_EQ(fws.contains(3), is1); 715 | EXPECT_EQ(fws.size(), 1u); 716 | 717 | first = fws.begin(); 718 | fws.erase(first); 719 | EXPECT_FALSE(fws.contains(1)); 720 | EXPECT_FALSE(fws.contains(3)); 721 | EXPECT_EQ(fws.size(), 0u); 722 | } 723 | { 724 | flat_wset fws{1, 3}; 725 | auto first = fws.begin(); 726 | bool is1 = *first == 1; 727 | 728 | auto it = fws.erase_(first); 729 | ASSERT_NE(it, fws.end()); 730 | EXPECT_EQ(*it, is1 ? 3 : 1); 731 | EXPECT_EQ(fws.contains(1), !is1); 732 | EXPECT_EQ(fws.contains(3), is1); 733 | EXPECT_EQ(fws.size(), 1u); 734 | 735 | first = fws.begin(); 736 | it = fws.erase_(first); 737 | ASSERT_EQ(it, fws.end()); 738 | EXPECT_FALSE(fws.contains(1)); 739 | EXPECT_FALSE(fws.contains(3)); 740 | EXPECT_EQ(fws.size(), 0u); 741 | } 742 | { 743 | flat_wset fws; 744 | for (int i = 1; i <= 100; ++i) 745 | fws.emplace(i); 746 | EXPECT_EQ(fws.size(), 100u); 747 | 748 | auto it = fws.begin(); 749 | while (!fws.empty()) 750 | it = fws.erase_(it); 751 | EXPECT_EQ(fws.size(), 0u); 752 | } 753 | // No object leak 754 | EXPECT_EQ(DbgClass::count, 0); 755 | } 756 | 757 | TEST(FlatWSetTest, EraseIf) 758 | { 759 | { 760 | flat_wset fws; 761 | 762 | erase_if(fws, [](const auto& item){ return (item.id % 2); }); 763 | EXPECT_EQ(fws.size(), 0u); 764 | } 765 | { 766 | flat_wset fws{1, 3}; 767 | 768 | erase_if(fws, [](const auto& item){ return (item.id % 2); }); 769 | EXPECT_EQ(fws.size(), 0u); 770 | } 771 | { 772 | flat_wset fws{1, 2, 3}; 773 | 774 | erase_if(fws, [](const auto& item){ return (item.id % 2); }); 775 | EXPECT_EQ(fws.size(), 1u); 776 | EXPECT_FALSE(fws.contains(1)); 777 | EXPECT_TRUE(fws.contains(2)); 778 | EXPECT_FALSE(fws.contains(3)); 779 | } 780 | { 781 | flat_wset fws{2, 4}; 782 | 783 | erase_if(fws, [](const auto& item){ return (item.id % 2); }); 784 | EXPECT_EQ(fws.size(), 2u); 785 | EXPECT_TRUE(fws.contains(2)); 786 | EXPECT_TRUE(fws.contains(4)); 787 | } 788 | // No object leak 789 | EXPECT_EQ(DbgClass::count, 0); 790 | } 791 | 792 | TEST(FlatWSetTest, Swap) 793 | { 794 | { 795 | flat_wset fws1; 796 | flat_wset fws2; 797 | 798 | fws2.swap(fws1); 799 | EXPECT_EQ(fws1.size(), 0u); 800 | EXPECT_EQ(fws2.size(), 0u); 801 | 802 | fws2.swap(fws1); 803 | EXPECT_EQ(fws1.size(), 0u); 804 | EXPECT_EQ(fws2.size(), 0u); 805 | } 806 | { 807 | flat_wset fws1{1, 3, 5}; 808 | flat_wset fws2; 809 | 810 | fws2.swap(fws1); 811 | EXPECT_EQ(fws1.size(), 0u); 812 | EXPECT_EQ(fws2.size(), 3u); 813 | EXPECT_FALSE(fws1.contains(3)); 814 | EXPECT_TRUE(fws2.contains(3)); 815 | 816 | fws2.swap(fws1); 817 | EXPECT_EQ(fws1.size(), 3u); 818 | EXPECT_EQ(fws2.size(), 0u); 819 | EXPECT_TRUE(fws1.contains(3)); 820 | EXPECT_FALSE(fws2.contains(3)); 821 | } 822 | { 823 | flat_wset fws1{1, 3}; 824 | flat_wset fws2{5}; 825 | 826 | fws2.swap(fws1); 827 | EXPECT_EQ(fws1.size(), 1u); 828 | EXPECT_EQ(fws2.size(), 2u); 829 | EXPECT_TRUE(fws1.contains(5)); 830 | EXPECT_FALSE(fws1.contains(1)); 831 | EXPECT_TRUE(fws2.contains(1)); 832 | EXPECT_FALSE(fws2.contains(5)); 833 | 834 | fws2.swap(fws1); 835 | EXPECT_EQ(fws1.size(), 2u); 836 | EXPECT_EQ(fws2.size(), 1u); 837 | EXPECT_TRUE(fws1.contains(3)); 838 | EXPECT_FALSE(fws1.contains(5)); 839 | EXPECT_TRUE(fws2.contains(5)); 840 | EXPECT_FALSE(fws2.contains(3)); 841 | } 842 | { 843 | using std::swap; 844 | 845 | flat_wset fws1{1, 3}; 846 | flat_wset fws2{5}; 847 | 848 | swap(fws1, fws2); 849 | EXPECT_EQ(fws1.size(), 1u); 850 | EXPECT_EQ(fws2.size(), 2u); 851 | EXPECT_TRUE(fws1.contains(5)); 852 | EXPECT_FALSE(fws1.contains(1)); 853 | EXPECT_TRUE(fws2.contains(1)); 854 | EXPECT_FALSE(fws2.contains(5)); 855 | 856 | swap(fws1, fws2); 857 | EXPECT_EQ(fws1.size(), 2u); 858 | EXPECT_EQ(fws2.size(), 1u); 859 | EXPECT_TRUE(fws1.contains(3)); 860 | EXPECT_FALSE(fws1.contains(5)); 861 | EXPECT_TRUE(fws2.contains(5)); 862 | EXPECT_FALSE(fws2.contains(3)); 863 | } 864 | { 865 | using std::swap; 866 | 867 | flat_wset fws1{1}; 868 | flat_wset fws2{5, 4}; 869 | 870 | swap(fws1, fws1); 871 | EXPECT_EQ(fws1.size(), 1u); 872 | EXPECT_TRUE(fws1.contains(1)); 873 | 874 | swap(fws2, fws1); 875 | fws1.swap(fws2); 876 | EXPECT_EQ(fws1.size(), 1u); 877 | EXPECT_EQ(fws2.size(), 2u); 878 | EXPECT_TRUE(fws1.contains(1)); 879 | EXPECT_FALSE(fws1.contains(5)); 880 | EXPECT_TRUE(fws2.contains(5)); 881 | EXPECT_FALSE(fws2.contains(1)); 882 | } 883 | // No object leak 884 | EXPECT_EQ(DbgClass::count, 0); 885 | } 886 | 887 | TEST(FlatWSetTest, Equality) 888 | { 889 | { 890 | flat_wset fws1; 891 | flat_wset fws2; 892 | EXPECT_EQ(fws1, fws2); 893 | EXPECT_EQ(fws2, fws1); 894 | } 895 | { 896 | flat_wset fws1{1}; 897 | flat_wset fws2{1}; 898 | EXPECT_EQ(fws1, fws2); 899 | EXPECT_EQ(fws2, fws1); 900 | } 901 | { 902 | flat_wset fws1{1}; 903 | flat_wset fws2{5}; 904 | EXPECT_NE(fws1, fws2); 905 | EXPECT_NE(fws2, fws1); 906 | } 907 | { 908 | flat_wset fws1{}; 909 | flat_wset fws2{1}; 910 | EXPECT_NE(fws1, fws2); 911 | EXPECT_NE(fws2, fws1); 912 | } 913 | { 914 | flat_wset fws1{1}; 915 | flat_wset fws2{1, 3}; 916 | EXPECT_NE(fws1, fws2); 917 | EXPECT_NE(fws2, fws1); 918 | } 919 | // No object leak 920 | EXPECT_EQ(DbgClass::count, 0); 921 | } 922 | 923 | TEST(FlatWSetTest, BadHash) 924 | { 925 | struct bad_hash { 926 | size_t operator()(const int& x) const { return x; } 927 | }; 928 | 929 | flat_wset fws; 930 | std::unordered_set set; 931 | for (int i = 0; i < 100000; ++i) 932 | { 933 | fws.emplace(i); 934 | set.emplace(i); 935 | } 936 | EXPECT_EQ(fws.size(), set.size()); 937 | 938 | for (const auto& item : set) 939 | { 940 | auto itF = fws.find(item); 941 | ASSERT_NE(itF, fws.end()); 942 | } 943 | } 944 | 945 | TEST(FlatWSetTest, Stress) 946 | { 947 | { 948 | auto seed = time(NULL); 949 | auto get_rand = []() { 950 | int r = rand() + 1; 951 | return (RAND_MAX > 32768) ? r : r * rand() + 1; 952 | }; 953 | 954 | std::cout << "Stress seed: " << seed << "\n"; 955 | srand((unsigned int)seed); 956 | 957 | flat_wset fws; 958 | std::unordered_set set; 959 | std::vector vec; 960 | 961 | int addCount = 0; 962 | int findCount = 0; 963 | int removeCount = 0; 964 | int rehashCount = 0; 965 | 966 | for (int i = 0; i < 1000000; ++i) 967 | { 968 | int op = rand() % 8; 969 | switch(op) 970 | { 971 | case 0: // insert 972 | { 973 | int k = get_rand(); 974 | fws.insert(k); 975 | set.insert(k); 976 | vec.push_back(k); 977 | ++addCount; 978 | EXPECT_EQ(fws.size(), set.size()) << "insert"; 979 | break; 980 | } 981 | case 1: // emplace 982 | case 2: 983 | { 984 | int k = get_rand(); 985 | fws.emplace(k); 986 | set.emplace(k); 987 | vec.push_back(k); 988 | ++addCount; 989 | EXPECT_EQ(fws.size(), set.size()) << "emplace"; 990 | break; 991 | } 992 | case 3: // count 993 | { 994 | if (!vec.empty()) 995 | { 996 | auto k = vec[rand() % vec.size()]; 997 | EXPECT_EQ(fws.count(k), set.count(k)); 998 | ++findCount; 999 | } 1000 | EXPECT_EQ(fws.size(), set.size()) << "count"; 1001 | break; 1002 | } 1003 | case 4: // erase 1004 | { 1005 | if (!vec.empty()) 1006 | { 1007 | auto k = vec[rand() % vec.size()]; 1008 | auto countF = fws.erase(k); 1009 | auto countS = set.erase(k); 1010 | ++removeCount; 1011 | EXPECT_EQ(countF, countS); 1012 | } 1013 | EXPECT_EQ(fws.size(), set.size()) << "erase"; 1014 | break; 1015 | } 1016 | case 5: // erase first 1017 | { 1018 | if (!set.empty()) 1019 | { 1020 | auto itS = set.begin(); 1021 | auto itF = fws.find(*itS); 1022 | ASSERT_NE(itF, fws.end()); 1023 | ASSERT_NE(itS, set.end()); 1024 | fws.erase_(itF); 1025 | set.erase(itS); 1026 | ++removeCount; 1027 | } 1028 | EXPECT_EQ(fws.size(), set.size()) << "erase first"; 1029 | break; 1030 | } 1031 | case 6: // find 1032 | { 1033 | if (!vec.empty()) 1034 | { 1035 | auto k = vec[rand() % vec.size()]; 1036 | auto itF = fws.find(k); 1037 | auto itS = set.find(k); 1038 | if (itF != fws.end()) 1039 | { 1040 | EXPECT_NE(itS, set.end()); 1041 | EXPECT_EQ(*itF, *itS) << "find"; 1042 | } 1043 | else 1044 | { 1045 | EXPECT_EQ(itS, set.end()) << "find none"; 1046 | } 1047 | ++findCount; 1048 | } 1049 | break; 1050 | } 1051 | case 7: // rehash 1052 | { 1053 | if (rand() < RAND_MAX / 50) 1054 | { 1055 | auto j = vec.size() ? rand() % vec.size() : 0; 1056 | fws.rehash(j); 1057 | ++rehashCount; 1058 | } 1059 | EXPECT_EQ(fws.size(), set.size()) << "rehash"; 1060 | break; 1061 | } 1062 | default: 1063 | assert(false); 1064 | } 1065 | } 1066 | std::cout << "Stress final size: " << set.size() 1067 | << ", add: " << addCount 1068 | << ", find: " << findCount 1069 | << ", remove: " << removeCount 1070 | << ", rehash: " << rehashCount << "\n"; 1071 | EXPECT_EQ(fws.size(), set.size()) << "final"; 1072 | 1073 | for (const auto& item : set) 1074 | { 1075 | auto itF = fws.find(item); 1076 | ASSERT_NE(itF, fws.end()); 1077 | } 1078 | } 1079 | // No object leak 1080 | EXPECT_EQ(DbgClass::count, 0); 1081 | } 1082 | --------------------------------------------------------------------------------