├── .clang-format ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── clang-format-check.yml │ ├── codeql-analysis.yml │ ├── macos.yml │ ├── ubuntu.yml │ └── windows.yml ├── .gitignore ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── benchmark ├── CMakeLists.txt ├── benchhelpers.hpp ├── sets_benchmark.cpp └── vectors_benchmark.cpp ├── cmake └── amcConfig.cmake.in ├── docs ├── set_bench_int.svg ├── set_bench_reloctype.svg └── vector_bench_reloctype.svg ├── include └── amc │ ├── algorithm.hpp │ ├── allocator.hpp │ ├── config.hpp │ ├── fixedcapacityvector.hpp │ ├── flatset.hpp │ ├── hasreallocate.hpp │ ├── isdetected.hpp │ ├── istransparent.hpp │ ├── memory.hpp │ ├── smallset.hpp │ ├── smallvector.hpp │ ├── type_traits.hpp │ ├── utility.hpp │ ├── vector.hpp │ └── vectorcommon.hpp └── test ├── CMakeLists.txt ├── amc_isdetected_test.cpp ├── sets_test.cpp ├── testhelpers.hpp ├── testtypes.hpp └── vectors_test.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: Google 2 | ColumnLimit: 120 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/clang-format-check.yml: -------------------------------------------------------------------------------- 1 | name: clang-format-check 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | formatting-check: 11 | name: Formatting Check 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | path: 16 | - "benchmark" 17 | - "include" 18 | - "test" 19 | steps: 20 | - uses: actions/checkout@v4 21 | - name: Run clang-format style check for C/C++/Protobuf programs. 22 | uses: jidicula/clang-format-action@v4.14.0 23 | with: 24 | check-path: ${{ matrix.path }} 25 | fallback-style: "Google" 26 | clang-format-version: '19' 27 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: [ main ] 17 | schedule: 18 | - cron: '24 8 * * 2' 19 | 20 | jobs: 21 | analyze: 22 | name: Analyze 23 | runs-on: ubuntu-latest 24 | permissions: 25 | actions: read 26 | contents: read 27 | security-events: write 28 | 29 | strategy: 30 | fail-fast: false 31 | matrix: 32 | language: [ 'cpp' ] 33 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 34 | # Learn more about CodeQL language support at https://git.io/codeql-language-support 35 | 36 | steps: 37 | - name: Checkout repository 38 | uses: actions/checkout@v2 39 | 40 | # Initializes the CodeQL tools for scanning. 41 | - name: Initialize CodeQL 42 | uses: github/codeql-action/init@v1 43 | with: 44 | languages: ${{ matrix.language }} 45 | # If you wish to specify custom queries, you can do so here or in a config file. 46 | # By default, queries listed here will override any specified in a config file. 47 | # Prefix the list here with "+" to use these queries and those in the config file. 48 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 49 | 50 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 51 | # If this step fails, then you should remove it and run the build manually (see below) 52 | - name: Autobuild 53 | uses: github/codeql-action/autobuild@v1 54 | 55 | # ℹ️ Command-line programs to run using the OS shell. 56 | # 📚 https://git.io/JvXDl 57 | 58 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 59 | # and modify them (or add more) to build your code if your project 60 | # uses a compiled language 61 | 62 | #- run: | 63 | # make bootstrap 64 | # make release 65 | 66 | - name: Perform CodeQL Analysis 67 | uses: github/codeql-action/analyze@v1 68 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macOS 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | xcode: 11 | name: Build on MacOS 12 | runs-on: macos-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - uses: maxim-lobanov/setup-xcode@v1 16 | with: 17 | xcode-version: latest-stable 18 | - name: cmake 19 | run: cmake -S . -B build -D CMAKE_BUILD_TYPE=Release 20 | - name: build 21 | run: cmake --build build --parallel 2 22 | - name: test 23 | run: cd build && ctest -j 2 --output-on-failure 24 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: Ubuntu 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | ci_test_gcc_release: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | compiler: [g++-7, g++-9, g++-11] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Install compiler 18 | run: | 19 | echo "deb http://dk.archive.ubuntu.com/ubuntu/ focal main universe" | sudo tee -a /etc/apt/sources.list 20 | sudo apt-get update 21 | sudo apt-get install -y ${{ matrix.compiler }} 22 | - name: cmake 23 | run: cmake -S . -B build -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_BUILD_TYPE=Release 24 | - name: build 25 | run: cmake --build build --parallel 2 26 | - name: test 27 | run: cd build && ctest -j 2 --output-on-failure 28 | 29 | ci_test_gcc_debug: 30 | runs-on: ubuntu-latest 31 | strategy: 32 | matrix: 33 | compiler: [g++-8, g++-10, g++-12] 34 | steps: 35 | - uses: actions/checkout@v4 36 | - name: Install compiler 37 | run: | 38 | echo "deb http://dk.archive.ubuntu.com/ubuntu/ focal main universe" | sudo tee -a /etc/apt/sources.list 39 | sudo apt-get update 40 | sudo apt-get install -y ${{ matrix.compiler }} 41 | - name: cmake 42 | run: cmake -S . -B build -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_BUILD_TYPE=Debug 43 | - name: build 44 | run: cmake --build build --parallel 2 45 | - name: test 46 | run: cd build && ctest -j 2 --output-on-failure 47 | 48 | ci_test_clang_release: 49 | runs-on: ubuntu-latest 50 | strategy: 51 | matrix: 52 | compiler: ["17", "19"] 53 | steps: 54 | - uses: actions/checkout@v4 55 | - name: Install compiler 56 | run: | 57 | wget https://apt.llvm.org/llvm.sh 58 | chmod +x llvm.sh 59 | sudo ./llvm.sh ${{ matrix.compiler }} 60 | - name: cmake 61 | run: cmake -S . -B build -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_BUILD_TYPE=Release 62 | - name: build 63 | run: cmake --build build --parallel 2 64 | - name: test 65 | run: cd build && ctest -j 2 --output-on-failure 66 | 67 | ci_test_clang_debug: 68 | runs-on: ubuntu-latest 69 | strategy: 70 | matrix: 71 | compiler: ["20"] 72 | steps: 73 | - uses: actions/checkout@v4 74 | - name: Install compiler 75 | run: | 76 | wget https://apt.llvm.org/llvm.sh 77 | chmod +x llvm.sh 78 | sudo ./llvm.sh ${{ matrix.compiler }} 79 | - name: cmake 80 | run: cmake -S . -B build -DCMAKE_CXX_COMPILER=clang++-${{ matrix.compiler }} -DCMAKE_BUILD_TYPE=Debug 81 | - name: build 82 | run: cmake --build build --parallel 2 83 | - name: test 84 | run: cd build && ctest -j 2 --output-on-failure 85 | 86 | ci_test_standards: 87 | runs-on: ubuntu-latest 88 | strategy: 89 | matrix: 90 | standard: [11, 14, 17, 20] 91 | compiler: [g++-13] 92 | steps: 93 | - uses: actions/checkout@v4 94 | - name: Install compiler 95 | run: | 96 | sudo apt-get update 97 | sudo apt-get install -y ${{ matrix.compiler }} 98 | - name: cmake 99 | run: cmake -S . -B build -DCMAKE_CXX_COMPILER=${{ matrix.compiler }} -DCMAKE_CXX_STANDARD=${{ matrix.standard }} -DCMAKE_BUILD_TYPE=Debug 100 | - name: build 101 | run: cmake --build build --parallel 2 102 | - name: test 103 | run: cd build && ctest -j 2 --output-on-failure 104 | 105 | ci_nonstandard_features: 106 | runs-on: ubuntu-latest 107 | steps: 108 | - uses: actions/checkout@v4 109 | - name: cmake 110 | run: cmake -S . -B build -DCMAKE_CXX_COMPILER=g++ -DAMC_PEDANTIC=ON -DCMAKE_BUILD_TYPE=Debug 111 | - name: build 112 | run: cmake --build build --parallel 2 113 | - name: test 114 | run: cd build && ctest -j 2 --output-on-failure 115 | -------------------------------------------------------------------------------- /.github/workflows/windows.yml: -------------------------------------------------------------------------------- 1 | name: Windows 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | jobs: 10 | msvc2022: 11 | runs-on: windows-2022 12 | strategy: 13 | matrix: 14 | build_type: [Debug, Release] 15 | architecture: [Win32, x64] 16 | 17 | steps: 18 | - uses: actions/checkout@v4 19 | - name: cmake 20 | run: cmake -S . -B build -A ${{ matrix.architecture }} 21 | - name: build 22 | run: cmake --build build --config ${{ matrix.build_type }} --parallel 2 23 | - name: test 24 | run: cd build && ctest -j 2 -C ${{ matrix.build_type }} --output-on-failure 25 | 26 | clang-cl-11: 27 | runs-on: windows-latest 28 | strategy: 29 | matrix: 30 | architecture: [Win32, x64] 31 | 32 | steps: 33 | - uses: actions/checkout@v4 34 | - name: cmake 35 | run: cmake -S . -B build -A ${{ matrix.architecture }} -T ClangCL 36 | - name: build 37 | run: cmake --build build --config Debug --parallel 2 38 | - name: test 39 | run: cd build && ctest -j 2 -C Debug --output-on-failure 40 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #==============================================================================# 2 | # File extensions to be ignored anywhere in the tree. 3 | #==============================================================================# 4 | 5 | *.so* 6 | *.cflags 7 | *.cxxflags 8 | *.config 9 | *.creator 10 | *.creator* 11 | *.files 12 | *.includes 13 | *.cmake 14 | *.code-workspace 15 | *.orig 16 | *.tmp 17 | *.vscode 18 | 19 | **/build*/ 20 | .cache 21 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.14) 2 | 3 | project(amc 4 | VERSION 2.5.2 5 | DESCRIPTION "Header base library of C++ containers" 6 | LANGUAGES CXX 7 | ) 8 | 9 | # This library needs C++11, except for SmallSet which requires std::variant from C++17. 10 | # Emulations of newer C++ functions is provided for non C++14 / C++17 compilers. 11 | set(CMAKE_CXX_STANDARD 17 CACHE STRING "C++ standard") 12 | set(CMAKE_CXX_EXTENSIONS OFF CACHE STRING "Deactivate C++ extensions for maximum portability") 13 | 14 | ## 15 | ## MAIN_PROJECT CHECK 16 | ## determine if amc is built as a subproject or if it is the main project 17 | ## 18 | set(MAIN_PROJECT OFF) 19 | if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 20 | set(MAIN_PROJECT ON) 21 | endif() 22 | 23 | # By default, build benchmarks only if not Debug and main project (benchs do not make sense in Debug) 24 | set(ASAN_BUILD OFF) 25 | set(BENCH_BUILD ${MAIN_PROJECT}) 26 | if(CMAKE_BUILD_TYPE STREQUAL "Debug") 27 | set(ASAN_BUILD ON) 28 | set(BENCH_BUILD OFF) 29 | endif() 30 | 31 | option(AMC_ENABLE_TESTS "Build the unit tests" ${MAIN_PROJECT}) 32 | option(AMC_ENABLE_BENCHMARKS "Build the benchmarks" ${BENCH_BUILD}) 33 | option(AMC_ENABLE_ASAN "Compile with AddressSanitizer" ${ASAN_BUILD}) 34 | option(AMC_PEDANTIC "If set, only standard API (in respect to STL) will be defined. Additional features are enabled when OFF" OFF) 35 | 36 | # Activate all warnings, in all build modes 37 | if(MAIN_PROJECT) 38 | if (MSVC) 39 | add_compile_options(/W4) 40 | else() 41 | add_compile_options(-Wall -Wextra -pedantic) 42 | endif() 43 | endif() 44 | 45 | if(AMC_ENABLE_ASAN AND NOT MSVC) 46 | add_compile_options(-g -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero -fno-sanitize-recover=all) 47 | endif() 48 | 49 | if (AMC_PEDANTIC) 50 | message(STATUS "AMC - Compile without non standard features") 51 | else() 52 | message(STATUS "AMC - Compile with non standard features") 53 | add_compile_definitions(AMC_NONSTD_FEATURES) 54 | endif() 55 | 56 | # Create interface library for header files 57 | include(GNUInstallDirs) 58 | 59 | add_library(${PROJECT_NAME} INTERFACE) 60 | # add alias so the project can be uses with add_subdirectory 61 | add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 62 | 63 | target_include_directories( 64 | ${PROJECT_NAME} 65 | INTERFACE $ 66 | $) 67 | 68 | target_compile_features(${PROJECT_NAME} INTERFACE cxx_std_11) 69 | 70 | if (AMC_ENABLE_TESTS) 71 | if (MSVC) 72 | add_definitions(/bigobj) 73 | endif() 74 | 75 | enable_testing() 76 | 77 | add_subdirectory(test) 78 | endif() 79 | 80 | if (AMC_ENABLE_BENCHMARKS) 81 | add_subdirectory(benchmark) 82 | endif() 83 | 84 | install(TARGETS ${PROJECT_NAME} 85 | EXPORT ${PROJECT_NAME}_Targets 86 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 87 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 88 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 89 | 90 | include(CMakePackageConfigHelpers) 91 | write_basic_package_version_file( 92 | "${PROJECT_NAME}ConfigVersion.cmake" 93 | VERSION ${PROJECT_VERSION} 94 | COMPATIBILITY AnyNewerVersion 95 | ) 96 | 97 | configure_package_config_file( 98 | "${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in" 99 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 100 | INSTALL_DESTINATION 101 | ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake 102 | ) 103 | 104 | install(EXPORT ${PROJECT_NAME}_Targets 105 | FILE ${PROJECT_NAME}Targets.cmake 106 | NAMESPACE ${PROJECT_NAME}:: 107 | DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake) 108 | 109 | install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 110 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" 111 | DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake) 112 | 113 | install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/${PROJECT_NAME} DESTINATION include) 114 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | . 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Amadeus s.a.s 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Ubuntu](https://github.com/AmadeusITGroup/amc/actions/workflows/ubuntu.yml/badge.svg)](https://github.com/AmadeusITGroup/amc/actions/workflows/ubuntu.yml) 2 | [![Windows](https://github.com/AmadeusITGroup/amc/actions/workflows/windows.yml/badge.svg)](https://github.com/AmadeusITGroup/amc/actions/workflows/windows.yml) 3 | [![MacOS](https://github.com/AmadeusITGroup/amc/actions/workflows/macos.yml/badge.svg)](https://github.com/AmadeusITGroup/amc/actions/workflows/macos.yml) 4 | 5 | [![formatted](https://github.com/AmadeusITGroup/amc/actions/workflows/clang-format-check.yml/badge.svg)](https://github.com/AmadeusITGroup/amc/actions/workflows/clang-format-check.yml) 6 | 7 | [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/AmadeusITGroup/amc/master/LICENSE) 8 | [![GitHub Releases](https://img.shields.io/github/release/AmadeusITGroup/amc.svg)](https://github.com/AmadeusITGroup/amc/releases) 9 | 10 | # AMadeus (C++) Containers 11 | 12 |
Sections 13 |

