├── test ├── CMakeLists.txt ├── unit │ ├── catch_main.cpp │ ├── shared_ptr_small_vector.cpp │ ├── ptr_wrapper.cpp │ ├── CMakeLists.txt │ ├── small_string_non_member.cpp │ └── small_string_access.cpp └── cmake │ ├── main.cpp │ └── CMakeLists.txt ├── dev-tools ├── CMakeLists.txt └── linter │ ├── CMakeLists.txt │ └── main.cpp ├── .github ├── ISSUE_TEMPLATE │ ├── documentation.md │ ├── feature-request.md │ ├── config.yml │ └── bug-report.md └── workflows │ ├── docs.yml │ └── build.yml ├── cmake ├── functions │ ├── all.cmake │ ├── link_external_libraries.cmake │ ├── project_flags.cmake │ ├── sanitizers.cmake │ └── target_options.cmake └── small │ ├── in_source_guard.cmake │ ├── variables.cmake │ └── dev-options.cmake ├── include ├── small │ ├── stack.hpp │ ├── detail │ │ ├── traits │ │ │ ├── is_pair.hpp │ │ │ ├── ptr_to_const.hpp │ │ │ ├── extract_value_type.hpp │ │ │ ├── has_allocator.hpp │ │ │ ├── is_relocatable.hpp │ │ │ ├── is_range.hpp │ │ │ ├── little_endian.hpp │ │ │ ├── default_inline_storage.hpp │ │ │ ├── add_key_const.hpp │ │ │ └── enable_allocator_from_this.hpp │ │ ├── algorithm │ │ │ ├── strlen.hpp │ │ │ ├── leading_zeros.hpp │ │ │ ├── to_unsigned.hpp │ │ │ ├── console_unicode_guard.hpp │ │ │ ├── shift.hpp │ │ │ └── intcmp.hpp │ │ ├── exception │ │ │ ├── throw.hpp │ │ │ └── scope_guard.hpp │ │ └── iterator │ │ │ ├── iterator_type_traits.hpp │ │ │ ├── pointer_wrapper.hpp │ │ │ ├── const_key_iterator.hpp │ │ │ └── ordered_concat_iterator.hpp │ ├── queue.hpp │ ├── set.hpp │ └── map.hpp └── CMakeLists.txt ├── small-config.cmake.in ├── docs ├── img │ └── small_banner.svg └── overrides │ ├── main.html │ └── partials │ └── footer.html ├── examples ├── unicode_strings.cpp ├── CMakeLists.txt ├── default_inline_storage.cpp ├── associative.cpp └── main.cpp ├── .gitignore ├── mkdocs.yml ├── LICENSE ├── .clang-format └── CMakeLists.txt /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | add_subdirectory(unit) -------------------------------------------------------------------------------- /dev-tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | 9 | add_subdirectory(linter) 10 | -------------------------------------------------------------------------------- /test/unit/catch_main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #define CATCH_CONFIG_MAIN 9 | #define CATCH_CONFIG_CPP17_STRING_VIEW 10 | #include -------------------------------------------------------------------------------- /test/cmake/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #include 9 | 10 | int 11 | main() { 12 | small::vector v(5); 13 | return (!v.empty()) ? 0 : 1; 14 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Documentation 3 | about: Something is missing from the docs 4 | title: '' 5 | labels: documentation 6 | assignees: '' 7 | --- 8 | 9 | ## The problem 10 | 11 | 12 | ## Proposed solution 13 | 14 | ## Alternatives I've considered 15 | 16 | ## Additional context 17 | 18 | -------------------------------------------------------------------------------- /cmake/functions/all.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | include(${CMAKE_CURRENT_LIST_DIR}/project_flags.cmake) 9 | include(${CMAKE_CURRENT_LIST_DIR}/sanitizers.cmake) 10 | include(${CMAKE_CURRENT_LIST_DIR}/target_options.cmake) 11 | include(${CMAKE_CURRENT_LIST_DIR}/link_external_libraries.cmake) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: For formal improvement proposals. For discussing new ideas, please use the "Small Ideas 💡" link below. 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | --- 8 | 9 | ## The problem 10 | 11 | 12 | ## Proposed solution 13 | 14 | ## Alternatives I've considered 15 | 16 | ## Additional context 17 | 18 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Small Q&A 🙏 4 | url: https://github.com/alandefreitas/small/discussions/new?category_id=31250560 5 | about: Please ask and answer questions here. Let's find out what's going on first. 6 | - name: Small Ideas 💡 7 | url: https://github.com/alandefreitas/small/discussions/new?category_id=31250561 8 | about: Please discuss informal enhancement proposals here before creating an issue. 9 | -------------------------------------------------------------------------------- /cmake/small/in_source_guard.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | 9 | # ---- In-source guard ---- 10 | 11 | if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) 12 | message( 13 | FATAL_ERROR 14 | "In-source builds are not supported. " 15 | "Please read the BUILDING document before trying to build this project. " 16 | "You may need to delete 'CMakeCache.txt' and 'CMakeFiles/' first." 17 | ) 18 | endif() -------------------------------------------------------------------------------- /include/small/stack.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_STACK_HPP 9 | #define SMALL_STACK_HPP 10 | 11 | #include 12 | #include 13 | 14 | namespace small { 15 | template < 16 | class T, 17 | size_t N = default_inline_storage_v, 18 | class Container = small::vector> 19 | using stack = std::stack; 20 | } // namespace small 21 | 22 | #endif // SMALL_STACK_HPP 23 | -------------------------------------------------------------------------------- /small-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | # How small installation was built 4 | set(SMALL_BUILT_SHARED "@BUILD_SHARED_LIBS@") 5 | set(SMALL_BUILT_CXX_COMPILER_ID "@CMAKE_CXX_COMPILER_ID@") 6 | set(SMALL_BUILT_CXX_COMPILER_VERSION "@CMAKE_CXX_COMPILER_VERSION@") 7 | 8 | # Find dependencies 9 | if(NOT ${SMALL_BUILT_SHARED}) 10 | include(CMakeFindDependencyMacro) 11 | list(APPEND CMAKE_MODULE_PATH ${SMALL_CONFIG_INSTALL_DIR}) 12 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}") 13 | list(POP_BACK CMAKE_MODULE_PATH) 14 | endif() 15 | 16 | # Create imported targets 17 | include("${CMAKE_CURRENT_LIST_DIR}/small-targets.cmake") 18 | check_required_components(small) -------------------------------------------------------------------------------- /docs/img/small_banner.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | small 11 | 12 | 13 | c++ small containers 14 | 15 | -------------------------------------------------------------------------------- /test/unit/shared_ptr_small_vector.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 Yat Ho (lagoho7@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | // C++ 9 | #include 10 | 11 | // External 12 | #include 13 | 14 | // Small 15 | #include 16 | 17 | TEST_CASE("Shared Ptr Vector") { 18 | using namespace small; 19 | 20 | STATIC_REQUIRE(!is_relocatable_v>); 21 | 22 | SECTION("Erase in middle") { 23 | vector> a; 24 | 25 | for (int i = 0; i < 2; ++i) { 26 | a.emplace_back(std::make_shared(i)); 27 | } 28 | 29 | a.erase(a.begin()); 30 | 31 | REQUIRE(*a[0] == 1); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /include/small/detail/traits/is_pair.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_TRAITS_IS_PAIR_HPP 9 | #define SMALL_DETAIL_TRAITS_IS_PAIR_HPP 10 | 11 | #include 12 | 13 | namespace small { 14 | namespace detail { 15 | /// Check if type is a pair 16 | template 17 | struct is_pair : std::false_type 18 | {}; 19 | 20 | template 21 | struct is_pair> : std::true_type 22 | {}; 23 | 24 | template 25 | constexpr bool is_pair_v = is_pair::value; 26 | } // namespace detail 27 | } // namespace small 28 | 29 | #endif // SMALL_DETAIL_TRAITS_IS_PAIR_HPP 30 | -------------------------------------------------------------------------------- /include/small/detail/traits/ptr_to_const.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 Yat Ho (lagoho7@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_TRAITS_PTR_TO_CONST_HPP 9 | #define SMALL_DETAIL_TRAITS_PTR_TO_CONST_HPP 10 | 11 | #include 12 | 13 | namespace small { 14 | namespace detail { 15 | /// Convert a pointer to pointer-to-const 16 | template >> 17 | using ptr_to_const = std::add_pointer< 18 | std::add_const_t>>; 19 | 20 | template 21 | using ptr_to_const_t = typename ptr_to_const::type; 22 | } // namespace detail 23 | } // namespace small 24 | 25 | #endif // SMALL_DETAIL_TRAITS_PTR_TO_CONST_HPP 26 | -------------------------------------------------------------------------------- /include/small/queue.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_QUEUE_HPP 9 | #define SMALL_QUEUE_HPP 10 | 11 | #include 12 | #include 13 | 14 | namespace small { 15 | template < 16 | class T, 17 | size_t N = default_inline_storage_v, 18 | class Container = small::vector> 19 | using queue = std::queue; 20 | 21 | template < 22 | class T, 23 | size_t N = default_inline_storage_v, 24 | class Container = small::vector, 25 | class Compare = std::less<>> 26 | using priority_queue = std::priority_queue; 27 | } // namespace small 28 | 29 | #endif // SMALL_QUEUE_HPP 30 | -------------------------------------------------------------------------------- /examples/unicode_strings.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #include 9 | #include 10 | 11 | int 12 | main() { 13 | // Input is UTF32, but string stores it as UTF8 14 | small::string str = U"Hello 🌎!"; 15 | 16 | // Accessing bytes 17 | std::cout << static_cast(str[4]) << '\n'; // o 18 | std::cout << static_cast(str[6]) << '\n'; // � 19 | 20 | // Accessing codepoints 21 | using cp_idx = small::string::codepoint_index; 22 | std::cout << str[cp_idx(4)] << '\n'; // o 23 | std::cout << str[cp_idx(6)] << '\n'; // 🌎 24 | 25 | // Malformed unicode strings 26 | assert(not small::is_malformed(str)); 27 | str[7] = 'a'; 28 | assert(small::is_malformed(str)); 29 | 30 | return 0; 31 | } -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_executable(quickstart-example main.cpp) 2 | target_link_libraries(quickstart-example small) 3 | 4 | add_executable(default_inline_storage-example default_inline_storage.cpp) 5 | target_link_libraries(default_inline_storage-example small) 6 | 7 | add_executable(unicode_strings-example unicode_strings.cpp) 8 | target_link_libraries(unicode_strings-example small) 9 | 10 | add_executable(associative-example associative.cpp) 11 | target_link_libraries(associative-example small) 12 | 13 | if (SMALL_BUILD_TESTS) 14 | add_test(NAME quickstart-example-as-test 15 | COMMAND quickstart-example) 16 | add_test(NAME default_inline_storage-example-as-test 17 | COMMAND default_inline_storage-example) 18 | add_test(NAME unicode_strings-example-as-test 19 | COMMAND unicode_strings-example) 20 | add_test(NAME associative-example-as-test 21 | COMMAND associative-example) 22 | endif () -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build directories 2 | build 3 | cmake-build-* 4 | cmake-build-debug 5 | cmake-build-release 6 | cmake-build-debug-gcc 7 | cmake-build-release-gcc 8 | cmake-build-debug-msvc 9 | cmake-build-release-msvc 10 | cmake-build-debug-* 11 | cmake-build-release-* 12 | build*/ 13 | out 14 | 15 | # Local dependencies build 16 | cpp_modules 17 | 18 | # IDE settings 19 | .idea 20 | codealike.json 21 | .vs 22 | CMakeLists.txt.user 23 | CMakeSettings.json 24 | gh-md-toc 25 | /.vs 26 | /CMakeSettings.json 27 | 28 | # Hidden files 29 | .DS_Store 30 | 31 | # Prerequisites 32 | *.d 33 | 34 | # Compiled Object files 35 | *.slo 36 | *.lo 37 | *.o 38 | *.obj 39 | 40 | # Precompiled Headers 41 | *.gch 42 | *.pch 43 | 44 | # Compiled Dynamic libraries 45 | *.so 46 | *.dylib 47 | *.dll 48 | 49 | # Fortran module files 50 | *.mod 51 | *.smod 52 | 53 | # Compiled Static libraries 54 | *.lai 55 | *.la 56 | *.a 57 | *.lib 58 | 59 | # Executables 60 | *.exe 61 | *.out 62 | *.app 63 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | --- 8 | 9 | ## The problem 10 | 11 | 12 | ### Steps to Reproduce 13 | 14 | ```bash 15 | # Your steps go here 16 | mkdir build 17 | cd build 18 | cmake .. -DCMAKE_BUILD_TYPE=Release 19 | ``` 20 | 21 | ### Output 22 | 23 |
24 | 25 | ```console 26 | # The output 27 | ``` 28 |
29 | 30 | ## Platform 31 | 32 | 33 | - [ ] *cross-platform issue - linux* 34 | - [ ] *cross-platform issue - windows* 35 | - [ ] *cross-platform issue - macos* 36 | - [ ] *other:* ___________ 37 | 38 | ### Environment Details 39 | - OS: 40 | - OS Version: 41 | - Compiler: 42 | - Compiler version: 43 | 44 | ## Proposed solution 45 | 46 | ## Alternatives I've considered 47 | 48 | ### Additional context 49 | 50 | -------------------------------------------------------------------------------- /include/small/detail/traits/extract_value_type.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_TRAITS_EXTRACT_VALUE_TYPE_HPP 9 | #define SMALL_DETAIL_TRAITS_EXTRACT_VALUE_TYPE_HPP 10 | 11 | namespace small { 12 | namespace detail { 13 | /// \struct Get value type from T, if present 14 | template 15 | struct extract_value_type 16 | { 17 | using type = void; 18 | }; 19 | 20 | template 21 | struct extract_value_type> 22 | { 23 | using type = typename T::value_type; 24 | }; 25 | 26 | template 27 | using extract_value_type_t = typename extract_value_type::type; 28 | 29 | } // namespace detail 30 | } // namespace small 31 | 32 | #endif // SMALL_DETAIL_TRAITS_EXTRACT_VALUE_TYPE_HPP 33 | -------------------------------------------------------------------------------- /include/small/detail/traits/has_allocator.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_TRAITS_HAS_ALLOCATOR_HPP 9 | #define SMALL_DETAIL_TRAITS_HAS_ALLOCATOR_HPP 10 | 11 | namespace small { 12 | namespace detail { 13 | /// Check if type has an associated allocator type 14 | template 15 | struct has_allocator : std::false_type 16 | {}; 17 | 18 | template 19 | struct has_allocator< 20 | T, 21 | std::void_t< 22 | decltype(std::declval().get_allocator()), 23 | typename T::allocator_type>> : std::true_type 24 | {}; 25 | 26 | template 27 | static constexpr bool has_allocator_v = has_allocator::value; 28 | 29 | } // namespace detail 30 | } // namespace small 31 | 32 | #endif // SMALL_DETAIL_TRAITS_HAS_ALLOCATOR_HPP 33 | -------------------------------------------------------------------------------- /include/small/detail/traits/is_relocatable.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_TRAITS_IS_RELOCATABLE_HPP 9 | #define SMALL_DETAIL_TRAITS_IS_RELOCATABLE_HPP 10 | 11 | namespace small { 12 | /// \brief True type if a type is relocatable 13 | /// We use this only for trivially copyable types, but one can 14 | /// use this as an extension point to make their vectors faster. 15 | /// Almost any data structure without internal pointers is 16 | /// relocatable. 17 | template 18 | struct is_relocatable 19 | : std::conjunction< 20 | std::is_trivially_copy_constructible, 21 | std::is_trivially_copy_assignable, 22 | std::is_trivially_destructible> 23 | {}; 24 | 25 | template 26 | constexpr bool is_relocatable_v = is_relocatable::value; 27 | } // namespace small 28 | 29 | #endif // SMALL_DETAIL_TRAITS_IS_RELOCATABLE_HPP 30 | -------------------------------------------------------------------------------- /include/small/detail/traits/is_range.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_TRAITS_IS_RANGE_HPP 9 | #define SMALL_DETAIL_TRAITS_IS_RANGE_HPP 10 | 11 | namespace small { 12 | namespace detail { 13 | /// Check if type is a range (has begin() and end() functions) 14 | template 15 | struct is_range : std::false_type 16 | {}; 17 | 18 | template 19 | struct is_range< 20 | T, 21 | std::void_t< 22 | decltype(std::declval().begin()), 23 | decltype(std::declval().end()), 24 | typename T::value_type>> : std::true_type 25 | {}; 26 | 27 | /// True if type is a range (has begin() and end() functions) 28 | template 29 | constexpr bool is_range_v = is_range>::value; 30 | } // namespace detail 31 | } // namespace small 32 | 33 | #endif // SMALL_DETAIL_TRAITS_IS_RANGE_HPP 34 | -------------------------------------------------------------------------------- /dev-tools/linter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | ########################################################### 9 | ### Linter application ### 10 | ########################################################### 11 | # This is a linter with a few rules that go beyond clang-format 12 | add_executable(linter main.cpp) 13 | target_compile_features(linter PUBLIC cxx_std_17) 14 | 15 | ########################################################### 16 | ### Lint library files ### 17 | ########################################################### 18 | # Generate a single-header version of small 19 | add_custom_target( 20 | lint_small 21 | COMMAND linter 22 | # Files 23 | --include_paths "${SMALL_ROOT_DIR}/include" 24 | # Linter options 25 | --show_progress 26 | # CMake command options 27 | DEPENDS linter ${SMALL_HEADERS} 28 | COMMENT "Run linter" 29 | VERBATIM 30 | ) 31 | add_dependencies(small lint_small) -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | site_name: Small 2 | site_url: https://alandefreitas.github.io/small/ 3 | repo_url: https://github.com/alandefreitas/small 4 | repo_name: alandefreitas/small 5 | 6 | site_description: "Small: C++ Small Containers" 7 | 8 | theme: 9 | name: material 10 | custom_dir: docs/overrides 11 | palette: 12 | scheme: preference 13 | icon: 14 | repo: fontawesome/brands/git-alt 15 | logo: fontawesome/solid/code 16 | 17 | edit_uri: "" 18 | 19 | plugins: 20 | - search 21 | - awesome-pages 22 | 23 | google_analytics: 24 | - UA-109858331-6 25 | - auto 26 | 27 | copyright: Copyright © Alan Freitas 28 | 29 | markdown_extensions: 30 | - admonition 31 | - pymdownx.details 32 | - pymdownx.highlight: 33 | use_pygments: true 34 | linenums: true 35 | linenums_style: pymdownx.inline 36 | - pymdownx.inlinehilite 37 | - pymdownx.superfences 38 | - pymdownx.tabbed 39 | - pymdownx.snippets 40 | - pymdownx.arithmatex: 41 | generic: true 42 | - meta 43 | 44 | extra_javascript: 45 | - https://polyfill.io/v3/polyfill.min.js?features=es6 46 | - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js 47 | - https://media.ethicalads.io/media/client/ethicalads.min.js -------------------------------------------------------------------------------- /include/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | ####################################################### 9 | ### Small containers library ### 10 | ####################################################### 11 | # # CONFIGURE_DEPENDS works on most generators now 12 | file(GLOB_RECURSE SMALL_HEADERS CONFIGURE_DEPENDS small/**/*.hpp) 13 | add_library(small INTERFACE) 14 | add_library(small::small ALIAS small) 15 | 16 | # This library requires C++17 17 | target_compile_features(small INTERFACE cxx_std_17) 18 | 19 | # Include directories (development and installation) 20 | target_include_directories(small ${warning_guard} INTERFACE 21 | $ 22 | $) 23 | 24 | # Compiler options 25 | # MSVC compatibility 26 | target_bigobj_options(small) 27 | target_utf8_options(small) 28 | target_nominmax_definition(small) 29 | 30 | if (SMALL_BUILD_WITH_EXCEPTIONS) 31 | target_exception_options(small) 32 | endif () 33 | 34 | # Always enable warnings in development mode 35 | maybe_target_pedantic_warnings(small) 36 | -------------------------------------------------------------------------------- /cmake/functions/link_external_libraries.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | 9 | function(target_link_libraries_system target) 10 | set(options PRIVATE PUBLIC INTERFACE) 11 | cmake_parse_arguments(TLLS "${options}" "" "" ${ARGN}) 12 | foreach(op ${options}) 13 | if(TLLS_${op}) 14 | set(scope ${op}) 15 | endif() 16 | endforeach(op) 17 | set(libs ${TLLS_UNPARSED_ARGUMENTS}) 18 | 19 | foreach(lib ${libs}) 20 | get_target_property(lib_include_dirs ${lib} INTERFACE_INCLUDE_DIRECTORIES) 21 | if(lib_include_dirs) 22 | if(scope) 23 | target_include_directories(${target} SYSTEM ${scope} ${lib_include_dirs}) 24 | else() 25 | target_include_directories(${target} SYSTEM PRIVATE ${lib_include_dirs}) 26 | endif() 27 | else() 28 | message("Warning: ${lib} doesn't set INTERFACE_INCLUDE_DIRECTORIES. No include_directories set.") 29 | endif() 30 | if(scope) 31 | target_link_libraries(${target} ${scope} ${lib}) 32 | else() 33 | target_link_libraries(${target} ${lib}) 34 | endif() 35 | endforeach() 36 | endfunction(target_link_libraries_system) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /include/small/detail/algorithm/strlen.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_ALGORITHM_STRLEN_HPP 9 | #define SMALL_DETAIL_ALGORITHM_STRLEN_HPP 10 | 11 | #include 12 | 13 | namespace small { 14 | namespace detail { 15 | /// \brief strlen for different character types 16 | template 17 | inline std::size_t 18 | strlen(const T *str) { 19 | std::size_t len = 0u; 20 | while (*str++) { 21 | ++len; 22 | } 23 | return len; 24 | } 25 | 26 | /// \brief Usual strlen function 27 | template <> 28 | inline std::size_t 29 | strlen(const char *str) { 30 | return std::strlen(str); 31 | } 32 | 33 | /// \brief strlen for different character types with a size limit 34 | template 35 | inline std::size_t 36 | strlen(const T *str, std::size_t limit) { 37 | std::size_t len = 0u; 38 | while (*str++ && len < limit) { 39 | ++len; 40 | } 41 | return len; 42 | } 43 | } // namespace detail 44 | } // namespace small 45 | 46 | #endif // SMALL_DETAIL_ALGORITHM_STRLEN_HPP 47 | -------------------------------------------------------------------------------- /include/small/detail/traits/little_endian.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_TRAITS_LITTLE_ENDIAN_HPP 9 | #define SMALL_DETAIL_TRAITS_LITTLE_ENDIAN_HPP 10 | 11 | namespace small { 12 | namespace detail { 13 | /// \brief Helper to detect little endian 14 | class is_little_endian 15 | { 16 | constexpr static std::uint32_t u4 = 1; 17 | constexpr static std::uint8_t u1 = (const std::uint8_t &) u4; 18 | 19 | public: 20 | constexpr static bool value = u1; 21 | }; 22 | 23 | /// \brief Helper to modify the last (address-wise) byte of a little 24 | /// endian value of type 'T' 25 | template 26 | union last_byte 27 | { 28 | T number; 29 | struct 30 | { 31 | char dummy[sizeof(T) - 1]; 32 | char last; 33 | } bytes; 34 | }; 35 | template 36 | union last_byte 37 | { 38 | T number; 39 | struct 40 | { 41 | char last; 42 | } bytes; 43 | }; 44 | } // namespace detail 45 | } // namespace small 46 | 47 | #endif // SMALL_DETAIL_TRAITS_LITTLE_ENDIAN_HPP 48 | -------------------------------------------------------------------------------- /examples/default_inline_storage.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #include 9 | #include 10 | 11 | // A relocatable custom type whose default inline storage should be at least 10 12 | // elements 13 | struct my_type 14 | { 15 | int a; 16 | double b; 17 | }; 18 | 19 | namespace small { 20 | template <> 21 | struct is_relocatable : std::true_type 22 | {}; 23 | template <> 24 | struct default_inline_storage : std::integral_constant 25 | {}; 26 | } // namespace small 27 | 28 | int 29 | main() { 30 | // Inline storage for at least 5 elements 31 | small::vector v1 = { 32 | {1, 1.1}, 33 | {2, 2.2}, 34 | {3, 3.3}, 35 | {4, 4.4}, 36 | {5, 5.5} 37 | }; 38 | for (const auto &x: v1) { 39 | std::cout << '<' << x.a << ',' << x.b << '>' << ' '; 40 | } 41 | std::cout << "\n"; // <1,1.1> <2,2.2> <3,3.3> <4,4.4> <5,5.5> 42 | 43 | // Default inline storage for at least 10 elements 44 | small::vector v2 = { 45 | {1, 1.1}, 46 | {2, 2.2}, 47 | {3, 3.3}, 48 | {4, 4.4}, 49 | {5, 5.5} 50 | }; 51 | for (const auto &x: v2) { 52 | std::cout << '<' << x.a << ',' << x.b << '>' << ' '; 53 | } 54 | std::cout << "\n"; // <1,1.1> <2,2.2> <3,3.3> <4,4.4> <5,5.5> 55 | 56 | return 0; 57 | } -------------------------------------------------------------------------------- /docs/overrides/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | {{ super() }} 5 | 6 | 14 | {% endblock %} 15 | 16 | 17 | 18 | {% block site_nav %} 19 | 20 | {% if nav %} 21 | {% if page and page.meta and page.meta.hide %} 22 | {% set hidden = "hidden" if "navigation" in page.meta.hide %} 23 | {% endif %} 24 | 35 | {% endif %} 36 | 37 | 38 |
39 |
40 |
41 |
42 | {% include "partials/toc.html" %} 43 |
44 |
45 |
46 | {% endblock %} -------------------------------------------------------------------------------- /include/small/detail/traits/default_inline_storage.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_TRAITS_DEFAULT_INLINE_STORAGE_HPP 9 | #define SMALL_DETAIL_TRAITS_DEFAULT_INLINE_STORAGE_HPP 10 | 11 | namespace small { 12 | /// The default number of inline elements for a type 13 | namespace detail { 14 | /// We rarely want less than 5 elements in a small vector 15 | /// That's why they are vectors 16 | constexpr size_t expected_min_reasonable_inline_vector = 5; 17 | 18 | /// The number of values we can fit instead of a pointer and the size 19 | /// value, if the it's size_t We almost never want less inline elements 20 | /// than that 21 | template 22 | constexpr size_t expected_inline_values_per_heap_pointers 23 | = (sizeof(T *) + sizeof(size_t)) / sizeof(T); 24 | } // namespace detail 25 | 26 | template 27 | struct default_inline_storage 28 | : std::integral_constant< 29 | size_t, 30 | std:: 31 | max(detail::expected_min_reasonable_inline_vector, 32 | detail::expected_inline_values_per_heap_pointers)> 33 | {}; 34 | 35 | template 36 | constexpr size_t default_inline_storage_v = default_inline_storage::value; 37 | 38 | } // namespace small 39 | 40 | #endif // SMALL_DETAIL_TRAITS_DEFAULT_INLINE_STORAGE_HPP 41 | -------------------------------------------------------------------------------- /include/small/detail/traits/add_key_const.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_TRAITS_ADD_KEY_CONST_HPP 9 | #define SMALL_DETAIL_TRAITS_ADD_KEY_CONST_HPP 10 | 11 | #include 12 | 13 | namespace small { 14 | namespace detail { 15 | /// Make pair key constant 16 | template 17 | struct add_key_const : std::add_const 18 | {}; 19 | 20 | template 21 | struct add_key_const> 22 | { 23 | typedef std::pair, U> type; 24 | }; 25 | 26 | /// Make pair key constant from pointer to pair 27 | template 28 | struct add_key_const 29 | { 30 | typedef typename add_key_const::type *type; 31 | }; 32 | 33 | /// Make pair key constant from reference to pair 34 | template 35 | struct add_key_const 36 | { 37 | typedef typename add_key_const::type &type; 38 | }; 39 | 40 | /// Check if pair has a const key 41 | template 42 | struct is_key_const : std::false_type 43 | {}; 44 | 45 | template 46 | struct is_key_const> : std::is_const 47 | {}; 48 | 49 | template 50 | constexpr bool is_key_const_v = is_key_const::value; 51 | } // namespace detail 52 | } // namespace small 53 | 54 | #endif // SMALL_DETAIL_TRAITS_ADD_KEY_CONST_HPP 55 | -------------------------------------------------------------------------------- /examples/associative.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | // A relocatable custom type whose default inline storage should be at least 10 13 | // elements 14 | struct my_type 15 | { 16 | int a; 17 | double b; 18 | friend bool 19 | operator<(const my_type &lhs, const my_type &rhs) { 20 | return lhs.a < rhs.a; 21 | } 22 | }; 23 | 24 | namespace small { 25 | template <> 26 | struct is_relocatable : std::true_type 27 | {}; 28 | template <> 29 | struct default_inline_storage : std::integral_constant 30 | {}; 31 | } // namespace small 32 | 33 | int 34 | main() { 35 | // Inline storage for at least 5 elements 36 | small::max_size_map v1 = { 37 | {1, { 1, 1.1 }}, 38 | {2, { 2, 2.2 }}, 39 | {3, { 3, 3.3 }}, 40 | {4, { 4, 4.4 }}, 41 | {5, { 5, 5.5 }} 42 | }; 43 | for (const auto &x: v1) { 44 | std::cout << '<' << x.first << ',' << '<' << x.second.a << ',' 45 | << x.second.b << '>' << '>' << ' '; 46 | } 47 | std::cout << "\n"; 48 | 49 | // Default inline storage for at least 10 elements 50 | small::unordered_multiset v2 = { 51 | {1, 1.1}, 52 | {2, 2.2}, 53 | {3, 3.3}, 54 | {4, 4.4}, 55 | {5, 5.5} 56 | }; 57 | for (const auto &x: v2) { 58 | std::cout << '<' << x.a << ',' << x.b << '>' << ' '; 59 | } 60 | std::cout << "\n"; 61 | 62 | return 0; 63 | } -------------------------------------------------------------------------------- /include/small/detail/exception/throw.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_EXCEPTION_THROW_HPP 9 | #define SMALL_DETAIL_EXCEPTION_THROW_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #ifndef cpp_exceptions 17 | # define SMALL_DISABLE_EXCEPTIONS 18 | #endif 19 | 20 | namespace small { 21 | namespace detail { 22 | 23 | /// \brief Throw an exception but terminate if we can't throw 24 | template 25 | [[noreturn]] void 26 | throw_exception(Ex &&ex) { 27 | #ifndef SMALL_DISABLE_EXCEPTIONS 28 | throw static_cast(ex); 29 | #else 30 | (void) ex; 31 | std::terminate(); 32 | #endif 33 | } 34 | 35 | /// \brief Construct and throw an exception but terminate otherwise 36 | template 37 | [[noreturn]] void 38 | throw_exception(Args &&...args) { 39 | throw_exception(Ex(std::forward(args)...)); 40 | } 41 | 42 | /// \brief Throw an exception but terminate if we can't throw 43 | template 44 | void 45 | catch_exception(ThrowFn &&thrower, CatchFn &&catcher) { 46 | #ifndef SMALL_DISABLE_EXCEPTIONS 47 | try { 48 | return static_cast(thrower)(); 49 | } 50 | catch (std::exception &) { 51 | return static_cast(catcher)(); 52 | } 53 | #else 54 | return static_cast(thrower)(); 55 | #endif 56 | } 57 | 58 | } // namespace detail 59 | } // namespace small 60 | 61 | #endif // SMALL_DETAIL_EXCEPTION_THROW_HPP 62 | -------------------------------------------------------------------------------- /cmake/functions/project_flags.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | # @brief Set variable indicating if this is a master project 9 | # - This is important to avoid building tests and examples when project is not master 10 | macro(set_master_project_booleans) 11 | if (${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_SOURCE_DIR}) 12 | set(SMALL_MASTER_PROJECT ON) 13 | else () 14 | set(SMALL_MASTER_PROJECT OFF) 15 | endif () 16 | endmacro() 17 | 18 | # @brief Set variables indicating if mode is Debug or Release 19 | # - The mode might be neither of them 20 | macro(set_debug_booleans) 21 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 22 | set(DEBUG_MODE ON) 23 | set(NOT_DEBUG_MODE OFF) 24 | set(RELEASE_MODE OFF) 25 | set(NOT_RELEASE_MODE ON) 26 | else () 27 | set(DEBUG_MODE OFF) 28 | set(NOT_DEBUG_MODE ON) 29 | set(RELEASE_MODE ON) 30 | set(NOT_RELEASE_MODE OFF) 31 | endif () 32 | endmacro() 33 | 34 | # @brief Create booleans GCC and CLANG to identify the compiler more easily 35 | # - A boolean for MSVC already exists by default 36 | macro(set_compiler_booleans) 37 | set(CLANG OFF) 38 | set(GCC OFF) 39 | if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") 40 | set(CLANG ON) 41 | elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") 42 | set(GCC ON) 43 | endif () 44 | 45 | # Check if we are using the "expected" compilers, which are usually 46 | # Win+MSVC, Linux+GCC, Mac+Clang 47 | set(EXPECTED_COMPILER OFF) 48 | if (WIN32 AND MSVC) 49 | set(EXPECTED_COMPILER ON) 50 | elseif (APPLE AND CLANG) 51 | set(EXPECTED_COMPILER ON) 52 | elseif (UNIX AND NOT APPLE AND GCC) 53 | set(EXPECTED_COMPILER ON) 54 | endif() 55 | endmacro() 56 | -------------------------------------------------------------------------------- /.github/workflows/docs.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | name: Docs Generator 9 | 10 | on: push 11 | 12 | jobs: 13 | generateDOC: 14 | name: Docs Generator 15 | runs-on: ubuntu-20.04 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Generate Table of Contents 19 | uses: technote-space/toc-generator@v2 20 | with: 21 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 22 | MAX_HEADER_LEVEL: 3 23 | FOLDING: true 24 | - name: Update Contributors 25 | if: github.ref == 'refs/heads/master' 26 | uses: akhilmhdh/contributors-readme-action@v2.0.1 27 | env: 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | with: 30 | image_size: 100 31 | columns_per_row: 6 32 | - name: Download mdsplit 33 | uses: carlosperate/download-file-action@v1.0.3 34 | id: download-mdsplit 35 | with: 36 | file-url: 'https://github.com/alandefreitas/mdsplit/releases/download/v0.1.1/Executable.Linux.zip' 37 | file-name: 'mdsplit.zip' 38 | location: '.' 39 | - name: Unzip mdsplit 40 | run: | 41 | unzip mdsplit.zip 42 | rm -f mdsplit.zip 43 | sudo chmod +x mdsplit 44 | ls 45 | - name: Generate Documentation Source 46 | run: ./mdsplit -r alandefreitas/small 47 | - name: Setup Python 48 | uses: actions/setup-python@v2 49 | with: 50 | python-version: 3.x 51 | - name: Install Mkdocs Material 52 | run: pip install mkdocs-material 53 | - name: Install Awesome Pages Plugin # https://github.com/lukasgeiter/mkdocs-awesome-pages-plugin 54 | run: pip install mkdocs-awesome-pages-plugin 55 | - name: Deploy mkdocs to gh-pages branch 56 | run: mkdocs gh-deploy --force 57 | -------------------------------------------------------------------------------- /cmake/small/variables.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | ####################################################### 9 | ### Environment ### 10 | ####################################################### 11 | include(cmake/functions/all.cmake) 12 | set_master_project_booleans() 13 | set_debug_booleans() 14 | set_compiler_booleans() 15 | 16 | ####################################################### 17 | ### Installer ### 18 | ####################################################### 19 | # CMake dependencies for installer 20 | include(CMakePackageConfigHelpers) 21 | include(GNUInstallDirs) 22 | 23 | ####################################################### 24 | ### Enable Find*.cmake scripts ### 25 | ####################################################### 26 | # Append ./cmake directory to our include paths for the find_package scripts 27 | list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 28 | 29 | ####################################################### 30 | ### Enable FetchContent ### 31 | ####################################################### 32 | # Functions to find or download packages if we can't find_package 33 | include(FetchContent) 34 | 35 | ####################################################### 36 | ### Disable warnings in FetchContent ### 37 | ####################################################### 38 | # target_include_directories with the SYSTEM modifier will request the compiler 39 | # to omit warnings from the provided paths, if the compiler supports that 40 | # This is to provide a user experience similar to find_package when 41 | # add_subdirectory or FetchContent is used to consume this project 42 | set(warning_guard "") 43 | if (NOT SMALL_MASTER_PROJECT) 44 | option( 45 | SMALL_INCLUDES_WITH_SYSTEM 46 | "Use SYSTEM modifier for small's includes, disabling warnings" 47 | ON 48 | ) 49 | mark_as_advanced(SMALL_INCLUDES_WITH_SYSTEM) 50 | if (SMALL_INCLUDES_WITH_SYSTEM) 51 | set(warning_guard SYSTEM) 52 | endif () 53 | endif () -------------------------------------------------------------------------------- /test/unit/ptr_wrapper.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | // C++ 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // External 17 | #include 18 | 19 | // Small 20 | #include 21 | 22 | TEST_CASE("Pointer wrapper") { 23 | using namespace small; 24 | using namespace small::detail; 25 | 26 | SECTION("Constructor") { 27 | SECTION("Empty") { 28 | [[maybe_unused]] pointer_wrapper p; 29 | } 30 | 31 | SECTION("From pointer") { 32 | int a = 2; 33 | pointer_wrapper p(&a); 34 | REQUIRE(p.base() == &a); 35 | } 36 | 37 | SECTION("From another pointer wrapper") { 38 | int a = 2; 39 | pointer_wrapper p1(&a); 40 | REQUIRE(p1.base() == &a); 41 | 42 | pointer_wrapper p2(p1); 43 | REQUIRE(p2.base() == &a); 44 | } 45 | } 46 | 47 | int a[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 48 | pointer_wrapper begin(&a[0]); 49 | pointer_wrapper end(&a[0] + 9); 50 | 51 | SECTION("Element access") { 52 | REQUIRE(begin != end); 53 | REQUIRE(*begin == 1); 54 | REQUIRE(*std::prev(end) == 9); 55 | REQUIRE(begin.base() == &a[0]); 56 | REQUIRE(begin[0] == 1); 57 | REQUIRE(begin[1] == 2); 58 | REQUIRE(begin[2] == 3); 59 | } 60 | 61 | SECTION("Modifiers") { 62 | ++begin; 63 | REQUIRE(*begin == 2); 64 | begin++; 65 | REQUIRE(*begin == 3); 66 | --begin; 67 | REQUIRE(*begin == 2); 68 | begin--; 69 | REQUIRE(*begin == 1); 70 | auto it = begin + 1; 71 | REQUIRE(*it == 2); 72 | it = begin + 2; 73 | REQUIRE(*it == 3); 74 | begin += 2; 75 | REQUIRE(*begin == 3); 76 | it = begin - 1; 77 | REQUIRE(*it == 2); 78 | it = begin - 2; 79 | REQUIRE(*it == 1); 80 | begin -= 2; 81 | REQUIRE(*begin == 1); 82 | } 83 | 84 | SECTION("Algorithms") { 85 | int b[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 86 | pointer_wrapper b_begin(&b[0]); 87 | pointer_wrapper b_end(&b[0] + 9); 88 | 89 | std::copy(begin, end, b_begin); 90 | REQUIRE(std::equal(begin, end, b_begin, b_end)); 91 | } 92 | } -------------------------------------------------------------------------------- /include/small/detail/traits/enable_allocator_from_this.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_TRAITS_ENABLE_ALLOCATOR_FROM_THIS_HPP 9 | #define SMALL_DETAIL_TRAITS_ENABLE_ALLOCATOR_FROM_THIS_HPP 10 | 11 | namespace small { 12 | namespace detail { 13 | /// \brief A class to enable empty base optimization for allocators 14 | template 15 | class enable_allocator_from_this 16 | {}; 17 | 18 | /// \brief Store nothing when allocator is empty 19 | template 20 | class enable_allocator_from_this 21 | { 22 | public: 23 | constexpr enable_allocator_from_this() = default; 24 | constexpr explicit enable_allocator_from_this(const Allocator &) {} 25 | constexpr Allocator 26 | get_allocator() const noexcept { 27 | return Allocator(); 28 | } 29 | 30 | protected: 31 | constexpr void 32 | set_allocator(const Allocator &) const noexcept {} 33 | constexpr void 34 | set_allocator(Allocator &&) const noexcept {} 35 | constexpr void 36 | swap_allocator(Allocator &&) const noexcept {} 37 | }; 38 | 39 | /// \brief Store allocator when allocator is not empty 40 | template 41 | class enable_allocator_from_this 42 | { 43 | public: 44 | constexpr enable_allocator_from_this() : alloc_(Allocator()) {} 45 | constexpr explicit enable_allocator_from_this( 46 | const Allocator &alloc) 47 | : alloc_(alloc) {} 48 | constexpr Allocator 49 | get_allocator() const noexcept { 50 | return alloc_; 51 | } 52 | 53 | protected: 54 | constexpr void 55 | set_allocator(const Allocator &alloc) const noexcept { 56 | alloc_ = alloc; 57 | } 58 | constexpr void 59 | set_allocator(Allocator &&alloc) const noexcept { 60 | alloc_ = std::move(alloc); 61 | } 62 | constexpr void 63 | swap_allocator(Allocator &&alloc) const noexcept { 64 | using std::swap; 65 | swap(alloc_, alloc); 66 | } 67 | 68 | private: 69 | Allocator alloc_; 70 | }; 71 | 72 | } // namespace detail 73 | } // namespace small 74 | 75 | #endif // SMALL_DETAIL_TRAITS_ENABLE_ALLOCATOR_FROM_THIS_HPP 76 | -------------------------------------------------------------------------------- /include/small/set.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_SET_HPP 9 | #define SMALL_SET_HPP 10 | 11 | #include 12 | #include 13 | 14 | /// \file A set container for small sets 15 | 16 | namespace small { 17 | template < 18 | class T, 19 | size_t N = default_inline_storage_v, 20 | class Compare = std::less<>, 21 | class Allocator = std::allocator> 22 | using set = detail:: 23 | associative_vector, Compare>; 24 | 25 | template < 26 | class T, 27 | size_t N = default_inline_storage_v, 28 | class Compare = std::less<>> 29 | using max_size_set = small::detail:: 30 | associative_vector, Compare>; 31 | 32 | template < 33 | class T, 34 | size_t N = default_inline_storage_v, 35 | class Compare = std::less<>, 36 | class Allocator = std::allocator> 37 | using multiset = detail:: 38 | associative_vector, Compare>; 39 | 40 | template < 41 | class T, 42 | size_t N = default_inline_storage_v, 43 | class Compare = std::less<>> 44 | using max_size_multiset = small::detail:: 45 | associative_vector, Compare>; 46 | 47 | template < 48 | class T, 49 | size_t N = default_inline_storage_v, 50 | class Compare = std::less<>, 51 | class Allocator = std::allocator> 52 | using unordered_set = detail:: 53 | associative_vector, Compare>; 54 | 55 | template < 56 | class T, 57 | size_t N = default_inline_storage_v, 58 | class Compare = std::less<>> 59 | using max_size_unordered_set = small::detail:: 60 | associative_vector, Compare>; 61 | 62 | template < 63 | class T, 64 | size_t N = default_inline_storage_v, 65 | class Compare = std::less<>, 66 | class Allocator = std::allocator> 67 | using unordered_multiset = detail:: 68 | associative_vector, Compare>; 69 | 70 | template < 71 | class T, 72 | size_t N = default_inline_storage_v, 73 | class Compare = std::less<>> 74 | using max_size_unordered_multiset = small::detail:: 75 | associative_vector, Compare>; 76 | 77 | } // namespace small 78 | 79 | #endif // SMALL_SET_HPP 80 | -------------------------------------------------------------------------------- /cmake/functions/sanitizers.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | # @brief Add sanitizer flag for Clang and GCC to all targets 9 | # - You shouldn't use sanitizers in Release Mode 10 | # - It's usually best to do that per target 11 | macro(add_sanitizer flag) 12 | include(CheckCXXCompilerFlag) 13 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") 14 | message("Looking for -fsanitize=${flag}") 15 | set(CMAKE_REQUIRED_FLAGS "-Werror -fsanitize=${flag}") 16 | check_cxx_compiler_flag(-fsanitize=${flag} HAVE_FLAG_SANITIZER) 17 | if (HAVE_FLAG_SANITIZER) 18 | message("Adding -fsanitize=${flag}") 19 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=${flag} -fno-omit-frame-pointer") 20 | set(DCMAKE_C_FLAGS "${DCMAKE_C_FLAGS} -fsanitize=${flag} -fno-omit-frame-pointer") 21 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${flag}") 22 | set(DCMAKE_MODULE_LINKER_FLAGS "${DCMAKE_MODULE_LINKER_FLAGS} -fsanitize=${flag}") 23 | else () 24 | message("-fsanitize=${flag} unavailable") 25 | endif () 26 | endif () 27 | endmacro() 28 | 29 | # @brief Add address sanitizer to all targets 30 | # - You shouldn't use sanitizers in Release Mode 31 | # - It's usually best to do that per target 32 | macro(add_address_sanitizer) 33 | add_sanitizer("address") 34 | endmacro() 35 | 36 | # @brief Add thread sanitizer to all targets 37 | # - You shouldn't use sanitizers in Release Mode 38 | # - It's usually best to do that per target 39 | macro(add_thread_sanitizer) 40 | add_sanitizer("thread") 41 | endmacro() 42 | 43 | # @brief Add undefined sanitizer to all targets 44 | # - You shouldn't use sanitizers in Release Mode 45 | # - It's usually best to do that per target 46 | macro(add_undefined_sanitizer) 47 | add_sanitizer("undefined") 48 | endmacro() 49 | 50 | # @brief Add memory sanitizer to all targets 51 | # - You shouldn't use sanitizers in Release Mode 52 | # - It's usually best to do that per target 53 | macro(add_memory_sanitizer) 54 | add_sanitizer("memory") 55 | endmacro() 56 | 57 | # @brief @brief Add leak sanitizer to all targets 58 | # - You shouldn't use sanitizers in Release Mode 59 | # - It's usually best to do that per target 60 | macro(add_leak_sanitizer) 61 | add_sanitizer("leak") 62 | endmacro() 63 | 64 | # @brief Add all sanitizers to all targets 65 | # - You shouldn't use sanitizers in Release Mode 66 | # - It's usually best to do that per target 67 | macro(add_sanitizers) 68 | add_address_sanitizer() 69 | add_leak_sanitizer() 70 | add_undefined_sanitizer() 71 | # not allowed with address sanitizer 72 | # add_thread_sanitizer() 73 | # not supported 74 | # add_memory_sanitizer() 75 | endmacro() -------------------------------------------------------------------------------- /test/cmake/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | ####################################################### 9 | ### Project properties ### 10 | ####################################################### 11 | cmake_minimum_required(VERSION 3.15) 12 | 13 | project(cmake_subdir_test LANGUAGES CXX) 14 | 15 | ####################################################### 16 | ### Integration ### 17 | ####################################################### 18 | if (BOOST_CI_INSTALL_TEST) 19 | find_package(small REQUIRED) 20 | else () 21 | set(SMALL_DIR ../..) 22 | set(SMALL_BINARY_DIR alandefreitas/small) 23 | add_subdirectory(${SMALL_DIR} ${SMALL_BINARY_DIR}) 24 | endif () 25 | 26 | ####################################################### 27 | ### Print small target properties ### 28 | ####################################################### 29 | # This is useful output for CI 30 | if(NOT CMAKE_PROPERTY_LIST) 31 | execute_process(COMMAND cmake --help-property-list OUTPUT_VARIABLE CMAKE_PROPERTY_LIST) 32 | 33 | # Convert command output into a CMake list 34 | string(REGEX REPLACE ";" "\\\\;" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") 35 | string(REGEX REPLACE "\n" ";" CMAKE_PROPERTY_LIST "${CMAKE_PROPERTY_LIST}") 36 | endif() 37 | 38 | function(print_properties) 39 | message("CMAKE_PROPERTY_LIST = ${CMAKE_PROPERTY_LIST}") 40 | endfunction() 41 | 42 | function(print_target_properties target) 43 | if(NOT TARGET ${target}) 44 | message(STATUS "There is no target named '${target}'") 45 | return() 46 | endif() 47 | 48 | foreach(property ${CMAKE_PROPERTY_LIST}) 49 | string(REPLACE "" "${CMAKE_BUILD_TYPE}" property ${property}) 50 | 51 | # Fix https://stackoverflow.com/questions/32197663/how-can-i-remove-the-the-location-property-may-not-be-read-from-target-error-i 52 | if(property STREQUAL "LOCATION" OR property MATCHES "^LOCATION_" OR property MATCHES "_LOCATION$") 53 | continue() 54 | endif() 55 | 56 | get_property(was_set TARGET ${target} PROPERTY ${property} SET) 57 | if(was_set) 58 | get_target_property(value ${target} ${property}) 59 | message("${target} ${property} = ${value}") 60 | endif() 61 | endforeach() 62 | endfunction() 63 | 64 | print_target_properties(small::small) 65 | 66 | ####################################################### 67 | ### Target ### 68 | ####################################################### 69 | add_executable(main main.cpp) 70 | target_link_libraries(main small::small) 71 | if (MSVC) 72 | target_compile_options(main PRIVATE /EHsc) 73 | endif() 74 | 75 | ####################################################### 76 | ### Tests ### 77 | ####################################################### 78 | enable_testing() 79 | add_test(NAME main COMMAND main) 80 | add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure -C $) -------------------------------------------------------------------------------- /include/small/detail/algorithm/leading_zeros.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_ALGORITHM_LEADING_ZEROS_HPP 9 | #define SMALL_DETAIL_ALGORITHM_LEADING_ZEROS_HPP 10 | 11 | //! Implementation Detail 12 | namespace small { 13 | namespace detail { 14 | 15 | /// \brief Count leading zeros utility 16 | #if defined(__GNUC__) 17 | # ifndef SMALL_HAS_LEADING_ZEROS_FUNCTION 18 | # define SMALL_HAS_LEADING_ZEROS_FUNCTION true 19 | # endif 20 | inline unsigned int 21 | leading_zeros(char value) noexcept { 22 | return (char) __builtin_clz(value); 23 | } 24 | inline unsigned int 25 | leading_zeros(unsigned int value) noexcept { 26 | return (unsigned int) __builtin_clz(value); 27 | } 28 | inline unsigned int 29 | leading_zeros(unsigned long int value) noexcept { 30 | return (unsigned int) __builtin_clzl(value); 31 | } 32 | inline unsigned int 33 | leading_zeros(char32_t value) noexcept { 34 | return sizeof(char32_t) == sizeof(unsigned long int) ? 35 | (unsigned int) __builtin_clzl(value) : 36 | (unsigned int) __builtin_clz(value); 37 | } 38 | #elif defined(_MSC_VER) 39 | # ifndef SMALL_HAS_LEADING_ZEROS_FUNCTION 40 | # define SMALL_HAS_LEADING_ZEROS_FUNCTION true 41 | # endif 42 | template 43 | inline unsigned int 44 | lzcnt(T value) noexcept { 45 | unsigned long value_log2; 46 | # if !defined(WIN32) && !defined(_WIN32) && !defined(__WIN32__) 47 | _BitScanReverse64(&value_log2, static_cast(value)); 48 | # else 49 | _BitScanReverse(&value_log2, static_cast(value)); 50 | # endif 51 | return sizeof(T) * 8 - value_log2 - 1; 52 | } 53 | inline unsigned int 54 | leading_zeros(std::uint16_t value) noexcept { 55 | return lzcnt(value); 56 | } 57 | inline unsigned int 58 | leading_zeros(std::uint32_t value) noexcept { 59 | return lzcnt(value); 60 | } 61 | # ifndef WIN32 62 | inline unsigned int 63 | leading_zeros(std::uint64_t value) noexcept { 64 | return lzcnt(value); 65 | } 66 | # endif // WIN32 67 | inline unsigned int 68 | leading_zeros(char32_t value) noexcept { 69 | return lzcnt(value); 70 | } 71 | #endif 72 | 73 | /// \brief Wrapping this functionality in a trait 74 | 75 | #if defined(SMALL_HAS_LEADING_ZEROS_FUNCTION) \ 76 | && SMALL_HAS_LEADING_ZEROS_FUNCTION == true 77 | struct system_has_leading_zeros : std::true_type 78 | {}; 79 | #else 80 | struct system_has_leading_zeros : std::false_type 81 | {}; 82 | #endif 83 | 84 | constexpr bool system_has_leading_zeros_v = system_has_leading_zeros:: 85 | value; 86 | 87 | } // namespace detail 88 | } // namespace small 89 | 90 | #endif // SMALL_DETAIL_ALGORITHM_LEADING_ZEROS_HPP 91 | -------------------------------------------------------------------------------- /include/small/detail/algorithm/to_unsigned.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_ALGORITHM_TO_UNSIGNED_HPP 9 | #define SMALL_DETAIL_ALGORITHM_TO_UNSIGNED_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace small { 16 | namespace detail { 17 | /// \brief Convert a range of bytes to an unsigned number 18 | /// We could use *(const std::uint8_t *)key_ptr, but this doesn't 19 | /// generalize well specially because we have lots of cases where the 20 | /// output span has three bytes, and mixing the two doesn't work well. 21 | template < 22 | typename Unsigned, 23 | typename InputIt, 24 | std::enable_if_t< 25 | std::is_unsigned_v< 26 | Unsigned> && sizeof(typename std::iterator_traits::value_type) == 1, 27 | int> = 0> 28 | constexpr Unsigned 29 | to_unsigned(InputIt first, InputIt last) { 30 | Unsigned result = 0; 31 | while (first != last) { 32 | result <<= 8; 33 | result |= static_cast(*first); 34 | ++first; 35 | } 36 | return result; 37 | } 38 | 39 | template < 40 | typename Unsigned, 41 | typename Range, 42 | std::enable_if_t< 43 | std::is_unsigned_v && is_range_v, 44 | int> = 0> 45 | constexpr Unsigned 46 | to_unsigned(Range &&r) { 47 | return to_unsigned(r.begin(), r.end()); 48 | } 49 | 50 | /// \brief Convert an unsigned number to a span of bytes to an unsigned 51 | /// number 52 | template < 53 | typename Unsigned, 54 | typename OutputIt, 55 | std::enable_if_t< 56 | std::is_unsigned_v< 57 | Unsigned> && sizeof(typename std::iterator_traits::value_type) == 1, 58 | int> = 0> 59 | constexpr void 60 | to_bytes(Unsigned v, OutputIt first, OutputIt last) { 61 | using byte_type = typename std::iterator_traits< 62 | OutputIt>::value_type; 63 | using difference_type = typename std::iterator_traits< 64 | OutputIt>::difference_type; 65 | difference_type distance = last - first; 66 | assert(std::abs(distance) < std::numeric_limits::max()); 67 | uint8_t bytes_to_fill = static_cast(std::abs(distance)); 68 | assert(bytes_to_fill <= sizeof(Unsigned)); 69 | uint8_t byte_shift = bytes_to_fill - 1; 70 | while (first != last) { 71 | *first = static_cast((v >> (byte_shift * 8)) & 0xFF); 72 | ++first; 73 | --byte_shift; 74 | } 75 | } 76 | 77 | template < 78 | typename Unsigned, 79 | typename Range, 80 | std::enable_if_t< 81 | std::is_unsigned_v && is_range_v, 82 | int> = 0> 83 | constexpr void 84 | to_bytes(Unsigned v, Range &&r) { 85 | return to_bytes(v, r.begin(), r.end()); 86 | } 87 | 88 | } // namespace detail 89 | } // namespace small 90 | 91 | #endif // SMALL_DETAIL_ALGORITHM_TO_UNSIGNED_HPP 92 | -------------------------------------------------------------------------------- /examples/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | template 17 | void 18 | print(R&& v); 19 | 20 | void 21 | print_codepoints(const small::string& v); 22 | 23 | int 24 | main() { 25 | // Vector as usual 26 | small::vector v1 = { 1, 2, 3, 4, 5 }; 27 | print(v1); // 1 2 3 4 5 28 | 29 | // Vector with inline storage for at least 10 elements 30 | small::vector v2 = { 1, 2, 3, 4 }; 31 | v2.push_back(5); 32 | print(v2); // 1 2 3 4 5 33 | 34 | // Vector with inline storage only 35 | small::max_size_vector v3 = { 1, 2, 3, 4 }; 36 | v3.push_back(5); 37 | print(v3); // 1 2 3 4 5 38 | 39 | // String 40 | small::string s1 = "Hello world!"; 41 | print(s1); // H e l l o w o r l d ! 42 | 43 | // String with custom inline storage 44 | small::basic_string s2 = "Hello world!"; 45 | print(s2); // H e l l o w o r l d ! 46 | 47 | // UTF8 String from larger UTF-32 string 48 | small::string s3 = U"This works too! 😀 é!"; 49 | std::cout << s3 << '\n'; // T h i s w o r k s t o o ! 😀 50 | print_codepoints(s3); // T|h|i|s| |w|o|r|k|s| |t|o|o|!| |😀| | |é|!| 51 | 52 | // Associative containers 53 | small::set a1 = { 2, 1, 5, 4, 3 }; 54 | print(a1); // 1 2 3 4 5 55 | 56 | small::map a2 = { 57 | {1, 10}, 58 | {2, 20}, 59 | {3, 30}, 60 | {4, 40}, 61 | {5, 50} 62 | }; 63 | print(a2); // <1,10> <2,20> <3,30> <4,40> <5,50> 64 | 65 | small::multimap a3 = { 66 | {1, 10}, 67 | {1, 20}, 68 | {1, 30}, 69 | {1, 40}, 70 | {1, 50} 71 | }; 72 | print(a3); // <1,10> <1,20> <1,30> <1,40> <1,50> 73 | 74 | small::unordered_set a4 = { 2, 1, 5, 4, 3 }; 75 | print(a4); // 2 1 5 4 3 76 | 77 | // Container adaptors 78 | small::stack c1; 79 | c1.push(1); 80 | c1.push(2); 81 | c1.push(3); 82 | c1.push(4); 83 | c1.push(5); 84 | std::cout << c1.top() << '\n'; // 5 85 | 86 | small::queue c2; 87 | c2.push(1); 88 | c2.push(2); 89 | c2.push(3); 90 | c2.push(4); 91 | c2.push(5); 92 | std::cout << c2.front() << '\n'; // 1 93 | std::cout << c2.back() << '\n'; // 5 94 | 95 | small::priority_queue c3; 96 | c3.push(1); 97 | c3.push(2); 98 | c3.push(3); 99 | c3.push(4); 100 | c3.push(5); 101 | std::cout << c3.top() << '\n'; // 5 102 | 103 | return 0; 104 | } 105 | 106 | template 107 | void 108 | print(R&& v) { 109 | for (const auto& x: v) { 110 | constexpr bool x_is_pair = small::detail::is_pair_v< 111 | std::decay_t>; 112 | if constexpr (not x_is_pair) { 113 | std::cout << x << ' '; 114 | } else { 115 | std::cout << '<' << x.first << ',' << x.second << '>' << ' '; 116 | } 117 | } 118 | std::cout << "\n"; 119 | } 120 | 121 | void 122 | print_codepoints(const small::string& v) { 123 | for (auto it = v.begin_codepoint(); it != v.end_codepoint(); ++it) { 124 | std::cout << *it << '|'; 125 | } 126 | std::cout << "\n"; 127 | } -------------------------------------------------------------------------------- /cmake/small/dev-options.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | ####################################################### 9 | ### Developer mode ### 10 | ####################################################### 11 | # Developer mode enables targets and code paths in the CMake scripts that are 12 | # only relevant for the developer(s) of small. 13 | # Targets necessary to build the project must be provided unconditionally, so 14 | # consumers can trivially build and package the project 15 | if (SMALL_MASTER_PROJECT) 16 | option(SMALL_DEVELOPER_MODE "Enable developer mode" OFF) 17 | option(BUILD_SHARED_LIBS "Build shared libs." OFF) 18 | endif () 19 | 20 | if (NOT SMALL_DEVELOPER_MODE) 21 | return() 22 | endif() 23 | 24 | ####################################################### 25 | ### What to build ### 26 | ####################################################### 27 | # C++ targets 28 | option(SMALL_BUILD_TESTS "Build tests" ON) 29 | option(SMALL_BUILD_EXAMPLES "Build examples" ON) 30 | 31 | # Custom targets 32 | option(SMALL_BUILD_DOCS "Build documentation" OFF) 33 | option(SMALL_BUILD_COVERAGE_REPORT "Enable coverage support" OFF) 34 | option(SMALL_BUILD_LINT "Enable linting" OFF) 35 | 36 | ####################################################### 37 | ### How to build ### 38 | ####################################################### 39 | option(SMALL_BUILD_WITH_PEDANTIC_WARNINGS "Use pedantic warnings." ON) 40 | option(SMALL_BUILD_WITH_SANITIZERS "Build with sanitizers." ${DEBUG_MODE}) 41 | option(SMALL_CATCH2_REPORTER "Reporter Catch2 should use when invoked from ctest." "console") 42 | 43 | ####################################################### 44 | ### How to build ### 45 | ####################################################### 46 | option(SMALL_BUILD_WITH_MSVC_HACKS "Accept utf-8 in MSVC by default." ON) 47 | option(SMALL_BUILD_WITH_UTF8 "Accept utf-8 in MSVC by default." ON) 48 | 49 | ####################################################### 50 | ### Apply global developer options ### 51 | ####################################################### 52 | # In development, we can set some options for all targets 53 | if (SMALL_MASTER_PROJECT) 54 | message("Setting global options") 55 | 56 | # This whole project is for coverage 57 | if (SMALL_BUILD_COVERAGE_REPORT) 58 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") 59 | set(CMAKE_C_FLAGS "${DCMAKE_C_FLAGS} --coverage") 60 | endif () 61 | 62 | # Maybe add sanitizers to all targets 63 | if (SMALL_BUILD_WITH_SANITIZERS AND NOT EMSCRIPTEN) 64 | add_sanitizers() 65 | endif () 66 | 67 | # Allow exceptions in MSVC 68 | if (MSVC AND SMALL_BUILD_WITH_EXCEPTIONS) 69 | add_compile_options(/EHsc) 70 | endif () 71 | 72 | # Allow utf-8 in MSVC 73 | if (SMALL_BUILD_WITH_UTF8 AND MSVC) 74 | set(CMAKE_CXX_FLAGS "/utf-8") 75 | endif () 76 | 77 | # MSVC hack to disable windows min/max 78 | # http://www.suodenjoki.dk/us/archive/2010/min-max.htm 79 | if (MSVC AND SMALL_BUILD_WITH_MSVC_HACKS) 80 | # Check for min in Windows.h 81 | # include(CheckSymbolExists) 82 | # check_symbol_exists(min "WinDef.h" HAVE_WINDOWS_MINMAX) 83 | # if (NOT HAVE_WINDOWS_MINMAX) 84 | # check_symbol_exists(min "Windows.h" HAVE_WINDOWS_MINMAX) 85 | # endif () 86 | # if (HAVE_WINDOWS_MINMAX) 87 | add_compile_definitions(NOMINMAX) 88 | # endif () 89 | endif () 90 | endif () -------------------------------------------------------------------------------- /include/small/map.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_MAP_HPP 9 | #define SMALL_MAP_HPP 10 | 11 | #include 12 | #include 13 | 14 | /// \file A map container for small maps 15 | 16 | namespace small { 17 | template < 18 | class K, 19 | class T, 20 | size_t N = default_inline_storage_v>, 21 | class Compare = std::less<>, 22 | class Allocator = std::allocator>> 23 | using map = detail::associative_vector< 24 | true, 25 | false, 26 | true, 27 | vector, N, Allocator>, 28 | Compare>; 29 | 30 | template < 31 | class K, 32 | class T, 33 | size_t N = default_inline_storage_v>, 34 | class Compare = std::less<>> 35 | using max_size_map = small::detail::associative_vector< 36 | true, 37 | false, 38 | true, 39 | max_size_vector, N>, 40 | Compare>; 41 | 42 | template < 43 | class K, 44 | class T, 45 | size_t N = default_inline_storage_v>, 46 | class Compare = std::less<>, 47 | class Allocator = std::allocator>> 48 | using multimap = detail::associative_vector< 49 | true, 50 | true, 51 | true, 52 | vector, N, Allocator>, 53 | Compare>; 54 | 55 | template < 56 | class K, 57 | class T, 58 | size_t N = default_inline_storage_v>, 59 | class Compare = std::less<>> 60 | using max_size_multimap = small::detail::associative_vector< 61 | true, 62 | true, 63 | true, 64 | max_size_vector, N>, 65 | Compare>; 66 | 67 | template < 68 | class K, 69 | class T, 70 | size_t N = default_inline_storage_v>, 71 | class Compare = std::less<>, 72 | class Allocator = std::allocator>> 73 | using unordered_map = detail::associative_vector< 74 | true, 75 | false, 76 | false, 77 | vector, N, Allocator>, 78 | Compare>; 79 | 80 | template < 81 | class K, 82 | class T, 83 | size_t N = default_inline_storage_v>, 84 | class Compare = std::less<>> 85 | using max_size_unordered_map = small::detail::associative_vector< 86 | true, 87 | false, 88 | false, 89 | max_size_vector, N>, 90 | Compare>; 91 | 92 | template < 93 | class K, 94 | class T, 95 | size_t N = default_inline_storage_v>, 96 | class Compare = std::less<>, 97 | class Allocator = std::allocator>> 98 | using unordered_multimap = detail::associative_vector< 99 | true, 100 | true, 101 | false, 102 | vector, N, Allocator>, 103 | Compare>; 104 | 105 | template < 106 | class K, 107 | class T, 108 | size_t N = default_inline_storage_v>, 109 | class Compare = std::less<>> 110 | using max_size_unordered_multimap = small::detail::associative_vector< 111 | true, 112 | true, 113 | false, 114 | max_size_vector, N>, 115 | Compare>; 116 | 117 | } // namespace small 118 | 119 | #endif // SMALL_MAP_HPP 120 | -------------------------------------------------------------------------------- /cmake/functions/target_options.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | 9 | # @brief Enable pedantic warnings for a target 10 | # - This does not propagate to other targets 11 | function(target_pedantic_warnings TARGET_NAME) 12 | # Set warning levels to about the same level for MSVC, GCC, and Clang 13 | if (MSVC) 14 | target_compile_options(${TARGET_NAME} INTERFACE /W4 /WX) 15 | else () 16 | target_compile_options(${TARGET_NAME} INTERFACE -Wall -Wextra -pedantic -Werror) 17 | endif () 18 | endfunction() 19 | 20 | # @brief Maybe enable pedantic warnings for a target 21 | function(maybe_target_pedantic_warnings TARGET_NAME) 22 | if (SMALL_BUILD_WITH_PEDANTIC_WARNINGS) 23 | target_pedantic_warnings(${TARGET_NAME}) 24 | endif () 25 | endfunction () 26 | 27 | # @brief Sets pedantic compiler options for all targets 28 | # - In a more serious project, we would do that per target 29 | # - Setting it for all targets unfortunately affects 30 | # external libraries, which often lead to some warnings. 31 | macro(add_pedantic_warnings) 32 | if (MSVC) 33 | add_compile_options(/W4 /WX) 34 | else () 35 | add_compile_options(-Wall -Wextra -pedantic -Werror) 36 | endif () 37 | endmacro() 38 | 39 | # @brief Maybe set pedantic compiler options for all targets 40 | macro(maybe_add_pedantic_warnings) 41 | if (SMALL_BUILD_WITH_PEDANTIC_WARNINGS) 42 | add_pedantic_warnings() 43 | endif () 44 | endmacro() 45 | 46 | # @brief Enable compile options for MSVC only 47 | # - Adding special compile options to make your code work on MSVC 48 | # is unfortunately very common, so this pattern becomes useful. 49 | function(target_msvc_compile_options TARGET_NAME DEFINITION) 50 | if (MSVC) 51 | target_compile_options(${TARGET_NAME} PUBLIC ${DEFINITION}) 52 | endif () 53 | endfunction() 54 | 55 | # Enable exceptions for the target 56 | function(target_exception_options TARGET_NAME) 57 | # MSVC requires this flag if the code uses C++ exception handling 58 | target_msvc_compile_options(${TARGET_NAME} PRIVATE /EHsc) 59 | endfunction() 60 | 61 | 62 | 63 | # @brief Enable big object files for the target 64 | function(target_bigobj_options TARGET_NAME) 65 | # MSVC requires this flag if the file has a lot of code 66 | target_msvc_compile_options(${TARGET_NAME} PRIVATE /bigobj) 67 | endfunction() 68 | 69 | # @brief Enable utf-8 for the target 70 | function(target_utf8_options TARGET_NAME) 71 | # MSVC requires this flag if the file has a lot of code 72 | target_msvc_compile_options(${TARGET_NAME} PRIVATE /utf-8) 73 | endfunction() 74 | 75 | # @brief Disable minmax for target 76 | function(target_disable_minmax TARGET_NAME) 77 | message("Checking if min exists for ${TARGET_NAME}") 78 | if (MSVC) 79 | # Another hack to check for min in Windows.h 80 | # http://www.suodenjoki.dk/us/archive/2010/min-max.htm 81 | include(CheckSymbolExists) 82 | message("Looking in WinDef.h") 83 | check_symbol_exists(min "WinDef.h" HAVE_WINDOWS_MINMAX) 84 | if (NOT HAVE_WINDOWS_MINMAX) 85 | message("Looking in Windows.h") 86 | check_symbol_exists(min "Windows.h" HAVE_WINDOWS_MINMAX) 87 | endif() 88 | if (HAVE_WINDOWS_MINMAX) 89 | target_compile_definitions(${TARGET_NAME} PUBLIC NOMINMAX) 90 | else() 91 | message("MINMAX not found") 92 | endif() 93 | else() 94 | message("Compiler is not MSVC") 95 | endif() 96 | endfunction() 97 | 98 | # @brief Disable minmax for target 99 | # This forces the NOMINMAX definition without even looking for 100 | # min in WinDef.h. This is necessary because the solution 101 | # based on check_symbol_exists hasn't been enough. 102 | function(target_nominmax_definition TARGET_NAME) 103 | target_compile_definitions(${TARGET_NAME} INTERFACE NOMINMAX) 104 | endfunction() -------------------------------------------------------------------------------- /test/unit/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | ####################################################### 9 | ### Dependencies ### 10 | ####################################################### 11 | find_package(Catch2 2.13.8 CONFIG) 12 | if (Catch2_FOUND) 13 | include(${Catch2_DIR}/Catch.cmake) 14 | else () 15 | FetchContent_Declare(Catch2 URL https://github.com/catchorg/Catch2/archive/refs/tags/v2.13.8.zip) 16 | FetchContent_GetProperties(Catch2) 17 | if (NOT Catch2_POPULATED) 18 | FetchContent_Populate(Catch2) 19 | set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) 20 | set(CATCH_USE_VALGRIND OFF) # "Perform SelfTests with Valgrind" 21 | set(CATCH_BUILD_EXAMPLES OFF) # "Build documentation examples" 22 | set(CATCH_BUILD_EXTRA_TESTS OFF) # "Build extra tests" 23 | set(CATCH_BUILD_STATIC_LIBRARY OFF) # "Builds static library from the main implementation. EXPERIMENTAL" 24 | set(CATCH_ENABLE_COVERAGE OFF) # "Generate coverage for codecov.io" 25 | set(CATCH_ENABLE_WERROR OFF) # "Enable all warnings as errors" 26 | set(CATCH_INSTALL_DOCS OFF) # "Install documentation alongside library" 27 | set(CATCH_INSTALL_HELPERS ON) # "Install contrib alongside library" 28 | add_subdirectory(${catch2_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}/catch2) 29 | include(${catch2_SOURCE_DIR}/contrib/Catch.cmake) 30 | endif () 31 | endif () 32 | 33 | # Create a common catch main for all tests 34 | add_library(catch_main catch_main.cpp) 35 | target_link_libraries(catch_main PUBLIC Catch2::Catch2) 36 | target_compile_features(catch_main PUBLIC cxx_std_17) 37 | 38 | ####################################################### 39 | ### Unit Tests ### 40 | ####################################################### 41 | # Macro to create test targets and register with ctest 42 | # First parameter: filename without extension 43 | # Other parameters: libraries to link in this test (if empty, it links libcppm) 44 | macro(add_small_test TEST_NAME) 45 | # Identify libraries to link with 46 | set(EXTRA_MACRO_ARGS ${ARGN}) 47 | list(LENGTH EXTRA_MACRO_ARGS NUM_EXTRA_ARGS) 48 | if (${NUM_EXTRA_ARGS} GREATER 0) 49 | set(LINK_LIBS ${ARGN}) 50 | else () 51 | set(LINK_LIBS small::small) 52 | endif () 53 | 54 | # Check if these libraries really exist 55 | set(LINK_LIBS_EXIST TRUE) 56 | foreach (LINK_LIB ${LINK_LIBS}) 57 | if (NOT TARGET ${LINK_LIB}) 58 | set(LINK_LIBS_EXIST FALSE) 59 | break() 60 | endif () 61 | endforeach () 62 | 63 | if (LINK_LIBS_EXIST) 64 | # Create executable for test 65 | add_executable(ut_${TEST_NAME} ${TEST_NAME}.cpp) 66 | 67 | # Link with catch-main 68 | target_link_libraries(ut_${TEST_NAME} PUBLIC ${LINK_LIBS} catch_main) 69 | 70 | # Enable UTF-8 on windows 71 | target_msvc_compile_options(ut_${TEST_NAME} INTERFACE "/utf-8") 72 | 73 | # Register with ctest 74 | catch_discover_tests(ut_${TEST_NAME}) 75 | else () 76 | # Library not found. Throw. 77 | message(FATAL_ERROR "${LINK_LIBS} does not exist") 78 | endif () 79 | endmacro() 80 | 81 | # Main tests 82 | add_small_test(ptr_wrapper small::small) 83 | add_small_test(pod_small_vector small::small) 84 | add_small_test(string_small_vector small::small) 85 | add_small_test(custom_small_vector small::small) 86 | add_small_test(shared_ptr_small_vector small::small) 87 | 88 | add_small_test(unicode_functions small::small) 89 | add_small_test(small_string_make small::small) 90 | add_small_test(small_string_access small::small) 91 | add_small_test(small_string_modify small::small) 92 | add_small_test(small_string_modify_algorithms small::small) 93 | add_small_test(small_string_const_algorithms small::small) 94 | add_small_test(small_string_non_member small::small) 95 | 96 | add_small_test(small_map small::small) 97 | add_small_test(small_set small::small) 98 | -------------------------------------------------------------------------------- /docs/overrides/partials/footer.html: -------------------------------------------------------------------------------- 1 | 22 | 23 | {% import "partials/language.html" as lang with context %} 24 | 25 | 26 | -------------------------------------------------------------------------------- /include/small/detail/algorithm/console_unicode_guard.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_ALGORITHM_CONSOLE_UNICODE_GUARD_HPP 9 | #define SMALL_DETAIL_ALGORITHM_CONSOLE_UNICODE_GUARD_HPP 10 | 11 | /// \section This guard encapsulates the logic to temporarily set the Windows 12 | /// console to UTF8 if: 13 | /// - the platform is windows 14 | /// - the input string requires unicode 15 | /// - the ostream is the console 16 | /// - the terminal doesn't currently support unicode 17 | /// It might also need to temporarily change the console font if the input 18 | /// string includes large code points 19 | 20 | #include 21 | 22 | #if defined(_WIN32) && __has_include() 23 | # include 24 | # undef small 25 | #endif 26 | 27 | namespace small { 28 | namespace detail { 29 | class console_unicode_guard 30 | { 31 | public: 32 | inline console_unicode_guard( 33 | std::ostream &os, 34 | size_t size, 35 | size_t codepoints, 36 | bool above_10000 = true) { 37 | #if defined(_WIN32) && __has_include() 38 | const bool is_console 39 | = &os == reinterpret_cast(&std::cout) 40 | || &os == reinterpret_cast(&std::wcout); 41 | const bool requires_unicode = size != codepoints; 42 | prev_console_output_cp = GetConsoleOutputCP(); 43 | requires_another_console_cp = is_console && requires_unicode 44 | && prev_console_output_cp 45 | != CP_UTF8; 46 | if (requires_another_console_cp) { 47 | SetConsoleOutputCP(CP_UTF8); 48 | } 49 | // If the highest codepoint is above U+10000, we also need to 50 | // change the default console font to one that supports these 51 | // characters if the current one can't support them yet. 52 | // Unfortunately, cmd.exe won't support high codepoints even if 53 | // the corresponding fonts support them. But this solves the 54 | // problem for the terminals, like most IDE terminals, and 55 | // future versions of cmd.exe. 56 | if (requires_unicode && above_10000) { 57 | // Console handle 58 | HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); 59 | // Get current font 60 | ZeroMemory(&prev_console_font, sizeof(prev_console_font)); 61 | prev_console_font.cbSize = sizeof(prev_console_font); 62 | if (GetCurrentConsoleFontEx( 63 | hStdOut, 64 | false, 65 | &prev_console_font)) { 66 | // Check if current font supports unicode above 10000 67 | // There's no simple heuristic to do that, be we do know 68 | // 1) the default console font (consolas) do not support 69 | // unicode above 10000 and 2) the user probably doesn't 70 | // want another font if they explicitly chose that font. 71 | requires_another_font = std:: 72 | wcscmp(prev_console_font.FaceName, L"Consolas"); 73 | if (requires_another_font) { 74 | CONSOLE_FONT_INFOEX new_font; 75 | ZeroMemory(&new_font, sizeof(new_font)); 76 | new_font.cbSize = sizeof(new_font); 77 | lstrcpyW(new_font.FaceName, L"Lucida Console"); 78 | SetCurrentConsoleFontEx(hStdOut, false, &new_font); 79 | } 80 | } 81 | } 82 | #else 83 | // Discard values 84 | (void) os; 85 | (void) size; 86 | (void) codepoints; 87 | (void) above_10000; 88 | #endif 89 | } 90 | 91 | inline ~console_unicode_guard() { 92 | #if defined(_WIN32) && __has_include() 93 | if (requires_another_console_cp) { 94 | SetConsoleOutputCP(prev_console_output_cp); 95 | } 96 | if (requires_another_font) { 97 | HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); 98 | SetCurrentConsoleFontEx(hStdOut, false, &prev_console_font); 99 | } 100 | #endif 101 | } 102 | 103 | private: 104 | #if defined(_WIN32) && __has_include() 105 | bool requires_another_console_cp{ false }; 106 | UINT prev_console_output_cp{ 0 }; 107 | bool requires_another_font{ false }; 108 | CONSOLE_FONT_INFOEX prev_console_font{}; 109 | #endif 110 | }; 111 | 112 | } // namespace detail 113 | } // namespace small 114 | 115 | #endif // SMALL_DETAIL_ALGORITHM_CONSOLE_UNICODE_GUARD_HPP 116 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | name: Small 9 | 10 | on: 11 | push: 12 | paths: 13 | - '**.c' 14 | - '**.cpp' 15 | - '**.h' 16 | - '**.hpp' 17 | - '**.cmake' 18 | - '**/CMakeLists.txt' 19 | - '.github/workflows/build.yml' 20 | pull_request: 21 | paths: 22 | - '**.c' 23 | - '**.cpp' 24 | - '**.h' 25 | - '**.hpp' 26 | - '**.cmake' 27 | - '**/CMakeLists.txt' 28 | - '.github/workflows/build.yml' 29 | 30 | concurrency: 31 | group: ${{format('{0}:{1}', github.repository, github.ref)}} 32 | cancel-in-progress: true 33 | 34 | jobs: 35 | Build: 36 | name: ${{ matrix.config.name }} 37 | runs-on: ${{ matrix.config.os }} 38 | strategy: 39 | fail-fast: false 40 | matrix: 41 | config: 42 | # see: https://github.com/actions/virtual-environments 43 | - { 44 | name: "Windows-MSVC/2019/Static/X86/Release", 45 | os: windows-2019, 46 | config: Release, 47 | cmake_extra_args: -G "Visual Studio 16 2019" -A Win32 -DCMAKE_CXX_FLAGS="/O2", 48 | sudocmd: "", 49 | artifact_name: "Windows x86", 50 | cores: 2, 51 | install_dir: "C:/Program Files (x86)/small" 52 | } 53 | - { 54 | name: "Windows-MSVC/2019/Static/X64/Release", 55 | os: windows-2019, 56 | config: Release, 57 | cmake_extra_args: -G "Visual Studio 16 2019" -A x64 -DCMAKE_CXX_FLAGS="/O2", 58 | sudocmd: "", 59 | artifact_name: "Windows x64", 60 | cores: 2, 61 | install_dir: "C:/Program Files/small" 62 | } 63 | - { 64 | name: "Ubuntu/20.04/Static/X64/Release", 65 | os: ubuntu-20.04, 66 | config: Release, 67 | cmake_extra_args: "-DCMAKE_C_COMPILER=/usr/bin/gcc-10 -DCMAKE_CXX_COMPILER=/usr/bin/g++-10 -DCMAKE_CXX_FLAGS=\"-O2\"", 68 | sudocmd: "sudo", 69 | artifact_name: "Linux", 70 | cores: 2, 71 | install_dir: "/usr/local/" 72 | } 73 | - { 74 | name: "Ubuntu/22.04/Static/X64/fsanitize", 75 | os: ubuntu-22.04, 76 | config: Debug, 77 | cmake_extra_args: "-DCMAKE_C_COMPILER=/usr/bin/clang-15 -DCMAKE_CXX_COMPILER=/usr/bin/clang++-15 -DCMAKE_C_FLAGS='-fsanitize=address,pointer-compare,pointer-subtract,leak,undefined' -DCMAKE_CXX_FLAGS='-fsanitize=address,pointer-compare,pointer-subtract,leak,undefined'", 78 | sudocmd: "sudo", 79 | artifact_name: "Linux", 80 | cores: 2, 81 | install_dir: "/usr/local/" 82 | } 83 | - { 84 | name: "MacOSX/12/Static/X64/Release", 85 | os: macos-12, 86 | config: Release, 87 | cmake_extra_args: "-DCMAKE_CXX_FLAGS=\"-O2\"", 88 | sudocmd: "sudo", 89 | artifact_name: "MacOSX", 90 | cores: 4, 91 | install_dir: "/usr/local/" 92 | } 93 | steps: 94 | - uses: actions/checkout@v2 95 | - name: Create Work Dir 96 | run: mkdir build 97 | - name: Configure 98 | working-directory: ./build 99 | run: | 100 | cmake .. ${{ matrix.config.cmake_extra_args }} -D CMAKE_BUILD_TYPE=${{ matrix.config.config }} -DSMALL_DEVELOPER_MODE=ON 101 | - name: Build 102 | working-directory: ./build 103 | run: cmake --build . --parallel ${{ matrix.config.cores }} --config ${{ matrix.config.config }} 104 | - name: Test 105 | working-directory: ./build 106 | run: ctest --parallel ${{ matrix.config.cores }} -C ${{ matrix.config.config }} --verbose 107 | - name: Install 108 | working-directory: ./build 109 | run: ${{ matrix.config.sudocmd }} cmake --install . --prefix "${{ matrix.config.install_dir }}" 110 | - name: CMake Subdir Test 111 | working-directory: ./test/cmake 112 | run: | 113 | mkdir build_with_subdir 114 | cd build_with_subdir 115 | cmake .. ${{ matrix.config.cmake_extra_args }} -D CMAKE_BUILD_TYPE=${{ matrix.config.config }} 116 | cmake --build . --parallel ${{ matrix.config.cores }} --config ${{ matrix.config.config }} 117 | ctest -C ${{ matrix.config.config }} --verbose 118 | - name: CMake Find Package Test 119 | working-directory: ./test/cmake 120 | run: | 121 | mkdir build_with_package 122 | cd build_with_package 123 | cmake .. ${{ matrix.config.cmake_extra_args }} -D CMAKE_BUILD_TYPE=${{ matrix.config.config }} -D BOOST_CI_INSTALL_TEST=ON -D Small_DIR="${{ matrix.config.install_dir }}" 124 | cmake --build . --parallel ${{ matrix.config.cores }} --config ${{ matrix.config.config }} 125 | ctest -C ${{ matrix.config.config }} --verbose 126 | - name: Create packages 127 | working-directory: ./build 128 | run: ${{ matrix.config.sudocmd }} cpack 129 | - name: Archive Packages 130 | uses: actions/upload-artifact@v2 131 | with: 132 | name: Binary Packages ${{ matrix.config.artifact_name }} 133 | path: build/small-?.?.?-*.* 134 | - name: Archive Installer Packages as is 135 | uses: kittaakos/upload-artifact-as-is@v0 136 | with: 137 | path: build/small-?.?.?-*.* 138 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # Main style properties 2 | Language: Cpp 3 | Standard: Latest 4 | BasedOnStyle: Microsoft 5 | 6 | # Columns and Lines 7 | ColumnLimit: 80 8 | ReflowComments: true 9 | TabWidth: 8 10 | UseCRLF: false 11 | UseTab: Never 12 | DeriveLineEnding: true 13 | 14 | # Breaking around braces 15 | BreakBeforeBraces: Custom 16 | BraceWrapping: 17 | # Control 18 | AfterControlStatement: MultiLine 19 | AfterCaseLabel: true 20 | BeforeCatch: true 21 | BeforeElse: false 22 | BeforeWhile: true 23 | # Definition 24 | AfterNamespace: false 25 | AfterStruct: true 26 | AfterClass: true 27 | SplitEmptyRecord: false 28 | AfterFunction: false 29 | SplitEmptyFunction: false 30 | BeforeLambdaBody: false 31 | AfterEnum: true 32 | SplitEmptyNamespace: true 33 | AfterUnion: true 34 | AfterExternBlock: true 35 | # Extra 36 | IndentBraces: false 37 | 38 | # Breaking around specifiers 39 | # Namespaces 40 | CompactNamespaces: false 41 | # Templates 42 | AlwaysBreakTemplateDeclarations: Yes 43 | BreakBeforeConceptDeclarations: true 44 | # Classes 45 | BreakInheritanceList: BeforeComma 46 | EmptyLineAfterAccessModifier: Never 47 | EmptyLineBeforeAccessModifier: Leave 48 | # Functions 49 | AlwaysBreakAfterDefinitionReturnType: All 50 | AlwaysBreakAfterReturnType: All 51 | MaxEmptyLinesToKeep: 2 52 | # Strings 53 | AlwaysBreakBeforeMultilineStrings: false 54 | BreakStringLiterals: true 55 | # Expressions 56 | BreakConstructorInitializers: BeforeColon 57 | BreakBeforeBinaryOperators: All 58 | BreakBeforeTernaryOperators: false 59 | 60 | # Breaking single line blocks 61 | # Control 62 | AllowShortBlocksOnASingleLine: Never 63 | AllowShortCaseLabelsOnASingleLine: false 64 | AllowShortIfStatementsOnASingleLine: Never 65 | AllowShortLoopsOnASingleLine: false 66 | # Declarations 67 | AllowShortEnumsOnASingleLine: false 68 | # Function 69 | AllowAllParametersOfDeclarationOnNextLine: false 70 | AllowShortFunctionsOnASingleLine: Empty 71 | AllowShortLambdasOnASingleLine: Inline 72 | # Expressions 73 | AllowAllArgumentsOnNextLine: false 74 | 75 | # Indentation 76 | # Parameters 77 | IndentWidth: 4 78 | # Definitions 79 | NamespaceIndentation: All 80 | IndentExternBlock: NoIndent 81 | IndentPPDirectives: AfterHash 82 | # Classes 83 | AccessModifierOffset: -4 84 | IndentAccessModifiers: false 85 | # Templates 86 | IndentRequires: false 87 | # Functions 88 | IndentWrappedFunctionNames: false 89 | LambdaBodyIndentation: OuterScope 90 | # Control 91 | ConstructorInitializerIndentWidth: 4 92 | IndentCaseBlocks: false 93 | IndentCaseLabels: false 94 | IndentGotoLabels: true 95 | # Expressions 96 | ContinuationIndentWidth: 4 97 | InsertTrailingCommas: None 98 | KeepEmptyLinesAtTheStartOfBlocks: false 99 | 100 | # Alignment 101 | # Macros 102 | AlignConsecutiveMacros: Consecutive 103 | AttributeMacros: ['SMALL_CONSTEXPR'] 104 | # Declaration 105 | PointerAlignment: Left 106 | ReferenceAlignment: Pointer 107 | DerivePointerAlignment: true 108 | AlignConsecutiveDeclarations: None 109 | # Namespace 110 | ShortNamespaceLines: 0 111 | # Brackets 112 | AlignAfterOpenBracket: AlwaysBreak 113 | # Expressions 114 | AlignArrayOfStructures: Right 115 | AlignConsecutiveAssignments: None 116 | AlignConsecutiveBitFields: None 117 | AlignEscapedNewlines: Left 118 | AlignOperands: Align 119 | AlignTrailingComments: true 120 | 121 | # Spaces 122 | SpaceAfterCStyleCast: true 123 | SpaceAfterLogicalNot: false 124 | SpaceAfterTemplateKeyword: true 125 | SpaceAroundPointerQualifiers: Default 126 | SpaceBeforeAssignmentOperators: true 127 | SpaceBeforeCaseColon: false 128 | SpaceBeforeCpp11BracedList: false 129 | SpaceBeforeCtorInitializerColon: true 130 | SpaceBeforeInheritanceColon: true 131 | SpaceBeforeParens: ControlStatements 132 | SpaceBeforeRangeBasedForLoopColon: false 133 | SpacesInSquareBrackets: false 134 | SpaceBeforeSquareBrackets: false 135 | SpacesBeforeTrailingComments: 1 136 | SpaceInEmptyBlock: false 137 | SpaceInEmptyParentheses: false 138 | SpacesInAngles: Never 139 | SpacesInCStyleCastParentheses: false 140 | SpacesInConditionalStatement: false 141 | SpacesInParentheses: false 142 | Cpp11BracedListStyle: false 143 | 144 | # BinPack 145 | BinPackArguments: false 146 | BinPackParameters: false 147 | BitFieldColonSpacing: After 148 | ExperimentalAutoDetectBinPacking: true 149 | 150 | # Penalties 151 | PenaltyBreakAssignment: 512 152 | PenaltyBreakBeforeFirstCallParameter: 512 153 | PenaltyBreakComment: 512 154 | PenaltyBreakFirstLessLess: 512 155 | PenaltyBreakString: 512 156 | PenaltyBreakTemplateDeclaration: 512 157 | PenaltyExcessCharacter: 256 158 | PenaltyIndentedWhitespace: 8 159 | PenaltyReturnTypeOnItsOwnLine: 2 160 | 161 | # Sorting 162 | SortIncludes: CaseInsensitive 163 | SortUsingDeclarations: true 164 | IncludeBlocks: Merge 165 | IncludeCategories: 166 | - Regex: '^["<]small/.*/detail/prologue.hpp' # config 167 | Priority: 1 168 | - Regex: '^["<]small/.*/detail/epilogue.hpp' # config 169 | Priority: 10 170 | - Regex: '^["<]small/config/' # config 171 | Priority: 2 172 | - Regex: '^["<]small/detail/config.hpp' # config 173 | Priority: 2 174 | - Regex: '^["<]small/.*/detail/' # details 175 | Priority: 4 176 | - Regex: '^["<]small/detail/' # details 177 | Priority: 4 178 | - Regex: '^["<]small/' # internal 179 | Priority: 3 180 | - Regex: '^(["<](boost)/)' # external 181 | Priority: 5 182 | - Regex: '<[[:alnum:].]+>' # C++ 183 | Priority: 6 184 | - Regex: '.*' # any other 185 | Priority: 7 186 | 187 | # Comments 188 | FixNamespaceComments: true 189 | CommentPragmas: '^ clang-format' 190 | -------------------------------------------------------------------------------- /include/small/detail/algorithm/shift.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_ALGORITHM_SHIFT_HPP 9 | #define SMALL_DETAIL_ALGORITHM_SHIFT_HPP 10 | 11 | /// \headerfile Adapted from https://github.com/danra/shift_proposal 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace small { 19 | namespace detail { 20 | namespace shift { 21 | 22 | template 23 | using difference_type_t = typename std::iterator_traits< 24 | I>::difference_type; 25 | 26 | template 27 | using iterator_category_t = typename std::iterator_traits< 28 | I>::iterator_category; 29 | 30 | template 31 | constexpr bool is_category = false; 32 | template 33 | constexpr bool is_category< 34 | I, 35 | Tag, 36 | std::enable_if_t< 37 | std::is_convertible_v, Tag>>> = true; 38 | 39 | /// Increment (decrement for negative n) i |n| times or until i == 40 | /// bound, whichever comes first. Returns n - the difference between 41 | /// i's final position and its initial position. (Note: "advance" 42 | /// has overloads with this behavior in the Ranges TS.) 43 | template 44 | constexpr difference_type_t 45 | bounded_advance(I &i, difference_type_t n, I const bound) { 46 | if constexpr (is_category) { 47 | for (; n < 0 && i != bound; ++n, void(--i)) { 48 | ; 49 | } 50 | } 51 | 52 | for (; n > 0 && i != bound; --n, void(++i)) { 53 | ; 54 | } 55 | 56 | return n; 57 | } 58 | 59 | template 60 | ForwardIt 61 | shift_left( 62 | ForwardIt first, 63 | ForwardIt last, 64 | difference_type_t n) { 65 | if (n <= 0) { 66 | return last; 67 | } 68 | 69 | auto mid = first; 70 | if (bounded_advance(mid, n, last)) { 71 | return first; 72 | } 73 | 74 | return std:: 75 | move(std::move(mid), std::move(last), std::move(first)); 76 | } 77 | 78 | template 79 | ForwardIt 80 | shift_right( 81 | ForwardIt first, 82 | ForwardIt last, 83 | difference_type_t n) { 84 | if (n <= 0) { 85 | return first; 86 | } 87 | 88 | if constexpr (is_category< 89 | ForwardIt, 90 | std::bidirectional_iterator_tag>) { 91 | auto mid = last; 92 | if (bounded_advance(mid, -n, first)) { 93 | return last; 94 | } 95 | return std::move_backward( 96 | std::move(first), 97 | std::move(mid), 98 | std::move(last)); 99 | } else { 100 | auto result = first; 101 | if (bounded_advance(result, n, last)) { 102 | return last; 103 | } 104 | 105 | // Invariant: next(first, n) == result 106 | // Invariant: next(trail, n) == lead 107 | 108 | auto lead = result; 109 | auto trail = first; 110 | 111 | for (; trail != result; ++lead, void(++trail)) { 112 | if (lead == last) { 113 | // The range looks like: 114 | // 115 | // |-- (n - k) elements --|-- k elements --|-- (n 116 | // - k) elements --| 117 | // ^-first trail-^ ^-result last-^ 118 | // 119 | // Note that distance(first, trail) == 120 | // distance(result, last) 121 | std::move( 122 | std::move(first), 123 | std::move(trail), 124 | std::move(result)); 125 | return result; 126 | } 127 | } 128 | 129 | for (;;) { 130 | for (auto mid = first; mid != result; 131 | ++lead, void(++trail), ++mid) { 132 | if (lead == last) { 133 | // The range looks like: 134 | // 135 | // |-- (n - k) elements --|-- k elements --|-- 136 | // ... --|-- n elements --| 137 | // ^-first mid-^ result-^ 138 | // ^-trail last-^ 139 | // 140 | trail = std::move(mid, result, std::move(trail)); 141 | std::move( 142 | std::move(first), 143 | std::move(mid), 144 | std::move(trail)); 145 | return result; 146 | } 147 | std::iter_swap(mid, trail); 148 | } 149 | } 150 | } 151 | } 152 | } // namespace shift 153 | } // namespace detail 154 | } // namespace small 155 | 156 | #endif // !defined(SMALL_DETAIL_ALGORITHM_SHIFT_HPP) -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | # 4 | # Distributed under the Boost Software License, Version 1.0. 5 | # https://www.boost.org/LICENSE_1_0.txt 6 | # 7 | 8 | ####################################################### 9 | ### Small ### 10 | ####################################################### 11 | # Project information 12 | cmake_minimum_required(VERSION 3.15) 13 | project( 14 | small 15 | VERSION 0.2.1 16 | DESCRIPTION "C++ small containers" 17 | HOMEPAGE_URL "https://alandefreitas.github.io/small" 18 | ) 19 | 20 | set(SMALL_ROOT_DIR ${CMAKE_CURRENT_SOURCE_DIR}) 21 | set(SMALL_VERSION ${CMAKE_PROJECT_VERSION}) 22 | message(STATUS "${CMAKE_PROJECT_NAME} v${CMAKE_PROJECT_VERSION}: ${CMAKE_PROJECT_HOMEPAGE_URL}") 23 | 24 | include(cmake/small/in_source_guard.cmake) 25 | include(cmake/small/variables.cmake) 26 | 27 | ####################################################### 28 | ### Options ### 29 | ####################################################### 30 | # What to build 31 | option(SMALL_BUILD_INSTALLER "Build installer target" ${SMALL_MASTER_PROJECT}) 32 | option(SMALL_BUILD_PACKAGE "Build package" ${SMALL_MASTER_PROJECT}) 33 | 34 | # How to build 35 | option(SMALL_BUILD_WITH_EXCEPTIONS "Add compiler flags to use exceptions." ON) 36 | 37 | # Development options 38 | include(cmake/small/dev-options.cmake) 39 | 40 | ####################################################### 41 | ### Libraries ### 42 | ####################################################### 43 | add_subdirectory(include) 44 | 45 | ####################################################### 46 | ### Installer ### 47 | ####################################################### 48 | if (SMALL_BUILD_INSTALLER) 49 | # Install targets 50 | install(TARGETS small 51 | EXPORT small-targets 52 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 53 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 54 | ) 55 | 56 | # Install headers 57 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/small 58 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 59 | FILES_MATCHING PATTERN "*.hpp" 60 | ) 61 | 62 | # Install cmake targets script 63 | install(EXPORT small-targets 64 | FILE small-targets.cmake 65 | NAMESPACE small:: 66 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/small 67 | ) 68 | 69 | # https://cliutils.gitlab.io/modern-cmake/chapters/install/installing.html 70 | # Set variable where the cmake config is 71 | set(CONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/small) 72 | message(STATUS "${CMAKE_PROJECT_NAME} install directory: ${CMAKE_INSTALL_PREFIX}") 73 | message(STATUS "${CMAKE_PROJECT_NAME} library install directory: ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") 74 | 75 | # Create small-config-version.cmake and install it 76 | if (CMAKE_PROJECT_VERSION VERSION_LESS 1.0.0) 77 | set(compatibility_mode SameMajorVersion) 78 | else () 79 | set(compatibility_mode SameMinorVersion) 80 | endif () 81 | write_basic_package_version_file( 82 | small-config-version.cmake 83 | VERSION ${PACKAGE_VERSION} 84 | COMPATIBILITY ${compatibility_mode} 85 | ) 86 | 87 | # Install the file small-config-version.cmake 88 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/small-config-version.cmake 89 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/small) 90 | 91 | # Create small-config.cmake from small-config.cmake.in 92 | configure_package_config_file( 93 | ${CMAKE_CURRENT_SOURCE_DIR}/small-config.cmake.in # input file 94 | ${CMAKE_CURRENT_BINARY_DIR}/small-config.cmake # output file 95 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/small 96 | ) 97 | 98 | # Install the file small-config.cmake 99 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/small-config.cmake 100 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/small) 101 | endif () 102 | 103 | ####################################################### 104 | ### Packages ### 105 | ####################################################### 106 | if (SMALL_BUILD_INSTALLER AND SMALL_BUILD_PACKAGE) 107 | # Set the cpack variables 108 | # https://cliutils.gitlab.io/modern-cmake/chapters/install/packaging.html 109 | 110 | # The most common cpack variables 111 | set(CPACK_PACKAGE_VENDOR "small") 112 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "small: C++ small containers") 113 | set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) 114 | set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) 115 | set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) 116 | set(CPACK_RESOURCE_FILE_LICENSE "${SMALL_ROOT_DIR}/LICENSE") 117 | set(CPACK_RESOURCE_FILE_README "${SMALL_ROOT_DIR}/README.md") 118 | 119 | # Set CPACK_SOURCE_IGNORE_FILES with files source packages shouldn't install 120 | # We get these from .gitignore to avoid redundancy 121 | FILE(READ .gitignore GITIGNORE_CONTENTS) 122 | STRING(REGEX REPLACE ";" "\\\\;" GITIGNORE_CONTENTS "${GITIGNORE_CONTENTS}") 123 | STRING(REGEX REPLACE "\n" ";" GITIGNORE_CONTENTS "${GITIGNORE_CONTENTS}") 124 | set(CPACK_SOURCE_IGNORE_FILES ${GITIGNORE_CONTENTS}) 125 | 126 | # Always include CPack at last 127 | include(CPack) 128 | endif () 129 | 130 | ####################################################### 131 | ### Developer mode ### 132 | ####################################################### 133 | if (NOT SMALL_DEVELOPER_MODE) 134 | return() 135 | elseif (NOT SMALL_MASTER_PROJECT) 136 | message( 137 | AUTHOR_WARNING 138 | "Developer mode is intended for developers" 139 | ) 140 | endif () 141 | 142 | add_subdirectory(dev-tools) 143 | 144 | if (SMALL_BUILD_TESTS) 145 | include(CTest) 146 | enable_testing() 147 | add_subdirectory(test) 148 | endif () 149 | 150 | if (SMALL_BUILD_DOCS) 151 | add_subdirectory(docs) 152 | endif () 153 | 154 | if (SMALL_BUILD_COVERAGE_REPORT) 155 | include(cmake/small/coverage-report.cmake) 156 | endif () 157 | 158 | if (SMALL_BUILD_LINT) 159 | include(cmake/small/lint-targets.cmake) 160 | endif () 161 | 162 | ####################################################### 163 | ### Examples ### 164 | ####################################################### 165 | if (SMALL_BUILD_EXAMPLES) 166 | add_subdirectory(examples) 167 | endif () 168 | -------------------------------------------------------------------------------- /include/small/detail/algorithm/intcmp.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_ALGORITHM_INTCMP_HPP 9 | #define SMALL_DETAIL_ALGORITHM_INTCMP_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | /// \headerfile Compare numbers of different types 16 | 17 | namespace small { 18 | namespace detail { 19 | 20 | template 21 | constexpr bool 22 | cmp_equal(T t, U u) noexcept { 23 | using UT = std::make_unsigned_t; 24 | using UU = std::make_unsigned_t; 25 | if constexpr (std::is_signed_v == std::is_signed_v) 26 | return t == u; 27 | else if constexpr (std::is_signed_v) 28 | return t < 0 ? false : UT(t) == u; 29 | else 30 | return u < 0 ? false : t == UU(u); 31 | } 32 | 33 | template 34 | constexpr bool 35 | cmp_not_equal(T t, U u) noexcept { 36 | return !cmp_equal(t, u); 37 | } 38 | 39 | template 40 | constexpr bool 41 | cmp_less(T t, U u) noexcept { 42 | using UT = std::make_unsigned_t; 43 | using UU = std::make_unsigned_t; 44 | if constexpr (std::is_signed_v == std::is_signed_v) 45 | return t < u; 46 | else if constexpr (std::is_signed_v) 47 | return t < 0 ? true : UT(t) < u; 48 | else 49 | return u < 0 ? false : t < UU(u); 50 | } 51 | 52 | template 53 | constexpr bool 54 | cmp_greater(T t, U u) noexcept { 55 | return cmp_less(u, t); 56 | } 57 | 58 | template 59 | constexpr bool 60 | cmp_less_equal(T t, U u) noexcept { 61 | return !cmp_greater(t, u); 62 | } 63 | 64 | template 65 | constexpr bool 66 | cmp_greater_equal(T t, U u) noexcept { 67 | return !cmp_less(t, u); 68 | } 69 | 70 | namespace detail { 71 | /// \section Traits to promote integer types 72 | 73 | /// \struct Get the integer type for the given parameters 74 | template 75 | struct custom_int; 76 | 77 | template <> 78 | struct custom_int<8, true> 79 | { 80 | typedef int8_t type; 81 | }; 82 | 83 | template <> 84 | struct custom_int<16, true> 85 | { 86 | typedef int16_t type; 87 | }; 88 | 89 | template <> 90 | struct custom_int<32, true> 91 | { 92 | typedef int32_t type; 93 | }; 94 | 95 | template <> 96 | struct custom_int<64, true> 97 | { 98 | typedef int64_t type; 99 | }; 100 | 101 | template <> 102 | struct custom_int<8, false> 103 | { 104 | typedef uint8_t type; 105 | }; 106 | 107 | template <> 108 | struct custom_int<16, false> 109 | { 110 | typedef uint16_t type; 111 | }; 112 | 113 | template <> 114 | struct custom_int<32, false> 115 | { 116 | typedef uint32_t type; 117 | }; 118 | 119 | template <> 120 | struct custom_int<64, false> 121 | { 122 | typedef uint64_t type; 123 | }; 124 | 125 | template 126 | using custom_int_t = typename custom_int::type; 127 | 128 | template 129 | struct promoted_size 130 | { 131 | static constexpr std::size_t first_size = sizeof(T); 132 | static constexpr std::size_t second_size = sizeof(U); 133 | static constexpr bool first_is_signed = std::is_signed_v; 134 | static constexpr bool second_is_signed = std::is_signed_v; 135 | static constexpr std::size_t largest_size = std:: 136 | max(first_size, second_size); 137 | static constexpr std::size_t smallest_size = std:: 138 | min(first_size, second_size); 139 | static constexpr bool largest_is_signed 140 | = first_size < second_size ? 141 | second_is_signed : 142 | first_is_signed; 143 | static constexpr bool smallest_is_signed 144 | = first_size < second_size ? 145 | first_is_signed : 146 | second_is_signed; 147 | static constexpr bool largest_needs_double 148 | = largest_size == smallest_size && largest_size != 64 / 8 149 | && not largest_is_signed && smallest_is_signed; 150 | static constexpr size_t value = largest_size 151 | * (largest_needs_double ? 2 : 1); 152 | }; 153 | 154 | template 155 | constexpr std::size_t promoted_size_v = promoted_size::value; 156 | 157 | /// \struct Promote an integer to the proper type capable of 158 | /// representing both integers 159 | template 160 | using promoted = custom_int< 161 | promoted_size_v * 8, 162 | std::is_signed_v || std::is_signed_v>; 163 | 164 | template 165 | using promoted_t = typename promoted::type; 166 | } // namespace detail 167 | 168 | /// \brief Minimum number of two types. This returns a value rather than 169 | template 170 | constexpr detail::promoted_t 171 | min_value(T t, U u) noexcept { 172 | if (cmp_less(t, u)) { 173 | return static_cast>(t); 174 | } else { 175 | return static_cast>(u); 176 | } 177 | } 178 | 179 | template 180 | constexpr detail::promoted_t 181 | max_value(T t, U u) noexcept { 182 | if (cmp_less(t, u)) { 183 | return static_cast>(u); 184 | } else { 185 | return static_cast>(t); 186 | } 187 | } 188 | 189 | inline long long int 190 | div_ceil(long long int numerator, long long int denominator) { 191 | lldiv_t res = std::div(numerator, denominator); 192 | return res.rem ? (res.quot + 1) : res.quot; 193 | } 194 | 195 | } // namespace detail 196 | } // namespace small 197 | 198 | #endif // SMALL_DETAIL_ALGORITHM_INTCMP_HPP 199 | -------------------------------------------------------------------------------- /include/small/detail/exception/scope_guard.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_EXCEPTION_SCOPE_GUARD_HPP 9 | #define SMALL_DETAIL_EXCEPTION_SCOPE_GUARD_HPP 10 | 11 | #include 12 | #include // uintptr_t 13 | #include 14 | #include 15 | #include 16 | 17 | 18 | namespace small { 19 | namespace detail { 20 | /// \brief The common functions in a scope guard 21 | /// \note Adapted from folly / abseil 22 | class scope_guard_impl_base 23 | { 24 | public: 25 | /// \brief Tell the scope guard not to run the function 26 | void 27 | dismiss() noexcept { 28 | dismissed_ = true; 29 | } 30 | 31 | /// \brief Tell the scope guard to run the function again 32 | void 33 | rehire() noexcept { 34 | dismissed_ = false; 35 | } 36 | 37 | protected: 38 | /// Create the guard 39 | explicit scope_guard_impl_base(bool dismissed = false) noexcept 40 | : dismissed_(dismissed) {} 41 | 42 | /// Terminate if we have an exception 43 | inline static void 44 | terminate() noexcept { 45 | // Ensure the availability of std::cerr 46 | std::ios_base::Init ioInit; 47 | std::cerr << "This program will now terminate because a " 48 | "scope_guard callback " 49 | "threw an \nexception.\n"; 50 | std::rethrow_exception(std::current_exception()); 51 | } 52 | 53 | static scope_guard_impl_base 54 | make_empty_scope_guard() noexcept { 55 | return scope_guard_impl_base{}; 56 | } 57 | 58 | template 59 | static const T & 60 | as_const(const T &t) noexcept { 61 | return t; 62 | } 63 | 64 | bool dismissed_; 65 | }; 66 | 67 | /// \brief A scope guard that calls a function when being destructed 68 | /// unless told otherwise 69 | template 70 | class scope_guard_impl : public scope_guard_impl_base 71 | { 72 | public: 73 | explicit scope_guard_impl(FunctionType &fn) noexcept( 74 | std::is_nothrow_copy_constructible::value) 75 | : scope_guard_impl( 76 | as_const(fn), 77 | makeFailsafe( 78 | std::is_nothrow_copy_constructible{}, 79 | &fn)) {} 80 | 81 | explicit scope_guard_impl(const FunctionType &fn) noexcept( 82 | std::is_nothrow_copy_constructible::value) 83 | : scope_guard_impl( 84 | fn, 85 | makeFailsafe( 86 | std::is_nothrow_copy_constructible{}, 87 | &fn)) {} 88 | 89 | explicit scope_guard_impl(FunctionType &&fn) noexcept( 90 | std::is_nothrow_move_constructible::value) 91 | : scope_guard_impl( 92 | std::move_if_noexcept(fn), 93 | makeFailsafe( 94 | std::is_nothrow_move_constructible{}, 95 | &fn)) {} 96 | 97 | /// A tag for a dismissed scope guard 98 | struct scope_guard_dismissed 99 | {}; 100 | 101 | explicit scope_guard_impl( 102 | FunctionType &&fn, 103 | scope_guard_dismissed) noexcept(std:: 104 | is_nothrow_move_constructible< 105 | FunctionType>::value) 106 | // No need for failsafe in this case, as the guard is dismissed. 107 | : scope_guard_impl_base{ true }, 108 | function_(std::forward(fn)) {} 109 | 110 | scope_guard_impl(scope_guard_impl &&other) noexcept( 111 | std::is_nothrow_move_constructible::value) 112 | : function_(std::move_if_noexcept(other.function_)) { 113 | // If the above line attempts a copy and the copy throws, other 114 | // is left owning the cleanup action and will execute it (or 115 | // not) depending on the value of other.dismissed_. The 116 | // following lines only execute if the move/copy succeeded, in 117 | // which case *this assumes ownership of the cleanup action and 118 | // dismisses other. 119 | dismissed_ = std::exchange(other.dismissed_, true); 120 | } 121 | 122 | ~scope_guard_impl() noexcept(InvokeNoexcept) { 123 | if (!dismissed_) { 124 | execute(); 125 | } 126 | } 127 | 128 | private: 129 | static scope_guard_impl_base 130 | makeFailsafe(std::true_type, const void *) noexcept { 131 | return make_empty_scope_guard(); 132 | } 133 | 134 | template 135 | static auto 136 | makeFailsafe(std::false_type, Fn *fn) noexcept 137 | -> scope_guard_impl { 138 | return scope_guard_impl{ 139 | std::ref(*fn) 140 | }; 141 | } 142 | 143 | template 144 | explicit scope_guard_impl(Fn &&fn, scope_guard_impl_base &&failsafe) 145 | : scope_guard_impl_base{}, function_(std::forward(fn)) { 146 | failsafe.dismiss(); 147 | } 148 | 149 | void *operator new(std::size_t) = delete; 150 | 151 | void 152 | execute() noexcept(InvokeNoexcept) { 153 | if (InvokeNoexcept) { 154 | using R = decltype(function_()); 155 | auto catcher_word = reinterpret_cast(&terminate); 156 | auto catcher = reinterpret_cast(catcher_word); 157 | catch_exception(function_, catcher); 158 | } else { 159 | function_(); 160 | } 161 | } 162 | 163 | FunctionType function_; 164 | }; 165 | 166 | /// A decayed type scope guard 167 | template 168 | using scope_guard_impl_decay 169 | = scope_guard_impl::type, InvokeNoExcept>; 170 | 171 | } // namespace detail 172 | 173 | /// \brief The default scope guard alias for a function 174 | template 175 | using scope_guard = detail::scope_guard_impl_decay; 176 | 177 | /// \brief Make a scope guard with a function 178 | template 179 | [[nodiscard]] scope_guard 180 | make_guard(F &&f) noexcept(noexcept(scope_guard(static_cast(f)))) { 181 | return scope_guard(static_cast(f)); 182 | } 183 | } // namespace small 184 | 185 | #endif // SMALL_DETAIL_EXCEPTION_SCOPE_GUARD_HPP 186 | -------------------------------------------------------------------------------- /include/small/detail/iterator/iterator_type_traits.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_ITERATOR_ITERATOR_TYPE_TRAITS_HPP 9 | #define SMALL_DETAIL_ITERATOR_ITERATOR_TYPE_TRAITS_HPP 10 | 11 | #include 12 | #include 13 | 14 | namespace small { 15 | namespace detail { 16 | /// Difference type for a custom iterator 17 | /// \note Adapted from https://github.com/danra/shift_proposal/ 18 | template 19 | using difference_type_type = typename std::iterator_traits< 20 | I>::difference_type; 21 | 22 | /// Category type for a custom iterator 23 | /// \note Adapted from https://github.com/danra/shift_proposal/ 24 | template 25 | using iterator_category_type = typename std::iterator_traits< 26 | I>::iterator_category; 27 | 28 | /// Whether iterator is in a category Tag (base case = false) 29 | /// \note Adapted from https://github.com/danra/shift_proposal/ 30 | template 31 | static constexpr bool is_category_convertible = false; 32 | 33 | /// Whether iterator is in a category Tag (usual case) 34 | /// \note Adapted from https://github.com/danra/shift_proposal/ 35 | template 36 | static constexpr bool is_category_convertible< 37 | I, 38 | Tag, 39 | std::enable_if_t< 40 | std::is_convertible_v, Tag>>> = true; 41 | 42 | /// Check if a type has an iterator category 43 | template 44 | struct has_iterator_category 45 | { 46 | private: 47 | /// An arbitrary type of size == 2 48 | struct two 49 | { 50 | char lx; 51 | char lxx; 52 | }; 53 | 54 | /// Return type of size == 2 when UP doesn't has iterator category 55 | template 56 | static two 57 | test(...) { 58 | return two{ char(), char() }; 59 | } 60 | 61 | /// Return type of size == 1 when UP has iterator category 62 | template 63 | static char 64 | test(typename UP::iterator_category * = 0) { 65 | return char(); 66 | } 67 | 68 | public: 69 | /// Indicates if I has a category iterator (when return type of test 70 | /// has size == 1) 71 | static const bool value = sizeof(test(0)) == 1; 72 | }; 73 | 74 | /// Check if type has an iterator category convertible to UP 75 | template < 76 | class I, 77 | class UP, 78 | bool = has_iterator_category>::value> 79 | struct has_iterator_category_convertible_to 80 | : public std::integral_constant< 81 | bool, 82 | std::is_convertible< 83 | typename std::iterator_traits::iterator_category, 84 | UP>::value> 85 | {}; 86 | 87 | /// Check if type has an iterator category convertible to UP 88 | template 89 | struct has_iterator_category_convertible_to 90 | : public std::false_type 91 | {}; 92 | 93 | /// Check if type is convertible to input_iterator 94 | template 95 | struct is_input_iterator 96 | : public has_iterator_category_convertible_to< 97 | I, 98 | std::input_iterator_tag> 99 | {}; 100 | 101 | /// Type that is only valid if input type is convertible to an input 102 | /// iterator 103 | template 104 | using enable_if_iterator_t = typename std::enable_if_t< 105 | is_input_iterator::value 106 | && std::is_constructible< 107 | value_type, 108 | typename std::iterator_traits::reference>::value, 109 | I>; 110 | 111 | template 112 | constexpr bool is_input_iterator_v = is_input_iterator::value; 113 | 114 | /// Common iterator category primary template. 115 | /// A variant iterator will always have the minimal iterator category 116 | /// requirements for its final type. The implementation should always 117 | /// fall into one of the specializations though. 118 | template 119 | struct common_iterator_tag; 120 | 121 | /// Base iterator: 1 Iterator type 122 | template 123 | struct common_iterator_tag 124 | { 125 | using type = Tag1; 126 | }; 127 | 128 | /// Base iterator: 2 Iterator types 129 | template 130 | struct common_iterator_tag 131 | { 132 | template 133 | static constexpr bool both_base_of_v = std::is_base_of_v 134 | &&std::is_base_of_v; 135 | 136 | static constexpr bool both_input 137 | = both_base_of_v; 138 | static constexpr bool both_forward 139 | = both_base_of_v; 140 | static constexpr bool both_bidirectional 141 | = both_base_of_v; 142 | static constexpr bool both_random_access 143 | = both_base_of_v; 144 | static constexpr bool both_output 145 | = both_base_of_v; 146 | 147 | using type = std::conditional_t< 148 | std::is_same_v, 149 | Tag1, 150 | std::conditional_t< 151 | both_random_access, 152 | std::random_access_iterator_tag, 153 | std::conditional_t< 154 | both_bidirectional, 155 | std::bidirectional_iterator_tag, 156 | std::conditional_t< 157 | both_forward, 158 | std::forward_iterator_tag, 159 | std::conditional_t< 160 | both_input, 161 | std::input_iterator_tag, 162 | std::conditional_t< 163 | both_output, 164 | std::output_iterator_tag, 165 | void>>>>>>; 166 | }; 167 | 168 | /// Base iterator: > 2 Iterator types 169 | template 170 | struct common_iterator_tag 171 | { 172 | using type = typename common_iterator_tag< 173 | typename common_iterator_tag::type, 174 | typename common_iterator_tag::type>::type; 175 | }; 176 | 177 | /// Base iterator type 178 | template 179 | using common_iterator_tag_t = typename common_iterator_tag< 180 | Types...>::type; 181 | 182 | } // namespace detail 183 | } // namespace small 184 | 185 | #endif // SMALL_DETAIL_ITERATOR_ITERATOR_TYPE_TRAITS_HPP 186 | -------------------------------------------------------------------------------- /test/unit/small_string_non_member.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | // UTF8 string literals are really not safe in MSVC. 20 | // u8"" doesn't work properly even with escape sequences. 21 | // More recent versions might improve on that a little, but we will still 22 | // need a more robust solution to support older versions in any case. 23 | // Until then, it seems like the most robust solution is to initialize 24 | // small::strings with U"...", even if that requires converting the 25 | // codepoints. 26 | constexpr bool 27 | is_windows() { 28 | #if defined(_WIN32) 29 | return true; 30 | #else 31 | return false; 32 | #endif 33 | } 34 | 35 | TEST_CASE("String Non-Member") { 36 | using namespace small; 37 | 38 | SECTION("Non-member") { 39 | SECTION("Concatenate strings") { 40 | SECTION("const string & + const string &") { 41 | string lhs = "abc"; 42 | string rhs = "def"; 43 | string c = lhs + rhs; 44 | REQUIRE(c == "abcdef"); 45 | } 46 | SECTION("const string & + const char *") { 47 | string lhs = "abc"; 48 | string c = lhs + "def"; 49 | REQUIRE(c == "abcdef"); 50 | } 51 | SECTION("const string & + char ") { 52 | string lhs = "abc"; 53 | char rhs = 'd'; 54 | string c = lhs + rhs; 55 | REQUIRE(c == "abcd"); 56 | } 57 | SECTION("const char * + const string &") { 58 | string rhs = "def"; 59 | string c = "abc" + rhs; 60 | REQUIRE(c == "abcdef"); 61 | } 62 | SECTION("char + const string &") { 63 | char lhs = 'a'; 64 | string rhs = "def"; 65 | string c = lhs + rhs; 66 | REQUIRE(c == "adef"); 67 | } 68 | SECTION("string && + string &&") { 69 | string lhs = "abc"; 70 | string rhs = "def"; 71 | string c = std::move(lhs) + std::move(rhs); 72 | REQUIRE(c == "abcdef"); 73 | } 74 | SECTION("string && + const string &") { 75 | string lhs = "abc"; 76 | string rhs = "def"; 77 | string c = std::move(lhs) + rhs; 78 | REQUIRE(c == "abcdef"); 79 | } 80 | SECTION("string && + const char *") { 81 | string lhs = "abc"; 82 | string c = std::move(lhs) + "def"; 83 | REQUIRE(c == "abcdef"); 84 | } 85 | SECTION("string && + char ") { 86 | string lhs = "abc"; 87 | char rhs = 'd'; 88 | string c = std::move(lhs) + rhs; 89 | REQUIRE(c == "abcd"); 90 | } 91 | SECTION("const string & + string &&") { 92 | string lhs = "abc"; 93 | string rhs = "def"; 94 | string c = lhs + std::move(rhs); 95 | REQUIRE(c == "abcdef"); 96 | } 97 | SECTION("const char * + string &&") { 98 | string rhs = "def"; 99 | string c = "abc" + std::move(rhs); 100 | REQUIRE(c == "abcdef"); 101 | } 102 | SECTION("char + string &&") { 103 | char lhs = 'a'; 104 | string rhs = "def"; 105 | string c = lhs + std::move(rhs); 106 | REQUIRE(c == "adef"); 107 | } 108 | } 109 | 110 | SECTION("Erase") { 111 | string cnt(10, ' '); 112 | std::iota(cnt.begin(), cnt.end(), '0'); 113 | 114 | SECTION("Values") { 115 | erase(cnt, '3'); 116 | REQUIRE(cnt == "012456789"); 117 | } 118 | 119 | SECTION("Condition") { 120 | size_t n_erased = erase_if(cnt, [](char x) { 121 | return (x - '0') % 2 == 0; 122 | }); 123 | REQUIRE(cnt == "13579"); 124 | REQUIRE(n_erased == 5); 125 | } 126 | } 127 | 128 | SECTION("Streams") { 129 | small::string a = "123456"; 130 | 131 | SECTION("Output") { 132 | std::stringstream ss; 133 | ss << a; 134 | REQUIRE(ss.str() == "123456"); 135 | } 136 | 137 | SECTION("Input") { 138 | std::stringstream ss; 139 | ss << "123"; 140 | REQUIRE(ss.str() == "123"); 141 | ss >> a; 142 | REQUIRE(a == "123"); 143 | } 144 | 145 | SECTION("Getline") { 146 | std::stringstream ss; 147 | ss << "123 456\n789\n"; 148 | getline(ss, a); 149 | REQUIRE(a == "123 456"); 150 | } 151 | } 152 | 153 | SECTION("String to number") { 154 | SECTION("Integer") { 155 | small::string i = "123"; 156 | std::unique_ptr size = std::make_unique(0); 157 | SECTION("stoi") { 158 | int n = stoi(i, size.get(), 10); 159 | REQUIRE(n == 123); 160 | REQUIRE(*size == 3); 161 | } 162 | SECTION("stol") { 163 | long n = stol(i, size.get(), 10); 164 | REQUIRE(n == 123); 165 | REQUIRE(*size == 3); 166 | } 167 | SECTION("stoll") { 168 | long long n = stoll(i, size.get(), 10); 169 | REQUIRE(n == 123); 170 | REQUIRE(*size == 3); 171 | } 172 | SECTION("stoul") { 173 | unsigned long n = stoul(i, size.get(), 10); 174 | REQUIRE(n == 123); 175 | REQUIRE(*size == 3); 176 | } 177 | SECTION("stoull") { 178 | unsigned long long n = stoull(i, size.get(), 10); 179 | REQUIRE(n == 123); 180 | REQUIRE(*size == 3); 181 | } 182 | } 183 | 184 | SECTION("Floating") { 185 | small::string d = "123.456"; 186 | std::unique_ptr size = std::make_unique(0); 187 | SECTION("stof") { 188 | float n = stof(d, size.get()); 189 | REQUIRE(n >= 123.455); 190 | REQUIRE(n <= 123.457); 191 | REQUIRE(*size == 7); 192 | } 193 | SECTION("stod") { 194 | double n = stod(d, size.get()); 195 | REQUIRE(n >= 123.455); 196 | REQUIRE(n <= 123.457); 197 | REQUIRE(*size == 7); 198 | } 199 | SECTION("stold") { 200 | long double n = stold(d, size.get()); 201 | REQUIRE(n >= 123.455); 202 | REQUIRE(n <= 123.457); 203 | REQUIRE(*size == 7); 204 | } 205 | } 206 | } 207 | 208 | SECTION("Number to string") { 209 | REQUIRE(small::to_string(static_cast(123)) == "123"); 210 | REQUIRE(small::to_string(static_cast(123)) == "123"); 211 | REQUIRE(small::to_string(static_cast(123)) == "123"); 212 | REQUIRE(small::to_string(static_cast(123)) == "123"); 213 | REQUIRE(small::to_string(static_cast(123)) == "123"); 214 | REQUIRE( 215 | small::to_string(static_cast(123)) 216 | == "123"); 217 | REQUIRE(small::to_string(static_cast(123)) == "123"); 218 | REQUIRE(small::to_string(static_cast(123)) == "123"); 219 | REQUIRE(small::to_string(static_cast(123)) == "123"); 220 | } 221 | 222 | SECTION("Hash") { 223 | SECTION("Isolated") { 224 | std::hash hasher; 225 | string a = "abc"; 226 | REQUIRE_FALSE(hasher(a) == 0); 227 | } 228 | 229 | SECTION("Hash table") { 230 | std::unordered_set s; 231 | s.insert("abc"); 232 | s.insert("def"); 233 | s.insert("ghi"); 234 | REQUIRE(s.size() == 3); 235 | } 236 | } 237 | 238 | SECTION("Relocatable in inline vector") { 239 | small::vector v(5); 240 | v.emplace_back("new str"); 241 | v.emplace(v.begin() + 3, "middle str"); 242 | REQUIRE(v.size() == 7); 243 | } 244 | } 245 | } -------------------------------------------------------------------------------- /test/unit/small_string_access.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | // UTF8 string literals are really not safe in MSVC. 20 | // u8"" doesn't work properly even with escape sequences. 21 | // More recent versions might improve on that a little, but we will still 22 | // need a more robust solution to support older versions in any case. 23 | // Until then, it seems like the most robust solution is to initialize 24 | // small::strings with U"...", even if that requires converting the 25 | // codepoints. 26 | constexpr bool 27 | is_windows() { 28 | #if defined(_WIN32) 29 | return true; 30 | #else 31 | return false; 32 | #endif 33 | } 34 | 35 | TEST_CASE("String") { 36 | using namespace small; 37 | 38 | SECTION("Element access") { 39 | SECTION("At") { 40 | string s = "123456"; 41 | REQUIRE(s.at(0) == '1'); 42 | REQUIRE(s.at(1) == '2'); 43 | REQUIRE(s.at(2) == '3'); 44 | REQUIRE(s.at(3) == '4'); 45 | REQUIRE(s.at(4) == '5'); 46 | REQUIRE(s.at(5) == '6'); 47 | } 48 | 49 | using cp_index = string::codepoint_index; 50 | 51 | SECTION("At codepoint (through references)") { 52 | SECTION("No unicode") { 53 | string s = "123456"; 54 | REQUIRE(s.at(cp_index(0)) == '1'); 55 | REQUIRE(s.at(cp_index(1)) == '2'); 56 | REQUIRE(s.at(cp_index(2)) == '3'); 57 | REQUIRE(s.at(cp_index(3)) == '4'); 58 | REQUIRE(s.at(cp_index(4)) == '5'); 59 | REQUIRE(s.at(cp_index(5)) == '6'); 60 | } 61 | SECTION("Half unicode") { 62 | string s = "1😀2😀3😀"; 63 | REQUIRE(s.at(cp_index(0)) == '1'); 64 | REQUIRE(s.at(cp_index(1)) == U'😀'); 65 | REQUIRE(s.at(cp_index(2)) == '2'); 66 | REQUIRE(s.at(cp_index(3)) == U'😀'); 67 | REQUIRE(s.at(cp_index(4)) == '3'); 68 | REQUIRE(s.at(cp_index(5)) == U'😀'); 69 | REQUIRE(s.at(cp_index(0)) == "1"); 70 | REQUIRE(s.at(cp_index(1)) == "😀"); 71 | REQUIRE(s.at(cp_index(2)) == "2"); 72 | REQUIRE(s.at(cp_index(3)) == "😀"); 73 | REQUIRE(s.at(cp_index(4)) == "3"); 74 | REQUIRE(s.at(cp_index(5)) == "😀"); 75 | } 76 | SECTION("Full unicode") { 77 | string s = "🙂😀🙂😀🙂😀"; 78 | REQUIRE(s.at(cp_index(0)) == U'🙂'); 79 | REQUIRE(s.at(cp_index(1)) == U'😀'); 80 | REQUIRE(s.at(cp_index(2)) == U'🙂'); 81 | REQUIRE(s.at(cp_index(3)) == U'😀'); 82 | REQUIRE(s.at(cp_index(4)) == U'🙂'); 83 | REQUIRE(s.at(cp_index(5)) == U'😀'); 84 | REQUIRE(s.at(cp_index(0)) == "🙂"); 85 | REQUIRE(s.at(cp_index(1)) == "😀"); 86 | REQUIRE(s.at(cp_index(2)) == "🙂"); 87 | REQUIRE(s.at(cp_index(3)) == "😀"); 88 | REQUIRE(s.at(cp_index(4)) == "🙂"); 89 | REQUIRE(s.at(cp_index(5)) == "😀"); 90 | } 91 | } 92 | 93 | SECTION("Subscript") { 94 | string s = "123456"; 95 | REQUIRE(s[0] == '1'); 96 | REQUIRE(s[1] == '2'); 97 | REQUIRE(s[2] == '3'); 98 | REQUIRE(s[3] == '4'); 99 | REQUIRE(s[4] == '5'); 100 | REQUIRE(s[5] == '6'); 101 | } 102 | 103 | SECTION("Subscript codepoint") { 104 | string s = "123456"; 105 | REQUIRE(s[cp_index(0)] == '1'); 106 | REQUIRE(s[cp_index(1)] == '2'); 107 | REQUIRE(s[cp_index(2)] == '3'); 108 | REQUIRE(s[cp_index(3)] == '4'); 109 | REQUIRE(s[cp_index(4)] == '5'); 110 | REQUIRE(s[cp_index(5)] == '6'); 111 | } 112 | 113 | SECTION("Subscript codepoint (direct values)") { 114 | string s = "1😀2😀3😀"; 115 | REQUIRE(s(cp_index(0)) == '1'); 116 | REQUIRE(s(cp_index(1)) == U'😀'); 117 | REQUIRE(s(cp_index(2)) == '2'); 118 | REQUIRE(s(cp_index(3)) == U'😀'); 119 | REQUIRE(s(cp_index(4)) == '3'); 120 | REQUIRE(s(cp_index(5)) == U'😀'); 121 | } 122 | 123 | SECTION("Subscript codepoint (through references)") { 124 | string s = "1😀2😀3😀"; 125 | REQUIRE(s[cp_index(0)] == '1'); 126 | REQUIRE(s[cp_index(1)] == U'😀'); 127 | REQUIRE(s[cp_index(2)] == '2'); 128 | REQUIRE(s[cp_index(3)] == U'😀'); 129 | REQUIRE(s[cp_index(4)] == '3'); 130 | REQUIRE(s[cp_index(5)] == U'😀'); 131 | } 132 | 133 | SECTION("Front/Back") { 134 | string s = "1😀2😀3😀5"; 135 | REQUIRE(s.front() == '1'); 136 | REQUIRE(s.back() == '5'); 137 | } 138 | 139 | SECTION("Front/Back Codepoints") { 140 | string s = "😀1😀2😀3😀5😀"; 141 | REQUIRE(s.front_codepoint() == U'😀'); 142 | REQUIRE(s.back_codepoint() == U'😀'); 143 | REQUIRE(s.front_codepoint() == "😀"); 144 | REQUIRE(s.back_codepoint() == "😀"); 145 | } 146 | 147 | SECTION("Data") { 148 | string s = "1😀2😀3😀5"; 149 | string_view sv(s.data(), s.size()); 150 | REQUIRE(s == sv); 151 | REQUIRE(s.data() == s.c_str()); 152 | REQUIRE(s.operator string_view() == sv); 153 | } 154 | } 155 | 156 | SECTION("Iterators") { 157 | SECTION("Byte Iterators") { 158 | string a = "123"; 159 | REQUIRE(a.begin() == a.data()); 160 | REQUIRE(a.end() == a.data() + a.size()); 161 | 162 | REQUIRE(*a.begin() == '1'); 163 | REQUIRE(*std::next(a.begin()) == '2'); 164 | REQUIRE(*std::prev(a.end()) == '3'); 165 | 166 | REQUIRE(a.cbegin() == a.data()); 167 | REQUIRE(a.cend() == a.data() + a.size()); 168 | 169 | REQUIRE(*a.cbegin() == '1'); 170 | REQUIRE(*std::next(a.cbegin()) == '2'); 171 | REQUIRE(*std::prev(a.cend()) == '3'); 172 | 173 | REQUIRE(*a.rbegin() == '3'); 174 | REQUIRE(*std::next(a.rbegin()) == '2'); 175 | REQUIRE(*std::prev(a.rend()) == '1'); 176 | 177 | REQUIRE(*a.crbegin() == '3'); 178 | REQUIRE(*std::next(a.crbegin()) == '2'); 179 | REQUIRE(*std::prev(a.crend()) == '1'); 180 | } 181 | 182 | SECTION("Codepoint Iterators") { 183 | string a = "😐🙂😀"; 184 | REQUIRE( 185 | static_cast(a.end_codepoint() - a.begin_codepoint()) 186 | == a.size_codepoints()); 187 | 188 | REQUIRE(*a.begin_codepoint() == U'😐'); 189 | REQUIRE(*std::next(a.begin_codepoint()) == U'🙂'); 190 | REQUIRE(*std::prev(a.end_codepoint()) == U'😀'); 191 | 192 | REQUIRE(*a.cbegin_codepoint() == a.front_codepoint()); 193 | REQUIRE(*std::prev(a.cend_codepoint()) == a.back_codepoint()); 194 | 195 | REQUIRE(*a.cbegin_codepoint() == U'😐'); 196 | REQUIRE(*std::next(a.cbegin_codepoint()) == U'🙂'); 197 | REQUIRE(*std::prev(a.cend_codepoint()) == U'😀'); 198 | 199 | REQUIRE(*a.rbegin_codepoint() == U'😀'); 200 | REQUIRE(*std::next(a.rbegin_codepoint()) == U'🙂'); 201 | REQUIRE(*std::prev(a.rend_codepoint()) == U'😐'); 202 | 203 | REQUIRE(*a.crbegin_codepoint() == U'😀'); 204 | REQUIRE(*std::next(a.crbegin_codepoint()) == U'🙂'); 205 | REQUIRE(*std::prev(a.crend_codepoint()) == U'😐'); 206 | } 207 | } 208 | 209 | SECTION("Capacity") { 210 | string a = U"1😀3"; 211 | 212 | REQUIRE_FALSE(a.empty()); 213 | REQUIRE(a.size() == 6); 214 | REQUIRE(a.size_codepoints() == 3); 215 | REQUIRE(a.max_size() > 100000); 216 | REQUIRE_FALSE(a.empty()); 217 | REQUIRE(a.capacity() >= 13); 218 | REQUIRE(a.capacity() <= 15); 219 | size_t old_cap = a.capacity(); 220 | 221 | a.reserve(10); 222 | REQUIRE_FALSE(a.empty()); 223 | REQUIRE(a.size() == 6); 224 | REQUIRE(a.size_codepoints() == 3); 225 | REQUIRE(a.max_size() > 100000); 226 | REQUIRE_FALSE(a.empty()); 227 | size_t new_cap = a.capacity(); 228 | REQUIRE(new_cap >= old_cap); 229 | 230 | a.reserve(20); 231 | REQUIRE_FALSE(a.empty()); 232 | REQUIRE(a.size() == 6); 233 | REQUIRE(a.size_codepoints() == 3); 234 | REQUIRE(a.max_size() > 100000); 235 | REQUIRE_FALSE(a.empty()); 236 | new_cap = a.capacity(); 237 | REQUIRE(new_cap > old_cap); 238 | 239 | a.shrink_to_fit(); 240 | REQUIRE_FALSE(a.empty()); 241 | REQUIRE(a.size() == 6); 242 | REQUIRE(a.size_codepoints() == 3); 243 | REQUIRE(a.max_size() > 100000); 244 | REQUIRE_FALSE(a.empty()); 245 | new_cap = a.capacity(); 246 | REQUIRE( 247 | new_cap 248 | >= 6); // larger than initial size but might not be inline anymore 249 | 250 | a = U"1😀3"; 251 | a.shrink_to_fit(); 252 | REQUIRE(a.size() == 6); 253 | REQUIRE(a.max_size() > 5); 254 | REQUIRE_FALSE(a.empty()); 255 | REQUIRE(a.capacity() >= a.size()); 256 | REQUIRE_FALSE(is_malformed(a)); 257 | } 258 | } -------------------------------------------------------------------------------- /dev-tools/linter/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | /** \file 9 | * \brief A very simple amalgamator to generate the single header version of 10 | * futures 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace fs = std::filesystem; 24 | 25 | struct config 26 | { 27 | // Input 28 | std::vector include_paths; 29 | // Log 30 | bool show_progress{ false }; 31 | bool verbose{ false }; 32 | // Linting options 33 | bool fix_include_guards{ true }; 34 | }; 35 | 36 | constexpr bool 37 | is_key(std::string_view arg) { 38 | return !arg.empty() && arg.front() == '-'; 39 | }; 40 | 41 | constexpr bool 42 | is_false(std::string_view value) { 43 | return is_key(value) || value.empty() || value == "false" 44 | || value == "FALSE" || value == "0"; 45 | } 46 | 47 | bool 48 | parse_config(config &c, const std::vector &args) { 49 | auto find_key = [&](std::string_view key) { 50 | return std::find_if(args.begin(), args.end(), [&](std::string_view arg) { 51 | return is_key(arg) && arg.substr(arg.find_first_not_of('-')) == key; 52 | }); 53 | }; 54 | 55 | auto key_it = find_key("show_progress"); 56 | if (key_it != args.end()) { 57 | c.show_progress = is_key(*key_it) || !is_false(*key_it); 58 | } 59 | 60 | key_it = find_key("verbose"); 61 | if (key_it != args.end()) { 62 | c.verbose = is_key(*key_it) || !is_false(*key_it); 63 | } 64 | 65 | key_it = find_key("fix_include_guards"); 66 | if (key_it != args.end()) { 67 | c.fix_include_guards = is_key(*key_it) || !is_false(*key_it); 68 | } 69 | 70 | auto get_values = [&](std::string_view key) { 71 | auto arg_begin = find_key(key); 72 | if (arg_begin == args.end()) { 73 | return std::make_pair(arg_begin, arg_begin); 74 | } 75 | ++arg_begin; 76 | return std:: 77 | make_pair(arg_begin, std::find_if(arg_begin, args.end(), is_key)); 78 | }; 79 | 80 | auto [include_paths_begin, include_paths_end] = get_values("include_paths"); 81 | c.include_paths = { include_paths_begin, include_paths_end }; 82 | if (c.include_paths.empty()) { 83 | std::cerr << "No include paths provided\n"; 84 | return false; 85 | } 86 | 87 | auto exist_as_directory = [](const fs::path &p) { 88 | if (!fs::exists(p)) { 89 | std::cerr << "Path " << p << " does not exist\n"; 90 | return false; 91 | } 92 | if (!fs::is_directory(p)) { 93 | std::cerr << "Path " << p << " is not a directory\n"; 94 | return false; 95 | } 96 | return true; 97 | }; 98 | if (!std::all_of( 99 | c.include_paths.begin(), 100 | c.include_paths.end(), 101 | exist_as_directory)) 102 | { 103 | return false; 104 | } 105 | 106 | return true; 107 | } 108 | 109 | bool 110 | parse_config(config &c, int argc, char **argv) { 111 | std::vector args(argv, argv + argc); 112 | return parse_config(c, args); 113 | } 114 | 115 | std::pair 116 | find_file( 117 | const std::vector &include_paths, 118 | const std::ssub_match &filename) { 119 | for (const auto &path: include_paths) { 120 | auto p = path / filename.str(); 121 | if (fs::exists(p)) { 122 | return std::make_pair(p, true); 123 | } 124 | } 125 | return std::make_pair(fs::path(filename.first, filename.second), false); 126 | } 127 | 128 | bool 129 | is_parent(const fs::path &dir, const fs::path &p) { 130 | for (auto b = dir.begin(), s = p.begin(); b != dir.end(); ++b, ++s) { 131 | if (s == p.end() || *s != *b) { 132 | return false; 133 | } 134 | } 135 | return true; 136 | } 137 | 138 | std::vector::const_iterator 139 | find_parent_path( 140 | const std::vector &include_paths, 141 | const fs::path &filename) { 142 | return std::find_if( 143 | include_paths.begin(), 144 | include_paths.end(), 145 | [&filename](const fs::path &dir) { return is_parent(dir, filename); }); 146 | } 147 | 148 | bool 149 | is_cpp_file(const fs::path &p) { 150 | constexpr std::string_view extensions[] = {".h", ".hpp", ".cpp"}; 151 | for (const auto &extension: extensions) { 152 | if (p.extension() == extension) { 153 | return true; 154 | } 155 | } 156 | return false; 157 | } 158 | 159 | int 160 | main(int argc, char **argv) { 161 | config c; 162 | if (!parse_config(c, argc, argv)) { 163 | return 1; 164 | } 165 | 166 | // Find files in directory 167 | std::string content; 168 | std::vector file_paths; 169 | for (const auto &include_path: c.include_paths) { 170 | fs::recursive_directory_iterator dir_paths{ include_path }; 171 | for (auto &p: dir_paths) { 172 | if (is_cpp_file(p.path())) { 173 | file_paths.emplace_back(p); 174 | } 175 | } 176 | } 177 | 178 | // Fix include guards 179 | if (c.fix_include_guards) { 180 | if (c.show_progress) { 181 | std::cout << "Fixing include guards in patched files\n"; 182 | } 183 | for (auto &p: file_paths) { 184 | // Validate the file 185 | if (c.verbose) { 186 | std::cout << p << '\n'; 187 | } 188 | if (!fs::exists(p)) { 189 | if (c.verbose) { 190 | std::cout << "File is not in include paths\n"; 191 | } 192 | continue; 193 | } 194 | 195 | std::ifstream t(p); 196 | if (!t) { 197 | if (c.show_progress) { 198 | std::cout << "Failed to open file\n"; 199 | } 200 | continue; 201 | } 202 | 203 | auto parent_it = find_parent_path(c.include_paths, p); 204 | if (parent_it == c.include_paths.end()) { 205 | if (c.show_progress) { 206 | std::cout << "Cannot find include paths for " << p << "\n"; 207 | } 208 | continue; 209 | } 210 | 211 | // Look for current guard 212 | std::string file_content{ 213 | std::istreambuf_iterator(t), 214 | std::istreambuf_iterator() 215 | }; 216 | std::regex include_guard_expression( 217 | "(^|\n) *# *ifndef *([a-zA-Z0-9_/\\. ]+)"); 218 | std::smatch include_guard_match; 219 | if (std::regex_search( 220 | file_content, 221 | include_guard_match, 222 | include_guard_expression)) 223 | { 224 | if (c.verbose) { 225 | std::cout 226 | << "Found guard " << include_guard_match[2] << '\n'; 227 | } 228 | } else { 229 | if (c.show_progress) { 230 | std::cout << "Cannot find include guard for " << p << "\n"; 231 | } 232 | continue; 233 | } 234 | 235 | // Calculate expected guard 236 | std::string prev_guard = include_guard_match[2].str(); 237 | fs::path relative_p = fs::relative(p, *parent_it); 238 | std::string expected_guard = relative_p.string(); 239 | std::transform( 240 | expected_guard.begin(), 241 | expected_guard.end(), 242 | expected_guard.begin(), 243 | [](char x) { 244 | if (x == '/' || x == '.' || x == '-') { 245 | return '_'; 246 | } 247 | return static_cast(std::toupper(x)); 248 | }); 249 | 250 | if (prev_guard == expected_guard) { 251 | if (c.verbose) { 252 | std::cout << "Guard " << prev_guard << " is correct\n"; 253 | } 254 | continue; 255 | } else { 256 | if (c.show_progress) { 257 | std::cout << "Convert guard from " << prev_guard << " to " 258 | << expected_guard << '\n'; 259 | } 260 | } 261 | 262 | // Check if the expected guard is OK 263 | bool new_guard_ok = std::all_of( 264 | expected_guard.begin(), 265 | expected_guard.end(), 266 | [](char x) { return std::isalnum(x) || x == '_'; }); 267 | if (!new_guard_ok) { 268 | if (c.show_progress) { 269 | std::cout << "Inferred guard " << expected_guard 270 | << " is not OK\n"; 271 | } 272 | continue; 273 | } 274 | 275 | // Replace all guards in the file 276 | std::size_t guard_search_begin = 0; 277 | std::size_t guard_match_pos; 278 | while ((guard_match_pos = file_content 279 | .find(prev_guard, guard_search_begin)) 280 | != std::string::npos) 281 | { 282 | file_content 283 | .replace(guard_match_pos, prev_guard.size(), expected_guard); 284 | guard_search_begin = guard_match_pos + prev_guard.size(); 285 | } 286 | 287 | t.close(); 288 | std::ofstream fout(p); 289 | fout << file_content; 290 | } 291 | } 292 | 293 | return 0; 294 | } 295 | -------------------------------------------------------------------------------- /include/small/detail/iterator/pointer_wrapper.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_ITERATOR_POINTER_WRAPPER_HPP 9 | #define SMALL_DETAIL_ITERATOR_POINTER_WRAPPER_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace small { 17 | namespace detail { 18 | 19 | /// \brief Wraps a pointer as an iterator 20 | template 21 | class pointer_wrapper 22 | { 23 | public: 24 | static_assert( 25 | std::is_pointer_v, 26 | "pointer_wrapper can only wrap pointers"); 27 | typedef POINTER iterator_type; 28 | typedef 29 | typename std::iterator_traits::iterator_category 30 | iterator_category; 31 | typedef typename std::iterator_traits::value_type 32 | value_type; 33 | typedef 34 | typename std::iterator_traits::difference_type 35 | difference_type; 36 | typedef 37 | typename std::iterator_traits::pointer pointer; 38 | typedef typename std::iterator_traits::reference 39 | reference; 40 | 41 | public /* constructors */: 42 | /// \brief Construct empty pointer wrapper 43 | constexpr pointer_wrapper() noexcept : base_(nullptr) {} 44 | 45 | /// \brief Construct pointer wrapper from pointer x 46 | constexpr explicit pointer_wrapper(iterator_type x) noexcept 47 | : base_(x) {} 48 | 49 | /// \brief Construct pointer wrapper from pointer wrapper u, which 50 | /// might be another type 51 | template 52 | constexpr pointer_wrapper( // NOLINT(google-explicit-constructor): 53 | // expected for iterators 54 | const pointer_wrapper &u, 55 | typename std::enable_if_t< 56 | std::is_convertible_v> * = 0) noexcept 57 | : base_(u.base()) {} 58 | 59 | public /* element access */: 60 | /// \brief Dereference iterator 61 | constexpr reference 62 | operator*() const noexcept { 63 | return *base_; 64 | } 65 | 66 | /// \brief Dereference iterator and get member 67 | constexpr pointer 68 | operator->() const noexcept { 69 | return static_cast(std::addressof(*base_)); 70 | } 71 | 72 | /// \brief Dereference iterator n positions ahead 73 | constexpr reference 74 | operator[](difference_type n) const noexcept { 75 | return base_[n]; 76 | } 77 | 78 | /// \brief Get base pointer 79 | constexpr iterator_type 80 | base() const noexcept { 81 | return base_; 82 | } 83 | 84 | public /* modifiers */: 85 | /// \brief Advance iterator 86 | constexpr pointer_wrapper & 87 | operator++() noexcept { 88 | ++base_; 89 | return *this; 90 | } 91 | 92 | /// \brief Advance iterator (postfix) 93 | constexpr pointer_wrapper 94 | operator++(int) noexcept { // NOLINT(cert-dcl21-cpp) 95 | pointer_wrapper tmp(*this); 96 | ++(*this); 97 | return tmp; 98 | } 99 | 100 | /// \brief Rewind iterator 101 | constexpr pointer_wrapper & 102 | operator--() noexcept { 103 | --base_; 104 | return *this; 105 | } 106 | 107 | /// \brief Rewind iterator (postfix) 108 | constexpr pointer_wrapper 109 | operator--(int) noexcept { // NOLINT(cert-dcl21-cpp) 110 | pointer_wrapper tmp(*this); 111 | --(*this); 112 | return tmp; 113 | } 114 | 115 | /// \brief Return copy of iterator advanced by n positions 116 | constexpr pointer_wrapper 117 | operator+(difference_type n) const noexcept { 118 | pointer_wrapper w(*this); 119 | w += n; 120 | return w; 121 | } 122 | 123 | /// \brief Advance iterator by n positions 124 | constexpr pointer_wrapper & 125 | operator+=(difference_type n) noexcept { 126 | base_ += n; 127 | return *this; 128 | } 129 | 130 | /// \brief Return copy of iterator n positions behind 131 | constexpr pointer_wrapper 132 | operator-(difference_type n) const noexcept { 133 | return *this + (-n); 134 | } 135 | 136 | /// \brief Rewind iterator by n positions 137 | constexpr pointer_wrapper & 138 | operator-=(difference_type n) noexcept { 139 | *this += -n; 140 | return *this; 141 | } 142 | 143 | public /* relational operators */: 144 | /// Make any other pointer wrapper a friend 145 | template 146 | friend class pointer_wrapper; 147 | 148 | public /* friends */: 149 | /// \brief Get distance between iterators 150 | template 151 | constexpr friend auto 152 | operator-( 153 | const pointer_wrapper &x, 154 | const pointer_wrapper &y) noexcept 155 | -> decltype(x.base() - y.base()); 156 | 157 | /// \brief Sum iterators 158 | template 159 | constexpr friend pointer_wrapper operator+( 160 | typename pointer_wrapper::difference_type, 161 | pointer_wrapper) noexcept; 162 | 163 | private: 164 | /// Base pointer 165 | iterator_type base_; 166 | }; 167 | 168 | template 169 | inline constexpr bool 170 | operator==( 171 | const pointer_wrapper &x, 172 | const pointer_wrapper &y) noexcept { 173 | return x.base() == y.base(); 174 | } 175 | 176 | template 177 | inline constexpr bool 178 | operator!=( 179 | const pointer_wrapper &x, 180 | const pointer_wrapper &y) noexcept { 181 | return !(x == y); 182 | } 183 | 184 | template 185 | inline constexpr bool 186 | operator<( 187 | const pointer_wrapper &x, 188 | const pointer_wrapper &y) noexcept { 189 | return x.base() < y.base(); 190 | } 191 | 192 | template 193 | inline constexpr bool 194 | operator>( 195 | const pointer_wrapper &x, 196 | const pointer_wrapper &y) noexcept { 197 | return y < x; 198 | } 199 | 200 | template 201 | inline constexpr bool 202 | operator<=( 203 | const pointer_wrapper &x, 204 | const pointer_wrapper &y) noexcept { 205 | return !(y < x); 206 | } 207 | 208 | template 209 | inline constexpr bool 210 | operator>=( 211 | const pointer_wrapper &x, 212 | const pointer_wrapper &y) noexcept { 213 | return !(x < y); 214 | } 215 | 216 | template 217 | inline constexpr bool 218 | operator==( 219 | const pointer_wrapper &x, 220 | ptr_to_const_t y) noexcept { 221 | return x.base() == y; 222 | } 223 | 224 | template 225 | inline constexpr bool 226 | operator!=( 227 | const pointer_wrapper &x, 228 | ptr_to_const_t y) noexcept { 229 | return !(x == y); 230 | } 231 | 232 | template 233 | inline constexpr bool 234 | operator<( 235 | const pointer_wrapper &x, 236 | ptr_to_const_t y) noexcept { 237 | return x.base() < y; 238 | } 239 | 240 | template 241 | inline constexpr bool 242 | operator>( 243 | const pointer_wrapper &x, 244 | ptr_to_const_t y) noexcept { 245 | return y < x.base(); 246 | } 247 | 248 | template 249 | inline constexpr bool 250 | operator<=( 251 | const pointer_wrapper &x, 252 | ptr_to_const_t y) noexcept { 253 | return !(y < x); 254 | } 255 | 256 | template 257 | inline constexpr bool 258 | operator>=( 259 | const pointer_wrapper &x, 260 | ptr_to_const_t y) noexcept { 261 | return !(x < y); 262 | } 263 | 264 | template 265 | inline constexpr bool 266 | operator==( 267 | ptr_to_const_t x, 268 | const pointer_wrapper &y) noexcept { 269 | return x == y.base(); 270 | } 271 | 272 | template 273 | inline constexpr bool 274 | operator!=( 275 | ptr_to_const_t x, 276 | const pointer_wrapper &y) noexcept { 277 | return !(x == y); 278 | } 279 | 280 | template 281 | inline constexpr bool 282 | operator>( 283 | ptr_to_const_t x, 284 | const pointer_wrapper &y) noexcept { 285 | return y < x; 286 | } 287 | 288 | template 289 | inline constexpr bool 290 | operator<( 291 | ptr_to_const_t x, 292 | const pointer_wrapper &y) noexcept { 293 | return y > x; 294 | } 295 | 296 | template 297 | inline constexpr bool 298 | operator<=( 299 | ptr_to_const_t x, 300 | const pointer_wrapper &y) noexcept { 301 | return !(y < x); 302 | } 303 | 304 | template 305 | inline constexpr bool 306 | operator>=( 307 | ptr_to_const_t x, 308 | const pointer_wrapper &y) noexcept { 309 | return !(x < y); 310 | } 311 | 312 | template 313 | inline constexpr auto 314 | operator-( 315 | const pointer_wrapper &x, 316 | const pointer_wrapper &y) noexcept 317 | -> decltype(x.base() - y.base()) { 318 | return x.base() - y.base(); 319 | } 320 | 321 | template 322 | inline constexpr pointer_wrapper 323 | operator+( 324 | typename pointer_wrapper::difference_type n, 325 | pointer_wrapper x) noexcept { 326 | x += n; 327 | return x; 328 | } 329 | } // namespace detail 330 | } // namespace small 331 | 332 | #endif // SMALL_DETAIL_ITERATOR_POINTER_WRAPPER_HPP 333 | -------------------------------------------------------------------------------- /include/small/detail/iterator/const_key_iterator.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_ITERATOR_CONST_KEY_ITERATOR_HPP 9 | #define SMALL_DETAIL_ITERATOR_CONST_KEY_ITERATOR_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace small { 18 | namespace detail { 19 | /// \brief Iterator wrapper that makes the key of the underlying pair 20 | /// constant 21 | template 22 | class const_key_iterator 23 | { 24 | public: 25 | // Base iterator 26 | typedef ITERATOR base_iterator_type; 27 | typedef 28 | typename std::iterator_traits::value_type 29 | base_value_type; 30 | 31 | static_assert( 32 | (is_pair_v), 33 | "const_key_iterator: base_value_type must be std::pair"); 34 | 35 | // Wrapped types 36 | typedef typename std::iterator_traits< 37 | base_iterator_type>::iterator_category iterator_category; 38 | typedef typename add_key_const::type value_type; 39 | typedef typename std::iterator_traits< 40 | base_iterator_type>::difference_type difference_type; 41 | typedef typename std::add_pointer_t pointer; 42 | typedef typename std::add_lvalue_reference_t reference; 43 | 44 | public /* constructors */: 45 | /// \brief Construct empty iterator wrapper 46 | constexpr const_key_iterator() noexcept : base_(nullptr) {} 47 | 48 | /// \brief Construct iterator wrapper from pointer x 49 | constexpr explicit const_key_iterator(base_iterator_type x) noexcept 50 | : base_(x) {} 51 | 52 | /// \brief Construct const iterator wrapper from another wrapper u, 53 | /// which might be another type 54 | template 55 | constexpr const_key_iterator( // NOLINT(google-explicit-constructor): 56 | // expected for iterators 57 | const const_key_iterator &u, 58 | typename std::enable_if_t< 59 | std::is_convertible_v> 60 | * = 0) noexcept 61 | : base_(u.base()) {} 62 | 63 | public /* element access */: 64 | /// \brief Dereference iterator 65 | constexpr reference 66 | operator*() const noexcept { 67 | return *(operator->()); 68 | } 69 | 70 | /// \brief Dereference iterator and get member 71 | constexpr pointer 72 | operator->() const noexcept { 73 | const auto base_pointer = std::addressof(*base_); 74 | auto mutable_base_pointer = const_cast< 75 | std::add_pointer_t>>( 76 | base_pointer); 77 | return reinterpret_cast(mutable_base_pointer); 78 | } 79 | 80 | /// \brief Dereference iterator n positions ahead 81 | constexpr reference 82 | operator[](difference_type n) const noexcept { 83 | return reinterpret_cast(base_[n]); 84 | } 85 | 86 | /// \brief Get base pointer 87 | constexpr base_iterator_type 88 | base() const noexcept { 89 | return base_; 90 | } 91 | 92 | public /* modifiers */: 93 | /// \brief Advance iterator 94 | constexpr const_key_iterator & 95 | operator++() noexcept { 96 | ++base_; 97 | return *this; 98 | } 99 | 100 | /// \brief Advance iterator (postfix) 101 | constexpr const_key_iterator 102 | operator++(int) noexcept { // NOLINT(cert-dcl21-cpp) 103 | const_key_iterator tmp(*this); 104 | ++(*this); 105 | return tmp; 106 | } 107 | 108 | /// \brief Rewind iterator 109 | constexpr const_key_iterator & 110 | operator--() noexcept { 111 | --base_; 112 | return *this; 113 | } 114 | 115 | /// \brief Rewind iterator (postfix) 116 | constexpr const_key_iterator 117 | operator--(int) noexcept { // NOLINT(cert-dcl21-cpp) 118 | const_key_iterator tmp(*this); 119 | --(*this); 120 | return tmp; 121 | } 122 | 123 | /// \brief Return copy of iterator advanced by n positions 124 | constexpr const_key_iterator 125 | operator+(difference_type n) const noexcept { 126 | const_key_iterator w(*this); 127 | w += n; 128 | return w; 129 | } 130 | 131 | /// \brief Advance iterator by n positions 132 | constexpr const_key_iterator & 133 | operator+=(difference_type n) noexcept { 134 | base_ += n; 135 | return *this; 136 | } 137 | 138 | /// \brief Return copy of iterator n positions behind 139 | constexpr const_key_iterator 140 | operator-(difference_type n) const noexcept { 141 | return *this + (-n); 142 | } 143 | 144 | /// \brief Rewind iterator by n positions 145 | constexpr const_key_iterator & 146 | operator-=(difference_type n) noexcept { 147 | *this += -n; 148 | return *this; 149 | } 150 | 151 | public /* relational operators */: 152 | /// Make any other iterator wrapper a friend 153 | template 154 | friend class const_key_iterator; 155 | 156 | public /* friends */: 157 | /// \brief Get distance between iterators 158 | template 159 | constexpr friend auto 160 | operator-( 161 | const const_key_iterator &x, 162 | const const_key_iterator &y) noexcept 163 | -> decltype(x.base() - y.base()); 164 | 165 | /// \brief Sum iterators 166 | template 167 | constexpr friend const_key_iterator operator+( 168 | typename const_key_iterator::difference_type, 169 | const_key_iterator) noexcept; 170 | 171 | private: 172 | /// Base pointer 173 | base_iterator_type base_; 174 | }; 175 | 176 | template 177 | inline constexpr bool 178 | operator==( 179 | const const_key_iterator &x, 180 | const const_key_iterator &y) noexcept { 181 | return x.base() == y.base(); 182 | } 183 | 184 | template 185 | inline constexpr bool 186 | operator!=( 187 | const const_key_iterator &x, 188 | const const_key_iterator &y) noexcept { 189 | return !(x == y); 190 | } 191 | 192 | template 193 | inline constexpr bool 194 | operator<( 195 | const const_key_iterator &x, 196 | const const_key_iterator &y) noexcept { 197 | return x.base() < y.base(); 198 | } 199 | 200 | template 201 | inline constexpr bool 202 | operator>( 203 | const const_key_iterator &x, 204 | const const_key_iterator &y) noexcept { 205 | return y < x; 206 | } 207 | 208 | template 209 | inline constexpr bool 210 | operator>=( 211 | const const_key_iterator &x, 212 | const const_key_iterator &y) noexcept { 213 | return !(x < y); 214 | } 215 | 216 | template 217 | inline constexpr bool 218 | operator<=( 219 | const const_key_iterator &x, 220 | const const_key_iterator &y) noexcept { 221 | return !(y < x); 222 | } 223 | 224 | template 225 | inline constexpr bool 226 | operator==( 227 | const const_key_iterator &x, 228 | const const_key_iterator &y) noexcept { 229 | return x.base() == y.base(); 230 | } 231 | 232 | template 233 | inline constexpr bool 234 | operator!=( 235 | const const_key_iterator &x, 236 | const const_key_iterator &y) noexcept { 237 | return !(x == y); 238 | } 239 | 240 | template 241 | inline constexpr bool 242 | operator>( 243 | const const_key_iterator &x, 244 | const const_key_iterator &y) noexcept { 245 | return y < x; 246 | } 247 | 248 | template 249 | inline constexpr bool 250 | operator>=( 251 | const const_key_iterator &x, 252 | const const_key_iterator &y) noexcept { 253 | return !(x < y); 254 | } 255 | 256 | template 257 | inline constexpr bool 258 | operator<=( 259 | const const_key_iterator &x, 260 | const const_key_iterator &y) noexcept { 261 | return !(y < x); 262 | } 263 | 264 | template 265 | inline constexpr bool 266 | operator==( 267 | const const_key_iterator &x, 268 | const Pointer &y) noexcept { 269 | return x.base() == y; 270 | } 271 | 272 | template 273 | inline constexpr bool 274 | operator!=( 275 | const const_key_iterator &x, 276 | const Pointer &y) noexcept { 277 | return !(x == y); 278 | } 279 | 280 | template 281 | inline constexpr bool 282 | operator>(const const_key_iterator &x, const Pointer &y) noexcept { 283 | return y < x; 284 | } 285 | 286 | template 287 | inline constexpr bool 288 | operator>=( 289 | const const_key_iterator &x, 290 | const Pointer &y) noexcept { 291 | return !(x < y); 292 | } 293 | 294 | template 295 | inline constexpr bool 296 | operator<=( 297 | const const_key_iterator &x, 298 | const Pointer &y) noexcept { 299 | return !(y < x); 300 | } 301 | 302 | template 303 | inline constexpr bool 304 | operator==( 305 | const Pointer &x, 306 | const const_key_iterator &y) noexcept { 307 | return x.base() == y.base(); 308 | } 309 | 310 | template 311 | inline constexpr bool 312 | operator!=( 313 | const Pointer &x, 314 | const const_key_iterator &y) noexcept { 315 | return !(x == y); 316 | } 317 | 318 | template 319 | inline constexpr bool 320 | operator>(const Pointer &x, const const_key_iterator &y) noexcept { 321 | return y < x; 322 | } 323 | 324 | template 325 | inline constexpr bool 326 | operator>=( 327 | const Pointer &x, 328 | const const_key_iterator &y) noexcept { 329 | return !(x < y); 330 | } 331 | 332 | template 333 | inline constexpr bool 334 | operator<=( 335 | const Pointer &x, 336 | const const_key_iterator &y) noexcept { 337 | return !(y < x); 338 | } 339 | 340 | template 341 | inline constexpr auto 342 | operator-( 343 | const const_key_iterator &x, 344 | const const_key_iterator &y) noexcept 345 | -> decltype(x.base() - y.base()) { 346 | return x.base() - y.base(); 347 | } 348 | 349 | template 350 | inline constexpr const_key_iterator 351 | operator+( 352 | typename const_key_iterator::difference_type n, 353 | const_key_iterator x) noexcept { 354 | x += n; 355 | return x; 356 | } 357 | } // namespace detail 358 | } // namespace small 359 | 360 | #endif // SMALL_DETAIL_ITERATOR_CONST_KEY_ITERATOR_HPP 361 | -------------------------------------------------------------------------------- /include/small/detail/iterator/ordered_concat_iterator.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 alandefreitas (alandefreitas@gmail.com) 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // https://www.boost.org/LICENSE_1_0.txt 6 | // 7 | 8 | #ifndef SMALL_DETAIL_ITERATOR_ORDERED_CONCAT_ITERATOR_HPP 9 | #define SMALL_DETAIL_ITERATOR_ORDERED_CONCAT_ITERATOR_HPP 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace small { 17 | namespace detail { 18 | /// \brief Iterator that iterates two ordered containers as one 19 | template 20 | class ordered_concat_iterator 21 | { 22 | public: 23 | // Base iterator 24 | typedef ITERATOR1 base_iterator_type1; 25 | typedef 26 | typename std::iterator_traits::value_type 27 | base_value_type1; 28 | typedef ITERATOR2 base_iterator_type2; 29 | typedef 30 | typename std::iterator_traits::value_type 31 | base_value_type2; 32 | 33 | // Wrapped types 34 | typedef std::bidirectional_iterator_tag iterator_category; 35 | typedef 36 | typename std::iterator_traits::value_type 37 | value_type; 38 | typedef typename std::iterator_traits< 39 | base_iterator_type1>::difference_type difference_type; 40 | typedef typename std::add_pointer_t pointer; 41 | typedef typename std::add_lvalue_reference_t reference; 42 | 43 | static_assert( 44 | (std::is_same_v< 45 | typename std::iterator_traits< 46 | base_iterator_type1>::value_type, 47 | typename std::iterator_traits< 48 | base_iterator_type2>::value_type>), 49 | "ordered_concat_iterator: iterators should have the same value " 50 | "type"); 51 | 52 | public /* constructors */: 53 | /// \brief Construct empty iterator wrapper 54 | constexpr ordered_concat_iterator() noexcept 55 | : base1_(), base2_(), begin1_(), begin2_(), end1_(), end2_() {} 56 | 57 | /// \brief Construct iterator wrapper from pointer x 58 | constexpr explicit ordered_concat_iterator( 59 | base_iterator_type1 x1, 60 | base_iterator_type2 x2, 61 | base_iterator_type1 begin1, 62 | base_iterator_type2 begin2, 63 | base_iterator_type1 end1, 64 | base_iterator_type2 end2) noexcept 65 | : base1_(x1), base2_(x2), begin1_(begin1), begin2_(begin2), 66 | end1_(end1), end2_(end2) {} 67 | 68 | /// \brief Construct const iterator wrapper from another wrapper u, 69 | /// which might be another type 70 | template 71 | constexpr ordered_concat_iterator( // NOLINT(google-explicit-constructor): 72 | // expected for iterators 73 | const ordered_concat_iterator &u, 74 | typename std::enable_if_t< 75 | std::is_convertible_v< 76 | UP1, 77 | base_iterator_type1> && std::is_convertible_v> 78 | * = 0) noexcept 79 | : base1_(u.base1()), base2_(u.base2()), begin1_(u.begin1_), 80 | begin2_(u.begin2_), end1_(u.end1_), end2_(u.end2_) {} 81 | 82 | public /* element access */: 83 | /// \brief Dereference iterator 84 | constexpr reference 85 | operator*() const noexcept { 86 | return *(operator->()); 87 | } 88 | 89 | /// \brief Dereference iterator and get member 90 | constexpr pointer 91 | operator->() const noexcept { 92 | const bool can_dereference_first = base1_ != end1_; 93 | const bool dereference_first = can_dereference_first 94 | && (base2_ == end2_ 95 | || *base1_ < *base2_); 96 | if (dereference_first) { 97 | const auto base_pointer = std::addressof(*base1_); 98 | auto mutable_base_pointer = const_cast>>(base_pointer); 100 | return reinterpret_cast(mutable_base_pointer); 101 | } else { 102 | const auto base_pointer = std::addressof(*base2_); 103 | auto mutable_base_pointer = const_cast>>(base_pointer); 105 | return reinterpret_cast(mutable_base_pointer); 106 | } 107 | } 108 | 109 | /// \brief Dereference iterator n positions ahead 110 | constexpr reference 111 | operator[](difference_type n) const noexcept { 112 | auto it = *this; 113 | std::advance(it, n); 114 | return it; 115 | } 116 | 117 | /// \brief Get base pointer 118 | constexpr base_iterator_type1 119 | base1() const noexcept { 120 | return base1_; 121 | } 122 | constexpr base_iterator_type2 123 | base2() const noexcept { 124 | return base2_; 125 | } 126 | 127 | public /* modifiers */: 128 | /// \brief Advance iterator 129 | constexpr ordered_concat_iterator & 130 | operator++() noexcept { 131 | const bool can_advance_first = base1_ != end1_; 132 | const bool can_advance_second = base2_ != end2_; 133 | const bool should_advance_first 134 | = can_advance_first 135 | && (!can_advance_second || *base1_ < *base2_); 136 | if (should_advance_first) { 137 | base1_.operator++(); 138 | } else { 139 | base2_.operator++(); 140 | } 141 | return *this; 142 | } 143 | 144 | /// \brief Advance iterator (postfix) 145 | constexpr ordered_concat_iterator 146 | operator++(int) noexcept { // NOLINT(cert-dcl21-cpp) 147 | ordered_concat_iterator tmp(*this); 148 | ++(*this); 149 | return tmp; 150 | } 151 | 152 | /// \brief Decrement iterator 153 | /// This will undo the post-condition of operator++ 154 | constexpr ordered_concat_iterator & 155 | operator--() noexcept { 156 | const bool can_decrement_first = base1_ != begin1_; 157 | const bool can_decrement_second = base2_ != begin2_; 158 | const bool can_dereference_first = base1_ != end1_; 159 | const bool can_dereference_second = base2_ != end2_; 160 | const auto prev_base1 = std::prev(base1_); 161 | const auto prev_base2 = std::prev(base2_); 162 | const bool should_decrement_first 163 | = can_decrement_first 164 | && (!can_decrement_second 165 | || (can_dereference_second && *prev_base1 < *base2_) 166 | || (can_dereference_first && !(*base1_ < *prev_base2)) 167 | || !(*prev_base1 < *prev_base2)); 168 | // i.e.: if std::prev(base1_) is the iterator to which we 169 | // applied operator++ last time 170 | if (should_decrement_first) { 171 | // Then we should undo that 172 | base1_.operator--(); 173 | } else { 174 | base2_.operator--(); 175 | } 176 | return *this; 177 | } 178 | 179 | /// \brief Rewind iterator (postfix) 180 | constexpr ordered_concat_iterator 181 | operator--(int) noexcept { // NOLINT(cert-dcl21-cpp) 182 | ordered_concat_iterator tmp(*this); 183 | --(*this); 184 | return tmp; 185 | } 186 | 187 | /// \brief Return copy of iterator advanced by n positions 188 | constexpr ordered_concat_iterator 189 | operator+(difference_type n) const noexcept { 190 | ordered_concat_iterator w(*this); 191 | w += n; 192 | return w; 193 | } 194 | 195 | /// \brief Advance iterator by n positions 196 | constexpr ordered_concat_iterator & 197 | operator+=(difference_type n) noexcept { 198 | auto it = *this; 199 | std::advance(it, n); 200 | return *this; 201 | } 202 | 203 | /// \brief Return copy of iterator n positions behind 204 | constexpr ordered_concat_iterator 205 | operator-(difference_type n) const noexcept { 206 | return *this + (-n); 207 | } 208 | 209 | /// \brief Rewind iterator by n positions 210 | constexpr ordered_concat_iterator & 211 | operator-=(difference_type n) noexcept { 212 | *this += -n; 213 | return *this; 214 | } 215 | 216 | public /* relational operators */: 217 | /// Make any other iterator wrapper a friend 218 | template 219 | friend class ordered_concat_iterator; 220 | 221 | public /* friends */: 222 | /// \brief Get distance between iterators 223 | template 224 | constexpr friend auto 225 | operator-( 226 | const ordered_concat_iterator &x, 227 | const ordered_concat_iterator &y) noexcept 228 | -> decltype(x.base() - y.base()); 229 | 230 | /// \brief Sum iterators 231 | template 232 | constexpr friend ordered_concat_iterator operator+( 233 | typename ordered_concat_iterator:: 234 | difference_type, 235 | ordered_concat_iterator) noexcept; 236 | 237 | private: 238 | /// Base pointer 239 | base_iterator_type1 base1_; 240 | base_iterator_type2 base2_; 241 | 242 | /// We need to know where std::begin and std::end are so that we 243 | /// know which iterators we can/should advance 244 | base_iterator_type1 begin1_; 245 | base_iterator_type2 begin2_; 246 | base_iterator_type1 end1_; 247 | base_iterator_type2 end2_; 248 | }; 249 | 250 | template 251 | inline constexpr bool 252 | operator==( 253 | const ordered_concat_iterator &x, 254 | const ordered_concat_iterator &y) noexcept { 255 | return x.base1() == y.base1() && x.base2() == y.base2(); 256 | } 257 | 258 | template 259 | inline constexpr bool 260 | operator!=( 261 | const ordered_concat_iterator &x, 262 | const ordered_concat_iterator &y) noexcept { 263 | return !(x == y); 264 | } 265 | 266 | template 267 | inline constexpr bool 268 | operator<( 269 | const ordered_concat_iterator &x, 270 | const ordered_concat_iterator &y) noexcept { 271 | return x.base() < y.base(); 272 | } 273 | 274 | template 275 | inline constexpr bool 276 | operator>( 277 | const ordered_concat_iterator &x, 278 | const ordered_concat_iterator &y) noexcept { 279 | return y < x; 280 | } 281 | 282 | template 283 | inline constexpr bool 284 | operator>=( 285 | const ordered_concat_iterator &x, 286 | const ordered_concat_iterator &y) noexcept { 287 | return !(x < y); 288 | } 289 | 290 | template 291 | inline constexpr bool 292 | operator<=( 293 | const ordered_concat_iterator &x, 294 | const ordered_concat_iterator &y) noexcept { 295 | return !(y < x); 296 | } 297 | 298 | template 299 | inline constexpr bool 300 | operator==( 301 | const ordered_concat_iterator &x, 302 | const ordered_concat_iterator &y) noexcept { 303 | return x.base1() == y.base1() && x.base2() == y.base2(); 304 | } 305 | 306 | template 307 | inline constexpr bool 308 | operator!=( 309 | const ordered_concat_iterator &x, 310 | const ordered_concat_iterator &y) noexcept { 311 | return !(x == y); 312 | } 313 | 314 | template 315 | inline constexpr bool 316 | operator>( 317 | const ordered_concat_iterator &x, 318 | const ordered_concat_iterator &y) noexcept { 319 | return y < x; 320 | } 321 | 322 | template 323 | inline constexpr bool 324 | operator>=( 325 | const ordered_concat_iterator &x, 326 | const ordered_concat_iterator &y) noexcept { 327 | return !(x < y); 328 | } 329 | 330 | template 331 | inline constexpr bool 332 | operator<=( 333 | const ordered_concat_iterator &x, 334 | const ordered_concat_iterator &y) noexcept { 335 | return !(y < x); 336 | } 337 | 338 | template 339 | inline constexpr bool 340 | operator==( 341 | const ordered_concat_iterator &x, 342 | const Pointer &y) noexcept { 343 | return x.base() == y; 344 | } 345 | 346 | template 347 | inline constexpr bool 348 | operator!=( 349 | const ordered_concat_iterator &x, 350 | const Pointer &y) noexcept { 351 | return !(x == y); 352 | } 353 | 354 | template 355 | inline constexpr bool 356 | operator>( 357 | const ordered_concat_iterator &x, 358 | const Pointer &y) noexcept { 359 | return y < x; 360 | } 361 | 362 | template 363 | inline constexpr bool 364 | operator>=( 365 | const ordered_concat_iterator &x, 366 | const Pointer &y) noexcept { 367 | return !(x < y); 368 | } 369 | 370 | template 371 | inline constexpr bool 372 | operator<=( 373 | const ordered_concat_iterator &x, 374 | const Pointer &y) noexcept { 375 | return !(y < x); 376 | } 377 | 378 | template 379 | inline constexpr bool 380 | operator==( 381 | const Pointer &x, 382 | const ordered_concat_iterator &y) noexcept { 383 | return x.base() == y.base(); 384 | } 385 | 386 | template 387 | inline constexpr bool 388 | operator!=( 389 | const Pointer &x, 390 | const ordered_concat_iterator &y) noexcept { 391 | return !(x == y); 392 | } 393 | 394 | template 395 | inline constexpr bool 396 | operator>( 397 | const Pointer &x, 398 | const ordered_concat_iterator &y) noexcept { 399 | return y < x; 400 | } 401 | 402 | template 403 | inline constexpr bool 404 | operator>=( 405 | const Pointer &x, 406 | const ordered_concat_iterator &y) noexcept { 407 | return !(x < y); 408 | } 409 | 410 | template 411 | inline constexpr bool 412 | operator<=( 413 | const Pointer &x, 414 | const ordered_concat_iterator &y) noexcept { 415 | return !(y < x); 416 | } 417 | 418 | template 419 | inline constexpr auto 420 | operator-( 421 | const ordered_concat_iterator &x, 422 | const ordered_concat_iterator &y) noexcept 423 | -> decltype(x.base() - y.base()) { 424 | return x.base() - y.base(); 425 | } 426 | 427 | template 428 | inline constexpr ordered_concat_iterator 429 | operator+( 430 | typename ordered_concat_iterator::difference_type n, 431 | ordered_concat_iterator x) noexcept { 432 | x += n; 433 | return x; 434 | } 435 | } // namespace detail 436 | } // namespace small 437 | 438 | #endif // SMALL_DETAIL_ITERATOR_ORDERED_CONCAT_ITERATOR_HPP 439 | --------------------------------------------------------------------------------