14 | 15 | - [AMadeus (C++) Containers](#amadeus-c-containers) 16 | - [Brief](#brief) 17 | - [Contents](#contents) 18 | - [Benefits](#benefits) 19 | - [Performance optimizations](#performance-optimizations) 20 | - [Vectors](#vectors) 21 | - [Sets](#sets) 22 | - [Other benefits](#other-benefits) 23 | - [For vector types](#for-vector-types) 24 | - [For FlatSet](#for-flatset) 25 | - [What is a trivially relocatable type?](#what-is-a-trivially-relocatable-type) 26 | - [Build with CMake](#build-with-cmake) 27 | - [Options](#options) 28 | - [As a main project](#as-a-main-project) 29 | - [As a sub-project with cmake](#as-a-sub-project-with-cmake) 30 | - [With FetchContent](#with-fetchcontent) 31 | - [By installing amc](#by-installing-amc) 32 | - [Tested environments](#tested-environments) 33 | - [Usage examples](#usage-examples) 34 | - [Vectors](#vectors-1) 35 | - [amc::vector](#amcvector) 36 | - [SmallVector](#smallvector) 37 | - [FixedCapacityVector](#fixedcapacityvector) 38 | - [Sets](#sets-1) 39 | - [FlatSet](#flatset) 40 | - [SmallSet (c++17)](#smallset-c17) 41 | 42 |

43 |
44 | 45 | ## Brief 46 | 47 | Collection of high performance C++ containers, drop-in replacements for `std::vector` and `std::set`, used in Amadeus pricing and shopping engines instead of standard ones. 48 | 49 | ## Contents 50 | 51 | This header based library (to be more precise, `cmake` interface) provides the following containers: 52 | 53 | | Container Name | STL equivalent | Brief | Why? | 54 | | ------------------- | -------------- | ------------------------------------------------------------------- | ------------------------------------------------------------ | 55 | | FixedCapacityVector | std::vector | Vector-like which cannot grow, max capacity defined at compile time | No dynamic memory allocation | 56 | | SmallVector | std::vector | Vector-like optimized for small sizes | No dynamic memory allocation for small sizes | 57 | | vector | std::vector | Vector optimized for trivially relocatable types | Optimized for trivially relocatable types | 58 | | FlatSet | std::set | Set-like implemented as a sorted vector | Alternate structure for sets optimized for read-heavy usages | 59 | | SmallSet (\*) | std::set | Set-like optimized for small sizes | No dynamic memory allocation and unsorted for small sizes | 60 | 61 | \*: C++17 compiler only (uses `std::variant` & `std::optional`) 62 | 63 | ## Benefits 64 | 65 | ### Performance optimizations 66 | 67 | - For types taking an integral `N` as template parameter, container does not allocate dynamic memory as long as its capacity does not exceed `N` 68 | - Vectors (and `FlatSet`, as it uses `amc::vector` by default) are all optimized for **trivially relocatable** types (definition below). 69 | 70 | Example of possible performance gains (directly extracted from the provided benchmarks, compiled with GCC 10.1 on Ubuntu 18:) 71 | 72 | #### Vectors 73 | 74 | ![Alt text](./docs/vector_bench_reloctype.svg) 75 | 76 | #### Sets 77 | 78 | For sets, time axis is in logarithmic scale. 79 | 80 | ![Alt text](./docs/set_bench_reloctype.svg) 81 | ![Alt text](./docs/set_bench_int.svg) 82 | 83 | ### Other benefits 84 | 85 | - All 3 vector flavors share the same code / algorithms for vector operations. 86 | - Templated code generation is minimized thanks to the late location of the integral N template parameter 87 | - Optimized emulations of standard library features for older C++ compilers are provided when C++ version < C++17 88 | 89 | A set of non standard methods and constructors are defined for convenience, provided that `amc` is compiled with `AMC_PEDANTIC` disabled (default, see [Options](#options)). 90 | Here is a brief summary of these extras (compared to their STL equivalents): 91 | 92 | #### For vector types 93 | For all vectors (`FixedCapacityVector`, `SmallVector`, `vector`) 94 | | Method | Description | 95 | | -------------- | ------------------------------------------------------------ | 96 | | `pop_back_val` | Same as `pop_back`, returning popped value. | 97 | | `append` | Same as `insert(vec.end(), ...)` | 98 | | `swap2` | Swap with all other flavors of vectors, not just `this` type | 99 | 100 | For `SmallVector` only, there is a constructor from a rvalue of a `amc::vector` that allows stealing of its dynamic storage. 101 | 102 | #### For FlatSet 103 | | Method | Description | 104 | | --------------- | ------------------------------------------------------------------------------------------ | 105 | | `data` | Returns `data` const pointer from underlying vector | 106 | | `operator[n]` | Access to the underlying value at position 'n' for the `FlatSet` | 107 | | `at(n)` | Access to the underlying value at position 'n' for the `FlatSet`, throwing if our of range | 108 | | `capacity` | Calls underlying vector `capacity` method | 109 | | `reserve` | Calls underlying vector `reserve` method | 110 | | `shrink_to_fit` | Calls `shrink_to_fit` of underlying vector | 111 | 112 | There is an additional constructor and assignment operator from a rvalue of the underlying vector type, stealing its dynamic storage. 113 | 114 | ## What is a trivially relocatable type? 115 | 116 | It describes the ability of moving around memory a value of type T by using `memcpy` (as opposed to the conservative approach of calling the copy constructor and the destroying the old temporary). 117 | It is a type trait currently not (yet?) present in the standard, although is has been proposed (more information [here](https://quuxplusone.github.io/blog/2018/07/18/announcing-trivially-relocatable/)). 118 | No need to use a modified compiler to benefit from trivially relocatibilty optimizations: you can use helper type traits provided by this library to mark explicitely types that you know **are** trivially relocatable. The conservative approach assumes that all trivially copyable types are trivially relocatable, so no need to mark them as such. 119 | With trivially relocatable types, performance gains are easily measurable for all operations of the Vector like container involving *relocation* of elements (grow, insert in middle, etc). 120 | 121 | Fortunately, most types are trivially relocatable. `amc::vector` itself is trivially relocatable (as well as `FixedCapacityVector` and `SmallVector` if T is). Types containing pointers to parts of themselves are typically not trivially relocatable, because moving them would require to update the internal pointers they hold to parts of themselves (`std::list`, `std::set`, `std::map` are for instance). `std::string` is not trivially relocatable in some implementations, but some open source equivalents are (for instance, [folly::fbstring](https://github.com/facebook/folly/blob/master/folly/FBString.h)). More information [here](https://quuxplusone.github.io/blog/2019/02/20/p1144-what-types-are-relocatable/). 122 | 123 | The most convenient way to mark a type as trivially relocatable is to declare in the public part of the class: 124 | 125 | `using trivially_relocatable = std::true_type;` 126 | 127 | This is only necessary for non trivially copyable types, because trivially copyable types are trivially relocatable by default. 128 | 129 | ## Build with CMake 130 | 131 | ### Options 132 | 133 | | CMake flag | Description | 134 | | --------------------- | ------------------------------------------------------------------------------------------------------------------ | 135 | | AMC_ENABLE_TESTS | Build **amc** with unit tests (default if main project) | 136 | | AMC_ENABLE_BENCHMARKS | Build **amc** with benchmarks against STL (default if main project and Release mode) | 137 | | AMC_ENABLE_ASAN | Build with Address Sanitizer mode (only GCC and Clang) | 138 | | AMC_PEDANTIC | If **OFF**, non standard methods and constructors are added for containers (see [Other benefits](#other-benefits)) | 139 | 140 | ### As a main project 141 | 142 | This library is header only library, with one file to be included per container. 143 | 144 | Vectors and `FlatSet` containers require a C++11 compiler. 145 | `SmallSet` however, needs a C++17 compiler because it uses `std::variant` and `std::optional`, although `boost::variant` could be used as a workaround if a C++17 compiler is not available. 146 | 147 | Unit tests and benchmarks are provided. They can be compiled with **cmake**. 148 | 149 | By default, both will be compiled only if 'amc' is instantiated as the main project. You can manually force the build of the tests and benchmarks thanks to following `cmake` flags: 150 | ``` 151 | AMC_ENABLE_TESTS 152 | AMC_ENABLE_BENCHMARKS 153 | ``` 154 | 155 | Bundled tests depend on [Google Test](https://github.com/google/googletest), benchmarks on [Google benchmarks](https://github.com/google/benchmark). 156 | 157 | If not installed on your machine, `cmake` will retrieve them automatically thanks to [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html) feature. 158 | 159 | To compile and launch the tests in `Debug` mode, simply launch 160 | 161 | `mkdir build && cd build && cmake -DCMAKE_BUILD_TYPE=Debug .. && make && ctest` 162 | 163 | ### As a sub-project with cmake 164 | 165 | #### With FetchContent 166 | 167 | ``` 168 | include(FetchContent) 169 | 170 | FetchContent_Declare( 171 | amadeusamc 172 | GIT_REPOSITORY https://github.com/AmadeusITGroup/amc.git 173 | GIT_TAG origin/main 174 | ) 175 | 176 | FetchContent_MakeAvailable(amadeusamc) 177 | ``` 178 | 179 | Official documentation [here](https://cmake.org/cmake/help/latest/module/FetchContent.html). 180 | 181 | By default, `amc` unit tests and benchmarks will not be compiled when used as a sub-project, which is probably what you want. 182 | 183 | `cmake` targets using amc containers can then be linked with the interface library `amc::amc`: 184 | ``` 185 | target_link_libraries(my_target PRIVATE amc::amc) 186 | ``` 187 | 188 | #### By installing amc 189 | 190 | Just use `sudo make install` or `sudo ninja install` depending on your generator to install headers on your machine. 191 | 192 | If you plan to use non standard extra features, make sure you add: 193 | ``` 194 | #define AMC_NONSTD_FEATURES 195 | ``` 196 | 197 | before any include of `amc` headers (it is defined if `AMC_PEDANTIC` CMake flag is `OFF` when building as a main project). 198 | 199 | And that's all. You just need to include the corresponding container's header file to be used in your application code, and why not define them in your namespace. 200 | 201 | ```cpp 202 | #define AMC_NONSTD_FEATURES // If you need non standard features 203 | #include 204 | #include 205 | #include 206 | 207 | #include 208 | #include // Requires C++17 209 | #undef AMC_NONSTD_FEATURES 210 | 211 | namespace my_namespace { 212 | using amc::vector; 213 | using amc::SmallVector; 214 | using amc::FixedCapacityVector; 215 | 216 | using amc::FlatSet; 217 | using amc::SmallSet; 218 | } 219 | ``` 220 | 221 | ### Tested environments 222 | 223 | This library has been tested on Ubuntu 18.04 and Windows 10 (Visual Studio 2019), from cmake 3.15 and the following compilers: 224 | - GCC from version 5.5 to 10 225 | - Clang from version 6.0 226 | - MSVC 19.28 227 | 228 | You can refer to the CI configurations (lots of compilers are tested) to see the full list of tested compilers. 229 | 230 | ## Usage examples 231 | 232 | ### Vectors 233 | 234 | #### amc::vector 235 | 236 | `amc::vector` can be used as drop-in replacement for `std::vector`, especially when the underlying type is *trivially relocatable*. 237 | If your type is *trivially copyable*, optimizations are automatically activated. 238 | If your type is not trivially copyable but is *trivially relocatable*, make sure to mark it as such to activate optimizations. 239 | 240 | ```cpp 241 | #include 242 | 243 | struct MyTriviallyRelocatableType { 244 | MyTriviallyRelocatableType() {} 245 | 246 | // MyTriviallyRelocatableType is not trivially copyable... 247 | ~MyTriviallyRelocatableType() { free(ptr); } 248 | 249 | //... but trivially relocatable! 250 | using trivially_relocatable = std::true_type; 251 | 252 | void *ptr{}; 253 | }; 254 | 255 | using MyTriviallyRelocatableTypeVector = amc::vector; 256 | ``` 257 | 258 | #### SmallVector 259 | 260 | Special variation of `amc::vector` which does not allocate memory and store objects inline up to a maximum capacity defined at compile-time. 261 | If `SmallVector` has to grow beyond this upper bound capacity, it will behave like a `amc::vector` by allocating dynamic memory. 262 | Once a `SmallVector` has allocated dynamic memory, it will not release its memory and come back to its 'small' state when its size goes back under the maximum inline capacity, unless `shrink_to_fit` is called. 263 | 264 | Use it when most of the time (let's say, for instance, in 90 % of the cases) the maximum size of the `SmallVector` does not exceed a compile-time constant to save memory allocations. 265 | 266 | ```cpp 267 | #include 268 | 269 | using ResidencesOfUser = amc::SmallVector; 270 | ``` 271 | 272 | #### FixedCapacityVector 273 | 274 | Use it when in your application constraints define a compile-time upper bound of the maximum size of your vector. 275 | Elements are stored inline in the object and no memory allocation occur. 276 | 277 | ```cpp 278 | #include 279 | #include 280 | 281 | using SoldUnitsPerDayInMonth = amc::FixedCapacityVector; 282 | ``` 283 | 284 | In the unlikely event that the vector attempts to grow beyond its maximum capacity, behavior can be controlled thanks to the third template parameter `GrowingPolicy`: 285 | - `ExceptionGrowingPolicy`: throw `std::out_of_range` exception (default) 286 | - `UncheckedGrowingPolicy`: assert check (nothing is done in `Release`, invoking undefined behavior, abort will be called in `Debug`). 287 | 288 | Compared to a `SmallVector` that would never grow, `FixedCapacityVector` will be slightly more efficient (less checks) and make the intent clear, with nice additional iterator validity properties (`begin()` is never invalidated, iterators before any insert / erase are never invalidated). 289 | In addition, if type is trivially destructible, `FixedCapacityVector` will be itself trivially destructible. 290 | 291 | ### Sets 292 | 293 | #### FlatSet 294 | 295 | Also sometimes called `SortedVector`, it uses a sorted `amc::vector` as storage (by default, provided as template type) and is thus cache friendly and memory efficient set-like container. 296 | It can be used as a drop-in replacement for `std::set` especially when the read operations occur much frequently than the writes. 297 | Even if there are a lot of writes, it is still very efficient for *trivially relocatable* types as it uses `amc::vector` by default which relocates elements very efficiently. 298 | 299 | Besides, the vector container is templated and thus can be combined with above vectors variations to optimize memory allocations (`SmallVector` or `FixedCapacityVector`). 300 | 301 | ```cpp 302 | #include 303 | #include 304 | #include 305 | 306 | using CapitalLettersSetCont = amc::FixedCapacityVector; 307 | using CapitalLettersSet = amc::FlatSet, CapitalLettersSetCont::allocator_type, CapitalLettersSetCont>; 308 | ``` 309 | 310 | #### SmallSet (c++17) 311 | 312 | Additional variation of `std::set` like container. This one has a hybrid behavior similar to `SmallVector`: 313 | - In its 'small' state, there is no dynamic allocation and elements are stored unordered in an inline vector 314 | - In its large state, `SmallSet` uses the templated provided Set type. It is a `std::set` by default, but it could be any type which provides a set like interface, like `FlatSet` for instance. In this case, `SmallSet` iterators are optimized into pointers. 315 | 316 | Note that insertions have linear complexity in the small state so the inline capacity should not be too large. 317 | 318 | ```cpp 319 | #include 320 | #include 321 | #include 322 | 323 | using VisitedCountries = amc::SmallSet; 324 | using VisitedCities = amc::SmallSet, amc::allocator, amc::FlatSet>; 325 | ``` -------------------------------------------------------------------------------- /benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | find_package(benchmark CONFIG) 3 | 4 | if (NOT benchmark_FOUND) 5 | include(FetchContent) 6 | 7 | message(STATUS "Could not find local installation of google/benchmark, fetching sources") 8 | 9 | # Do not test google benchmark 10 | set (BENCHMARK_ENABLE_INSTALL OFF) 11 | set (BENCHMARK_ENABLE_TESTING OFF) 12 | 13 | FetchContent_Declare( 14 | googlebenchmark 15 | URL https://github.com/google/benchmark/archive/refs/tags/v1.9.1.tar.gz 16 | URL_HASH SHA256=32131c08ee31eeff2c8968d7e874f3cb648034377dfc32a4c377fa8796d84981 17 | ) 18 | 19 | FetchContent_MakeAvailable(googlebenchmark) 20 | endif() 21 | 22 | function(add_bench name) 23 | set(options) 24 | set(oneValueArgs) 25 | set(multiValueArgs) 26 | cmake_parse_arguments(PARSE_ARGV 1 MY "${options}" "${oneValueArgs}" "${multiValueArgs}") 27 | add_executable(${name} ${MY_UNPARSED_ARGUMENTS}) 28 | 29 | target_include_directories(${name} PRIVATE ../include) 30 | target_include_directories(${name} PRIVATE ../test) 31 | target_link_libraries(${name} PRIVATE benchmark::benchmark) 32 | if (AMC_ENABLE_ASAN AND NOT MSVC) 33 | target_link_options(${name} PRIVATE -fsanitize=address -fsanitize=undefined -fsanitize=float-divide-by-zero) 34 | endif() 35 | endfunction() 36 | 37 | add_bench( 38 | vectors_benchmark 39 | vectors_benchmark.cpp 40 | ) 41 | 42 | add_bench( 43 | sets_benchmark 44 | sets_benchmark.cpp 45 | ) 46 | -------------------------------------------------------------------------------- /benchmark/benchhelpers.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "testtypes.hpp" 8 | 9 | namespace amc { 10 | constexpr uint32_t kMaxValue = 1000U; 11 | 12 | void PrintStats(benchmark::State &state) { 13 | const TypeStats &stats = TypeStats::_stats; 14 | 15 | size_t nbIt = state.iterations(); 16 | state.counters["Cons"] = static_cast(stats._nbConstructs) / nbIt; 17 | state.counters["Dest"] = static_cast(stats._nbDestructs) / nbIt; 18 | state.counters["CpyC"] = static_cast(stats._nbCopyConstructs) / nbIt; 19 | state.counters["CpyA"] = static_cast(stats._nbCopyAssignments) / nbIt; 20 | state.counters["MovC"] = static_cast(stats._nbMoveConstructs) / nbIt; 21 | state.counters["MovA"] = static_cast(stats._nbMoveAssignments) / nbIt; 22 | state.counters["Allc"] = static_cast(stats._nbMallocs) / nbIt; 23 | state.counters["Real"] = static_cast(stats._nbReallocs) / nbIt; 24 | state.counters["Free"] = static_cast(stats._nbFree) / nbIt; 25 | } 26 | 27 | } // namespace amc 28 | -------------------------------------------------------------------------------- /benchmark/sets_benchmark.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifdef AMC_SMALLSET 9 | #include 10 | #endif 11 | 12 | #include "benchhelpers.hpp" 13 | #include "testhelpers.hpp" 14 | #include "testtypes.hpp" 15 | 16 | namespace amc { 17 | TypeStats TypeStats::_stats; 18 | 19 | namespace { 20 | 21 | using REFRelocType = std::set; 22 | using REFNonRelocType = std::set; 23 | using REFInt = std::set; 24 | using REFUnoInt = std::unordered_set; 25 | 26 | using AMCRelocType = amc::FlatSet; 27 | using AMCNonRelocType = amc::FlatSet; 28 | using AMCInt = amc::FlatSet; 29 | 30 | template 31 | void InsertRandom(benchmark::State &state) { 32 | TypeStats::_stats = TypeStats(); 33 | TypeStats::_stats.start(); 34 | for (auto _ : state) { 35 | SetType v; 36 | for (uint32_t s = 0; v.size() < kMaxValue / 5; ++s) { 37 | uint32_t value = static_cast(HashValue64(s) % kMaxValue); 38 | v.emplace(value); 39 | } 40 | } 41 | TypeStats::_stats.end(); 42 | PrintStats(state); 43 | } 44 | 45 | template 46 | void EraseRandom(benchmark::State &state) { 47 | TypeStats::_stats = TypeStats(); 48 | uint32_t s = 0; 49 | SetType elems; 50 | using ValueType = typename SetType::value_type; 51 | std::vector remainingElems; 52 | for (uint32_t i = 0; i < InitNbInserts; ++i) { 53 | elems.emplace(i); 54 | if (remainingElems.empty()) { 55 | remainingElems.emplace_back(i); 56 | } else { 57 | remainingElems.emplace(remainingElems.begin() + (HashValue64(++s) % remainingElems.size()), i); 58 | } 59 | } 60 | TypeStats::_stats.start(); 61 | for (auto _ : state) { 62 | SetType v = elems; 63 | while (!remainingElems.empty() && !v.empty()) { 64 | ValueType elemToRemove = std::move(remainingElems.back()); 65 | remainingElems.pop_back(); 66 | v.erase(elemToRemove); 67 | } 68 | } 69 | TypeStats::_stats.end(); 70 | PrintStats(state); 71 | } 72 | 73 | template 74 | void LookUp(benchmark::State &state) { 75 | TypeStats::_stats = TypeStats(); 76 | uint32_t s = 0; 77 | SetType elems; 78 | using ValueType = typename SetType::value_type; 79 | for (uint32_t i = 0; i < Size; ++i) { 80 | elems.emplace(i); 81 | } 82 | TypeStats::_stats.start(); 83 | uint32_t out = 0; 84 | for (auto _ : state) { 85 | ValueType vToLookFor(HashValue64(++s) % Size); 86 | if (elems.find(vToLookFor) != elems.end()) { 87 | ++out; 88 | } 89 | benchmark::DoNotOptimize(out); 90 | } 91 | TypeStats::_stats.end(); 92 | PrintStats(state); 93 | } 94 | 95 | template 96 | void CommonUsage(benchmark::State &state) { 97 | TypeStats::_stats = TypeStats(); 98 | TypeStats::_stats.start(); 99 | uint32_t out = 0; 100 | uint32_t s = 0; 101 | for (auto _ : state) { 102 | SetType v; 103 | for (; v.size() < TypicalMaxSize; ++s) { 104 | uint32_t value = static_cast(HashValue64(s) % TypicalMaxSize); 105 | v.emplace(value); 106 | } 107 | auto it = v.find(HashValue64(++s) % TypicalMaxSize); 108 | if (it != v.end()) { 109 | v.erase(it); 110 | } 111 | for (const auto &el : v) { 112 | out += uint32_t(el); 113 | } 114 | } 115 | benchmark::DoNotOptimize(out); 116 | TypeStats::_stats.end(); 117 | PrintStats(state); 118 | } 119 | 120 | } // namespace 121 | 122 | BENCHMARK_TEMPLATE(InsertRandom, REFRelocType); 123 | BENCHMARK_TEMPLATE(InsertRandom, AMCRelocType); 124 | 125 | BENCHMARK_TEMPLATE(EraseRandom, REFRelocType, 1000); 126 | BENCHMARK_TEMPLATE(EraseRandom, AMCRelocType, 1000); 127 | 128 | BENCHMARK_TEMPLATE(LookUp, REFRelocType, 1000000); 129 | BENCHMARK_TEMPLATE(LookUp, AMCRelocType, 1000000); 130 | 131 | BENCHMARK_TEMPLATE(InsertRandom, REFNonRelocType); 132 | BENCHMARK_TEMPLATE(InsertRandom, AMCNonRelocType); 133 | 134 | BENCHMARK_TEMPLATE(EraseRandom, REFNonRelocType, 1000); 135 | BENCHMARK_TEMPLATE(EraseRandom, AMCNonRelocType, 1000); 136 | 137 | BENCHMARK_TEMPLATE(LookUp, REFNonRelocType, 100000); 138 | BENCHMARK_TEMPLATE(LookUp, AMCNonRelocType, 100000); 139 | 140 | BENCHMARK_TEMPLATE(InsertRandom, REFInt); 141 | BENCHMARK_TEMPLATE(InsertRandom, REFUnoInt); 142 | BENCHMARK_TEMPLATE(InsertRandom, AMCInt); 143 | 144 | BENCHMARK_TEMPLATE(EraseRandom, REFInt, 100000); 145 | BENCHMARK_TEMPLATE(EraseRandom, REFUnoInt, 100000); 146 | BENCHMARK_TEMPLATE(EraseRandom, AMCInt, 100000); 147 | 148 | BENCHMARK_TEMPLATE(LookUp, REFInt, 100000); 149 | BENCHMARK_TEMPLATE(LookUp, REFUnoInt, 100000); 150 | BENCHMARK_TEMPLATE(LookUp, AMCInt, 100000); 151 | 152 | #ifdef AMC_SMALLSET 153 | BENCHMARK_TEMPLATE(CommonUsage, amc::SmallSet, 50); 154 | BENCHMARK_TEMPLATE(CommonUsage, std::unordered_set, 50); 155 | #endif 156 | } // namespace amc 157 | 158 | BENCHMARK_MAIN(); 159 | -------------------------------------------------------------------------------- /benchmark/vectors_benchmark.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "benchhelpers.hpp" 12 | #include "testhelpers.hpp" 13 | #include "testtypes.hpp" 14 | 15 | namespace amc { 16 | 17 | TypeStats TypeStats::_stats; 18 | 19 | namespace { 20 | 21 | using AMCRelocType = amc::vector; 22 | using AMCNonRelocType = amc::vector; 23 | using AMCInt = amc::vector; 24 | 25 | using REFRelocType = std::vector; 26 | using REFNonRelocType = std::vector; 27 | using REFInt = std::vector; 28 | 29 | template 30 | void InsertNElemsRandom(benchmark::State &state) { 31 | TypeStats::_stats = TypeStats(); 32 | VecType v(1, 0); 33 | uint32_t s = 2U; 34 | TypeStats::_stats.start(); 35 | for (auto _ : state) { 36 | uint32_t i = s % 20U; 37 | uint32_t oldSize = static_cast(v.size()); 38 | uintmax_t hash = HashValue64(i); 39 | uint32_t count = static_cast(hash % static_cast(2)); 40 | uint32_t value = static_cast(hash % kMaxValue); 41 | v.insert(v.begin() + (hash % oldSize), count, value); 42 | v.push_back(value); 43 | v.pop_back(); 44 | v.insert(v.end() - 1, value); 45 | v.erase(v.end() - 2); 46 | v.erase(v.end() - (v.size() - oldSize), v.end()); 47 | v.push_back(value); 48 | ++s; 49 | } 50 | TypeStats::_stats.end(); 51 | PrintStats(state); 52 | } 53 | 54 | template 55 | void InsertFromPointerRandom(benchmark::State &state) { 56 | using ValueType = typename VecType::value_type; 57 | TypeStats::_stats = TypeStats(); 58 | VecType v(1, 0); 59 | std::array kTab; 60 | std::iota(kTab.begin(), kTab.end(), 10); 61 | uint32_t s = 0; 62 | for (auto _ : state) { 63 | uint32_t i = s % 20U; 64 | uint32_t oldSize = static_cast(v.size()); 65 | uintmax_t hash = HashValue64(i); 66 | TypeStats::_stats.start(); 67 | v.insert(v.begin() + (hash % v.size()), kTab.begin() + (hash % kTab.size()), kTab.end()); 68 | v.erase(v.end() - (v.size() - oldSize), v.end()); 69 | v.push_back(hash % kMaxValue); 70 | TypeStats::_stats.end(); 71 | ++s; 72 | } 73 | PrintStats(state); 74 | } 75 | 76 | template 77 | void InsertFromForwardItRandom(benchmark::State &state) { 78 | using ValueType = typename VecType::value_type; 79 | TypeStats::_stats = TypeStats(); 80 | VecType v(1, 0); 81 | std::array kTab; 82 | std::set kSet; 83 | std::iota(kTab.begin(), kTab.end(), 10); 84 | kSet.insert(kTab.begin(), kTab.end()); 85 | uint32_t s = 0; 86 | for (auto _ : state) { 87 | uint32_t i = s % 20U; 88 | uint64_t hashs = HashValue64(i); 89 | TypeStats::_stats.start(); 90 | typename std::set::const_iterator first = std::next(kSet.begin(), hashs % kSet.size()); 91 | auto nElemsToInsert = std::distance(first, kSet.end()); 92 | uint64_t insertPos = hashs % v.size(); 93 | v.insert(v.begin() + insertPos, first, kSet.end()); 94 | typename VecType::iterator vpos = v.begin() + insertPos; 95 | v.erase(vpos, vpos + nElemsToInsert); 96 | v.push_back(i); 97 | TypeStats::_stats.end(); 98 | ++s; 99 | } 100 | PrintStats(state); 101 | } 102 | 103 | template 104 | void EraseRandom(benchmark::State &state) { 105 | TypeStats::_stats = TypeStats(); 106 | VecType v(1, 0); 107 | uint32_t s = 2U; 108 | for (auto _ : state) { 109 | uint32_t i = s % 20U; 110 | uint32_t oldSize = static_cast(v.size()); 111 | uintmax_t hashs = HashValue64(i); 112 | uint32_t count = static_cast(hashs % static_cast(s)); 113 | uint32_t value = static_cast(hashs % kMaxValue); 114 | TypeStats::_stats.start(); 115 | v.insert(v.end(), count, value); 116 | v.erase(v.begin(), v.begin() + (v.size() - oldSize)); 117 | v.push_back(oldSize % kMaxValue); 118 | TypeStats::_stats.end(); 119 | ++s; 120 | } 121 | PrintStats(state); 122 | } 123 | 124 | template 125 | void AssignRandom(benchmark::State &state) { 126 | using ValueType = typename VecType::value_type; 127 | TypeStats::_stats = TypeStats(); 128 | VecType v; 129 | std::iota(v.begin(), v.end(), 0); 130 | std::array kTab; 131 | std::iota(kTab.begin(), kTab.end(), 1); 132 | uint32_t s = 0; 133 | for (auto _ : state) { 134 | uint32_t i = s % 20U; 135 | uintmax_t hashs = HashValue64(i); 136 | TypeStats::_stats.start(); 137 | if (hashs % 2 == 0) { 138 | v.assign(kTab.begin() + (hashs % kTab.size()), kTab.end()); 139 | } else { 140 | v.assign(i, kTab[hashs % kTab.size()]); 141 | } 142 | TypeStats::_stats.end(); 143 | ++s; 144 | } 145 | PrintStats(state); 146 | } 147 | 148 | template 149 | void SwapRandom(benchmark::State &state) { 150 | TypeStats::_stats = TypeStats(); 151 | VecType v; 152 | uint32_t s = 0; 153 | for (auto _ : state) { 154 | uint32_t i = 10U + s % 20U; 155 | VecType v2(i, 0); 156 | std::iota(v2.begin(), v2.end(), 10); 157 | TypeStats::_stats.start(); 158 | v2.swap(v); 159 | TypeStats::_stats.end(); 160 | ++s; 161 | } 162 | PrintStats(state); 163 | } 164 | 165 | template 166 | void Growing(benchmark::State &state) { 167 | using SizeType = typename VecType::size_type; 168 | TypeStats::_stats = TypeStats(); 169 | 170 | const SizeType kMaxSize = 1000000U; 171 | TypeStats::_stats.start(); 172 | for (auto _ : state) { 173 | VecType v; 174 | for (SizeType s = 0; s < kMaxSize; s = v.size()) { 175 | SizeType i = 1U + v.size() / 8U; 176 | uint32_t value = static_cast(HashValue64(i) % kMaxValue); 177 | v.emplace_back(value); 178 | } 179 | } 180 | TypeStats::_stats.end(); 181 | 182 | PrintStats(state); 183 | } 184 | 185 | template 186 | void CommonUsage(benchmark::State &state) { 187 | using ValueType = typename VecType::value_type; 188 | TypeStats::_stats = TypeStats(); 189 | 190 | uint32_t seed = 0; 191 | for (auto _ : state) { 192 | VecType v; 193 | for (uint32_t s = 0; s < TypicalMaxSize; s = v.size()) { 194 | auto i = 1U + v.size() / 8U; 195 | TypeStats::_stats.start(); 196 | uint64_t value = HashValue64(++seed); 197 | switch (value % 5) { 198 | case 0: 199 | v.insert(v.end(), i, static_cast(value % kMaxValue)); 200 | break; 201 | case 1: 202 | if (v.size() > 1) { 203 | v.erase(v.begin()); 204 | } else { 205 | v.emplace_back(value % kMaxValue); 206 | } 207 | break; 208 | default: 209 | v.emplace_back(value % kMaxValue); 210 | break; 211 | } 212 | int sum = 0; 213 | for (ValueType e : v) { 214 | sum += e; 215 | } 216 | v.back() = sum % kMaxValue; 217 | } 218 | } 219 | TypeStats::_stats.end(); 220 | PrintStats(state); 221 | } 222 | 223 | } // namespace 224 | 225 | BENCHMARK_TEMPLATE(AssignRandom, REFRelocType); 226 | BENCHMARK_TEMPLATE(AssignRandom, AMCRelocType); 227 | 228 | BENCHMARK_TEMPLATE(SwapRandom, REFRelocType); 229 | BENCHMARK_TEMPLATE(SwapRandom, AMCRelocType); 230 | 231 | BENCHMARK_TEMPLATE(EraseRandom, REFRelocType); 232 | BENCHMARK_TEMPLATE(EraseRandom, AMCRelocType); 233 | 234 | BENCHMARK_TEMPLATE(InsertNElemsRandom, REFRelocType); 235 | BENCHMARK_TEMPLATE(InsertNElemsRandom, AMCRelocType); 236 | 237 | BENCHMARK_TEMPLATE(InsertFromPointerRandom, REFRelocType); 238 | BENCHMARK_TEMPLATE(InsertFromPointerRandom, AMCRelocType); 239 | 240 | BENCHMARK_TEMPLATE(InsertFromForwardItRandom, REFRelocType); 241 | BENCHMARK_TEMPLATE(InsertFromForwardItRandom, AMCRelocType); 242 | 243 | BENCHMARK_TEMPLATE(Growing, REFRelocType); 244 | BENCHMARK_TEMPLATE(Growing, AMCRelocType); 245 | 246 | BENCHMARK_TEMPLATE(AssignRandom, REFInt); 247 | BENCHMARK_TEMPLATE(AssignRandom, AMCInt); 248 | 249 | BENCHMARK_TEMPLATE(SwapRandom, REFInt); 250 | BENCHMARK_TEMPLATE(SwapRandom, AMCInt); 251 | 252 | BENCHMARK_TEMPLATE(EraseRandom, REFInt); 253 | BENCHMARK_TEMPLATE(EraseRandom, AMCInt); 254 | 255 | BENCHMARK_TEMPLATE(InsertNElemsRandom, REFInt); 256 | BENCHMARK_TEMPLATE(InsertNElemsRandom, AMCInt); 257 | 258 | BENCHMARK_TEMPLATE(InsertFromPointerRandom, REFInt); 259 | BENCHMARK_TEMPLATE(InsertFromPointerRandom, AMCInt); 260 | 261 | BENCHMARK_TEMPLATE(InsertFromForwardItRandom, REFInt); 262 | BENCHMARK_TEMPLATE(InsertFromForwardItRandom, AMCInt); 263 | 264 | BENCHMARK_TEMPLATE(Growing, REFInt); 265 | BENCHMARK_TEMPLATE(Growing, AMCInt); 266 | 267 | BENCHMARK_TEMPLATE(AssignRandom, REFNonRelocType); 268 | BENCHMARK_TEMPLATE(AssignRandom, AMCNonRelocType); 269 | 270 | BENCHMARK_TEMPLATE(SwapRandom, REFNonRelocType); 271 | BENCHMARK_TEMPLATE(SwapRandom, AMCNonRelocType); 272 | 273 | BENCHMARK_TEMPLATE(EraseRandom, REFNonRelocType); 274 | BENCHMARK_TEMPLATE(EraseRandom, AMCNonRelocType); 275 | 276 | BENCHMARK_TEMPLATE(InsertNElemsRandom, REFNonRelocType); 277 | BENCHMARK_TEMPLATE(InsertNElemsRandom, AMCNonRelocType); 278 | 279 | BENCHMARK_TEMPLATE(InsertFromPointerRandom, REFNonRelocType); 280 | BENCHMARK_TEMPLATE(InsertFromPointerRandom, AMCNonRelocType); 281 | 282 | BENCHMARK_TEMPLATE(InsertFromForwardItRandom, REFNonRelocType); 283 | BENCHMARK_TEMPLATE(InsertFromForwardItRandom, AMCNonRelocType); 284 | 285 | BENCHMARK_TEMPLATE(Growing, REFNonRelocType); 286 | BENCHMARK_TEMPLATE(Growing, AMCNonRelocType); 287 | 288 | BENCHMARK_TEMPLATE(CommonUsage, amc::vector, 30); 289 | BENCHMARK_TEMPLATE(CommonUsage, amc::SmallVector, 30); 290 | BENCHMARK_TEMPLATE(CommonUsage, amc::FixedCapacityVector, 30); 291 | 292 | BENCHMARK_TEMPLATE(CommonUsage, amc::vector, 60); 293 | BENCHMARK_TEMPLATE(CommonUsage, amc::SmallVector, 60); 294 | BENCHMARK_TEMPLATE(CommonUsage, amc::FixedCapacityVector, 60); 295 | 296 | BENCHMARK_TEMPLATE(CommonUsage, amc::vector, 100); 297 | BENCHMARK_TEMPLATE(CommonUsage, amc::SmallVector, 100); 298 | 299 | } // namespace amc 300 | 301 | BENCHMARK_MAIN(); 302 | -------------------------------------------------------------------------------- /cmake/amcConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 4 | check_required_components("@PROJECT_NAME@") -------------------------------------------------------------------------------- /docs/set_bench_int.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | Insert 47 | Erase 48 | LookUp 49 | 10 50 | 100 51 | 1000 52 | 10000 53 | 100000 54 | 1000000 55 | 10000000 56 | Set operation time for int 57 | (less is better) 58 | 59 | 60 | 61 | std::set 62 | amc::FlatSet 63 | std::unordered_set 64 | Operation 65 | time (ns) 66 | -------------------------------------------------------------------------------- /docs/set_bench_reloctype.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | Insert 35 | Erase 36 | LookUp 37 | 100 38 | 1000 39 | 10000 40 | 100000 41 | Set operation time for complex trivially relocatable type 42 | (less is better) 43 | 44 | 45 | std::set 46 | amc::FlatSet 47 | Operation 48 | time (ns) 49 | -------------------------------------------------------------------------------- /docs/vector_bench_reloctype.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | InsertNElems 45 | InsertFromPtr 46 | InsertFromForwardIt 47 | Erase 48 | 0 49 | 50000 50 | 100000 51 | 150000 52 | 200000 53 | 250000 54 | Vector operation time for trivially relocatable types 55 | (less is better) 56 | 57 | 58 | std::vector 59 | amc::vector 60 | Operation 61 | time (ns) 62 | -------------------------------------------------------------------------------- /include/amc/algorithm.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "config.hpp" 6 | 7 | #if AMC_CXX20 8 | #include 9 | #endif 10 | 11 | namespace amc { 12 | 13 | #if AMC_CXX20 14 | #if !defined(_LIBCPP_VERSION) || _LIBCPP_VERSION >= 170000 15 | using std::lexicographical_compare_three_way; 16 | #else 17 | // Adjusted from https://en.cppreference.com/w/cpp/algorithm/lexicographical_compare_three_way 18 | template 19 | constexpr auto lexicographical_compare_three_way(I1 f1, I1 l1, I2 f2, I2 l2, Cmp comp) -> decltype(comp(*f1, *f2)) { 20 | using ret_t = decltype(comp(*f1, *f2)); 21 | static_assert(std::disjunction_v, std::is_same, 22 | std::is_same >, 23 | "The return type must be a comparison category type."); 24 | 25 | for (; f1 != l1; ++f1, ++f2) { 26 | if (f2 == l2) return std::strong_ordering::greater; 27 | if (auto c = comp(*f1, *f2); c != 0) return c; 28 | } 29 | return (f2 == l2) <=> true; 30 | } 31 | #if _LIBCPP_VERSION >= 14000 32 | using std::compare_three_way; 33 | #else 34 | struct compare_three_way { 35 | using is_transparent = void; 36 | template 37 | constexpr auto operator()(T&& t, U&& u) const noexcept(noexcept(std::declval() <=> std::declval())) { 38 | return static_cast(t) <=> static_cast(u); 39 | } 40 | }; 41 | #endif 42 | template 43 | constexpr auto lexicographical_compare_three_way(I1 f1, I1 l1, I2 f2, I2 l2) { 44 | return lexicographical_compare_three_way(f1, l1, f2, l2, amc::compare_three_way()); 45 | } 46 | #endif 47 | #endif 48 | 49 | } // namespace amc 50 | -------------------------------------------------------------------------------- /include/amc/allocator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "config.hpp" 9 | #include "memory.hpp" 10 | #include "type_traits.hpp" 11 | 12 | namespace amc { 13 | 14 | /** 15 | * Adaptor that wraps a singleton class Alloc into a "basic" allocator. 16 | * Alloc is expected to model the "basic" allocator concept and to provide 17 | * a static instance() method to access singleton. 18 | * 19 | * A basic allocator is a class that provides methods: 20 | * void *allocate(size_t n) 21 | * void *reallocate(void *p, size_t oldSz, size_t newSz) 22 | * void deallocate(void *p, size_t n) 23 | */ 24 | template 25 | class BasicSingletonAllocatorAdaptor { 26 | public: 27 | void *allocate(size_t n) { return Alloc::instance().allocate(n); } 28 | void *reallocate(void *p, size_t oldSz, size_t newSz) { return Alloc::instance().reallocate(p, oldSz, newSz); } 29 | void deallocate(void *p, size_t n) { Alloc::instance().deallocate(p, n); } 30 | }; 31 | 32 | /** 33 | * Creates a standard (STL conformant) allocator from a 'basic allocator' providing an extra 'reallocate' method. 34 | * 35 | * A basic allocator is a class that provides methods: 36 | * void *allocate(size_t n) 37 | * void *reallocate(void *p, size_t oldSz, size_t newSz) 38 | * void deallocate(void *p, size_t n) 39 | * 40 | * If type T is trivially relocatable, 'reallocate' will be optimized into a call to 'realloc', 41 | * otherwise it will simply allocate the new block and relocate all elements into it. 42 | */ 43 | template 44 | class BasicAllocatorWrapper : private BasicAllocator { 45 | public: 46 | using value_type = T; 47 | using size_type = size_t; 48 | using difference_type = ptrdiff_t; 49 | using pointer = T *; 50 | using const_pointer = const T *; 51 | using reference = T &; 52 | using const_reference = const T &; 53 | 54 | BasicAllocatorWrapper() = default; 55 | 56 | BasicAllocatorWrapper(const BasicAllocatorWrapper &) = default; 57 | BasicAllocatorWrapper(BasicAllocatorWrapper &&) = default; 58 | BasicAllocatorWrapper &operator=(const BasicAllocatorWrapper &) noexcept = default; 59 | BasicAllocatorWrapper &operator=(BasicAllocatorWrapper &&) noexcept = default; 60 | 61 | template 62 | BasicAllocatorWrapper(const BasicAllocatorWrapper &o) : BasicAllocator(o) {} 63 | 64 | pointer address(reference r) const noexcept { return std::addressof(r); } 65 | const_pointer address(const_reference r) const noexcept { return std::addressof(r); } 66 | 67 | pointer allocate(size_type n, const_pointer = 0) { 68 | return static_cast(BasicAllocator::allocate(n * sizeof(value_type))); 69 | } 70 | 71 | pointer reallocate(pointer p, size_type oldCapacity, size_type newCapacity, size_type nConstructedElems) { 72 | return Reallocate(*this, p, oldCapacity, newCapacity, nConstructedElems); 73 | } 74 | 75 | void deallocate(pointer p, size_type s) { BasicAllocator::deallocate(p, s * sizeof(T)); } 76 | 77 | constexpr size_type max_size() const { return static_cast(-1) / sizeof(value_type); } 78 | 79 | template 80 | void construct(U *p, Args &&...args) { 81 | amc::construct_at(p, std::forward(args)...); 82 | } 83 | 84 | template 85 | void destroy(U *p) { 86 | amc::destroy_at(p); 87 | } 88 | 89 | template 90 | struct rebind { 91 | using other = BasicAllocatorWrapper; 92 | }; 93 | 94 | template 95 | constexpr bool operator==(const BasicAllocatorWrapper &) const { 96 | return std::is_empty::value; 97 | } 98 | 99 | template 100 | constexpr bool operator!=(const BasicAllocatorWrapper &rhs) const { 101 | return !(*this == rhs); 102 | } 103 | 104 | private: 105 | template 106 | friend class BasicAllocatorWrapper; 107 | 108 | template 109 | static typename std::enable_if::value, T *>::type Reallocate( 110 | BasicAllocator &basicAlloc, V *p, size_t oldCapacity, size_t newCapacity, size_t nConstructedElems) { 111 | T *newPtr = static_cast(basicAlloc.allocate(newCapacity * sizeof(T))); 112 | amc::uninitialized_relocate_n(p, nConstructedElems, newPtr); 113 | basicAlloc.deallocate(p, oldCapacity * sizeof(T)); 114 | return newPtr; 115 | } 116 | 117 | template 118 | static typename std::enable_if::value, T *>::type Reallocate( 119 | BasicAllocator &basicAlloc, V *p, size_t oldCapacity, size_t newCapacity, size_t) { 120 | return static_cast(basicAlloc.reallocate(p, oldCapacity * sizeof(T), newCapacity * sizeof(T))); 121 | } 122 | }; 123 | 124 | template 125 | struct BasicAllocatorWrapper { 126 | using value_type = void; 127 | using size_type = size_t; 128 | using difference_type = ptrdiff_t; 129 | using pointer = void *; 130 | using const_pointer = const void *; 131 | 132 | template 133 | struct rebind { 134 | using other = BasicAllocatorWrapper; 135 | }; 136 | }; 137 | 138 | /** 139 | * Wrapper around std::allocator that models the "basic" allocator concept. 140 | * A basic allocator is a class that provides methods: 141 | * void * allocate(size_t n) 142 | * void *reallocate(void *p, size_t oldSz, size_t newSz) 143 | * void deallocate(void *p, size_t n) 144 | */ 145 | struct SimpleAllocator { 146 | void *allocate(size_t n) { 147 | void *ptr = malloc(n); 148 | if (AMC_UNLIKELY(!ptr)) { 149 | throw std::bad_alloc(); 150 | } 151 | return ptr; 152 | } 153 | 154 | void *reallocate(void *p, size_t, size_t newSz) { 155 | p = realloc(p, newSz); 156 | if (AMC_UNLIKELY(!p)) { 157 | throw std::bad_alloc(); 158 | } 159 | return p; 160 | } 161 | 162 | void deallocate(void *p, size_t) { free(p); } 163 | }; 164 | 165 | /** 166 | * Standard (STL conformant) allocator using std::allocator providing an extra 'reallocate' method. 167 | * 168 | * If type T is trivially relocatable, 'reallocate' will be optimized into a call to 'realloc', 169 | * otherwise it will simply allocate the new block and relocate all elements into it. 170 | */ 171 | template 172 | using allocator = BasicAllocatorWrapper; 173 | } // namespace amc 174 | -------------------------------------------------------------------------------- /include/amc/config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #if defined(__clang__) && defined(__clang_minor__) 6 | #define AMC_CLANG (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) 7 | #elif defined(__GNUC__) && defined(__GNUC_MINOR__) && defined(__GNUC_PATCHLEVEL__) 8 | #define AMC_GCC (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) 9 | #endif 10 | 11 | #if defined(AMC_GCC) && AMC_GCC < 40600 12 | #define AMC_PUSH_WARNING 13 | #define AMC_POP_WARNING 14 | #else 15 | #define AMC_PUSH_WARNING _Pragma("GCC diagnostic push") 16 | #define AMC_POP_WARNING _Pragma("GCC diagnostic pop") 17 | #endif 18 | #define AMC_DISABLE_WARNING_INTERNAL(warningName) #warningName 19 | #define AMC_DISABLE_WARNING(warningName) _Pragma(AMC_DISABLE_WARNING_INTERNAL(GCC diagnostic ignored warningName)) 20 | #ifdef AMC_CLANG 21 | #define AMC_CLANG_DISABLE_WARNING(warningName) AMC_DISABLE_WARNING(warningName) 22 | #define AMC_GCC_DISABLE_WARNING(warningName) 23 | #else 24 | #define AMC_CLANG_DISABLE_WARNING(warningName) 25 | #define AMC_GCC_DISABLE_WARNING(warningName) AMC_DISABLE_WARNING(warningName) 26 | #endif 27 | 28 | #if defined(__GNUC__) 29 | #define AMC_LIKELY(x) (__builtin_expect(!!(x), 1)) 30 | #define AMC_UNLIKELY(x) (__builtin_expect(!!(x), 0)) 31 | #else 32 | #define AMC_LIKELY(x) (!!(x)) 33 | #define AMC_UNLIKELY(x) (!!(x)) 34 | #endif 35 | 36 | #if defined(_MSC_VER) 37 | 38 | #define AMC_CXX14 1 39 | 40 | #if _MSVC_LANG >= 201703L 41 | #define AMC_CXX17 1 42 | #define AMC_SMALLSET 1 43 | #endif 44 | 45 | #if _MSVC_LANG >= 202002L 46 | #define AMC_CXX20 1 47 | #endif 48 | 49 | #if _MSVC_LANG > 202002L 50 | #define AMC_CXX23 1 51 | #endif 52 | 53 | #else 54 | #if __cplusplus >= 201402L 55 | #define AMC_CXX14 1 56 | #endif 57 | 58 | #if __cplusplus >= 201703L 59 | #define AMC_CXX17 1 60 | #define AMC_SMALLSET 1 61 | #endif 62 | 63 | #if __cplusplus >= 202002L 64 | #define AMC_CXX20 1 65 | #endif 66 | 67 | #if __cplusplus > 202002L 68 | #define AMC_CXX23 1 69 | #endif 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /include/amc/fixedcapacityvector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "config.hpp" 10 | #include "vectorcommon.hpp" 11 | 12 | namespace amc { 13 | namespace vec { 14 | 15 | /// Wrapper to get the smallest needed type for holding a maximum number of N elements 16 | template 17 | struct SmallestSizeType { 18 | // clang-format off 19 | #ifdef AMC_CXX14 20 | // this is to avoid potential harmless warning occurring for instance in GCC: 21 | // warning: comparison is always false due to limited range of data type [-Wtype-limits] 22 | // Activated only in C++14 as we need it to be constexpr 23 | using type = typename std::conditional()(N, std::numeric_limits::max()), uint8_t, 24 | typename std::conditional()(N, std::numeric_limits::max()), uint16_t, 25 | typename std::conditional()(N, std::numeric_limits::max()), uint32_t, 26 | uint64_t>::type>::type>::type; 27 | #else 28 | using type = typename std::conditional::max(), uint8_t, 29 | typename std::conditional::max(), uint16_t, 30 | typename std::conditional::max(), uint32_t, 31 | uint64_t>::type>::type>::type; 32 | #endif 33 | // clang-format on 34 | }; 35 | 36 | /// Throw exception in the case we attempt to exceed inplace capacity (default mode) 37 | struct ExceptionGrowingPolicy { 38 | static inline void Check(uintmax_t capacity, uintmax_t maxCapacity) { 39 | if (AMC_UNLIKELY(maxCapacity < capacity)) { 40 | throw std::out_of_range("Growing is not possible"); 41 | } 42 | } 43 | }; 44 | 45 | /// Abort the program in the case we attempt to exceed inplace capacity if assertions are enable only 46 | struct UncheckedGrowingPolicy { 47 | static inline void Check(uintmax_t capacity, uintmax_t maxCapacity) { 48 | (void)capacity; 49 | (void)maxCapacity; 50 | assert(capacity <= maxCapacity); 51 | } 52 | }; 53 | } // namespace vec 54 | 55 | /** 56 | * Vector-like container whose maximum number of elements cannot exceed N. 57 | * 58 | * It does not make dynamic memory allocations which, in addition of the obvious performance boost, 59 | * provides the following benefits: 60 | * - begin() is never invalidated 61 | * - iterators before any insert / erase are never invalidated 62 | * - reserve / shrink_to_fit are not needed (calling the first one is a capacity check, second one a no-op) 63 | * - vector is *trivially destructible* if T is 64 | * 65 | * If new element is about to be inserted in a full Vector, behavior can be controlled thanks to provided 66 | * 'GrowingPolicy': 67 | * - ExceptionGrowingPolicy: throw 'std::out_of_range' exception (default) 68 | * - UncheckedGrowingPolicy: assert check (nothing is done in Release, invoking undefined behavior, abort will be 69 | * called in Debug). 70 | * 71 | * API is compliant to C++17 std::vector, with the following extra methods: 72 | * - append: shortcut for myVec.insert(myVec.end(), ..., ...) 73 | * - pop_back_val: pop_back and return the popped value. 74 | * - swap2: generalized version of swap usable for all vectors of this library. 75 | * you could swap values from a amc::vector with a FixedCapacityVector for instance, 76 | * all combinations are possible between the 3 types of vectors. 77 | * Note that implementation is not noexcept, adjust capacity needs to be called for both operands. 78 | * 79 | * In addition, size_type can be configured and is the smallest unsigned integral type able to hold N elements. 80 | * 81 | * Exception safety: 82 | * It provides at least Basic exception safety. 83 | * If Object movement is noexcept, most operations provide strong exception safety, excepted: 84 | * - assign 85 | * - insert from InputIt 86 | * - insert from count elements 87 | * If Object movement can throw, only 'push_back' and 'emplace_back' modifiers provide strong exception warranty 88 | */ 89 | template ::type> 91 | using FixedCapacityVector = Vector()>; 92 | 93 | } // namespace amc 94 | -------------------------------------------------------------------------------- /include/amc/flatset.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "allocator.hpp" 11 | #include "config.hpp" 12 | #include "type_traits.hpp" 13 | #include "vector.hpp" 14 | 15 | #ifdef AMC_CXX14 16 | #include "istransparent.hpp" 17 | #ifdef AMC_CXX17 18 | #include 19 | #ifdef AMC_CXX20 20 | #include 21 | #endif 22 | #endif 23 | #endif 24 | namespace amc { 25 | 26 | /** 27 | * Set which keeps its elements into a sorted vector container. 28 | * The disadvantages of a sorted vector are linear-time insertions and linear-time deletions. 29 | * In exchange, a FlatSet offers a significant performance boost for all other operations, 30 | * with a much smaller memory impact. 31 | * 32 | * Detailed features and characteristics: 33 | * - Faster lookup than standard associative containers 34 | * - Much faster iteration than standard associative containers. Random-access iterators instead of bidirectional 35 | * iterators. 36 | * - Less memory consumption 37 | * - Improved cache performance (data is stored in contiguous memory) 38 | * - Non-stable iterators (iterators are invalidated when inserting and erasing elements) 39 | * - Non-copyable and non-movable values types can't be stored 40 | * - Weaker exception safety than standard associative containers (copy/move constructors can throw when shifting values 41 | * in erasures and insertions) 42 | * - Slower insertion and erasure than standard associative containers (specially for non-movable types) 43 | * 44 | * Consider usage of a FlatSet when read-only operations are favored against insertions / deletions. 45 | * 46 | * It does not allow duplicated elements. 47 | * Elements a, b are considered the same if !Compare(a, b) && !Compare(b, a) 48 | */ 49 | template , class Alloc = amc::allocator, class VecType = amc::vector> 50 | class FlatSet : private Compare { 51 | public: 52 | using key_type = T; 53 | using value_type = T; 54 | using difference_type = ptrdiff_t; 55 | using size_type = typename VecType::size_type; 56 | using iterator = typename VecType::const_iterator; 57 | using const_iterator = typename VecType::const_iterator; 58 | using const_reference = typename VecType::const_reference; 59 | using pointer = typename VecType::const_pointer; 60 | using const_pointer = typename VecType::const_pointer; 61 | using key_compare = Compare; 62 | using value_compare = Compare; 63 | using allocator_type = Alloc; 64 | 65 | static_assert(std::is_same::value, "Vector value type should be T"); 66 | static_assert(std::is_same::value, "Allocator should match vector's"); 67 | 68 | #ifdef AMC_CXX17 69 | class node_type : private Alloc { 70 | public: 71 | using value_type = T; 72 | using allocator_type = Alloc; 73 | 74 | node_type() noexcept = default; 75 | 76 | node_type(const node_type &) = delete; 77 | node_type(node_type &&o) noexcept(std::is_nothrow_move_constructible::value) = default; 78 | node_type &operator=(const node_type &) = delete; 79 | node_type &operator=(node_type &&o) noexcept(std::is_nothrow_move_assignable::value) = default; 80 | 81 | ~node_type() = default; 82 | 83 | bool empty() const noexcept { return !_optV.has_value(); } 84 | explicit operator bool() const noexcept { return _optV.has_value(); } 85 | allocator_type get_allocator() const { return static_cast(*this); } 86 | 87 | value_type &value() { return _optV.value(); } 88 | const value_type &value() const { return _optV.value(); } 89 | 90 | void swap(node_type &o) noexcept(std::is_nothrow_move_constructible::value && 91 | amc::is_nothrow_swappable::value) { 92 | _optV.swap(o._optV); 93 | } 94 | 95 | private: 96 | template 97 | friend class FlatSet; 98 | 99 | explicit node_type(Alloc alloc) : Alloc(alloc) {} 100 | explicit node_type(value_type &&v, Alloc alloc = Alloc()) : Alloc(alloc), _optV(std::move(v)) {} 101 | 102 | std::optional _optV; 103 | }; 104 | 105 | struct insert_return_type { 106 | iterator position; 107 | bool inserted; 108 | node_type node; 109 | }; 110 | #endif 111 | 112 | using trivially_relocatable = 113 | typename std::conditional::value && is_trivially_relocatable::value, 114 | std::true_type, std::false_type>::type; 115 | 116 | key_compare key_comp() const { return *this; } 117 | value_compare value_comp() const { return *this; } 118 | allocator_type get_allocator() const { return _sortedVector.get_allocator(); } 119 | 120 | FlatSet() noexcept(std::is_nothrow_default_constructible::value && 121 | std::is_nothrow_default_constructible::value) = default; 122 | 123 | explicit FlatSet(const Compare &comp, const Alloc &alloc = Alloc()) : Compare(comp), _sortedVector(alloc) {} 124 | 125 | explicit FlatSet(const Alloc &alloc) : _sortedVector(alloc) {} 126 | 127 | template 128 | FlatSet(InputIt first, InputIt last, const Compare &comp = Compare(), const Alloc &alloc = Alloc()) 129 | : Compare(comp), _sortedVector(first, last, alloc) { 130 | std::sort(_sortedVector.begin(), _sortedVector.end(), comp); 131 | eraseDuplicates(); 132 | } 133 | 134 | template 135 | FlatSet(InputIt first, InputIt last, const Alloc &alloc) : FlatSet(first, last, Compare(), alloc) {} 136 | 137 | FlatSet(const FlatSet &o, const Alloc &alloc) : Compare(o.key_comp()), _sortedVector(o._sortedVector, alloc) {} 138 | 139 | FlatSet(FlatSet &&o, const Alloc &alloc) : Compare(o.key_comp()), _sortedVector(std::move(o._sortedVector), alloc) {} 140 | 141 | FlatSet(std::initializer_list list, const Compare &comp = Compare(), const Alloc &alloc = Alloc()) 142 | : Compare(comp), _sortedVector(alloc) { 143 | insert(list.begin(), list.end()); 144 | } 145 | 146 | FlatSet(std::initializer_list list, const Alloc &alloc) : FlatSet(list, Compare(), alloc) {} 147 | 148 | #ifdef AMC_NONSTD_FEATURES 149 | using vector_type = VecType; 150 | 151 | /// Non standard constructor of a FlatSet from a Vector, stealing its dynamic memory. 152 | explicit FlatSet(vector_type &&v, const Compare &comp = Compare(), const Alloc &alloc = Alloc()) 153 | : Compare(comp), _sortedVector(std::move(v), alloc) { 154 | std::sort(_sortedVector.begin(), _sortedVector.end(), comp); 155 | eraseDuplicates(); 156 | } 157 | 158 | FlatSet &operator=(vector_type &&v) { 159 | _sortedVector = std::move(v); 160 | std::sort(_sortedVector.begin(), _sortedVector.end(), compRef()); 161 | eraseDuplicates(); 162 | return *this; 163 | } 164 | #endif 165 | 166 | FlatSet &operator=(std::initializer_list list) { 167 | _sortedVector.clear(); 168 | insert(list.begin(), list.end()); 169 | return *this; 170 | } 171 | 172 | const_iterator begin() const noexcept { return _sortedVector.begin(); } 173 | const_iterator end() const noexcept { return _sortedVector.end(); } 174 | const_iterator cbegin() const noexcept { return _sortedVector.begin(); } 175 | const_iterator cend() const noexcept { return _sortedVector.end(); } 176 | 177 | const_reference front() const { return _sortedVector.front(); } 178 | const_reference back() const { return _sortedVector.back(); } 179 | 180 | using reverse_iterator = std::reverse_iterator; 181 | using const_reverse_iterator = std::reverse_iterator; 182 | 183 | const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } 184 | const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } 185 | const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(cend()); } 186 | const_reverse_iterator crend() const noexcept { return const_reverse_iterator(cbegin()); } 187 | 188 | #ifdef AMC_NONSTD_FEATURES 189 | const_pointer data() const noexcept { return _sortedVector.data(); } 190 | 191 | const_reference operator[](size_type idx) const { return _sortedVector[idx]; } 192 | 193 | const_reference at(size_type idx) const { return _sortedVector.at(idx); } 194 | 195 | size_type capacity() const noexcept { return _sortedVector.capacity(); } 196 | void reserve(size_type size) { _sortedVector.reserve(size); } 197 | 198 | void shrink_to_fit() { _sortedVector.shrink_to_fit(); } 199 | #endif 200 | 201 | bool empty() const noexcept { return _sortedVector.empty(); } 202 | size_type size() const noexcept { return _sortedVector.size(); } 203 | size_type max_size() const noexcept { return _sortedVector.max_size(); } 204 | 205 | void clear() noexcept { _sortedVector.clear(); } 206 | 207 | std::pair insert(const T &v) { return insert_val(v); } 208 | 209 | std::pair insert(T &&v) { return insert_val(std::move(v)); } 210 | 211 | iterator insert(const_iterator hint, const T &v) { return insert_hint(hint, v); } 212 | 213 | iterator insert(const_iterator hint, T &&v) { return insert_hint(hint, std::move(v)); } 214 | 215 | template 216 | void insert(InputIt first, InputIt last) { 217 | miterator insertIt = _sortedVector.insert(_sortedVector.end(), first, last); 218 | // sort appended elements only (beginning is already sorted) 219 | std::sort(insertIt, mend(), compRef()); 220 | std::inplace_merge(mbegin(), insertIt, mend(), compRef()); 221 | eraseDuplicates(); 222 | } 223 | 224 | void insert(std::initializer_list ilist) { insert(ilist.begin(), ilist.end()); } 225 | 226 | #ifdef AMC_CXX17 227 | insert_return_type insert(node_type &&nh) { 228 | insert_return_type irt{end(), false, std::move(nh)}; 229 | if (irt.node) { 230 | std::tie(irt.position, irt.inserted) = insert(std::move(*irt.node._optV)); 231 | irt.node._optV = std::nullopt; 232 | } 233 | return irt; 234 | } 235 | 236 | iterator insert(const_iterator hint, node_type &&nh) { 237 | if (nh) { 238 | iterator retIt = insert(hint, std::move(*nh._optV)); 239 | nh._optV = std::nullopt; 240 | return retIt; 241 | } 242 | return end(); 243 | } 244 | #endif 245 | 246 | template 247 | std::pair emplace(Args &&...args) { 248 | return insert(T(std::forward(args)...)); 249 | } 250 | 251 | template 252 | iterator emplace_hint(const_iterator hint, Args &&...args) { 253 | return insert(hint, T(std::forward(args)...)); 254 | } 255 | 256 | const_iterator find(const_reference k) const { 257 | const_iterator lbIt = lower_bound(k); 258 | return lbIt == cend() || compRef()(k, *lbIt) ? cend() : lbIt; 259 | } 260 | 261 | bool contains(const_reference k) const { return find(k) != end(); } 262 | 263 | size_type count(const_reference k) const { return contains(k); } 264 | 265 | #ifdef AMC_CXX14 266 | template ::value && has_is_transparent::value, 267 | bool>::type = true> 268 | const_iterator find(const K &k) const { 269 | const_iterator lbIt = lower_bound(k); 270 | return lbIt == cend() || compRef()(k, *lbIt) ? cend() : lbIt; 271 | } 272 | 273 | template ::value && has_is_transparent::value, 274 | bool>::type = true> 275 | bool contains(const K &k) const { 276 | return find(k) != end(); 277 | } 278 | 279 | template ::value && has_is_transparent::value, 280 | bool>::type = true> 281 | size_type count(const K &k) const { 282 | return contains(k); 283 | } 284 | #endif 285 | 286 | size_type erase(const_reference v) { 287 | const_iterator it = find(v); 288 | if (it == end()) { 289 | return 0U; 290 | } 291 | _sortedVector.erase(it); 292 | return 1U; 293 | } 294 | 295 | iterator erase(const_iterator position) { return _sortedVector.erase(position); } 296 | iterator erase(const_iterator first, const_iterator last) { return _sortedVector.erase(first, last); } 297 | 298 | bool operator==(const FlatSet &o) const { return _sortedVector == o._sortedVector; } 299 | bool operator!=(const FlatSet &o) const { return !(*this == o); } 300 | 301 | #ifdef AMC_CXX20 302 | auto operator<=>(const FlatSet &o) const { return _sortedVector <=> o._sortedVector; } 303 | #else 304 | bool operator<(const FlatSet &o) const { return _sortedVector < o._sortedVector; } 305 | 306 | bool operator<=(const FlatSet &o) const { return !(o < *this); } 307 | bool operator>(const FlatSet &o) const { return o < *this; } 308 | bool operator>=(const FlatSet &o) const { return !(*this < o); } 309 | #endif 310 | 311 | void swap(FlatSet &o) noexcept(noexcept(std::declval().swap(std::declval())) && 312 | amc::is_nothrow_swappable::value) { 313 | std::swap(static_cast(*this), static_cast(o)); 314 | _sortedVector.swap(o._sortedVector); 315 | } 316 | 317 | #ifdef AMC_CXX17 318 | node_type extract(const_iterator position) { 319 | node_type nt(std::move(*const_cast(position)), get_allocator()); 320 | _sortedVector.erase(position); 321 | return nt; 322 | } 323 | 324 | node_type extract(const key_type &key) { 325 | miterator it = mfind(key); 326 | node_type nh(get_allocator()); 327 | if (it != _sortedVector.end()) { 328 | nh._optV = std::move(*it); 329 | _sortedVector.erase(it); 330 | } 331 | return nh; 332 | } 333 | #endif 334 | 335 | template ::value, bool>::type = true> 336 | void merge(FlatSet &o) { 337 | for (miterator oit = o.mbegin(); oit != o.mend();) { 338 | miterator lbIt = std::lower_bound(mbegin(), mend(), *oit, compRef()); 339 | if (lbIt == mend()) { 340 | _sortedVector.push_back(std::move(*oit)); 341 | oit = o._sortedVector.erase(oit); 342 | } else if (compRef()(*oit, *lbIt)) { 343 | _sortedVector.insert(lbIt, std::move(*oit)); 344 | oit = o._sortedVector.erase(oit); 345 | } else { 346 | // equal 347 | ++oit; 348 | } 349 | } 350 | } 351 | 352 | void merge(FlatSet &o) { 353 | // Do not use std::inplace_merge to avoid allocating memory if not needed 354 | miterator first1 = mbegin(), last1 = mend(); 355 | miterator first2 = o.mbegin(), last2 = o.mend(); 356 | while (first2 != last2) { 357 | if (first1 == last1) { 358 | _sortedVector.insert(last1, std::make_move_iterator(first2), std::make_move_iterator(last2)); 359 | o._sortedVector.erase(first2, last2); 360 | break; 361 | } 362 | if (compRef()(*first1, *first2)) { 363 | ++first1; 364 | } else if (compRef()(*first2, *first1)) { 365 | first1 = std::next(_sortedVector.insert(first1, std::move(*first2))); 366 | last1 = mend(); 367 | first2 = o._sortedVector.erase(first2); 368 | --last2; 369 | } else { 370 | // equal 371 | ++first1; 372 | ++first2; 373 | } 374 | } 375 | } 376 | 377 | std::pair equal_range(const key_type &key) const { 378 | const_iterator first = find(key); 379 | const_iterator second = first != end() ? std::next(first) : end(); 380 | return std::pair(first, second); 381 | } 382 | 383 | const_iterator lower_bound(const_reference v) const { return std::lower_bound(begin(), end(), v, compRef()); } 384 | const_iterator upper_bound(const_reference v) const { return std::upper_bound(begin(), end(), v, compRef()); } 385 | 386 | #ifdef AMC_CXX14 387 | template ::value && has_is_transparent::value, 388 | bool>::type = true> 389 | const_iterator lower_bound(const K &k) const { 390 | return std::lower_bound(begin(), end(), k, compRef()); 391 | } 392 | 393 | template ::value && has_is_transparent::value, 394 | bool>::type = true> 395 | const_iterator upper_bound(const K &k) const { 396 | return std::upper_bound(begin(), end(), k, compRef()); 397 | } 398 | #endif 399 | 400 | #ifdef AMC_NONSTD_FEATURES 401 | /// Get the underlying vector of elements of this FlatSet, by returning a newly move constructed vector. 402 | vector_type steal_vector() { 403 | vector_type ret(std::move(_sortedVector)); 404 | // Make sure our sorted vector is cleared to avoid unspecified state (not necessarily in order) 405 | _sortedVector.clear(); 406 | return ret; 407 | } 408 | #endif 409 | 410 | #ifdef AMC_CXX20 411 | template 412 | friend size_type erase_if(FlatSet &c, Pred pred) { 413 | return erase_if(c._sortedVector, pred); 414 | } 415 | #endif 416 | 417 | private: 418 | template 419 | friend class FlatSet; 420 | 421 | using miterator = typename VecType::iterator; // Custom non const iterator to allow modification of content 422 | 423 | miterator mbegin() noexcept { return _sortedVector.begin(); } 424 | miterator mend() noexcept { return _sortedVector.end(); } 425 | 426 | miterator mfind(const_reference v) { 427 | miterator lbIt = std::lower_bound(mbegin(), mend(), v, compRef()); 428 | return lbIt == mend() || compRef()(v, *lbIt) ? mend() : lbIt; 429 | } 430 | 431 | template 432 | std::pair insert_val(V &&v) { 433 | miterator insertIt = std::lower_bound(mbegin(), mend(), v, compRef()); 434 | bool newValue = insertIt == mend() || compRef()(v, *insertIt); 435 | if (newValue) { 436 | insertIt = _sortedVector.insert(insertIt, std::forward(v)); 437 | } 438 | return std::pair(insertIt, newValue); 439 | } 440 | 441 | template 442 | iterator insert_hint(const_iterator hint, V &&v) { 443 | const_iterator b = begin(); 444 | const_iterator e = end(); 445 | assert(hint >= b && hint <= e); 446 | if (hint == e || !compRef()(*hint, v)) { // [v, right side) is sorted 447 | const_iterator prevIt = b == e ? e : std::next(hint, -1); 448 | if (hint == b || !compRef()(v, *prevIt)) { // (left side, v] is sorted 449 | // hint is correct, but we need to check if equal 450 | if (hint != e && !compRef()(v, *hint)) { 451 | // *hint == v, do not insert v, return iterator pointing to value that prevented the insert 452 | return hint; 453 | } 454 | if (hint != b && !compRef()(*prevIt, v)) { 455 | // *prevIt == v, do not insert v, return iterator pointing to value that prevented the insert 456 | return prevIt; 457 | } 458 | // hint is correct and element not already present, insert 459 | return _sortedVector.insert(hint, std::forward(v)); 460 | } else { 461 | const_iterator insertIt = std::lower_bound(b, prevIt, v, compRef()); 462 | if (insertIt == prevIt || compRef()(v, *insertIt)) { 463 | return _sortedVector.insert(insertIt, std::forward(v)); 464 | } 465 | return insertIt; // no insert as equal elements 466 | } 467 | } else { 468 | const_iterator nextIt = std::next(hint); 469 | if (nextIt == e || !compRef()(*nextIt, v)) { // [*nextIt, right side) is sorted 470 | if (nextIt != e && !compRef()(v, *nextIt)) { 471 | return nextIt; 472 | } 473 | return _sortedVector.insert(nextIt, std::forward(v)); 474 | } 475 | } 476 | // hint does not bring any valuable information, use standard insert 477 | return insert(std::forward(v)).first; 478 | } 479 | 480 | static bool value_equi(const_reference v1, const_reference v2) { 481 | return !value_compare()(v1, v2) && !value_compare()(v2, v1); 482 | } 483 | 484 | Compare &compRef() { return static_cast(*this); } 485 | const Compare &compRef() const { return static_cast(*this); } 486 | 487 | void eraseDuplicates() { _sortedVector.erase(std::unique(mbegin(), mend(), value_equi), end()); } 488 | 489 | VecType _sortedVector; 490 | }; 491 | 492 | template 493 | inline void swap(FlatSet &lhs, FlatSet &rhs) { 494 | lhs.swap(rhs); 495 | } 496 | 497 | } // namespace amc 498 | -------------------------------------------------------------------------------- /include/amc/hasreallocate.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "isdetected.hpp" 6 | 7 | namespace amc { 8 | namespace vec { 9 | 10 | /// Implement the member detection idiom to know if allocator provides 'reallocate' method. 11 | template 12 | using has_reallocate_t = decltype(std::declval().reallocate( 13 | std::declval(), std::declval(), std::declval(), 14 | std::declval())); 15 | 16 | template 17 | using has_reallocate = is_detected; 18 | } // namespace vec 19 | } // namespace amc -------------------------------------------------------------------------------- /include/amc/isdetected.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "config.hpp" 6 | 7 | /// Emulation of std::experimental::is_detected feature. 8 | /// Sources: 9 | /// https://people.eecs.berkeley.edu/~brock/blog/detection_idiom.php 10 | /// - Excellent blog page explaining the evolution of C++ detection idiom implementations) 11 | /// https://en.cppreference.com/w/cpp/experimental/is_detected 12 | /// - Code taken from above page 13 | namespace amc { 14 | 15 | #ifdef AMC_CXX17 16 | using std::void_t; 17 | #else 18 | template 19 | using void_t = void; 20 | #endif 21 | 22 | namespace detail { 23 | template class Op, class... Args> 24 | struct detector { 25 | using value_t = std::false_type; 26 | using type = Default; 27 | }; 28 | 29 | template class Op, class... Args> 30 | struct detector>, Op, Args...> { 31 | using value_t = std::true_type; 32 | using type = Op; 33 | }; 34 | 35 | } // namespace detail 36 | 37 | struct nonesuch { 38 | ~nonesuch() = delete; 39 | nonesuch(nonesuch const&) = delete; 40 | void operator=(nonesuch const&) = delete; 41 | }; 42 | 43 | template