├── cmake ├── small_vectorsConfig.cmake.in ├── small_vectors_ut.cmake ├── extract_version.cmake ├── Config.cmake.in └── get_cpm.cmake ├── include └── small_vectors │ ├── safe_buffers_error_handler.h │ ├── relocatable │ └── std │ │ ├── vector.h │ │ ├── deque.h │ │ ├── set.h │ │ ├── map.h │ │ ├── unordered_set.h │ │ └── unordered_map.h │ ├── concepts │ ├── integral_or_byte.h │ ├── numeric_limits.h │ ├── stream_insertable.h │ ├── hashable.h │ ├── iterator_traits.h │ └── concepts.h │ ├── utils │ ├── static_call_operator.h │ ├── endian.h │ ├── strong_function.h │ ├── enum_support.h │ ├── unaligned.h │ └── meta_packed_struct.h │ ├── detail │ ├── conditional_trivial_reloc_base.h │ ├── safe_buffers.h │ └── adapter_iterator.h │ ├── stream │ ├── basic_string.h │ ├── strong_type.h │ └── basic_fixed_string.h │ ├── formattable │ ├── source_location.h │ ├── error_code.h │ ├── basic_fixed_string.h │ ├── basic_string.h │ └── strong_type.h │ ├── version.h │ ├── interprocess │ ├── fork.h │ ├── atomic_mutex.h │ ├── shared_mem_utils.h │ ├── ring_queue.h │ └── stack_buffer.h │ ├── ranges │ └── accumulate.h │ ├── algo │ ├── bound_leaning_lower_bound.h │ └── bwt.h │ ├── composed_pointer_with_data.h │ ├── basic_fixed_string.h │ └── inclass_storage.h ├── configure ├── source ├── expected.cc └── safe_buffers.cc ├── test.sh ├── unit_tests ├── ut_core │ └── boost_ut.h ├── unit_test_core.cc ├── byteswap_ut.cc ├── composed_pointer_with_data_ut.cc ├── ring_queue_ut.cc ├── ranges_ut.cc ├── unaligned_ut.cc ├── bwt_ut.cc ├── CMakeLists.txt ├── inclass_storage_ut.cc ├── math_ut.cc ├── stack_buffer_ut.cc └── strong_type_ut.cc ├── .gitignore ├── SECURITY.md ├── update_namespace.bash ├── git_hooks ├── full │ └── pre-commit └── check_only │ └── pre-commit ├── git_tag.sh ├── LICENSE ├── format_sources.sh ├── .clang-tidy ├── .github └── workflows │ └── cmake.yml ├── workflow.sh ├── gdb └── .gdbinit ├── CMakeLists.txt ├── .clang-format ├── README.md ├── .cmake-format.yaml └── CMakePresets.json /cmake/small_vectorsConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(CMakeFindDependencyMacro) 4 | 5 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 6 | 7 | check_required_components(@PROJECT_NAME@) 8 | 9 | -------------------------------------------------------------------------------- /include/small_vectors/safe_buffers_error_handler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace small_vectors::inline v3_3 6 | { 7 | auto set_error_report_handler(std::function) -> void; 8 | } 9 | -------------------------------------------------------------------------------- /configure: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | rm -R build 4 | cmake -G "Kate - Ninja" \ 5 | -DCMAKE_CXX_COMPILER=clang \ 6 | -DCMAKE_CXX_FLAGS="-stdlib=libstdc++ -lstdc++" \ 7 | -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \ 8 | -DCMAKE_BUILD_TYPE=Debug \ 9 | -S=./ -B=./build 10 | -------------------------------------------------------------------------------- /include/small_vectors/relocatable/std/vector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace std 6 | { 7 | template 8 | consteval bool adl_decl_trivially_destructible_after_move(vector const *) 9 | { 10 | return true; 11 | } 12 | } // namespace std 13 | -------------------------------------------------------------------------------- /source/expected.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace cxx23 4 | { 5 | #if !(defined(__cpp_lib_expected) && __cpp_lib_expected >= 202211L) 6 | char const * bad_expected_access::what() const noexcept { return "access to expected value without value"; } 7 | #endif 8 | } // namespace cxx23 9 | -------------------------------------------------------------------------------- /include/small_vectors/relocatable/std/deque.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace std 6 | { 7 | template 8 | consteval bool adl_decl_trivially_destructible_after_move(deque const *) 9 | { 10 | return true; 11 | } 12 | } // namespace std 13 | 14 | -------------------------------------------------------------------------------- /include/small_vectors/relocatable/std/set.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace std 6 | { 7 | template 8 | consteval bool adl_decl_trivially_destructible_after_move(set const *) 9 | { 10 | return true; 11 | } 12 | } // namespace std 13 | -------------------------------------------------------------------------------- /cmake/small_vectors_ut.cmake: -------------------------------------------------------------------------------- 1 | 2 | function( add_unittest name ) 3 | add_executable(${name}) 4 | target_sources(${name} PRIVATE ${name}.cc) 5 | target_link_libraries( ${name} PRIVATE small_vectors_ut_core ) 6 | 7 | add_dependencies(unit_tests ${name} ) 8 | add_test( NAME ${name}_test COMMAND ${name} ) 9 | endfunction() 10 | 11 | -------------------------------------------------------------------------------- /include/small_vectors/relocatable/std/map.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace std 6 | { 7 | template 8 | consteval bool adl_decl_trivially_destructible_after_move(map const *) 9 | { 10 | return true; 11 | } 12 | } // namespace std 13 | 14 | -------------------------------------------------------------------------------- /include/small_vectors/concepts/integral_or_byte.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace small_vectors::inline v3_3::concepts 8 | { 9 | template 10 | concept integral_or_byte = std::integral || std::is_same_v; 11 | } 12 | -------------------------------------------------------------------------------- /include/small_vectors/relocatable/std/unordered_set.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace std 6 | { 7 | template 8 | consteval bool adl_decl_trivially_destructible_after_move(unordered_set const *) 9 | { 10 | return true; 11 | } 12 | } // namespace std 13 | -------------------------------------------------------------------------------- /include/small_vectors/relocatable/std/unordered_map.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace std 6 | { 7 | template 8 | consteval bool adl_decl_trivially_destructible_after_move(unordered_map const *) 9 | { 10 | return true; 11 | } 12 | } // namespace std 13 | -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | cmake --workflow --preset="clang-17-release" 2 | cmake --workflow --preset="clang-17-libc++release" 3 | cmake --workflow --preset="clang-16-release" 4 | cmake --workflow --preset="clang-16-libc++release" 5 | cmake --workflow --preset="clang-15-release" 6 | cmake --workflow --preset="clang-15-libc++release" 7 | cmake --workflow --preset="gcc-13-release" 8 | cmake --workflow --preset="gcc-12-release" 9 | -------------------------------------------------------------------------------- /unit_tests/ut_core/boost_ut.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__clang__) 4 | #pragma clang diagnostic push 5 | #pragma clang diagnostic ignored "-Wsign-conversion" 6 | #pragma clang diagnostic ignored "-Wextra-semi" 7 | #pragma clang diagnostic ignored "-Wreserved-identifier" 8 | #endif 9 | 10 | #include 11 | 12 | #if defined(__clang__) 13 | #pragma clang diagnostic pop 14 | #endif 15 | -------------------------------------------------------------------------------- /include/small_vectors/concepts/numeric_limits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace small_vectors::inline v3_3::concepts 6 | { 7 | template 8 | concept has_numeric_limits_max = requires { 9 | { std::numeric_limits::max() } -> std::convertible_to; 10 | }; 11 | } // namespace small_vectors::inline v3_3::concepts 12 | 13 | -------------------------------------------------------------------------------- /include/small_vectors/utils/static_call_operator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #if defined(__cpp_static_call_operator) 6 | #define small_vector_static_call_operator static 7 | #define small_vector_static_call_operator_const 8 | #else 9 | #define small_vector_static_call_operator 10 | #define small_vector_static_call_operator_const const 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /include/small_vectors/concepts/stream_insertable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace small_vectors::inline v3_3::concepts 6 | { 7 | template 8 | concept stream_insertable = requires(std::ostream & os, value_type const & value) { 9 | { os << value } -> std::same_as; 10 | }; 11 | } // namespace small_vectors::inline v3_3::concepts 12 | -------------------------------------------------------------------------------- /cmake/extract_version.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) # Require at least CMake 3.14 for string(PREPEND ...) 2 | 3 | # Read the version header file 4 | file(READ include/small_vectors/version.h version_file_contents) 5 | 6 | # Extract the version number 7 | string(REGEX MATCH "SMALL_VECTORS_VERSION \"([0-9]+\\.[0-9]+\\.[0-9]+)\"" _ ${version_file_contents}) 8 | set(small_vectors_version ${CMAKE_MATCH_1}) 9 | 10 | -------------------------------------------------------------------------------- /cmake/Config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include(CMakeFindDependencyMacro) 4 | 5 | string(REGEX MATCHALL "[^;]+" SEPARATE_DEPENDENCIES "@PROJECT_DEPENDENCIES@") 6 | 7 | foreach(dependency ${SEPARATE_DEPENDENCIES}) 8 | string(REPLACE " " ";" args "${dependency}") 9 | find_dependency(${args}) 10 | endforeach() 11 | 12 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 13 | check_required_components("@PROJECT_NAME@") 14 | -------------------------------------------------------------------------------- /include/small_vectors/concepts/hashable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace small_vectors::inline v3_3::concepts 8 | { 9 | template 10 | concept hashable = requires(value_type value) { 11 | { std::hash{}(value) } -> std::convertible_to; 12 | }; 13 | } // namespace small_vectors::inline v3_3::concepts 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | build* 35 | .cache* 36 | log_output 37 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | Below are listed maintained and actively developed versions. 6 | | Version | Supported | 7 | | ------- | ------------------ | 8 | | 2.3.x | :white_check_mark: | 9 | | < 2.3 | :x: | 10 | 11 | ## Reporting a Vulnerability 12 | 13 | Report a vulnerability as issue or discussion. 14 | 15 | * https://github.com/arturbac/small_vectors/issues 16 | * https://github.com/arturbac/small_vectors/discussions 17 | -------------------------------------------------------------------------------- /update_namespace.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Define the search and replace strings 4 | search_string="small_vectors::inline v3_2" 5 | replace_string="small_vectors::inline v3_3" 6 | 7 | # Loop over files with specified extensions and perform the replacement 8 | find . -type f \( -name "*.h" -o -name "*.hpp" -o -name "*.cc" -o -name "*.cpp" \) -exec sed -i "s/$search_string/$replace_string/g" {} + 9 | 10 | search_string="small_vectors::v3_0" 11 | replace_string="small_vectors::v3_2" 12 | 13 | echo "Replacement complete." 14 | -------------------------------------------------------------------------------- /include/small_vectors/detail/conditional_trivial_reloc_base.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace small_vectors::inline v3_3 5 | { 6 | struct trivially_relocatable 7 | { 8 | }; 9 | 10 | struct not_trivially_relocatable 11 | { 12 | ~not_trivially_relocatable() {} 13 | }; 14 | 15 | template 16 | using conditional_trivial_reloc_base 17 | = std::conditional_t, trivially_relocatable, not_trivially_relocatable>; 18 | } // namespace small_vectors::inline v3_3 19 | -------------------------------------------------------------------------------- /include/small_vectors/stream/basic_string.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Artur Bać 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | #include 6 | #include 7 | 8 | namespace small_vectors::inline v3_3 9 | { 10 | template 11 | std::ostream & operator<<(std::ostream & os, basic_string_t const & str) 12 | { 13 | std::basic_string_view view(str); 14 | os << view; 15 | return os; 16 | } 17 | } // namespace small_vectors::inline v3_3 18 | -------------------------------------------------------------------------------- /include/small_vectors/formattable/source_location.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | template<> 7 | struct std::formatter 8 | { 9 | template 10 | constexpr auto parse(ParseContext & ctx) -> decltype(ctx.begin()) 11 | { 12 | return ctx.begin(); 13 | } 14 | 15 | template 16 | auto format(std::source_location const & loc, FormatContext & ctx) const -> decltype(ctx.out()) 17 | { 18 | return format_to(ctx.out(), "{}:{}", loc.line(), loc.file_name()); 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /include/small_vectors/formattable/error_code.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | template<> 7 | struct std::formatter 8 | { 9 | template 10 | constexpr auto parse(ParseContext & ctx) -> decltype(ctx.begin()) 11 | { 12 | return ctx.begin(); 13 | } 14 | 15 | template 16 | auto format(std::error_code const & ec, FormatContext & ctx) const -> decltype(ctx.out()) 17 | { 18 | return format_to(ctx.out(), "{}:{} - {}", ec.category().name(), ec.value(), ec.message()); 19 | } 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /include/small_vectors/stream/strong_type.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Artur Bać 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | 9 | namespace small_vectors::inline v3_3::utils 10 | { 11 | 12 | template 13 | std::ostream & operator<<(std::ostream & os, strong_type const & st) 14 | { 15 | os << st.get(); 16 | return os; 17 | } 18 | 19 | } // namespace small_vectors::inline v3_3::utils 20 | -------------------------------------------------------------------------------- /include/small_vectors/utils/endian.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace small_vectors::inline v3_3::utils 9 | { 10 | #if defined(__cpp_lib_endian) 11 | using std::endian; 12 | #else 13 | enum struct endian 14 | { 15 | #ifdef _WIN32 16 | little = 0, 17 | big = 1, 18 | native = little 19 | #else 20 | little = __ORDER_LITTLE_ENDIAN__, 21 | big = __ORDER_BIG_ENDIAN__, 22 | native = __BYTE_ORDER__ 23 | #endif 24 | }; 25 | #endif 26 | 27 | } // namespace small_vectors::inline v3_3::utils 28 | -------------------------------------------------------------------------------- /include/small_vectors/stream/basic_fixed_string.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Artur Bać 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | #include 6 | #include 7 | 8 | namespace small_vectors::inline v3_3 9 | { 10 | template 11 | std::basic_ostream & 12 | operator<<(std::basic_ostream & os, basic_fixed_string const & str) 13 | { 14 | // Directly use the string view for output. This works with char, wchar_t, etc. 15 | os << str.view(); 16 | return os; 17 | } 18 | } // namespace small_vectors::inline v3_3 19 | -------------------------------------------------------------------------------- /include/small_vectors/concepts/iterator_traits.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace small_vectors::inline v3_3::concepts 8 | { 9 | template 10 | concept iterator_traits_defined = requires { 11 | typename std::iterator_traits::value_type; 12 | typename std::iterator_traits::difference_type; 13 | typename std::iterator_traits::pointer; 14 | typename std::iterator_traits::reference; 15 | typename std::iterator_traits::iterator_category; 16 | }; 17 | 18 | } // namespace small_vectors::inline v3_3::concepts 19 | -------------------------------------------------------------------------------- /include/small_vectors/version.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Artur Bać 2 | // SPDX-License-Identifier: MIT 3 | // SPDX-PackageHomePage: https://github.com/arturbac/small_vectors 4 | 5 | #pragma once 6 | 7 | #define SMALL_VECTORS_VERSION "3.3.6" 8 | 9 | #ifdef __clang__ 10 | #define small_vectors_clang_do_pragma(x) _Pragma(#x) 11 | #define small_vectors_clang_unsafe_buffer_usage_begin small_vectors_clang_do_pragma(clang unsafe_buffer_usage begin) 12 | #define small_vectors_clang_unsafe_buffer_usage_end small_vectors_clang_do_pragma(clang unsafe_buffer_usage end) 13 | #else 14 | #define small_vectors_clang_unsafe_buffer_usage_begin 15 | #define small_vectors_clang_unsafe_buffer_usage_end 16 | #endif 17 | -------------------------------------------------------------------------------- /git_hooks/full/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Redirect output to stderr. 4 | exec 1>&2 5 | 6 | # Specify the file extensions you want to format. 7 | EXTENSIONS="(\.cpp|\.cxx|\.cc|\.hpp|\.h)$" 8 | 9 | # Get a list of all staged files with the specified extensions. 10 | FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E $EXTENSIONS) 11 | 12 | # Exit early if no files are staged. 13 | if [ -z "$FILES" ]; then 14 | exit 0 15 | fi 16 | 17 | # Format all staged files and add them back to the staging area. 18 | echo "Running clang-format on staged files..." 19 | for FILE in $FILES; do 20 | clang-format -i "$FILE" 21 | git add "$FILE" 22 | done 23 | 24 | # Inform the user. 25 | echo "All staged files have been formatted." 26 | 27 | exit 0 28 | 29 | -------------------------------------------------------------------------------- /unit_tests/unit_test_core.cc: -------------------------------------------------------------------------------- 1 | // #define BOOST_TEST_MAIN 2 | #include 3 | 4 | std::ostream & operator<<(std::ostream & stream, non_trivial_ptr const & v) 5 | { 6 | if(v.value_) 7 | stream << *v.value_; 8 | else 9 | stream << "nullptr"; 10 | return stream; 11 | } 12 | 13 | std::ostream & operator<<(std::ostream & stream, non_trivial_ptr_except const & v) 14 | { 15 | if(v.obj.value_) 16 | stream << *v.obj.value_; 17 | else 18 | stream << "nullptr"; 19 | return stream; 20 | } 21 | 22 | std::ostream & operator<<(std::ostream & stream, non_trivial_ptr_except_copy const & v) 23 | { 24 | if(v.obj.value_) 25 | stream << *v.obj.value_; 26 | else 27 | stream << "nullptr"; 28 | return stream; 29 | } 30 | -------------------------------------------------------------------------------- /git_tag.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Path to the version header file 4 | VERSION_FILE="include/small_vectors/version.h" 5 | 6 | # Extract the version number from the file 7 | VERSION=$(grep 'SMALL_VECTORS_VERSION' $VERSION_FILE | sed 's/.*"\(.*\)".*/\1/') 8 | 9 | # Check if the version was successfully extracted 10 | if [ -z "$VERSION" ]; then 11 | echo "Error: Could not extract version from $VERSION_FILE" 12 | exit 1 13 | fi 14 | 15 | # Create a git tag with the extracted version, prefixed by 'v' 16 | TAG="v$VERSION" 17 | 18 | echo "Creating git tag: $TAG" 19 | 20 | # Check if the tag already exists 21 | if git rev-parse "$TAG" >/dev/null 2>&1; then 22 | echo "Error: Tag $TAG already exists." 23 | exit 1 24 | else 25 | git tag -a "$TAG" -m "Version $VERSION" 26 | echo "Git tag $TAG created successfully." 27 | 28 | fi 29 | -------------------------------------------------------------------------------- /include/small_vectors/formattable/basic_fixed_string.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Artur Bać 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | #include 6 | #include 7 | 8 | namespace std 9 | { 10 | template 11 | struct formatter, CharType> 12 | { 13 | formatter, CharType> value_formatter; 14 | 15 | template 16 | constexpr auto parse(FormatContext & ctx) 17 | { 18 | return value_formatter.parse(ctx); 19 | } 20 | 21 | template 22 | [[nodiscard]] 23 | auto format(small_vectors::basic_fixed_string const & value, FormatContext & ctx) const -> 24 | typename FormatContext::iterator 25 | { 26 | return value_formatter.format(value.view(), ctx); 27 | } 28 | }; 29 | } // namespace std 30 | 31 | -------------------------------------------------------------------------------- /include/small_vectors/detail/safe_buffers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #if !defined(SMALL_VECTORS_CHECK_VALID_ELEMENT_ACCESS) 8 | #define SMALL_VECTORS_CHECK_VALID_ELEMENT_ACCESS true 9 | #endif 10 | 11 | namespace small_vectors::inline v3_3::detail 12 | { 13 | // motivated by to https://libcxx.llvm.org/Hardening.html 14 | // checks that any attempts to access a container element, whether through the container object or through an iterator, 15 | // are valid and do not attempt to go out of bounds or otherwise access a non-existent element. 16 | inline constexpr bool check_valid_element_access{SMALL_VECTORS_CHECK_VALID_ELEMENT_ACCESS}; 17 | 18 | [[noreturn]] 19 | void report_invalid_element_access( 20 | std::string_view, std::size_t size, std::size_t index, std::source_location loc = std::source_location::current() 21 | ); 22 | } // namespace small_vectors::inline v3_3::detail 23 | -------------------------------------------------------------------------------- /git_hooks/check_only/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Redirect output to stderr. 4 | exec 1>&2 5 | 6 | # Specify the file extensions you want to apply clang-format to 7 | FILES_PATTERN='\.(cpp|hpp|c|cxx|cc|h)$' 8 | 9 | # Get a list of all staged files matching the pattern 10 | STAGED_FILES=$(git diff --cached --name-only --diff-filter=d | grep -E $FILES_PATTERN) 11 | 12 | # Exit if there are no C/C++ files staged 13 | if [ -z "$STAGED_FILES" ]; then 14 | exit 0 15 | fi 16 | 17 | # Temporary file to store clang-format output 18 | FORMAT_DIFF=$(mktemp) 19 | 20 | # Check each file 21 | for FILE in $STAGED_FILES; do 22 | # Apply clang-format to all staged files and write the output to the temporary file 23 | git show ":$FILE" | clang-format "$FILE" > "$FORMAT_DIFF" 24 | if ! cmp -s "$FORMAT_DIFF" "$FILE"; then 25 | echo "Error: $FILE is not formatted correctly. Please run clang-format." 26 | rm "$FORMAT_DIFF" 27 | exit 1 28 | fi 29 | done 30 | 31 | rm "$FORMAT_DIFF" 32 | exit 0 33 | 34 | -------------------------------------------------------------------------------- /include/small_vectors/formattable/basic_string.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Artur Bać 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | #include 6 | #include 7 | 8 | template 9 | requires std::formattable, char_type> 10 | struct std::formatter, char_type> 11 | { 12 | std::formatter, char_type> value_formatter; 13 | 14 | template 15 | constexpr auto parse(FormatContext & ctx) 16 | { 17 | return value_formatter.parse(ctx); 18 | } 19 | 20 | template 21 | [[nodiscard]] 22 | auto format(small_vectors::basic_string_t const & value, FormatContext & ctx) const -> 23 | typename FormatContext::iterator 24 | { 25 | std::basic_string_view view(value); 26 | return value_formatter.format(view, ctx); 27 | } 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /cmake/get_cpm.cmake: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: MIT 2 | # 3 | # SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors 4 | 5 | set(CPM_DOWNLOAD_VERSION 0.40.2) 6 | set(CPM_HASH_SUM "c8cdc32c03816538ce22781ed72964dc864b2a34a310d3b7104812a5ca2d835d") 7 | 8 | if(CPM_SOURCE_CACHE) 9 | set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 10 | elseif(DEFINED ENV{CPM_SOURCE_CACHE}) 11 | set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 12 | else() 13 | set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") 14 | endif() 15 | 16 | # Expand relative path. This is important if the provided path contains a tilde (~) 17 | get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) 18 | 19 | file(DOWNLOAD 20 | https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake 21 | ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} 22 | ) 23 | 24 | include(${CPM_DOWNLOAD_LOCATION}) 25 | -------------------------------------------------------------------------------- /include/small_vectors/formattable/strong_type.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Artur Bać 2 | // SPDX-License-Identifier: MIT 3 | 4 | #pragma once 5 | #include 6 | #include 7 | 8 | template 9 | struct std::formatter> 10 | { 11 | std::formatter value_formatter; 12 | 13 | template 14 | #if __cplusplus >= 202301L 15 | requires std::formattable 16 | #endif 17 | constexpr auto parse(FormatContext & ctx) 18 | { 19 | return value_formatter.parse(ctx); 20 | } 21 | 22 | template 23 | #if __cplusplus >= 202301L 24 | requires std::formattable 25 | #endif 26 | [[nodiscard]] 27 | auto format(small_vectors::utils::strong_type const & strong_value, FormatContext & ctx) const -> 28 | typename FormatContext::iterator 29 | { 30 | return value_formatter.format(strong_value.value(), ctx); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Artur Bać 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /format_sources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Define directories to skip 4 | SKIP_DIRS=("build" "cmake") 5 | 6 | # Function to check if directory should be skipped 7 | should_skip() { 8 | for dir in "${SKIP_DIRS[@]}"; do 9 | if [[ "$1" == *"/$dir/"* ]]; then 10 | return 0 # 0 means true in bash, so skip 11 | fi 12 | done 13 | return 1 # 1 means false in bash, so don't skip 14 | } 15 | 16 | # Main loop to find and format files 17 | find . -type f \( -iname \*.h -o -iname \*.hpp -o -iname \*.cc -o -iname \*.c -o -iname \*.cpp \) | while read -r file; do 18 | if should_skip "$file"; then 19 | # echo "Skipping $file" 20 | continue 21 | fi 22 | 23 | # Create a temporary file to store the original content 24 | temp_file=$(mktemp) 25 | cp "$file" "$temp_file" 26 | 27 | # Format the file 28 | clang-format -i "$file" 29 | 30 | # Check if the file was changed by comparing it to the temporary file 31 | if ! cmp -s "$file" "$temp_file"; then 32 | echo "Formatted $file" 33 | # else 34 | # echo "$file unchanged." 35 | fi 36 | 37 | # Clean up the temporary file 38 | rm "$temp_file" 39 | done 40 | 41 | echo "Formatting complete." 42 | -------------------------------------------------------------------------------- /source/safe_buffers.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace small_vectors::inline v3_3 8 | { 9 | #pragma clang diagnostic push 10 | #pragma clang diagnostic ignored "-Wexit-time-destructors" 11 | #pragma clang diagnostic ignored "-Wglobal-constructors" 12 | static std::function error_handler = [](std::string_view error) { std::print("{}", error); }; 13 | #pragma clang diagnostic pop 14 | 15 | auto set_error_report_handler(std::function handler) -> void 16 | { 17 | error_handler = std::move(handler); 18 | } 19 | } // namespace small_vectors::inline v3_3 20 | 21 | namespace small_vectors::inline v3_3::detail 22 | { 23 | void report_invalid_element_access(std::string_view, std::size_t size, std::size_t index, std::source_location loc) 24 | { 25 | std::invoke( 26 | error_handler, std::format("[invalid_element_access] current size:{} index:{} called from {}", size, index, loc) 27 | ); 28 | std::abort(); 29 | } 30 | } // namespace small_vectors::inline v3_3::detail 31 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: 'clang-diagnostic-*,clang-analyzer-*,cppcoreguidelines-*,modernize-*,bugprone-*,readability-identifier-naming,-modernize-use-trailing-return-type,misc-const-correctness' 2 | CheckOptions: 3 | # General Naming Convention: lower_case 4 | - key: readability-identifier-naming.NamespaceCase 5 | value: 'lower_case' 6 | - key: readability-identifier-naming.ClassCase 7 | value: 'lower_case' 8 | - key: readability-identifier-naming.StructCase 9 | value: 'lower_case' 10 | - key: readability-identifier-naming.FunctionCase 11 | value: 'lower_case' 12 | - key: readability-identifier-naming.VariableCase 13 | value: 'lower_case' 14 | - key: readability-identifier-naming.PublicMemberCase 15 | value: 'lower_case' 16 | - key: readability-identifier-naming.PrivateMemberCase 17 | value: 'lower_case' 18 | - key: readability-identifier-naming.ProtectedMemberCase 19 | value: 'lower_case' 20 | - key: readability-identifier-naming.ConstantCase 21 | value: 'lower_case' 22 | # Member variables should end with '_' 23 | - key: readability-identifier-naming.MemberSuffix 24 | value: '_' 25 | # Macros should be UPPER_CASE 26 | - key: readability-identifier-naming.MacroDefinitionCase 27 | value: 'UPPER_CASE' 28 | -------------------------------------------------------------------------------- /include/small_vectors/utils/strong_function.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace small_vectors::inline v3_3::utils 6 | { 7 | template 8 | struct strong_function; // undefined 9 | 10 | template 11 | struct strong_function 12 | { 13 | std::function callable_; 14 | strong_function() = default; 15 | 16 | template, bool> = true> 17 | explicit strong_function(Cb callable) : callable_{callable} 18 | { 19 | } 20 | 21 | template 22 | auto operator()(fargs_type &&... args) const noexcept(noexcept(callable_(std::forward(args)...))) 23 | { 24 | return callable_(std::forward(args)...); 25 | } 26 | 27 | constexpr explicit operator bool() const noexcept { return static_cast(callable_); } 28 | }; 29 | 30 | template 31 | strong_function(Tag, ret_type (*)(args_type...)) -> strong_function; 32 | 33 | } // namespace small_vectors::inline v3_3::utils 34 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | paths-ignore: 7 | - 'README.md' 8 | - 'docs/**' 9 | 10 | pull_request: 11 | branches: [ "master" ] 12 | paths-ignore: 13 | - 'README.md' 14 | - 'docs/**' 15 | 16 | jobs: 17 | build-and-test: 18 | runs-on: ubuntu-24.04 19 | steps: 20 | - uses: actions/checkout@v3 21 | 22 | - name: Add LLVM Repository 23 | run: | 24 | wget https://apt.llvm.org/llvm.sh 25 | chmod +x llvm.sh 26 | sudo ./llvm.sh 19 27 | 28 | - name: Install dependencies 29 | run: | 30 | sudo apt-get update 31 | sudo apt-get install -y gcc-14 g++-14 cmake ninja-build clang-19 libfmt-dev libc++-19-dev libc++abi-19-dev 32 | sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 100 33 | sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-19 100 34 | 35 | - name: tests clang-19-release 36 | run: cmake --workflow --preset="clang-19-release" 37 | 38 | - name: tests clang-19-libc++release 39 | run: cmake --workflow --preset="clang-19-libc++release" 40 | 41 | - name: tests gcc-14-release 42 | run: cmake --workflow --preset="gcc-14-release" 43 | -------------------------------------------------------------------------------- /include/small_vectors/utils/enum_support.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace small_vectors::inline v3_3::utils 10 | { 11 | namespace internal 12 | { 13 | [[nodiscard]] 14 | constexpr bool enum_name_view_compare(std::string_view a, std::string_view b) noexcept 15 | { 16 | return a.size() == b.size() && a == b; 17 | } 18 | } // namespace internal 19 | 20 | namespace detail 21 | { 22 | template 23 | concept enum_concept = std::is_enum::value; 24 | } 25 | 26 | ///\brief tests if \ref value has set flag \ref test_flag 27 | template 28 | [[nodiscard]] 29 | inline constexpr bool enum_test_flag(enum_type value, enum_type test_flag) noexcept 30 | { 31 | return 0 != (cxx23::to_underlying(value) & cxx23::to_underlying(test_flag)); 32 | } 33 | 34 | ///\returns conditionaly set flag \param cond_e depending on \param cond otherwise returns {} 35 | template 36 | [[nodiscard]] 37 | inline constexpr enum_type enum_cond_flag(bool cond, enum_type cond_e) noexcept 38 | { 39 | if(cond) 40 | return cond_e; 41 | else 42 | return {}; 43 | } 44 | } // namespace small_vectors::inline v3_3::utils 45 | -------------------------------------------------------------------------------- /workflow.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Ensure the log_output directory exists 4 | rm -rf log_output 5 | mkdir -p log_output 6 | 7 | # Define an array of CMake commands 8 | commands=( 9 | "cmake --workflow --preset=gcc-12-release" 10 | "cmake --workflow --preset=gcc-13-release" 11 | "cmake --workflow --preset=clang-17-libc++release" 12 | "cmake --workflow --preset=clang-17-release" 13 | ) 14 | 15 | # Function to sanitize command strings into valid filenames 16 | sanitize_filename() { 17 | echo "$1" | sed 's/[^a-zA-Z0-9_-]/_/g' 18 | } 19 | 20 | # Export the function so it can be used by parallel 21 | export -f sanitize_filename 22 | 23 | # Run commands in parallel, each outputting to its own log file 24 | parallel --jobs 0 'command={}; sanitized_filename=$(sanitize_filename "{}"); echo "$command" > log_output/"$sanitized_filename".log; $command &> log_output/"$sanitized_filename".log' ::: "${commands[@]}" 25 | 26 | # After all commands have completed, print the outputs sequentially 27 | #for log_file in log_output/*.log; do 28 | # echo "Output from ${log_file}:" 29 | # cat "${log_file}" 30 | # echo "----------------------------------------" 31 | #done 32 | 33 | # After all commands have completed, filter and print the outputs for tests passed/failed 34 | for log_file in log_output/*.log; do 35 | echo "Test results from ${log_file}:" 36 | grep -E 'tests passed, [0-9]+ tests failed out of [0-9]+' "${log_file}" 37 | echo "----------------------------------------" 38 | done 39 | -------------------------------------------------------------------------------- /include/small_vectors/interprocess/fork.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | namespace small_vectors::inline v3_3::ip 11 | { 12 | struct fork_child_t 13 | { 14 | mutable pid_t pid_{}; 15 | 16 | constexpr auto joinable() const noexcept { return pid_ != pid_t{}; } 17 | 18 | auto join() const noexcept -> bool 19 | { 20 | if(joinable()) 21 | { 22 | int status; 23 | auto res = ::waitpid(pid_, &status, 0); 24 | pid_ = {}; 25 | if(-1 != res) 26 | return EXIT_SUCCESS == WEXITSTATUS(status); 27 | } 28 | return false; 29 | } 30 | }; 31 | 32 | template 33 | requires std::invocable 34 | [[nodiscard]] 35 | inline auto fork(function const & fn, Args... args) noexcept -> std::optional 36 | { 37 | auto pid = ::fork(); 38 | if(pid == 0) 39 | { 40 | bool status{}; 41 | if constexpr(std::is_nothrow_invocable_v) 42 | status = std::invoke(fn, args...); 43 | else 44 | try 45 | { 46 | status = std::invoke(fn, args...); 47 | } 48 | catch(...) 49 | { 50 | } 51 | std::exit(status ? EXIT_SUCCESS : EXIT_FAILURE); 52 | } 53 | else if(pid != -1) 54 | return fork_child_t{.pid_ = pid}; 55 | else 56 | return {}; 57 | } 58 | } // namespace small_vectors::inline v3_3::ip 59 | -------------------------------------------------------------------------------- /unit_tests/byteswap_ut.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #define BOOST_TEST_MAIN 3 | #define BOOST_TEST_DYN_LINK 4 | #include 5 | 6 | #define CONSTEXPR_TEST(a) \ 7 | if(!(a)) \ 8 | throw 9 | 10 | //----------------------------------------------------------------------------------------------------- 11 | 12 | consteval auto test_utils_byteswap() 13 | { 14 | CONSTEXPR_TEST(cxx23::byteswap(0) == 0); 15 | CONSTEXPR_TEST(cxx23::byteswap(uint16_t(0xa51f)) == 0x1fa5); 16 | CONSTEXPR_TEST(cxx23::byteswap(0xffa51fe0) == 0xe01fa5ff); 17 | CONSTEXPR_TEST(cxx23::byteswap(0xffa51fe000003a01ull) == 0x013a0000e01fa5ffull); 18 | 19 | double src{cxx20::bit_cast(0xffa51fe000003a01ull)}; 20 | double res{cxx23::byteswap(src)}; 21 | uint64_t res_dbl_byes{cxx20::bit_cast(res)}; 22 | CONSTEXPR_TEST(res_dbl_byes == 0x013a0000e01fa5ffull); 23 | return true; 24 | } 25 | 26 | BOOST_AUTO_TEST_CASE(utils_bytswap) 27 | { 28 | constexpr auto test_result(test_utils_byteswap()); 29 | static_assert(test_result); 30 | BOOST_TEST(test_result); 31 | 32 | BOOST_TEST(cxx23::byteswap(0) == 0); 33 | BOOST_TEST(cxx23::byteswap(uint16_t(0xa51f)) == 0x1fa5); 34 | BOOST_TEST(cxx23::byteswap(0xffa51fe0) == 0xe01fa5ff); 35 | BOOST_TEST(cxx23::byteswap(0xffa51fe000003a01ull) == 0x013a0000e01fa5ffull); 36 | 37 | double src{cxx20::bit_cast(0xffa51fe000003a01ull)}; 38 | double res{cxx23::byteswap(src)}; 39 | uint64_t res_dbl_byes{cxx20::bit_cast(res)}; 40 | BOOST_TEST(res_dbl_byes == 0x013a0000e01fa5ffull); 41 | } 42 | -------------------------------------------------------------------------------- /unit_tests/composed_pointer_with_data_ut.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace small_vectors; 5 | using boost::ut::expect; 6 | using metatests::constexpr_test; 7 | using boost::ut::operator""_test; 8 | 9 | enum struct some_enum : uint8_t 10 | { 11 | no, 12 | yes, 13 | maybe 14 | }; 15 | using type = composed_pointer_with_data; 16 | 17 | consteval auto test_composed_pointer_with_data() 18 | { 19 | constexpr_test(type::data_mask == 0b111); 20 | type x{}; 21 | constexpr_test(x.data() == some_enum{}); 22 | constexpr_test((x.data_ & type::pointer_mask) == 0); 23 | 24 | return true; 25 | } 26 | 27 | int main() 28 | { 29 | "composed_pointer_with_data_tests"_test = [] 30 | { 31 | constexpr auto test_result(test_composed_pointer_with_data()); 32 | static_assert(test_result); 33 | expect(test_result); 34 | 35 | { 36 | type x{}; 37 | expect(x.data() == some_enum{}); 38 | expect((x.data_ & type::pointer_mask) == 0); 39 | expect(x.ptr() == nullptr); 40 | } 41 | small_vectors_clang_unsafe_buffer_usage_begin // clang-18 42 | { 43 | double test[2]; 44 | type x{&test[1]}; 45 | expect(x.data() == some_enum{}); 46 | expect((x.data_ & type::pointer_mask) != 0); 47 | expect(x.ptr() == &test[1]); 48 | 49 | x.set_data(some_enum::yes); 50 | expect(x.data() == some_enum::yes); 51 | expect(x.ptr() == &test[1]); 52 | 53 | x.set_ptr(&test[0]); 54 | expect(x.data() == some_enum::yes); 55 | expect(x.ptr() == &test[0]); 56 | x.set_data(some_enum::maybe); 57 | expect(x.data() == some_enum::maybe); 58 | expect(x.ptr() == &test[0]); 59 | } 60 | small_vectors_clang_unsafe_buffer_usage_end // clang-18 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /unit_tests/ring_queue_ut.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace ut = boost::ut; 10 | using boost::ut::operator""_test; 11 | using namespace ut::operators::terse; 12 | using metatests::constexpr_test; 13 | 14 | namespace bip = boost::interprocess; 15 | using namespace std::string_view_literals; 16 | 17 | // constexpr auto shmem_name {"ring buffer shmem test"}; 18 | // struct message 19 | // { 20 | // std::array value; 21 | // }; 22 | // constexpr std::array messages 23 | // { 24 | // "Lorem ipsum dolor"sv, // 17 25 | // "sit amet,"sv, 26 | // "consectetur adipiscing"sv, 27 | // "elit, sed do eiusmo"sv, 28 | // "tempor incididunt ut"sv, 29 | // "labore et dolore"sv, 30 | // "magna aliqua."sv, 31 | // "Ut enim ad minim veniam,"sv, 32 | // "quis nostrud exercitation"sv 33 | // }; 34 | int main() 35 | { 36 | metatests::test_result result; 37 | "ring_queue_impl_basic"_test = [&] 38 | { 39 | auto fn_test = [] -> metatests::test_result 40 | { 41 | using queue_type = ip::detail::ring_queue_impl_t<16, ip::detail::constexpr_index>; 42 | queue_type queue; 43 | constexpr_test(queue.empty()); 44 | { 45 | std::array value{'a', 'b', 'c', 'd'}; 46 | queue.push(value.begin(), value.end()); 47 | } 48 | constexpr_test(!queue.empty()); 49 | { 50 | std::array value{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'}; 51 | queue.push(value.begin(), value.end()); 52 | } 53 | return {}; 54 | }; 55 | result |= metatests::run_constexpr_test(fn_test); 56 | result |= metatests::run_consteval_test(fn_test); 57 | }; 58 | } 59 | 60 | -------------------------------------------------------------------------------- /include/small_vectors/ranges/accumulate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace small_vectors::inline v3_3::ranges 9 | { 10 | struct accumulate_t 11 | { 12 | template sentinel> 13 | [[nodiscard]] 14 | small_vector_static_call_operator constexpr auto operator()( 15 | source_iterator beg, sentinel end, auto init, auto binary_op 16 | ) small_vector_static_call_operator_const noexcept 17 | { 18 | small_vectors_clang_unsafe_buffer_usage_begin // 19 | for(; beg != end; ++beg) init 20 | = binary_op(std::move(init), *beg); 21 | small_vectors_clang_unsafe_buffer_usage_end // 22 | return init; 23 | } 24 | 25 | template sentinel> 26 | [[nodiscard]] 27 | small_vector_static_call_operator constexpr auto operator()(source_iterator beg, sentinel end, auto init) 28 | { 29 | return operator()(beg, end, std::move(init), std::plus{}); 30 | } 31 | 32 | template 33 | [[nodiscard]] 34 | small_vector_static_call_operator constexpr auto 35 | operator()(input_range const & range, auto init, auto binary_op) small_vector_static_call_operator_const noexcept 36 | { 37 | return operator()(std::ranges::begin(range), std::ranges::end(range), std::move(init), binary_op); 38 | } 39 | 40 | template 41 | [[nodiscard]] 42 | small_vector_static_call_operator constexpr auto 43 | operator()(input_range const & range, auto init) small_vector_static_call_operator_const noexcept 44 | { 45 | return operator()(range, std::move(init), std::plus{}); 46 | } 47 | }; 48 | 49 | inline constexpr accumulate_t accumulate; 50 | } // namespace small_vectors::inline v3_3::ranges 51 | -------------------------------------------------------------------------------- /unit_tests/ranges_ut.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | struct test_tag : public small_vectors::utils::strong_type_default_traits 6 | { 7 | }; 8 | 9 | using test_user_type = small_vectors::utils::strong_type; 10 | using test_type_list = metatests::type_list; 11 | using namespace metatests; 12 | using boost::ut::operator""_test; 13 | using boost::ut::eq; 14 | using boost::ut::neq; 15 | 16 | //---------------------------------------------------------------------------------------------------------------------- 17 | int main() 18 | { 19 | test_result result; 20 | "test_accumulate"_test = [&result] 21 | { 22 | auto fn_tmpl = [](value_type const *) -> metatests::test_result 23 | { 24 | using limits = std::numeric_limits; 25 | { 26 | constexpr std::array range{}; 27 | constexpr_test(value_type(1) == small_vectors::ranges::accumulate(range, value_type(1))); 28 | } 29 | { 30 | constexpr std::array range{ 31 | limits::lowest(), value_type(0), limits::max() / value_type(4), value_type(1) 32 | }; 33 | constexpr auto sum(limits::lowest() + value_type(0) + limits::max() / value_type(4) + value_type(1)); 34 | constexpr_test(sum == small_vectors::ranges::accumulate(range, value_type(0))); 35 | } 36 | { 37 | constexpr std::array range{value_type(0), limits::max() / value_type(4), value_type(1)}; 38 | constexpr auto sum(value_type(0) + limits::max() / value_type(4) + value_type(1)); 39 | constexpr_test( 40 | sum * value_type(2) 41 | == small_vectors::ranges::accumulate( 42 | range, 43 | value_type(0), 44 | [](value_type init, value_type obj) noexcept { return value_type(init + obj * value_type(2)); } 45 | ) 46 | ); 47 | } 48 | return {}; 49 | }; 50 | result |= run_consteval_test(fn_tmpl); 51 | result |= run_constexpr_test(fn_tmpl); 52 | }; 53 | } 54 | -------------------------------------------------------------------------------- /include/small_vectors/interprocess/atomic_mutex.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | namespace small_vectors::inline v3_3::ip 7 | { 8 | /* NOTE 9 | memory_order_seq_cst - is used intentionaly, as read operations after store within mutex have to observe results by all 10 | threads 11 | 12 | A load operation with this memory order performs an acquire operation, a store performs a release operation, and 13 | read-modify-write performs both an acquire operation and a release operation, plus a single total order exists in which 14 | all threads observe all modifications in the same order 15 | */ 16 | 17 | struct atomic_mutex 18 | { 19 | std::atomic state_; 20 | 21 | explicit operator bool() const noexcept { return state_.load(std::memory_order_acquire); } 22 | 23 | bool is_locked() const noexcept { return state_.load(std::memory_order_acquire); } 24 | 25 | atomic_mutex() : state_{} {} 26 | 27 | atomic_mutex(atomic_mutex const &) = delete; 28 | atomic_mutex & operator=(atomic_mutex const &) = delete; 29 | 30 | ///\brief tries to lock 31 | ///\param sleep_delay delay between retries 32 | ///\param tries number of tries 33 | template 34 | bool try_lock(std::chrono::duration const & sleep_delay = 0, std::uint32_t tries = 1) noexcept 35 | { 36 | bool ret{}; 37 | for(std::uint32_t i = 0; ret == false && i < tries; i++) 38 | { 39 | bool expected{}; 40 | if(i != 0) // do not delay 1st try 41 | std::this_thread::sleep_for(sleep_delay); 42 | ret = state_.compare_exchange_strong(expected, true, std::memory_order_seq_cst); 43 | } 44 | return ret; 45 | } 46 | 47 | ///\brief locks mutex 48 | void lock() noexcept 49 | { 50 | do 51 | { 52 | bool expected{}; 53 | if(state_.compare_exchange_strong(expected, true, std::memory_order_seq_cst)) 54 | break; 55 | std::this_thread::yield(); 56 | } while(true); 57 | } 58 | 59 | void unlock() noexcept 60 | { 61 | bool expected{true}; 62 | state_.compare_exchange_strong(expected, false, std::memory_order_seq_cst); 63 | } 64 | }; 65 | } // namespace small_vectors::inline v3_3::ip 66 | -------------------------------------------------------------------------------- /include/small_vectors/algo/bound_leaning_lower_bound.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /// \brief Burrows–Wheeler transform 8 | namespace small_vectors::inline v3_3::algo::lower_bound 9 | { 10 | 11 | // Idea from Andrei Alexandrescu on improved lower bound 12 | struct bound_leaning_lower_bound_fn 13 | { 14 | template< 15 | std::random_access_iterator iterator, 16 | std::sentinel_for sentinel, 17 | typename value_type, 18 | typename compare_type> 19 | requires std:: 20 | invocable::value_type const &, value_type const &> 21 | constexpr auto operator()(iterator first, sentinel last, value_type const & v, compare_type less) const noexcept( 22 | noexcept(less(*first, v)) 23 | ) -> iterator 24 | { 25 | if(first == last) 26 | return iterator(last); // Convert sentinel to iterator if they're different types 27 | 28 | iterator middle; 29 | auto distance = std::distance(first, last); 30 | 31 | if(distance == 1) 32 | middle = first; 33 | else 34 | middle = first + distance / 2; 35 | 36 | if(less(*middle, v)) 37 | { 38 | for(first = middle + 1; first != last; first = middle + 1) 39 | { 40 | distance = std::distance(first, last); 41 | if(distance == 1) 42 | middle = first; 43 | else 44 | middle = first + 3 * distance / 4; 45 | 46 | if(!less(*middle, v)) 47 | { 48 | last = middle; 49 | break; 50 | } 51 | } 52 | } 53 | else 54 | { 55 | for(last = middle; first != last; last = middle) 56 | { 57 | distance = std::distance(first, last); 58 | if(distance == 1) 59 | middle = first; 60 | else 61 | middle = first + distance / 4; 62 | 63 | if(less(*middle, v)) 64 | { 65 | first = middle + 1; 66 | break; 67 | } 68 | } 69 | } 70 | return std::lower_bound(first, iterator(last), v, std::move(less)); // Convert sentinel to iterator if needed 71 | } 72 | }; 73 | 74 | inline constexpr bound_leaning_lower_bound_fn bound_leaning{}; 75 | } // namespace small_vectors::inline v3_3::algo::lower_bound 76 | -------------------------------------------------------------------------------- /include/small_vectors/composed_pointer_with_data.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | namespace small_vectors::inline v3_3 10 | { 11 | template 12 | // requires( alignof(T) > 1 ) //disabled due to not being able use declaration for recurent types 13 | struct composed_pointer_with_data 14 | { 15 | using pointer = T *; 16 | using data_value_type = V; 17 | 18 | static constexpr uintptr_t data_bits = std::bit_width; 19 | static constexpr uintptr_t data_mask = alignof(T) - 1; 20 | static constexpr uintptr_t pointer_mask = std::numeric_limits::max() ^ data_mask; 21 | 22 | std::uintptr_t data_; 23 | 24 | inline constexpr composed_pointer_with_data() noexcept 25 | requires(alignof(T) > 1) 26 | = default; 27 | 28 | inline explicit composed_pointer_with_data(pointer ptr) noexcept 29 | requires(alignof(T) > 1) 30 | : data_{reinterpret_cast(ptr)} // lower bits are empty 31 | { 32 | } 33 | 34 | inline explicit composed_pointer_with_data(pointer ptr, data_value_type value) 35 | requires(alignof(T) > 1) 36 | : data_{reinterpret_cast(ptr) | static_cast(value)} // lower bits are empty 37 | { 38 | } 39 | 40 | inline constexpr void validate(data_value_type value) 41 | { 42 | if(std::is_constant_evaluated()) 43 | { 44 | if(static_cast(value) > data_mask) 45 | throw; 46 | } 47 | else 48 | assert(static_cast(value) <= data_mask); 49 | } 50 | 51 | inline pointer ptr() const noexcept { return reinterpret_cast(data_ & pointer_mask); } 52 | 53 | inline void set_ptr(pointer ptr) noexcept { data_ = reinterpret_cast(ptr) | (data_ & data_mask); } 54 | 55 | inline pointer operator->() const noexcept { return ptr(); } 56 | 57 | inline constexpr explicit operator bool() const noexcept { return (data_ & pointer_mask) != 0; } 58 | 59 | inline constexpr data_value_type data() const noexcept { return static_cast(data_ & data_mask); } 60 | 61 | inline constexpr void set_data(data_value_type value) 62 | { 63 | validate(value); 64 | data_ = (data_ & pointer_mask) | static_cast(value); 65 | } 66 | }; 67 | } // namespace small_vectors::inline v3_3 68 | -------------------------------------------------------------------------------- /include/small_vectors/interprocess/shared_mem_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | namespace small_vectors::inline v3_3::ip 8 | { 9 | namespace detail 10 | { 11 | template 12 | concept trivially_copyable = std::is_trivially_copyable_v; 13 | 14 | template 15 | concept concept_aligned_offset = requires { requires(offset % alignof(Type)) == 0; }; 16 | 17 | template 18 | struct end_offset_of 19 | { 20 | static constexpr std::size_t value = type::end_offset; 21 | }; 22 | 23 | template<> 24 | struct end_offset_of 25 | { 26 | static constexpr std::size_t value = 0; 27 | }; 28 | } // namespace detail 29 | 30 | template 31 | requires detail::concept_aligned_offset::value> 32 | struct shared_type_decl 33 | { 34 | using type = Type; 35 | static constexpr std::size_t offset = detail::end_offset_of::value; 36 | static constexpr std::size_t end_offset = offset + sizeof(type); 37 | }; 38 | 39 | namespace detail 40 | { 41 | template 42 | concept concept_type_decl = requires { 43 | typename shared_type_decl_type::type; 44 | shared_type_decl_type::offset; 45 | shared_type_decl_type::end_offset; 46 | }; 47 | template 48 | concept concept_pointer = std::is_pointer_v; 49 | 50 | template 51 | concept concept_mapped_region = requires(type & region) { 52 | region.get_address(); 53 | { region.get_address() } -> concept_pointer; 54 | }; 55 | } // namespace detail 56 | 57 | template 58 | inline auto construct_at(detail::concept_mapped_region auto & region, Args &&... args) noexcept -> 59 | typename shared_type_decl_type::type * 60 | { 61 | using type = typename shared_type_decl_type::type; 62 | constexpr auto offset{static_cast(shared_type_decl_type::offset)}; 63 | auto addr{std::next(reinterpret_cast(region.get_address()), offset)}; 64 | return std::construct_at(reinterpret_cast(addr), args...); 65 | } 66 | 67 | template 68 | [[nodiscard]] 69 | inline auto ref(detail::concept_mapped_region auto & region) noexcept -> typename shared_type_decl_type::type & 70 | { 71 | using type = typename shared_type_decl_type::type; 72 | constexpr auto offset{static_cast(shared_type_decl_type::offset)}; 73 | auto addr{std::next(reinterpret_cast(region.get_address()), offset)}; 74 | return *std::launder(reinterpret_cast(addr)); 75 | } 76 | 77 | } // namespace small_vectors::inline v3_3::ip 78 | -------------------------------------------------------------------------------- /unit_tests/unaligned_ut.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | using traits_list = metatests::type_list; 7 | using namespace metatests; 8 | 9 | small_vectors_clang_unsafe_buffer_usage_begin // 10 | int main() 11 | { 12 | using namespace boost::ut; 13 | using namespace small_vectors::memutil; 14 | 15 | metatests::test_result result; 16 | 17 | "test_int"_test = [&] 18 | { 19 | auto fn_tmpl = [](value_type const *) -> metatests::test_result 20 | { 21 | value_type store[128]; 22 | using type = int32_t; 23 | type testv = 0x5555aaaa; 24 | 25 | unaligned_store(&store[1], testv); 26 | type r = unaligned_load(&store[1]); 27 | constexpr_test(r == testv); 28 | return {}; 29 | }; 30 | result |= run_constexpr_test(fn_tmpl); 31 | result |= run_consteval_test(fn_tmpl); 32 | }; 33 | 34 | "test double"_test = [&] 35 | { 36 | auto fn_tmpl = [](value_type const *) -> metatests::test_result 37 | { 38 | value_type store[128]; 39 | using type = double; 40 | type testv = 0.45671974353; 41 | unaligned_store(&store[1], testv); 42 | type r = unaligned_load(&store[1]); 43 | constexpr_test(r == testv); 44 | return {}; 45 | }; 46 | result |= run_constexpr_test(fn_tmpl); 47 | result |= run_consteval_test(fn_tmpl); 48 | }; 49 | 50 | "test int64_t"_test = [&] 51 | { 52 | auto fn_tmpl = [](value_type const *) -> metatests::test_result 53 | { 54 | value_type store[128]; 55 | using type = int64_t; 56 | type testv = 0x111177775555aaaa; 57 | 58 | unaligned_store(&store[1], testv); 59 | type r = unaligned_load(&store[1]); 60 | constexpr_test(r == testv); 61 | return {}; 62 | }; 63 | result |= run_constexpr_test(fn_tmpl); 64 | result |= run_consteval_test(fn_tmpl); 65 | }; 66 | 67 | "test enum"_test = [&] 68 | { 69 | enum struct test_enum : uint16_t 70 | { 71 | one, 72 | two, 73 | three 74 | }; 75 | auto fn_tmpl = [](value_type const *) -> metatests::test_result 76 | { 77 | value_type store[128]; 78 | 79 | using type = test_enum; 80 | { 81 | type testv = test_enum::three; 82 | unaligned_store(&store[3], testv); 83 | type r = unaligned_load(&store[3]); 84 | constexpr_test(r == testv); 85 | } 86 | { 87 | type testv = test_enum::two; 88 | unaligned_store(&store[3], testv); 89 | type r = unaligned_load(&store[3]); 90 | constexpr_test(r == testv); 91 | } 92 | return {}; 93 | }; 94 | result |= run_constexpr_test(fn_tmpl); 95 | result |= run_consteval_test(fn_tmpl); 96 | }; 97 | } 98 | 99 | small_vectors_clang_unsafe_buffer_usage_end // 100 | -------------------------------------------------------------------------------- /gdb/.gdbinit: -------------------------------------------------------------------------------- 1 | python 2 | import sys 3 | import gdb 4 | import traceback 5 | 6 | def member_exists(gdb_value, member_name): 7 | try: 8 | gdb_value[member_name] 9 | return True 10 | except gdb.error: 11 | return False 12 | 13 | class small_vectors: 14 | class BasicStringPrinter: 15 | def __init__(self, val): 16 | self.val = val 17 | 18 | def to_string(self): 19 | try: 20 | # print("BasicStringPrinter.to_string called") 21 | char_type = self.val.type.template_argument(0) 22 | # print(f"char_type: {char_type}") 23 | 24 | storage = self.val['storage_'] 25 | data = storage['data_'] 26 | size = int(storage['size_']) 27 | 28 | if member_exists(storage, 'active_'): 29 | # print("storage has 'active_' attribute") 30 | active = int(storage['active_']) 31 | if active == 0: # Assuming 0 is for buffered 32 | # print("Using buffered storage") 33 | char_data = data['buffered']['_M_elems'] 34 | else: 35 | # print("Using dynamic storage") 36 | char_data = data['dynamic']['data'] 37 | else: 38 | # print("storage does not have 'active_' attribute") 39 | char_data = data['_M_elems'] 40 | # print(f"size: {size}") 41 | 42 | # Get the string content 43 | if size > 0: 44 | result = f'"{char_data.string(length=size)}"' 45 | else: 46 | result = '""' 47 | 48 | # print(f"result: {result}") 49 | return result 50 | except Exception as e: 51 | print(f"Exception in BasicStringPrinter.to_string: {str(e)}") 52 | traceback.print_exc() 53 | return f"" 54 | 55 | def basic_string_lookup_function(val): 56 | try: 57 | # print(f"basic_string_lookup_function called with type: {val.type}") 58 | type_name = str(val.type.strip_typedefs()) 59 | # print(f"type_name: {type_name}") 60 | if 'small_vectors::v3_0::basic_string_t<' in type_name: 61 | # print("Returning BasicStringPrinter") 62 | return small_vectors.BasicStringPrinter(val) 63 | # print("No matching pretty-printer found") 64 | except Exception as e: 65 | print(f"Exception in basic_string_lookup_function: {str(e)}") 66 | traceback.print_exc() 67 | return None 68 | 69 | 70 | try: 71 | #sys.path.insert(0, '/home/artur/projects/small_vectors/gdb') 72 | #print("Python path updated") 73 | # import basic_string_printer 74 | gdb.pretty_printers.append(basic_string_lookup_function) 75 | 76 | except Exception as e: 77 | print("Error occurred:") 78 | print(str(e)) 79 | print("Traceback:") 80 | traceback.print_exc() 81 | end 82 | 83 | set print pretty on 84 | set print object on 85 | set print static-members on 86 | set print vtbl on 87 | set print demangle on 88 | set demangle-style gnu-v3 89 | set print sevenbit-strings off 90 | -------------------------------------------------------------------------------- /unit_tests/bwt_ut.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | using metatests::constexpr_test; 7 | using metatests::run_consteval_test; 8 | using metatests::run_constexpr_test; 9 | 10 | namespace ut = boost::ut; 11 | using ut::operator""_test; 12 | using namespace ut::operators::terse; 13 | using metatests::test_result; 14 | 15 | using value_type_list = metatests::type_list; 16 | // using value_type_list = metatests::type_list; 17 | using small_vectors::cast_fixed_string; 18 | using std::ranges::begin; 19 | 20 | namespace encode_test 21 | { 22 | static void do_test(test_result & result) 23 | { 24 | "encode"_test = [&] 25 | { 26 | auto fn_tmpl = [](char_type const *) -> metatests::test_result 27 | { 28 | using view_type = std::basic_string_view; 29 | std::array buffer; 30 | { 31 | auto constexpr text{cast_fixed_string("banana")}; 32 | auto constexpr expected{cast_fixed_string("annb$aa")}; 33 | auto outit{small_vectors::algo::bwt::encode<'$'>(text, begin(buffer))}; 34 | view_type v{begin(buffer), outit}; 35 | constexpr_test(v == expected); 36 | } 37 | { 38 | auto constexpr text{cast_fixed_string("abracadabra")}; 39 | auto constexpr expected{cast_fixed_string("ard$rcaaaabb")}; 40 | auto outit{small_vectors::algo::bwt::encode<'$'>(text, begin(buffer))}; 41 | view_type v{begin(buffer), outit}; 42 | constexpr_test(v == expected); 43 | } 44 | return {}; 45 | }; 46 | 47 | result |= run_consteval_test(fn_tmpl); 48 | result |= run_constexpr_test(fn_tmpl); 49 | }; 50 | } 51 | } // namespace encode_test 52 | 53 | namespace decode_test 54 | { 55 | static void do_test(test_result & result) 56 | { 57 | "encode"_test = [&] 58 | { 59 | auto fn_tmpl = [](char_type const *) -> metatests::test_result 60 | { 61 | using view_type = std::basic_string_view; 62 | std::array buffer; 63 | { 64 | auto constexpr text{cast_fixed_string("annb$aa")}; 65 | auto constexpr expected{cast_fixed_string("banana")}; 66 | auto outit{small_vectors::algo::bwt::decode<'$'>(text, begin(buffer))}; 67 | view_type v{begin(buffer), outit}; 68 | constexpr_test(v == expected); 69 | } 70 | { 71 | auto constexpr text{cast_fixed_string("ard$rcaaaabb")}; 72 | auto constexpr expected{cast_fixed_string("abracadabra")}; 73 | auto outit{small_vectors::algo::bwt::decode<'$'>(text, begin(buffer))}; 74 | view_type v{begin(buffer), outit}; 75 | constexpr_test(v == expected); 76 | } 77 | return {}; 78 | }; 79 | 80 | // result |= run_consteval_test(fn_tmpl); 81 | result |= run_constexpr_test(fn_tmpl); 82 | }; 83 | } 84 | } // namespace decode_test 85 | 86 | int main() 87 | { 88 | test_result result; 89 | encode_test::do_test(result); 90 | decode_test::do_test(result); 91 | } 92 | -------------------------------------------------------------------------------- /include/small_vectors/interprocess/ring_queue.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace small_vectors::inline v3_3::ip 12 | { 13 | namespace detail 14 | { 15 | 16 | enum struct push_status : uint8_t 17 | { 18 | error_no_enough_space, 19 | succeed 20 | }; 21 | namespace ranges = std::ranges; 22 | 23 | using atomic_index = std::atomic_size_t; 24 | 25 | struct constexpr_index 26 | { 27 | using enum std::memory_order; 28 | std::size_t value{}; 29 | 30 | constexpr std::size_t load(std::memory_order m = seq_cst) const noexcept { return value; } 31 | 32 | constexpr void store(std::size_t v, std::memory_order m = seq_cst) noexcept { value = v; } 33 | }; 34 | 35 | struct head_t 36 | { 37 | std::size_t ix, size; 38 | }; 39 | 40 | struct head_data_t 41 | { 42 | std::array data_; 43 | }; 44 | 45 | template 46 | struct ring_queue_impl_t 47 | { 48 | using enum std::memory_order; 49 | using index_type = IndexType; 50 | using buffer_type = std::array; 51 | 52 | buffer_type buffer_{}; 53 | index_type read_index_{0u}, write_index_{0u}; 54 | 55 | constexpr explicit ring_queue_impl_t() noexcept = default; 56 | 57 | static constexpr auto capacity() noexcept { return buffer_size - 1; } 58 | 59 | constexpr auto empty() const noexcept 60 | { 61 | auto const b = read_index_.load(acquire); 62 | auto const e = write_index_.load(relaxed); 63 | return b % capacity() == e; 64 | } 65 | 66 | template 67 | constexpr auto push(iterator data_beg, iterator data_end) noexcept -> push_status 68 | { 69 | push_status status{push_status::error_no_enough_space}; 70 | 71 | // calculate space required 72 | std::size_t write_ix{write_index_.load(acquire)}; 73 | std::size_t const read_ix{read_index_.load(relaxed)}; 74 | bool wraps_up = read_ix > write_ix; 75 | std::size_t data_size{static_cast(ranges::distance(data_beg, data_end))}; 76 | std::size_t block_size{sizeof(head_t) + data_size}; 77 | 78 | std::size_t free_space; 79 | std::size_t next; 80 | 81 | // store only continuous ranges 82 | if(wraps_up) 83 | { 84 | free_space = read_ix - write_ix; 85 | next = write_ix; 86 | } 87 | else 88 | { 89 | std::size_t free_space_low = read_ix; 90 | std::size_t free_space_hi = capacity() - write_ix; 91 | if(free_space_hi >= block_size) 92 | { 93 | free_space = free_space_hi; 94 | next = write_ix; 95 | } 96 | else 97 | { 98 | free_space = free_space_low; 99 | next = 0u; 100 | } 101 | } 102 | 103 | // store data 104 | if(free_space > block_size) [[likely]] 105 | { 106 | ranges::copy_n( 107 | data_beg, 108 | static_cast(data_size), 109 | ranges::next(ranges::begin(buffer_), static_cast(next)) 110 | ); 111 | write_ix = (write_ix + data_size) % capacity(); 112 | write_index_.store(write_ix); 113 | status = push_status::succeed; 114 | } 115 | return status; 116 | } 117 | }; 118 | } // namespace detail 119 | } // namespace small_vectors::inline v3_3::ip 120 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.21 FATAL_ERROR) 2 | 3 | include(cmake/extract_version.cmake) 4 | project( 5 | small_vectors 6 | VERSION ${small_vectors_version} 7 | LANGUAGES CXX 8 | HOMEPAGE_URL "https://github.com/arturbac/small_vectors") 9 | cmake_policy( 10 | SET 11 | CMP0167 12 | NEW) 13 | cmake_policy( 14 | SET 15 | CMP0175 16 | NEW) 17 | 18 | include(CheckCXXCompilerFlag) 19 | include(GNUInstallDirs) 20 | include(CMakePackageConfigHelpers) 21 | 22 | if(PROJECT_IS_TOP_LEVEL) 23 | include(FeatureSummary) 24 | endif() 25 | if(PROJECT_IS_TOP_LEVEL) 26 | message(STATUS "small_vectors v${small_vectors_version}") 27 | endif() 28 | if(PROJECT_SOURCE_DIR 29 | STREQUAL 30 | PROJECT_BINARY_DIR) 31 | message( 32 | FATAL_ERROR 33 | "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.") 34 | endif() 35 | 36 | set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) 37 | 38 | set(SMALL_VECTORS_EXPECTED_VTABLE_INSTANTATION OFF) 39 | 40 | # ----------------------------------------------------------------------------- 41 | # options 42 | # ----------------------------------------------------------------------------- 43 | if(PROJECT_IS_TOP_LEVEL) 44 | option( 45 | SMALL_VECTORS_ENABLE_UNIT_TESTS 46 | "unit tests available from CTest" 47 | ON) 48 | add_feature_info( 49 | "SMALL_VECTORS_ENABLE_UNIT_TESTS" 50 | SMALL_VECTORS_ENABLE_UNIT_TESTS 51 | "unit test available from CTest") 52 | else() 53 | set(SMALL_VECTORS_ENABLE_UNIT_TESTS OFF) 54 | endif() 55 | 56 | if(NOT 57 | DEFINED 58 | CMAKE_CXX_STANDARD 59 | OR CMAKE_CXX_STANDARD 60 | LESS 61 | 23) 62 | set(CMAKE_CXX_STANDARD 23) 63 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 64 | set(CMAKE_CXX_EXTENSIONS OFF) 65 | endif() 66 | 67 | option( 68 | SMALL_VECTORS_EXPORT_CMAKE_TARGETS 69 | "Enable cmake targets" 70 | ON) 71 | 72 | add_library(small_vectors) 73 | target_compile_features(small_vectors PUBLIC cxx_std_23) 74 | target_sources( 75 | small_vectors 76 | PRIVATE source/safe_buffers.cc 77 | PUBLIC FILE_SET 78 | HEADERS 79 | BASE_DIRS 80 | $ 81 | $) 82 | 83 | if(SMALL_VECTORS_EXPECTED_VTABLE_INSTANTATION) 84 | target_compile_definitions(small_vectors PUBLIC SMALL_VECTORS_EXPECTED_VTABLE_INSTANTATION=1) 85 | target_sources(small_vectors PRIVATE source/expected.cc) 86 | endif() 87 | 88 | install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ DESTINATION include) 89 | install( 90 | TARGETS small_vectors 91 | EXPORT small_vectors_targets 92 | INCLUDES 93 | DESTINATION include 94 | FILE_SET HEADERS) 95 | 96 | configure_package_config_file(cmake/small_vectorsConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/small_vectorsConfig.cmake 97 | INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/small_vectors) 98 | write_basic_package_version_file( 99 | ${CMAKE_CURRENT_BINARY_DIR}/small_vectorsConfigVersion.cmake 100 | VERSION ${PROJECT_VERSION} 101 | COMPATIBILITY SameMajorVersion) 102 | 103 | install(FILES ${CMAKE_CURRENT_BINARY_DIR}/small_vectorsConfig.cmake 104 | ${CMAKE_CURRENT_BINARY_DIR}/small_vectorsConfigVersion.cmake 105 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/small_vectors) 106 | 107 | if(SMALL_VECTORS_EXPORT_CMAKE_TARGETS) 108 | install( 109 | EXPORT small_vectors_targets 110 | FILE small_vectorsTargets.cmake 111 | NAMESPACE small_vectors:: 112 | DESTINATION lib/cmake/small_vectors) 113 | endif() 114 | 115 | if(SMALL_VECTORS_ENABLE_UNIT_TESTS AND PROJECT_IS_TOP_LEVEL) 116 | enable_testing() 117 | add_subdirectory(unit_tests) 118 | endif() 119 | 120 | if(PROJECT_IS_TOP_LEVEL) 121 | feature_summary(WHAT ALL) 122 | endif() 123 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AlignConsecutiveBitFields: 3 | Enabled: true 4 | AcrossEmptyLines: false 5 | AcrossComments: false 6 | BreakAfterAttributes: Always 7 | BracedInitializerIndentWidth: 2 8 | BraceWrapping: 9 | SplitEmptyNamespace: false 10 | AfterCaseLabel: true 11 | AfterClass: true 12 | AfterControlStatement: Always 13 | AfterEnum: true 14 | AfterFunction: true 15 | AfterNamespace: true 16 | AfterObjCDeclaration: true 17 | AfterStruct: true 18 | AfterExternBlock: true 19 | AfterUnion: true 20 | BeforeCatch: true 21 | BeforeElse: true 22 | BeforeLambdaBody: true 23 | BeforeWhile: true 24 | IndentBraces: true 25 | SplitEmptyFunction: false 26 | SplitEmptyRecord: true 27 | BitFieldColonSpacing: Both 28 | BreakBeforeBinaryOperators: All 29 | BreakBeforeBraces: Whitesmiths 30 | BreakBeforeConceptDeclarations: Always 31 | BreakBeforeInlineASMColon: Always 32 | BreakBeforeTernaryOperators: true 33 | BreakConstructorInitializers: AfterColon 34 | BreakInheritanceList: AfterColon 35 | BreakStringLiterals: true 36 | ColumnLimit: 120 37 | CompactNamespaces: false 38 | ConstructorInitializerIndentWidth: 4 39 | ContinuationIndentWidth: 2 40 | Cpp11BracedListStyle: true 41 | EmptyLineAfterAccessModifier: Never 42 | EmptyLineBeforeAccessModifier: LogicalBlock 43 | FixNamespaceComments: true 44 | IncludeBlocks: Preserve 45 | IndentAccessModifiers: false 46 | IndentCaseBlocks: true 47 | IndentCaseLabels: true 48 | IndentExternBlock: Indent 49 | IndentGotoLabels: false 50 | IndentPPDirectives: None 51 | IndentRequiresClause: true 52 | IndentWidth: 2 53 | IndentWrappedFunctionNames: true 54 | InsertBraces: false 55 | InsertNewlineAtEOF: true 56 | InsertTrailingCommas: None 57 | KeepEmptyLinesAtEOF: true 58 | KeepEmptyLinesAtTheStartOfBlocks: false 59 | LambdaBodyIndentation: Signature 60 | Language: Cpp 61 | LineEnding: DeriveLF 62 | NamespaceIndentation: Inner 63 | PPIndentWidth: 0 64 | PackConstructorInitializers: CurrentLine 65 | PointerAlignment: Middle 66 | QualifierAlignment: Right 67 | ReferenceAlignment: Middle 68 | ReflowComments: true 69 | RemoveBracesLLVM: true 70 | RemoveParentheses: MultipleParentheses 71 | RemoveSemicolon: true 72 | RequiresClausePosition: OwnLine 73 | RequiresExpressionIndentation: OuterScope 74 | SeparateDefinitionBlocks: Always 75 | ShortNamespaceLines: 1 76 | SortIncludes: Never 77 | SortUsingDeclarations: Lexicographic 78 | SpaceAfterCStyleCast: false 79 | SpaceAfterLogicalNot: false 80 | SpaceAfterTemplateKeyword: false 81 | SpaceAroundPointerQualifiers: Both 82 | SpaceBeforeAssignmentOperators: true 83 | SpaceBeforeCaseColon: false 84 | SpaceBeforeCpp11BracedList: false 85 | SpaceBeforeCtorInitializerColon: true 86 | SpaceBeforeInheritanceColon: true 87 | SpaceBeforeJsonColon: true 88 | SpaceBeforeParens: Never 89 | SpaceBeforeRangeBasedForLoopColon: false 90 | SpaceBeforeSquareBrackets: false 91 | SpaceInEmptyBlock: false 92 | SpacesBeforeTrailingComments: 2 93 | SpacesInAngles: Never 94 | SpacesInContainerLiterals: false 95 | SpacesInLineCommentPrefix: 96 | Minimum: 1 97 | Maximum: -1 98 | SpacesInParens: Never 99 | SpacesInParensOptions: 100 | InCStyleCasts: false 101 | InConditionalStatements: false 102 | InEmptyParentheses: false 103 | Other: false 104 | SpacesInSquareBrackets: false 105 | Standard: Latest 106 | TabWidth: 2 107 | UseTab: Never 108 | AlignConsecutiveAssignments: 109 | Enabled: false 110 | AcrossEmptyLines: false 111 | AcrossComments: false 112 | AlignConsecutiveDeclarations: 113 | Enabled: false 114 | AlignConsecutiveMacros: 115 | Enabled: false 116 | AlignConsecutiveShortCaseStatements: 117 | Enabled: true 118 | AlignEscapedNewlines: Left 119 | AlignOperands: Align 120 | AlignTrailingComments: 121 | Kind: Always 122 | AllowAllArgumentsOnNextLine: true 123 | AllowAllParametersOfDeclarationOnNextLine: true 124 | AllowShortBlocksOnASingleLine: true 125 | AllowShortCaseLabelsOnASingleLine: true 126 | AllowShortEnumsOnASingleLine: true 127 | AllowShortFunctionsOnASingleLine: All 128 | AlwaysBreakAfterReturnType: None 129 | AlwaysBreakBeforeMultilineStrings: false 130 | AlwaysBreakTemplateDeclarations: Yes 131 | BinPackArguments: false 132 | BinPackParameters: false 133 | AlignAfterOpenBracket: BlockIndent 134 | WhitespaceSensitiveMacros: 135 | - PRAGMA_CLANG_WARNING_OFF 136 | - PRAGMA_CLANG_WARNING_PUSH_OFF 137 | - PRAGMA_GCC_WARNING_PUSH_OFF 138 | - PRAGMA_GCC_WARNING_OFF 139 | - throw_if_fail 140 | - throw_on_fail 141 | - BOOST_TEST 142 | - BOOST_AUTO_TEST_CASE_TEMPLATE 143 | -------------------------------------------------------------------------------- /unit_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include(${PROJECT_SOURCE_DIR}/cmake/get_cpm.cmake) 2 | if(CMAKE_CXX_COMPILER_ID 3 | STREQUAL 4 | "Clang") 5 | option( 6 | SMALL_VECTORS_ENABLE_LLD_LINKER 7 | "enable lld linker for linking unit tests" 8 | ON) 9 | add_feature_info( 10 | "SMALL_VECTORS_ENABLE_LLD_LINKER" 11 | SMALL_VECTORS_ENABLE_LLD_LINKER 12 | "enable lld linker for linking unit tests") 13 | 14 | set(SMALL_VECTORS_COMPILE_OPTIONS 15 | -Weverything 16 | -Werror 17 | -Wno-c++98-compat 18 | -Wno-c++98-compat-pedantic 19 | -Wno-pre-c++14-compat 20 | -Wno-pre-c++17-compat 21 | -Wno-pre-c++20-compat-pedantic 22 | -Wno-c++20-compat 23 | -Wno-unused-parameter 24 | -Wno-padded 25 | -Wno-unused-command-line-argument) 26 | check_cxx_compiler_flag(-Wunsafe-buffer-usage WUNSAFE_BUFFER_USAGE) 27 | if(WUNSAFE_BUFFER_USAGE) 28 | list( 29 | APPEND 30 | SMALL_VECTORS_COMPILE_OPTIONS 31 | -Wunsafe-buffer-usage) 32 | endif() 33 | check_cxx_compiler_flag(-Wswitch-default WNO_SWITCH_DEFAULT) 34 | if(WNO_SWITCH_DEFAULT) 35 | list( 36 | APPEND 37 | SMALL_VECTORS_COMPILE_OPTIONS 38 | -Wno-switch-default) 39 | endif() 40 | 41 | if(SMALL_VECTORS_ENABLE_LLD_LINKER) 42 | add_link_options(-fuse-ld=lld) 43 | endif() 44 | endif() 45 | if(CMAKE_CXX_COMPILER_ID 46 | STREQUAL 47 | "GNU") 48 | set(SMALL_VECTORS_COMPILE_OPTIONS 49 | -Wall 50 | -Wextra 51 | -Werror) 52 | endif() 53 | 54 | # ---------------------------------------------------------------- 55 | # boost-ext/ut 56 | # ---------------------------------------------------------------- 57 | cpmaddpackage( 58 | ut 59 | GITHUB_REPOSITORY 60 | arturbac/ut-ext 61 | GIT_TAG 62 | master) 63 | add_custom_target(unit_tests) 64 | add_library(small_vectors_ut_core) 65 | target_sources(small_vectors_ut_core PRIVATE unit_test_core.cc) 66 | target_link_libraries(small_vectors_ut_core PUBLIC small_vectors Boost::ut) 67 | target_include_directories(small_vectors_ut_core PUBLIC ut_core) 68 | target_compile_options(small_vectors_ut_core PUBLIC ${SMALL_VECTORS_COMPILE_OPTIONS}) 69 | if(CMAKE_CXX_COMPILER_ID 70 | STREQUAL 71 | "Clang" 72 | OR CMAKE_CXX_COMPILER_ID 73 | STREQUAL 74 | "AppleClang") 75 | target_compile_options( 76 | small_vectors_ut_core 77 | PUBLIC -Wno-disabled-macro-expansion 78 | -Wno-used-but-marked-unused 79 | -Wno-global-constructors 80 | -Wno-exit-time-destructors 81 | -Wno-ctad-maybe-unsupported 82 | -Wno-weak-vtables 83 | -fconstexpr-backtrace-limit=0 84 | -Wno-misleading-indentation) 85 | endif() 86 | if(CMAKE_CXX_COMPILER_ID 87 | STREQUAL 88 | "GNU") 89 | target_compile_options( 90 | small_vectors_ut_core 91 | PUBLIC -Wno-misleading-indentation 92 | -Wno-attributes 93 | -Wno-unused-parameter 94 | -Wno-unknown-pragmas) 95 | endif() 96 | 97 | include(small_vectors_ut) 98 | set(COMPILER_ID "${CMAKE_CXX_COMPILER_ID}") 99 | set(COMPILER_VERSION "${CMAKE_CXX_COMPILER_VERSION}") 100 | set(COMPILER_INFO "${COMPILER_ID} ${COMPILER_VERSION}") 101 | message(STATUS "Compiler Info: ${COMPILER_INFO}") 102 | 103 | get_filename_component( 104 | LAST_PATH_ELEMENT 105 | "${CMAKE_BINARY_DIR}" 106 | NAME) 107 | set(COMPILER_INFO "${COMPILER_INFO}_at_${LAST_PATH_ELEMENT}") 108 | message(STATUS "Compiler and Standard Library Info: ${COMPILER_INFO}") 109 | 110 | add_unittest(unaligned_ut) 111 | target_compile_options(unaligned_ut PRIVATE -Wno-float-equal) 112 | 113 | add_unittest(uninitialized_constexpr_ut) 114 | add_unittest(static_vector_ut) 115 | target_compile_options(static_vector_ut PRIVATE -Wno-float-equal) 116 | 117 | add_unittest(small_vector_ut) 118 | add_unittest(composed_pointer_with_data_ut) 119 | add_unittest(meta_packed_struct_ut) 120 | add_unittest(strong_type_ut) 121 | add_unittest(string_ut) 122 | add_unittest(ranges_ut) 123 | add_unittest(expected_ut) 124 | add_unittest(inclass_storage_ut) 125 | 126 | # github ubuntu latest is very old 127 | find_package(Boost 1.74 COMPONENTS system) 128 | if(Boost_FOUND) 129 | add_unittest(bwt_ut) 130 | target_link_libraries(bwt_ut PRIVATE Boost::system) 131 | 132 | add_unittest(shared_mem_util_ut) 133 | target_link_libraries(shared_mem_util_ut PRIVATE Boost::system) 134 | target_compile_definitions(shared_mem_util_ut PRIVATE SMALL_VECTORS_COMPILER_INFO="${COMPILER_INFO}") 135 | # add_unittest(ring_queue_ut) target_link_libraries(ring_queue_ut PRIVATE Boost::system ) 136 | 137 | # add_unittest(stack_buffer_ut) target_link_libraries(stack_buffer_ut PRIVATE Boost::system ) 138 | # target_compile_definitions(stack_buffer_ut PRIVATE SMALL_VECTORS_COMPILER_INFO="${COMPILER_INFO}") 139 | endif() 140 | -------------------------------------------------------------------------------- /include/small_vectors/detail/adapter_iterator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace small_vectors::inline v3_3::detail 6 | { 7 | /// \brief adapter for wrapping use of pointer arithmetic 8 | template 9 | requires concepts::iterator_traits_defined 10 | struct adapter_iterator 11 | { 12 | using iterator_type = Iterator; 13 | using traits_type = std::iterator_traits; 14 | 15 | using iterator_category = typename traits_type::iterator_category; 16 | using value_type = typename traits_type::value_type; 17 | using difference_type = typename traits_type::difference_type; 18 | using reference = typename traits_type::reference; 19 | using pointer = typename traits_type::pointer; 20 | using iterator_concept = typename traits_type::iterator_concept; 21 | 22 | iterator_type current_{}; 23 | 24 | inline constexpr adapter_iterator() noexcept = default; 25 | 26 | inline constexpr explicit adapter_iterator(iterator_type const & i) noexcept : current_(i) {} 27 | 28 | template Iter> 29 | inline constexpr adapter_iterator(adapter_iterator const & i) noexcept : current_{i.base()} 30 | { 31 | } 32 | 33 | // forward 34 | [[nodiscard]] 35 | inline constexpr auto operator*() const noexcept -> reference 36 | { 37 | return *current_; 38 | } 39 | 40 | [[nodiscard]] 41 | inline constexpr auto operator->() const noexcept -> pointer 42 | { 43 | return current_; 44 | } 45 | 46 | inline constexpr auto operator++() noexcept -> adapter_iterator & 47 | requires std::forward_iterator 48 | { 49 | #ifdef __clang__ 50 | #pragma clang unsafe_buffer_usage begin 51 | #endif 52 | ++current_; 53 | #ifdef __clang__ 54 | #pragma clang unsafe_buffer_usage end 55 | #endif 56 | return *this; 57 | } 58 | 59 | [[nodiscard]] 60 | inline constexpr auto operator++(int) noexcept -> adapter_iterator 61 | requires std::forward_iterator 62 | { 63 | return adapter_iterator{current_++}; 64 | } 65 | 66 | // bidirectional 67 | inline constexpr auto operator--() noexcept -> adapter_iterator & 68 | requires std::bidirectional_iterator 69 | { 70 | #ifdef __clang__ 71 | #pragma clang unsafe_buffer_usage begin 72 | #endif 73 | --current_; 74 | #ifdef __clang__ 75 | #pragma clang unsafe_buffer_usage end 76 | #endif 77 | return *this; 78 | } 79 | 80 | [[nodiscard]] 81 | inline constexpr auto operator--(int) noexcept -> adapter_iterator 82 | requires std::bidirectional_iterator 83 | { 84 | return adapter_iterator{current_--}; 85 | } 86 | 87 | // random 88 | [[nodiscard]] 89 | inline constexpr auto operator[](std::size_t index) const noexcept -> reference 90 | requires std::random_access_iterator 91 | { 92 | #ifdef __clang__ 93 | #pragma clang unsafe_buffer_usage begin 94 | #endif 95 | return current_[index]; 96 | #ifdef __clang__ 97 | #pragma clang unsafe_buffer_usage end 98 | #endif 99 | } 100 | 101 | inline constexpr auto operator+=(difference_type index) noexcept -> adapter_iterator & 102 | requires std::random_access_iterator 103 | { 104 | #ifdef __clang__ 105 | #pragma clang unsafe_buffer_usage begin 106 | #endif 107 | current_ += index; 108 | #ifdef __clang__ 109 | #pragma clang unsafe_buffer_usage end 110 | #endif 111 | return *this; 112 | } 113 | 114 | [[nodiscard]] 115 | inline constexpr auto operator+(difference_type index) const noexcept -> adapter_iterator 116 | requires std::random_access_iterator 117 | { 118 | #ifdef __clang__ 119 | #pragma clang unsafe_buffer_usage begin 120 | #endif 121 | return adapter_iterator(current_ + index); 122 | #ifdef __clang__ 123 | #pragma clang unsafe_buffer_usage end 124 | #endif 125 | } 126 | 127 | inline constexpr auto operator-=(difference_type index) noexcept -> adapter_iterator & 128 | requires std::random_access_iterator 129 | { 130 | #ifdef __clang__ 131 | #pragma clang unsafe_buffer_usage begin 132 | #endif 133 | current_ -= index; 134 | #ifdef __clang__ 135 | #pragma clang unsafe_buffer_usage end 136 | #endif 137 | return *this; 138 | } 139 | 140 | [[nodiscard]] 141 | inline constexpr auto operator-(difference_type index) const noexcept -> adapter_iterator 142 | requires std::random_access_iterator 143 | { 144 | #ifdef __clang__ 145 | #pragma clang unsafe_buffer_usage begin 146 | #endif 147 | return adapter_iterator(current_ - index); 148 | #ifdef __clang__ 149 | #pragma clang unsafe_buffer_usage end 150 | #endif 151 | } 152 | 153 | [[nodiscard]] 154 | inline constexpr auto base() const noexcept -> iterator_type const & 155 | { 156 | return current_; 157 | } 158 | }; 159 | 160 | template 161 | adapter_iterator(iterator_type const & i) -> adapter_iterator; 162 | 163 | template iter2> 164 | [[nodiscard]] 165 | inline constexpr auto operator==(adapter_iterator const & l, adapter_iterator const & r) noexcept( 166 | noexcept(l.base() == r.base()) 167 | ) -> bool 168 | { 169 | return l.base() == r.base(); 170 | } 171 | 172 | template iter2> 173 | [[nodiscard]] 174 | inline constexpr auto operator<=>(adapter_iterator const & l, adapter_iterator const & r) noexcept( 175 | noexcept(l.base() <=> r.base()) 176 | ) 177 | { 178 | return l.base() <=> r.base(); 179 | } 180 | 181 | template 182 | [[nodiscard]] 183 | inline constexpr auto operator-(adapter_iterator const & l, adapter_iterator const & r) noexcept 184 | { 185 | return l.base() - r.base(); 186 | } 187 | 188 | template 189 | [[nodiscard]] 190 | inline constexpr auto 191 | operator+(typename adapter_iterator::difference_type n, adapter_iterator const & i) noexcept 192 | -> adapter_iterator 193 | { 194 | return adapter_iterator(i.base() + n); 195 | } 196 | } // namespace small_vectors::inline v3_3::detail 197 | -------------------------------------------------------------------------------- /include/small_vectors/algo/bwt.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /// \brief Burrows–Wheeler transform 11 | namespace small_vectors::inline v3_3::algo::bwt 12 | { 13 | namespace concepts 14 | { 15 | template 16 | concept char_type 17 | = std::same_as, char> || std::same_as, char8_t> 18 | || std::same_as, char16_t> 19 | || std::same_as, char32_t> 20 | || std::same_as, wchar_t>; 21 | } 22 | 23 | template 24 | struct encode_t 25 | { 26 | static constexpr char end_marker = EndMarker; 27 | 28 | template< 29 | std::contiguous_iterator source_iterator, 30 | std::sentinel_for sentinel, 31 | std::weakly_incrementable out_iterator> 32 | requires std::indirectly_copyable 33 | small_vector_static_call_operator constexpr auto 34 | operator()(source_iterator beg, sentinel end, out_iterator out) small_vector_static_call_operator_const noexcept 35 | -> out_iterator 36 | { 37 | namespace ranges = std::ranges; 38 | using char_type = std::iter_value_t; 39 | using string_type = small_vectors::basic_string; 40 | using size_type = typename string_type::size_type; 41 | if(beg != end) 42 | { 43 | size_type sz{size_type(ranges::distance(beg, end) + 1u)}; 44 | string_type table; 45 | table.resize_and_overwrite( 46 | sz + sz, 47 | [sz, beg, end](char_type * data, size_type /*cap*/) 48 | { 49 | auto it{data}; 50 | it = ranges::copy(beg, end, it).out; 51 | *it = char_type(end_marker); 52 | small_vectors_clang_unsafe_buffer_usage_begin // 53 | ++ it; 54 | small_vectors_clang_unsafe_buffer_usage_end // 55 | it 56 | = ranges::copy(beg, end, it).out; 57 | *it = char_type(end_marker); 58 | return sz + sz; 59 | } 60 | ); 61 | using view_type = std::basic_string_view; 62 | auto const f_view = [sz, &table](uint32_t ix) noexcept 63 | { 64 | auto begit{ranges::next(ranges::begin(table), ix)}; 65 | return view_type{begit, ranges::next(begit, sz)}; 66 | }; 67 | 68 | small_vectors::small_vector rotvec; 69 | rotvec.resize(sz); 70 | std::iota(ranges::begin(rotvec), ranges::end(rotvec), 0u); 71 | ranges::sort( 72 | rotvec, [&f_view](uint32_t lix, uint32_t rix) noexcept { return f_view(lix).compare(f_view(rix)) < 0; } 73 | ); 74 | 75 | out = ranges::transform( 76 | rotvec, 77 | out, 78 | [&table, sz](uint32_t ix) noexcept -> char_type 79 | { 80 | char_type c{table[ix + sz - 1u]}; 81 | return c; 82 | } 83 | ).out; 84 | } 85 | 86 | return out; 87 | } 88 | 89 | template 90 | small_vector_static_call_operator constexpr auto 91 | operator()(contiguous_range const & range, out_iterator out) small_vector_static_call_operator_const noexcept 92 | { 93 | return operator()(std::ranges::begin(range), std::ranges::end(range), out); 94 | } 95 | }; 96 | 97 | template 98 | inline constexpr encode_t encode; 99 | 100 | template 101 | struct decode_t 102 | { 103 | static constexpr char end_marker = EndMarker; 104 | 105 | template< 106 | std::contiguous_iterator source_iterator, 107 | std::sentinel_for sentinel, 108 | std::weakly_incrementable out_iterator> 109 | requires std::indirectly_copyable 110 | small_vector_static_call_operator constexpr auto 111 | operator()(source_iterator beg, sentinel end, out_iterator out) small_vector_static_call_operator_const noexcept 112 | -> out_iterator 113 | { 114 | namespace ranges = std::ranges; 115 | using small_vectors::small_vector; 116 | using char_type = std::iter_value_t; 117 | using string_type = small_vectors::basic_string; 118 | using size_type = typename string_type::size_type; 119 | using index_list = small_vector; 120 | if(beg != end) 121 | { 122 | small_vectors_clang_unsafe_buffer_usage_begin // 123 | std::span const btw_arr{beg, end}; 124 | small_vectors_clang_unsafe_buffer_usage_end // 125 | auto it_x{ranges::find(btw_arr, char_type(end_marker))}; 126 | if(it_x != ranges::end(btw_arr)) 127 | { 128 | size_type const sz{size_type(btw_arr.size())}; 129 | small_vector sorted_bwt(sz); 130 | ranges::copy(btw_arr, ranges::begin(sorted_bwt)); 131 | ranges::sort(sorted_bwt); 132 | index_list l_shift(sz); 133 | 134 | // Array of lists to compute l_shift 135 | std::unordered_map arr(sz); 136 | 137 | // Adds each character of bwtArr to a linked list 138 | // and appends to it the new node whose data part 139 | // contains index at which character occurs in bwtArr 140 | for(uint32_t i = 0; i != sz; i++) 141 | arr[btw_arr[i]].emplace_back(i); 142 | 143 | // Adds each character of sortedBwt to a linked list 144 | // and finds lShift 145 | for(uint32_t i = 0; i != sz; i++) 146 | { 147 | l_shift[i] = arr[sorted_bwt[i]][0u]; 148 | arr[sorted_bwt[i]].erase(ranges::begin(arr[sorted_bwt[i]])); 149 | } 150 | // Index at which original string appears 151 | // in the sorted rotations list 152 | uint32_t x = static_cast(ranges::distance(ranges::begin(btw_arr), it_x)); 153 | index_list decoded_ix(sz); 154 | for(uint32_t i = 0; i != sz; i++) 155 | { 156 | x = l_shift[x]; 157 | decoded_ix[sz - 1 - i] = x; 158 | } 159 | auto last{ranges::prev(std::make_reverse_iterator(ranges::begin(decoded_ix)))}; 160 | auto first{std::make_reverse_iterator(ranges::end(decoded_ix))}; 161 | 162 | out = ranges::transform(first, last, out, [&btw_arr](uint32_t ix) noexcept { return btw_arr[ix]; }).out; 163 | } 164 | } 165 | return out; 166 | } 167 | 168 | template 169 | small_vector_static_call_operator constexpr auto 170 | operator()(contiguous_range const & range, out_iterator out) small_vector_static_call_operator_const noexcept 171 | { 172 | return operator()(std::ranges::begin(range), std::ranges::end(range), out); 173 | } 174 | }; 175 | 176 | template 177 | inline constexpr decode_t decode; 178 | } // namespace small_vectors::inline v3_3::algo::bwt 179 | -------------------------------------------------------------------------------- /include/small_vectors/basic_fixed_string.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace small_vectors::inline v3_3 12 | { 13 | 14 | template 15 | struct [[clang::trivial_abi]] 16 | basic_fixed_string 17 | { 18 | using value_type = CharType; 19 | using char_type = value_type; 20 | using iterator = detail::adapter_iterator; 21 | using const_iterator = detail::adapter_iterator; 22 | 23 | char_type data_[N + 1]{}; 24 | 25 | [[nodiscard]] 26 | static constexpr auto size() noexcept -> std::size_t 27 | { 28 | return N; 29 | } 30 | 31 | [[nodiscard]] 32 | constexpr auto begin() const noexcept -> const_iterator 33 | { 34 | return const_iterator{&data_[0]}; 35 | } 36 | 37 | [[nodiscard]] 38 | constexpr auto begin() noexcept -> iterator 39 | { 40 | return iterator{&data_[0]}; 41 | } 42 | 43 | [[nodiscard]] 44 | constexpr auto data() noexcept -> char_type * 45 | { 46 | return &data_[0]; 47 | } 48 | 49 | [[nodiscard]] 50 | constexpr auto data() const noexcept -> char_type const * 51 | { 52 | return &data_[0]; 53 | } 54 | 55 | [[nodiscard]] 56 | constexpr auto end() const noexcept -> const_iterator 57 | { 58 | small_vectors_clang_unsafe_buffer_usage_begin // 59 | return const_iterator{&data_[N]}; 60 | small_vectors_clang_unsafe_buffer_usage_end // 61 | } 62 | 63 | [[nodiscard]] 64 | constexpr auto end() noexcept -> iterator 65 | { 66 | small_vectors_clang_unsafe_buffer_usage_begin // 67 | return iterator{&data_[N]}; 68 | small_vectors_clang_unsafe_buffer_usage_end // 69 | } 70 | 71 | [[nodiscard]] 72 | inline constexpr auto operator[](concepts::unsigned_arithmetic_integral auto index) const noexcept 73 | -> char_type const & 74 | { 75 | small_vectors_clang_unsafe_buffer_usage_begin // 76 | if constexpr(detail::check_valid_element_access) 77 | { 78 | if(N <= index) [[unlikely]] 79 | detail::report_invalid_element_access("out of bounds element access ", N, index); 80 | } 81 | return data_[index]; 82 | small_vectors_clang_unsafe_buffer_usage_end // 83 | } 84 | 85 | [[nodiscard]] 86 | inline constexpr auto operator[](concepts::unsigned_arithmetic_integral auto index) noexcept -> char_type & 87 | { 88 | small_vectors_clang_unsafe_buffer_usage_begin // 89 | if constexpr(detail::check_valid_element_access) 90 | { 91 | if(N <= index) [[unlikely]] 92 | detail::report_invalid_element_access("out of bounds element access ", N, index); 93 | } 94 | return data_[index]; 95 | small_vectors_clang_unsafe_buffer_usage_end // 96 | } 97 | 98 | [[nodiscard]] 99 | inline constexpr auto at(concepts::unsigned_arithmetic_integral auto index) const noexcept -> char_type const & 100 | { 101 | small_vectors_clang_unsafe_buffer_usage_begin // 102 | if constexpr(detail::check_valid_element_access) 103 | { 104 | if(N <= index) [[unlikely]] 105 | detail::report_invalid_element_access("out of bounds element access ", N, index); 106 | } 107 | return data_[index]; 108 | small_vectors_clang_unsafe_buffer_usage_end // 109 | } 110 | 111 | [[nodiscard]] 112 | inline constexpr auto at(concepts::unsigned_arithmetic_integral auto index) noexcept -> char_type & 113 | { 114 | small_vectors_clang_unsafe_buffer_usage_begin // 115 | if constexpr(detail::check_valid_element_access) 116 | { 117 | if(N <= index) [[unlikely]] 118 | detail::report_invalid_element_access("out of bounds element access ", N, index); 119 | } 120 | return data_[index]; 121 | small_vectors_clang_unsafe_buffer_usage_end // 122 | } 123 | 124 | constexpr basic_fixed_string() noexcept = default; 125 | 126 | template other_char_type> 127 | constexpr basic_fixed_string(other_char_type const (&foo)[N + 1]) noexcept 128 | { 129 | std::copy_n(foo, N + 1, data_); 130 | } 131 | 132 | template 133 | requires(!std::same_as) 134 | constexpr basic_fixed_string(other_char_type const (&foo)[N + 1]) noexcept 135 | { 136 | small_vectors_clang_unsafe_buffer_usage_begin // 137 | std::transform(foo, foo + N + 1, data_, [](other_char_type c) noexcept { return static_cast(c); }); 138 | small_vectors_clang_unsafe_buffer_usage_end // 139 | } 140 | 141 | constexpr auto view() const noexcept -> std::basic_string_view { return {&data_[0], N}; } 142 | 143 | /// this is for use cases where basic_fixed_string is used as buffor with extra length and length must be determined 144 | /// by "\0" 145 | constexpr auto null_terminated_buffor_view() const noexcept -> std::basic_string_view 146 | { 147 | return {&data_[0], std::char_traits::length(&data_[0])}; 148 | } 149 | 150 | constexpr operator std::basic_string_view() const noexcept { return {&data_[0], N}; } 151 | 152 | constexpr auto operator<=>(basic_fixed_string const &) const noexcept = default; 153 | 154 | template 155 | constexpr auto operator==(basic_fixed_string const & r) const noexcept -> bool 156 | { 157 | return N == M && view() == r.view(); 158 | } 159 | }; 160 | 161 | template 162 | basic_fixed_string(char_type const (&str)[N]) -> basic_fixed_string; 163 | 164 | template 165 | constexpr auto concat_fixed_string(basic_fixed_string l, basic_fixed_string r) noexcept 166 | -> basic_fixed_string 167 | { 168 | basic_fixed_string result; 169 | auto it{std::copy(l.begin(), l.end(), result.begin())}; 170 | it = std::copy(r.begin(), r.end(), it); 171 | *it = {}; 172 | return result; 173 | } 174 | 175 | template 176 | constexpr auto 177 | concat_fixed_string(basic_fixed_string l, basic_fixed_string r, U... u) noexcept 178 | { 179 | return concat_fixed_string(l, concat_fixed_string(r, u...)); 180 | } 181 | 182 | template 183 | constexpr auto operator+(basic_fixed_string l, basic_fixed_string r) noexcept 184 | { 185 | return concat_fixed_string(l, r); 186 | } 187 | 188 | template 189 | constexpr auto operator+(basic_fixed_string l, char_type const (&r)[M]) noexcept 190 | { 191 | return concat_fixed_string(l, basic_fixed_string{r}); 192 | } 193 | 194 | template 195 | constexpr auto operator+(char_type const (&l)[N], basic_fixed_string r) noexcept 196 | { 197 | return concat_fixed_string(basic_fixed_string{l}, r); 198 | } 199 | 200 | template 201 | inline consteval auto cast_fixed_string(char_type const (&str)[N]) noexcept -> basic_fixed_string 202 | { 203 | return basic_fixed_string(str); 204 | } 205 | 206 | } // namespace small_vectors::inline v3_3 207 | -------------------------------------------------------------------------------- /include/small_vectors/interprocess/stack_buffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace small_vectors::inline v3_3::ip 13 | { 14 | enum struct push_status : uint8_t 15 | { 16 | error_stack_changed, 17 | error_not_enough_space, // temporary out of space 18 | logic_error_not_enough_space, // block being pushed exceeds possible queue size 19 | succeed 20 | }; 21 | 22 | enum struct pop_status : bool 23 | { 24 | failed_stack_changed, 25 | succeed_range_valid 26 | }; 27 | namespace ranges = std::ranges; 28 | 29 | struct stack_index_t 30 | { 31 | std::size_t index; 32 | 33 | constexpr std::size_t pack() const noexcept { return index; } 34 | 35 | constexpr bool operator==(stack_index_t const & r) const noexcept = default; 36 | }; 37 | 38 | using atomic_index = std::atomic; 39 | 40 | struct constexpr_index 41 | { 42 | using enum std::memory_order; 43 | stack_index_t value{}; 44 | 45 | constexpr stack_index_t load([[maybe_unused]] std::memory_order m = seq_cst) const noexcept { return value; } 46 | 47 | constexpr void store(stack_index_t v, [[maybe_unused]] std::memory_order m = seq_cst) noexcept { value = v; } 48 | 49 | constexpr bool 50 | compare_exchange_strong(stack_index_t & e, stack_index_t i, [[maybe_unused]] std::memory_order m = seq_cst) noexcept 51 | { 52 | if(e == value) [[likely]] 53 | { 54 | value = i; 55 | return true; 56 | } 57 | else 58 | return false; 59 | } 60 | }; 61 | 62 | struct footer_t 63 | { 64 | std::size_t size; 65 | }; 66 | 67 | struct footer_data_t 68 | { 69 | std::array data_; 70 | }; 71 | 72 | ///\brief experimental interprocess stack buffer supports single writer and single reader 73 | template 74 | struct stack_buffer_t 75 | { 76 | using index_type = IndexType; 77 | using buffer_type = std::array; 78 | using buffer_const_iterator = buffer_type::const_iterator; 79 | 80 | buffer_type buffer_{}; 81 | index_type stack_index_{}; 82 | 83 | constexpr explicit stack_buffer_t() noexcept = default; 84 | }; 85 | 86 | using enum std::memory_order; 87 | 88 | struct capacity_t 89 | { 90 | template 91 | [[nodiscard]] 92 | small_vector_static_call_operator constexpr auto operator()(stack_buffer_t const & 93 | ) small_vector_static_call_operator_const noexcept 94 | { 95 | return buffer_size; 96 | } 97 | }; 98 | 99 | inline constexpr capacity_t capacity; 100 | 101 | struct empty_t 102 | { 103 | template 104 | [[nodiscard]] 105 | small_vector_static_call_operator constexpr auto operator()(stack_buffer_t const & buff 106 | ) small_vector_static_call_operator_const noexcept 107 | { 108 | auto const b = buff.stack_index_.load(acquire); 109 | return b.index == 0; 110 | } 111 | }; 112 | 113 | inline constexpr empty_t empty; 114 | 115 | namespace detail 116 | { 117 | template 118 | constexpr auto footer_from_index(stack_buffer_t & buff, stack_index_t six) noexcept 119 | { 120 | auto it_top_footer{ 121 | ranges::next(ranges::begin(buff.buffer_), static_cast(six.index - sizeof(footer_data_t))) 122 | }; 123 | footer_data_t footer_data; 124 | ranges::copy_n(it_top_footer, sizeof(footer_data_t), ranges::begin(footer_data.data_)); 125 | return std::make_pair(std::bit_cast(footer_data), it_top_footer); 126 | } 127 | } // namespace detail 128 | 129 | ///\brief push operation is lock free 130 | struct push_t 131 | { 132 | template 133 | [[nodiscard]] 134 | small_vector_static_call_operator constexpr auto operator()( 135 | stack_buffer_t & buff, iterator data_beg, iterator data_end 136 | ) small_vector_static_call_operator_const noexcept -> push_status 137 | { 138 | // calculate space required 139 | stack_index_t stack_index{buff.stack_index_.load(acquire)}; 140 | 141 | // emplace data even if read is in progress 142 | std::size_t const data_size{static_cast(ranges::distance(data_beg, data_end))}; 143 | std::size_t const free_space{capacity(buff) - stack_index.index}; 144 | std::size_t const block_size{sizeof(footer_data_t) + data_size}; 145 | 146 | if(block_size > free_space) [[unlikely]] 147 | { 148 | if(data_size > capacity(buff)) [[unlikely]] 149 | return push_status::logic_error_not_enough_space; 150 | else 151 | return push_status::error_not_enough_space; 152 | } 153 | 154 | stack_index_t next_index{stack_index.index + block_size}; 155 | if(buff.stack_index_.compare_exchange_strong(stack_index, next_index, release)) [[likely]] 156 | { 157 | // there was no pop during data write 158 | auto ix_iter{ 159 | ranges::copy( 160 | data_beg, data_end, ranges::next(ranges::begin(buff.buffer_), static_cast(stack_index.index)) 161 | ) 162 | .out 163 | }; 164 | auto index_info{std::bit_cast(data_size)}; 165 | ranges::copy(ranges::begin(index_info.data_), ranges::end(index_info.data_), ix_iter); 166 | buff.stack_index_.store(next_index, release); 167 | return push_status::succeed; 168 | } 169 | else 170 | return push_status::error_stack_changed; 171 | } 172 | }; 173 | 174 | inline constexpr push_t push; 175 | 176 | ///\brief top operation is lock free and is confirmed by pop 177 | struct top_t 178 | { 179 | template 180 | [[nodiscard]] 181 | small_vector_static_call_operator constexpr std::pair, stack_index_t> 182 | operator()(stack_buffer_t & buff) small_vector_static_call_operator_const noexcept 183 | { 184 | stack_index_t stack_during_copy_top{buff.stack_index_.load(acquire)}; 185 | if(stack_during_copy_top.index != 0) [[likely]] 186 | { 187 | auto [footer, it_top_footer]{detail::footer_from_index(buff, stack_during_copy_top)}; 188 | #ifdef __clang__ 189 | #pragma clang unsafe_buffer_usage begin 190 | #endif 191 | return std::make_pair( 192 | std::span{ranges::prev(it_top_footer, static_cast(footer.size)), it_top_footer}, 193 | stack_during_copy_top 194 | ); 195 | #ifdef __clang__ 196 | #pragma clang unsafe_buffer_usage end 197 | #endif 198 | } 199 | return {}; 200 | } 201 | }; 202 | 203 | inline constexpr top_t top; 204 | 205 | struct pop_t 206 | { 207 | template 208 | [[nodiscard]] 209 | small_vector_static_call_operator constexpr pop_status operator()( 210 | stack_buffer_t & buff, stack_index_t stack_during_copy_top 211 | ) small_vector_static_call_operator_const noexcept 212 | { 213 | auto [footer, _]{detail::footer_from_index(buff, stack_during_copy_top)}; 214 | stack_index_t next_index{stack_during_copy_top.index - footer.size - sizeof(footer_data_t)}; 215 | if(buff.stack_index_.compare_exchange_strong(stack_during_copy_top, next_index, release)) [[likely]] 216 | return pop_status::succeed_range_valid; 217 | else 218 | return pop_status::failed_stack_changed; 219 | } 220 | }; 221 | 222 | inline constexpr pop_t pop; 223 | } // namespace small_vectors::inline v3_3::ip 224 | -------------------------------------------------------------------------------- /include/small_vectors/concepts/concepts.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace small_vectors::inline v3_3::concepts 9 | { 10 | using std::convertible_to; 11 | 12 | template 13 | concept even_size = (sz & 1) == 0 && sz != 0; 14 | 15 | template 16 | using iterator_category_t = typename std::iterator_traits::iterator_category; 17 | 18 | template 19 | inline constexpr bool is_contiguous_iterator_v = std::contiguous_iterator; 20 | 21 | template 22 | concept forward_iterator = std::forward_iterator; 23 | 24 | template 25 | concept input_iterator = std::input_iterator; 26 | 27 | template 28 | concept output_iterator = convertible_to, std::input_iterator_tag>; 29 | 30 | template 31 | concept iterator = std::input_or_output_iterator; 32 | 33 | template 34 | concept random_access_iterator = convertible_to, std::random_access_iterator_tag>; 35 | 36 | template 37 | concept vector_constraints = (std::movable or std::copyable) and std::destructible; 38 | 39 | template 40 | concept nothrow_move_constructible = std::is_nothrow_move_constructible_v; 41 | 42 | template 43 | concept unsigned_arithmetic_integral = requires { 44 | requires not std::same_as, bool>; 45 | requires std::unsigned_integral; 46 | }; 47 | 48 | template 49 | concept allocate_constraint = requires { 50 | requires sizeof(value_type) != 0; 51 | requires sizeof(value_type) % alignof(value_type) == 0u; 52 | }; 53 | template 54 | concept trivially_copyable = std::is_trivially_copyable_v; 55 | 56 | template 57 | concept trivial = std::is_trivial_v; 58 | 59 | template 60 | inline constexpr bool is_nothrow_move_constr_and_constr_v 61 | = std::is_nothrow_constructible_v && std::is_nothrow_move_constructible_v; 62 | 63 | template 64 | inline constexpr bool is_nothrow_copy_constr_and_constr_v 65 | = std::is_nothrow_constructible_v && std::is_nothrow_copy_constructible_v; 66 | 67 | ///\brief explicit declared relocation capable 68 | template 69 | concept explicit_trivially_destructible_after_move = requires(T const * value) { 70 | { adl_decl_trivially_destructible_after_move(value) } -> std::same_as; 71 | requires adl_decl_trivially_destructible_after_move(static_cast(nullptr)); 72 | }; 73 | 74 | template 75 | concept trivially_destructible_after_move 76 | = explicit_trivially_destructible_after_move || std::is_trivially_destructible_v; 77 | 78 | template 79 | concept is_trivially_relocatable = 80 | // !std::is_volatile_v> // && (relocatable_tag>::value(0) || 81 | (std::is_trivially_move_constructible_v> 82 | && std::is_trivially_move_assignable_v> 83 | && std::is_trivially_destructible_v>) 84 | #if __has_builtin(__is_trivially_relocatable) 85 | || __is_trivially_relocatable(std::remove_all_extents_t) 86 | #endif 87 | ; 88 | template 89 | concept nothrow_relocatable_or_move_constr_and_constr_v 90 | = (is_trivially_relocatable or std::is_nothrow_move_constructible_v) 91 | and std::is_nothrow_constructible_v; 92 | 93 | template 94 | concept same_as_any_of = std::disjunction_v...>; 95 | 96 | template 97 | concept all_same = std::conjunction_v...>; 98 | 99 | template 100 | concept prefix_incrementable = requires(T value) { 101 | { ++value } -> std::same_as; 102 | }; 103 | 104 | template 105 | concept postfix_incrementable = requires(T value) { 106 | { ++value } -> std::same_as; 107 | }; 108 | 109 | template 110 | concept prefix_decrementable = requires(T value) { 111 | { --value } -> std::same_as; 112 | }; 113 | 114 | template 115 | concept addable = requires(T a, T b) { 116 | { a + b } -> std::convertible_to; 117 | }; 118 | 119 | template 120 | concept substractable = requires(T a, T b) { 121 | { a - b } -> std::convertible_to; 122 | }; 123 | 124 | template 125 | concept multiplicatable = requires(T a, T b) { 126 | { a * b } -> std::convertible_to; 127 | }; 128 | template 129 | concept multiplicatable_with = requires(T a, U b) { 130 | { a * b } -> std::convertible_to; 131 | }; 132 | 133 | template 134 | concept dividable = requires(T a, T b) { 135 | { a / b } -> std::convertible_to; 136 | }; 137 | template 138 | concept has_modulo_operator = requires(T a, T b) { 139 | { a % b } -> std::convertible_to; 140 | }; 141 | 142 | template 143 | concept has_bitwise_xor_operator = requires(T a, T b) { 144 | { a ^ b } -> std::convertible_to; 145 | }; 146 | 147 | template 148 | concept has_bitwise_not_operator = requires(T a) { 149 | { ~a } -> std::convertible_to; 150 | }; 151 | 152 | template 153 | concept has_left_shift_operator = requires(T obj, T obj2) { 154 | { obj << obj2 } -> std::convertible_to; 155 | }; 156 | 157 | template 158 | concept has_left_shift_operator_with = requires(T obj, U obj2) { 159 | { obj << obj2 } -> std::convertible_to; 160 | }; 161 | 162 | template 163 | concept has_left_shift_operator_with_integral = requires(T t, U u) { 164 | requires std::integral; 165 | { t << u } -> std::convertible_to; 166 | }; 167 | 168 | template 169 | concept has_right_shift_operator = requires(T obj, T obj2) { 170 | { obj >> obj2 } -> std::convertible_to; 171 | }; 172 | 173 | template 174 | concept has_right_shift_operator_with = requires(T obj, U obj2) { 175 | { obj >> obj2 } -> std::convertible_to; 176 | }; 177 | 178 | template 179 | concept has_right_shift_operator_with_integral = requires(T t, U u) { 180 | requires std::integral; 181 | { t >> u } -> std::convertible_to; 182 | }; 183 | 184 | template 185 | concept has_bitwise_and_operator = requires(T a, T b) { 186 | { a & b } -> std::convertible_to; 187 | }; 188 | 189 | template 190 | concept has_bitwise_and_operator_with = requires(T a, U b) { 191 | { a & b } -> std::convertible_to; 192 | }; 193 | 194 | template 195 | concept has_bitwise_or_operator = requires(T a, T b) { 196 | { a | b } -> std::convertible_to; 197 | }; 198 | 199 | template 200 | concept addable_assign = requires(T a, T const & b) { 201 | { a += b } -> same_as_any_of; 202 | }; 203 | 204 | template 205 | concept subtractable_assign = requires(T a, T const & b) { 206 | { a -= b } -> same_as_any_of; 207 | }; 208 | template 209 | concept multiplicatable_assign = requires(T a, T const & b) { 210 | { a *= b } -> same_as_any_of; 211 | }; 212 | template 213 | concept dividable_assign = requires(T a, T const & b) { 214 | { a /= b } -> same_as_any_of; 215 | }; 216 | 217 | template 218 | concept has_modulo_assign = requires(T a, T const & b) { 219 | { a %= b } -> same_as_any_of; 220 | }; 221 | 222 | template 223 | concept has_bitwise_xor_assign = requires(T a, T const & b) { 224 | { a ^= b } -> same_as_any_of; 225 | }; 226 | 227 | template 228 | concept has_left_shift_assign = requires(T a, T const & b) { 229 | { a <<= b } -> same_as_any_of; 230 | }; 231 | 232 | template 233 | concept has_right_shift_assign = requires(T a, T const & b) { 234 | { a >>= b } -> same_as_any_of; 235 | }; 236 | 237 | template 238 | concept has_bitwise_and_assign = requires(T a, T const & b) { 239 | { a &= b } -> same_as_any_of; 240 | }; 241 | 242 | template 243 | concept has_bitwise_or_assign = requires(T a, T const & b) { 244 | { a |= b } -> same_as_any_of; 245 | }; 246 | } // namespace small_vectors::inline v3_3::concepts 247 | -------------------------------------------------------------------------------- /unit_tests/inclass_storage_ut.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #ifdef __clang__ 9 | #pragma clang diagnostic ignored "-Wglobal-constructors" 10 | #pragma clang diagnostic ignored "-Wexit-time-destructors" 11 | #endif 12 | 13 | using namespace boost::ut; 14 | using small_vectors::inclass_store_t; 15 | 16 | struct test_trivial_struct 17 | { 18 | int content; 19 | }; 20 | 21 | static_assert(std::alignment_of_v == alignof(test_trivial_struct), "Alignment mismatch."); 22 | using namespace std::string_view_literals; 23 | 24 | static suite<"inclass_store_trivial_tests"> inclass_store_trivial_tests = [] 25 | { 26 | using inclass_trivial_store 27 | = inclass_store_t; 28 | "default_constructor"_test = [] 29 | { 30 | inclass_trivial_store store; 31 | expect(eq(store->content, 0)); 32 | }; 33 | "parameterized_constructor"_test = [] 34 | { 35 | inclass_trivial_store store{10}; 36 | expect(eq(10, store->content)); 37 | }; 38 | "copy_constructor"_test = [] 39 | { 40 | inclass_trivial_store const original{-10}; 41 | inclass_trivial_store const copy{original}; 42 | expect(eq(-10, copy->content)); 43 | }; 44 | "move_constructor"_test = [] 45 | { 46 | inclass_trivial_store original{0xfffffe}; 47 | inclass_trivial_store const moved{std::move(original)}; 48 | expect(eq(0xfffffe, moved->content)); 49 | }; 50 | 51 | "copy_assignment"_test = [] 52 | { 53 | inclass_trivial_store original{0xfffffe}; 54 | inclass_trivial_store copy; 55 | copy = original; 56 | expect(eq(0xfffffe, (*copy).content)); 57 | }; 58 | 59 | "move_assignment"_test = [] 60 | { 61 | inclass_trivial_store original{0xfffffe}; 62 | inclass_trivial_store moved; 63 | moved = std::move(original); 64 | expect(eq(0xfffffe, (*moved).content)); 65 | }; 66 | }; 67 | constexpr auto test_text 68 | = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis sit amet dictum neque." 69 | " Aliquam erat volutpat. Vivamus bibendum pretium eros, eu porta libero dictum ut." 70 | " Vivamus feugiat nisi elit, quis finibus risus pellentesque non. Cras accumsan felis quis dolor malesuada," 71 | " eu consequat velit malesuada."sv; 72 | 73 | struct test_non_trivial_struct 74 | { 75 | std::string content; 76 | }; 77 | 78 | static suite<"inclass_store_non_trivial_tests"> inclass_store_non_trivial_tests = [] 79 | { 80 | using inclass_string_store 81 | = inclass_store_t; 82 | 83 | "default_constructor"_test = [] 84 | { 85 | inclass_string_store store; 86 | expect(store->content.empty()); 87 | }; 88 | 89 | "parameterized_constructor"_test = [] 90 | { 91 | inclass_string_store store{std::string{test_text}}; 92 | expect(eq(test_text, (*store).content)); 93 | }; 94 | 95 | "copy_constructor"_test = [] 96 | { 97 | inclass_string_store const original{std::string{test_text}}; 98 | inclass_string_store copy{original}; 99 | expect(eq(test_text, (*copy).content)); 100 | }; 101 | 102 | "move_constructor"_test = [] 103 | { 104 | inclass_string_store original{std::string{test_text}}; 105 | inclass_string_store const moved{std::move(original)}; 106 | expect(eq(test_text, (*moved).content)); 107 | }; 108 | 109 | "copy_assignment"_test = [] 110 | { 111 | inclass_string_store original{std::string{test_text}}; 112 | inclass_string_store copy; 113 | copy = original; 114 | expect(eq(test_text, (*copy).content)); 115 | }; 116 | 117 | "move_assignment"_test = [] 118 | { 119 | inclass_string_store original{std::string{test_text}}; 120 | inclass_string_store moved; 121 | moved = std::move(original); 122 | expect(eq(test_text, (*moved).content)); 123 | }; 124 | }; 125 | 126 | struct test_multi_arg_struct 127 | { 128 | std::string content; 129 | int integral; 130 | }; 131 | 132 | static suite<"inclass_store_multi_arg_struct"> inclass_store_multi_arg_struct = [] 133 | { 134 | using inclass_string_store = inclass_store_t; 135 | 136 | "default_constructor"_test = [] 137 | { 138 | inclass_string_store store; 139 | expect(store->content.empty()); 140 | }; 141 | 142 | "parameterized_constructor"_test = [] 143 | { 144 | inclass_string_store store{std::string{test_text}, 0x1f55aafe}; 145 | expect(eq(test_text, (*store).content)); 146 | expect(eq(0x1f55aafe, store->integral)); 147 | }; 148 | }; 149 | struct forward_struct_t; 150 | 151 | struct test_pimpl 152 | { 153 | using inclass_foraward_store = small_vectors::inclass_storage_t; 154 | inclass_foraward_store store; 155 | 156 | // explicit test_pimpl(inclass_foraward_store const & data); 157 | test_pimpl(); 158 | explicit test_pimpl(forward_struct_t && data); 159 | explicit test_pimpl(std::string_view s, int i); 160 | test_pimpl(test_pimpl const &); 161 | test_pimpl(test_pimpl &&) noexcept; 162 | ~test_pimpl(); 163 | test_pimpl & operator=(test_pimpl const &); 164 | test_pimpl & operator=(test_pimpl &&); 165 | 166 | auto operator->() const noexcept -> forward_struct_t const *; 167 | auto operator->() noexcept -> forward_struct_t *; 168 | }; 169 | 170 | struct forward_struct_t 171 | { 172 | small_vectors::string content; 173 | int integral; 174 | }; 175 | 176 | static_assert(sizeof(forward_struct_t) == 48); 177 | 178 | // test_pimpl::test_pimpl(inclass_foraward_store const & data) : store{data} {} 179 | test_pimpl::test_pimpl() : store{small_vectors::inclass_storage::default_construct()} {} 180 | 181 | test_pimpl::~test_pimpl() { small_vectors::inclass_storage::destroy(store); } 182 | 183 | test_pimpl::test_pimpl(test_pimpl const & other) : store{small_vectors::inclass_storage::copy_construct(other.store)} {} 184 | 185 | test_pimpl::test_pimpl(test_pimpl && other) noexcept : 186 | store{small_vectors::inclass_storage::move_construct(std::move(other.store))} 187 | { 188 | } 189 | 190 | test_pimpl & test_pimpl::operator=(test_pimpl const & other) 191 | { 192 | small_vectors::inclass_storage::copy_assign(store, other.store); 193 | return *this; 194 | } 195 | 196 | test_pimpl & test_pimpl::operator=(test_pimpl && other) 197 | { 198 | small_vectors::inclass_storage::move_assign(store, std::move(other.store)); 199 | return *this; 200 | } 201 | 202 | test_pimpl::test_pimpl(forward_struct_t && data) : 203 | store{small_vectors::inclass_storage::construct_from(std::move(data))} 204 | { 205 | } 206 | 207 | test_pimpl::test_pimpl(std::string_view s, int i) : 208 | store{small_vectors::inclass_storage::construct_from( 209 | small_vectors::string{s}, i 210 | 211 | )} 212 | { 213 | } 214 | 215 | auto test_pimpl::operator->() const noexcept -> forward_struct_t const * 216 | { 217 | return small_vectors::inclass_storage::ptr(store); 218 | } 219 | 220 | auto test_pimpl::operator->() noexcept -> forward_struct_t * { return small_vectors::inclass_storage::ptr(store); } 221 | 222 | static suite<"inclass_forward__struct"> inclass_forward_struct = [] 223 | { 224 | "default_constructor"_test = [] 225 | { 226 | test_pimpl store; 227 | expect(store->content.empty()); 228 | }; 229 | "copy_constructor"_test = [] 230 | { 231 | test_pimpl const original{test_text, 0x55aaff}; 232 | test_pimpl copy{original}; 233 | expect(eq(test_text, copy->content)); 234 | expect(eq(0x55aaff, copy->integral)); 235 | }; 236 | "move_constructor"_test = [] 237 | { 238 | test_pimpl original{test_text, 0x55aaff}; 239 | test_pimpl const moved{std::move(original)}; 240 | expect(eq(test_text, moved->content)); 241 | expect(eq(0x55aaff, moved->integral)); 242 | }; 243 | "copy_assignment"_test = [] 244 | { 245 | test_pimpl original{forward_struct_t{small_vectors::string{test_text}, 0x55aaff}}; 246 | test_pimpl copy; 247 | copy = original; 248 | expect(eq(test_text, copy->content)); 249 | expect(eq(0x55aaff, copy->integral)); 250 | }; 251 | "move_assignment"_test = [] 252 | { 253 | test_pimpl original{forward_struct_t{small_vectors::string{test_text}, 0x55aaff}}; 254 | test_pimpl moved; 255 | moved = std::move(original); 256 | expect(eq(test_text, moved->content)); 257 | expect(eq(0x55aaff, moved->integral)); 258 | }; 259 | }; 260 | 261 | int main() 262 | { 263 | // Running the tests 264 | return boost::ut::cfg<>.run(); 265 | } 266 | -------------------------------------------------------------------------------- /unit_tests/math_ut.cc: -------------------------------------------------------------------------------- 1 | #define BOOST_TEST_MAIN 2 | #define BOOST_TEST_DYN_LINK 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | constexpr bool consteval_throw_if_fails(auto x) 10 | { 11 | throw; 12 | return false; 13 | } 14 | 15 | #define CONSTEXPR_TEST(a) \ 16 | if(!(a)) \ 17 | throw 18 | // return consteval_throw_if_fails(a) 19 | 20 | using traits_list 21 | = boost::mpl::list; 22 | using float_list = boost::mpl::list; 23 | 24 | //----------------------------------------------------------------------------------------------------- 25 | template 26 | consteval auto test_utils_math_abs() 27 | { 28 | CONSTEXPR_TEST(math::abs(0) == 0); 29 | CONSTEXPR_TEST(math::abs(1) == 1); 30 | CONSTEXPR_TEST(math::abs(std::numeric_limits::max()) == std::numeric_limits::max()); 31 | 32 | if constexpr(std::is_signed_v) 33 | { 34 | CONSTEXPR_TEST( 35 | math::abs(-(std::numeric_limits::max() - 1)) == std::numeric_limits::max() - 1 36 | ); 37 | CONSTEXPR_TEST(math::abs(-0) == 0); 38 | CONSTEXPR_TEST(math::abs(-1) == 1); 39 | } 40 | 41 | return true; 42 | } 43 | 44 | BOOST_AUTO_TEST_CASE_TEMPLATE(utils_math_abs, value_type, traits_list) 45 | { 46 | constexpr auto test_result(test_utils_math_abs()); 47 | static_assert(test_result); 48 | BOOST_TEST(test_result); 49 | } 50 | 51 | //----------------------------------------------------------------------------------------------------- 52 | consteval auto test_utils_math_bits_reverse() 53 | { 54 | { 55 | uint8_t val = 0b10100010; 56 | uint8_t exp = 0b01000101; 57 | uint8_t res = math::reverse_bits(val); 58 | CONSTEXPR_TEST(res == exp); 59 | } 60 | { 61 | uint8_t val = 0b10000000; 62 | uint8_t exp = 0b00000001; 63 | uint8_t res = math::reverse_bits(val); 64 | CONSTEXPR_TEST(res == exp); 65 | } 66 | { 67 | uint8_t val = 0b10100001; 68 | uint8_t exp = 0b10000101; 69 | uint8_t res = math::reverse_bits(val); 70 | CONSTEXPR_TEST(res == exp); 71 | } 72 | { 73 | uint8_t val = 0b00000000; 74 | uint8_t exp = 0b00000000; 75 | uint8_t res = math::reverse_bits(val); 76 | CONSTEXPR_TEST(res == exp); 77 | } 78 | { 79 | uint8_t val = 0b11111111; 80 | uint8_t exp = 0b11111111; 81 | uint8_t res = math::reverse_bits(val); 82 | CONSTEXPR_TEST(res == exp); 83 | } 84 | return true; 85 | } 86 | 87 | BOOST_AUTO_TEST_CASE(utils_math_bits_reverse) 88 | { 89 | constexpr auto test_result(test_utils_math_bits_reverse()); 90 | static_assert(test_result); 91 | BOOST_TEST(test_result); 92 | } 93 | 94 | using sse2::m128d_t; 95 | //----------------------------------------------------------------------------------------------------- 96 | #if !defined(__clang__) 97 | #define outcome_constexpr constexpr 98 | #else 99 | #define outcome_constexpr const 100 | #endif 101 | //----------------------------------------------------------------------------------------------------- 102 | BOOST_AUTO_TEST_CASE(cpuext_sse2_haddpd) 103 | { 104 | { 105 | outcome_constexpr m128d_t cnst_res = sse2::hadd_pd(m128d_t{}, m128d_t{}); 106 | m128d_t const rtres = _mm_hadd_pd(m128d_t{}, m128d_t{}); 107 | BOOST_TEST(cnst_res[0] == rtres[0]); 108 | BOOST_TEST(cnst_res[1] == rtres[1]); 109 | } 110 | { 111 | double constexpr x0 = 10.1234; 112 | double constexpr y0 = -10.791234; 113 | double constexpr x1 = 20.41234; 114 | double constexpr y1 = 40.61234; 115 | 116 | outcome_constexpr m128d_t cnst_res = sse2::hadd_pd(m128d_t{x0, x1}, m128d_t{y0, y1}); 117 | m128d_t const rtres = _mm_hadd_pd(m128d_t{x0, x1}, m128d_t{y0, y1}); 118 | BOOST_TEST(cnst_res[0] == rtres[0]); 119 | BOOST_TEST(cnst_res[1] == rtres[1]); 120 | } 121 | } 122 | 123 | //----------------------------------------------------------------------------------------------------- 124 | template 125 | consteval auto test_math_consteval_sqrt() 126 | { 127 | { 128 | constexpr value_type sqrt_ce = math::consteval_sqrt(value_type(0)); // 38.1635927291968 129 | CONSTEXPR_TEST(sqrt_ce < value_type(0.00001)); 130 | } 131 | { 132 | constexpr value_type expected = value_type(1.4142135623731); 133 | constexpr value_type sqrt_ce = math::consteval_sqrt(2); // 1.4142135623731 134 | CONSTEXPR_TEST(math::abs(sqrt_ce - expected) < value_type(0.00001)); 135 | } 136 | { 137 | constexpr value_type expected = value_type(38.1635927291968); 138 | constexpr value_type sqrt_ce = math::consteval_sqrt(value_type(1456.45981)); // 38.1635927291968 139 | CONSTEXPR_TEST(math::abs(sqrt_ce - expected) < value_type(0.00001)); 140 | } 141 | 142 | return true; 143 | } 144 | 145 | BOOST_AUTO_TEST_CASE_TEMPLATE(math_consteval_sqrt, value_type, float_list) 146 | { 147 | constexpr auto test_result(test_math_consteval_sqrt()); 148 | static_assert(test_result); 149 | BOOST_TEST(test_result); 150 | } 151 | 152 | //----------------------------------------------------------------------------------------------------- 153 | template 154 | consteval auto test_math_consteval_floor() 155 | { 156 | { 157 | constexpr value_type expected = 0; 158 | constexpr value_type result = math::floor(value_type(0.00000043821)); 159 | CONSTEXPR_TEST(result == expected); 160 | } 161 | { 162 | constexpr value_type expected = 0; 163 | constexpr value_type result = math::floor(value_type(0.43821)); 164 | CONSTEXPR_TEST(result == expected); 165 | } 166 | { 167 | constexpr value_type expected = 1; 168 | constexpr value_type result = math::floor(value_type(1.43821)); 169 | CONSTEXPR_TEST(result == expected); 170 | } 171 | { 172 | constexpr value_type expected = 1999999; 173 | constexpr value_type result = math::floor(value_type(1999999.43821)); 174 | CONSTEXPR_TEST(result == expected); 175 | } 176 | { 177 | constexpr value_type expected = -2000000; 178 | constexpr value_type result = math::floor(value_type(-1999999.43821)); 179 | CONSTEXPR_TEST(result == expected); 180 | } 181 | { 182 | constexpr value_type expected = -1; 183 | constexpr value_type result = math::floor(value_type(-0.43821)); 184 | CONSTEXPR_TEST(result == expected); 185 | } 186 | { 187 | constexpr value_type expected = -1; 188 | constexpr value_type result = math::floor(value_type(-0.0000043821)); 189 | CONSTEXPR_TEST(result == expected); 190 | } 191 | return true; 192 | } 193 | 194 | BOOST_AUTO_TEST_CASE_TEMPLATE(math_consteval_floor, value_type, float_list) 195 | { 196 | constexpr auto test_result(test_math_consteval_floor()); 197 | static_assert(test_result); 198 | BOOST_TEST(test_result); 199 | } 200 | 201 | //----------------------------------------------------------------------------------------------------- 202 | template 203 | consteval auto test_math_consteval_ceil() 204 | { 205 | { 206 | constexpr value_type expected = 1; 207 | constexpr value_type result = math::ceil(value_type(0.00000043821)); 208 | CONSTEXPR_TEST(result == expected); 209 | } 210 | { 211 | constexpr value_type expected = 1; 212 | constexpr value_type result = math::ceil(value_type(0.43821)); 213 | CONSTEXPR_TEST(result == expected); 214 | } 215 | { 216 | constexpr value_type expected = 2; 217 | constexpr value_type result = math::ceil(value_type(1.43821)); 218 | CONSTEXPR_TEST(result == expected); 219 | } 220 | { 221 | constexpr value_type expected = 2000000; 222 | constexpr value_type result = math::ceil(value_type(1999999.43821)); 223 | CONSTEXPR_TEST(result == expected); 224 | } 225 | { 226 | constexpr value_type expected = -1999999; 227 | constexpr value_type result = math::ceil(value_type(-1999999.43821)); 228 | CONSTEXPR_TEST(result == expected); 229 | } 230 | { 231 | constexpr value_type expected = 0; 232 | constexpr value_type result = math::ceil(value_type(-0.43821)); 233 | CONSTEXPR_TEST(result == expected); 234 | } 235 | { 236 | constexpr value_type expected = 0; 237 | constexpr value_type result = math::ceil(value_type(-0.0000043821)); 238 | CONSTEXPR_TEST(result == expected); 239 | } 240 | return true; 241 | } 242 | 243 | BOOST_AUTO_TEST_CASE_TEMPLATE(math_consteval_ceil, value_type, float_list) 244 | { 245 | constexpr auto test_result(test_math_consteval_ceil()); 246 | static_assert(test_result); 247 | BOOST_TEST(test_result); 248 | } 249 | -------------------------------------------------------------------------------- /include/small_vectors/utils/unaligned.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | // ----------------------------------------------------------------------- 14 | // 15 | // Functions 16 | // 17 | // ----------------------------------------------------------------------- 18 | namespace small_vectors::inline v3_3::memutil 19 | { 20 | namespace detail 21 | { 22 | template 23 | concept trivially_copyable = std::is_trivially_copyable_v; 24 | 25 | template 26 | using iterator_value_type_t = typename std::iterator_traits::value_type; 27 | 28 | template 29 | concept forward_iterator_to_byte = requires { 30 | requires std::forward_iterator; 31 | requires sizeof(iterator_value_type_t) == 1; 32 | }; 33 | 34 | template 35 | concept output_iterator_to_byte 36 | = requires { requires std::output_iterator || std::output_iterator; }; 37 | 38 | template 39 | concept memory_location = std::same_as, void *>; 40 | 41 | template 42 | concept floating_point = std::is_floating_point::value; 43 | 44 | template 45 | concept integral = std::is_integral_v; 46 | 47 | template 48 | concept unsigned_integral = !std::is_signed::value && integral; 49 | 50 | template 51 | concept signed_integral = std::is_signed::value && integral; 52 | 53 | template 54 | concept arithmetic = requires { 55 | requires integral or floating_point; 56 | requires not std::same_as; 57 | }; 58 | 59 | template 60 | concept arithmetic_or_bool = arithmetic or std::same_as>; 61 | 62 | ///\brief loads value from any forward iterator or unaligned int8_t/uint8_t memory location 63 | template 64 | [[nodiscard, gnu::always_inline]] 65 | inline constexpr output_type unaligned_load(iterator it) noexcept 66 | { 67 | using input_value_type = std::iter_value_t; 68 | static_assert(sizeof(input_value_type) == 1); 69 | 70 | struct value_buffer_type 71 | { 72 | std::array buffer; 73 | }; 74 | 75 | value_buffer_type store; 76 | std::copy_n(it, sizeof(output_type), store.buffer.begin()); 77 | return cxx20::bit_cast(store); 78 | } 79 | 80 | ///\brief loads value from any untyped pointer memory location 81 | template 82 | [[nodiscard, gnu::always_inline]] 83 | inline constexpr output_type unaligned_load(memory_location memloc) noexcept 84 | { 85 | return detail::unaligned_load(reinterpret_cast(memloc)); 86 | } 87 | } // namespace detail 88 | 89 | //--------------------------------------------------------------------------------------------------- 90 | ///\brief loads value from any forward iterator or unaligned memory location 91 | template 92 | [[nodiscard, gnu::always_inline]] 93 | inline constexpr output_type unaligned_load(iterator it) noexcept 94 | { 95 | return detail::unaligned_load(it); 96 | } 97 | 98 | ///\brief loads enum value from any forward iterator or unaligned memory location with size restriction check 99 | ///\details specifing expected_storage_size prevents unintended breaking changes with IO to trivially_copyable 100 | /// underlaing type 101 | template 102 | requires(expected_storage_size == sizeof(output_type)) 103 | [[nodiscard, gnu::always_inline]] 104 | inline constexpr output_type unaligned_load(iterator it) noexcept 105 | { 106 | return detail::unaligned_load(it); 107 | } 108 | 109 | //--------------------------------------------------------------------------------------------------- 110 | ///\brief loads value from any forward iterator or unaligned int8_t/uint8_t memory location and forwards iterator by the 111 | /// size of output_type and forwards the \ref it by the size was read \warning be aware that this function is prone to 112 | /// bugs when used in context of order of evaluation is unspecified, dont use it as function arguments, constructors 113 | /// because \ref it is modified when evaluated 114 | template 115 | [[nodiscard, gnu::always_inline]] 116 | inline constexpr output_type unaligned_load_fwd(iterator & it) noexcept 117 | { 118 | iterator stored_at{it}; 119 | std::advance(it, sizeof(output_type)); 120 | return unaligned_load(stored_at); 121 | } 122 | 123 | ///\brief loads enum value from any forward iterator or unaligned int8_t/uint8_t memory location and forwards iterator 124 | /// by the size of output_type and forwards the \param it by the size was read \details specifing expected_storage_size 125 | /// prevents unintended breaking changes with IO to enum underlaing type \warning be aware that this function is prone 126 | /// to bugs when used in context of order of evaluation is unspecified, dont use it as function arguments, constructors 127 | template 128 | requires(!detail::arithmetic_or_bool) 129 | [[nodiscard, gnu::always_inline]] 130 | inline constexpr output_type unaligned_load_fwd(iterator & it) noexcept 131 | { 132 | iterator stored_at{it}; 133 | std::advance(it, sizeof(output_type)); 134 | return unaligned_load(stored_at); 135 | } 136 | 137 | //--------------------------------------------------------------------------------------------------- 138 | namespace detail 139 | { 140 | template< 141 | detail::trivially_copyable store_type, 142 | detail::output_iterator_to_byte iterator, 143 | detail::trivially_copyable input_type> 144 | requires(std::same_as) 145 | [[gnu::always_inline]] 146 | inline constexpr iterator unaligned_store(iterator it, input_type value) noexcept 147 | { 148 | using output_range_type = std::iter_value_t; 149 | 150 | struct value_buffer_type 151 | { 152 | std::array buffer; 153 | }; 154 | 155 | value_buffer_type store = cxx20::bit_cast(value); 156 | return std::copy(store.buffer.begin(), store.buffer.end(), it); 157 | } 158 | } // namespace detail 159 | 160 | ///\brief stores \param value at \param it location, input value type must match requested storage type 161 | template< 162 | detail::trivially_copyable store_type, 163 | detail::output_iterator_to_byte iterator, 164 | detail::trivially_copyable input_type> 165 | requires(std::same_as) 166 | [[gnu::always_inline]] 167 | inline constexpr iterator unaligned_store(iterator it, input_type value) noexcept 168 | { 169 | return detail::unaligned_store(it, value); 170 | } 171 | 172 | ///\brief stores \param value at \param it location, input value type must match requested storage type 173 | template< 174 | detail::trivially_copyable store_type, 175 | std::size_t expected_storage_size, 176 | detail::output_iterator_to_byte iterator, 177 | detail::trivially_copyable input_type> 178 | requires(std::same_as && expected_storage_size == sizeof(store_type)) 179 | [[gnu::always_inline]] 180 | inline constexpr iterator unaligned_store(iterator it, input_type value) noexcept 181 | { 182 | return detail::unaligned_store(it, value); 183 | } 184 | 185 | //--------------------------------------------------------------------------------------------------- 186 | ///\brief cast from void * for working with deprecated software like wxwidgets storing user data as void * 187 | ///\returns \ref data casted by value to output_type 188 | template 189 | requires(sizeof(output_type) <= sizeof(uintptr_t) && std::same_as) 190 | [[nodiscard, gnu::always_inline]] 191 | inline output_type void_ptr_cast(pointer const * data) noexcept 192 | { 193 | return static_cast(reinterpret_cast(data)); 194 | } 195 | 196 | ///\brief cast to void * for working with deprecated software like wxwidgets storing user data as void * 197 | template 198 | requires(!std::same_as>) 199 | [[nodiscard, gnu::always_inline]] 200 | inline void * void_ptr_cast(input_type data) noexcept 201 | { 202 | return reinterpret_cast(static_cast(data)); 203 | } 204 | } // namespace small_vectors::inline v3_3::memutil 205 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # small_vectors 2 | ![MIT](https://img.shields.io/badge/license-MIT-blue.svg) ![CMake](https://github.com/arturbac/fixed_math/workflows/CMake/badge.svg) 3 | ![language](https://img.shields.io/badge/language-C%2B%2B23-red.svg) 4 | 5 | C++23 utilities library 6 | 7 | ## Features 8 | - **implements 2 relocation optimizations** - clang triviall relocatation (from trait __is_trivially_relocatable) and trivially_destructible_after_move by annotating non trivial classes with adl function 9 | - **Static Vector**: Trivially copyable for types that are trivially copyable, enabling compiler optimizations such as `memcpy` for copying operations (since v3.0.3-devel). 10 | - **Address Independence**: static vectors and static string offer in-class storage, making them address-independent and suitable for interprocess data exchange. 11 | - **Dynamic and Custom Sized Storage**: Small vectors support dynamic memory allocation with customizable size types. Static vectors adjust the minimal size type based on the number of elements. 12 | - **Constant Evaluation**: Static vectors can be fully evaluated at compile time for trivial element types. 13 | - **Basic String with Dual Storage**: Provides a basic string implementation with both dynamic and static in-class storage options. The static storage variant is address-independent. 14 | - **Basic Fixed String**: Enables manipulation of constant evaluated string literals. 15 | - **Expected/Unexpected Implementation**: Offers a C++23 standard `expected/unexpected` implementation with monadic operations for C++20 and up. 16 | 17 | ## Minor Utility Features 18 | - **Meta Packed Struct**: Supports bit-packing data with strong typing. Version 2.3.0 introduced signed type support. 19 | - **Strong Type Wrapping**: Allows for the strong type wrapping of primitive types. 20 | - **Unaligned Access**: Functions for memory unaligned access are fully capable of being executed at compile time starting with v2.4.2. 21 | 22 | ## Interprocess Features 23 | - **Fork Wrapper**: Simplifies process spawning with an interface similar to `std::async`. 24 | - **Shared Memory Utilities**: Facilitates the construction and access of data in shared interprocess memory with automated memory access indexing to prevent errors. 25 | 26 | ### examples 27 | 28 | #### small_vector and static_vector 29 | ```C++ 30 | 31 | #include 32 | 33 | //static vector with in buffer class memory for 10 elements 34 | coll::static_vector vec10; 35 | 36 | //small vector with in buffer class memory for coll::union_min_number_of_elements 37 | 38 | coll::small_vector vec10; 39 | 40 | //equivalent for std::vector with size type eq size_t and not in class buffer memory 41 | coll::small_vector vec10; 42 | 43 | ``` 44 | 45 | #### expected/unexpected 46 | ```C++ 47 | using expected_type = expected; 48 | auto f = [](value_type v) noexcept { return expected_type{ in_place, ++v}; }; 49 | expected_type ex{ in_place, value_type{2} }; 50 | auto res { std::move(ex).and_then(f) }; 51 | constexpr_test( std::same_as); 52 | constexpr_test( res == value_type{3}); 53 | ``` 54 | #### memutil::unaligned 55 | ```C++ 56 | consteval auto make_version_data(string_view sub_ver, string_view data_ver, uint16_t ver_minor, uint16_t comp_minor) 57 | { 58 | constexpr auto converter = [](char c) noexcept -> std::byte { return static_cast(c); }; 59 | std::array res{}; 60 | auto it{ranges::transform(sub_ver, res.begin(), converter).out}; 61 | *it = std::byte(' '); // make space 62 | ++it; 63 | it = ranges::transform(data_ver, it, converter).out; 64 | *it = std::byte{}; 65 | it = ranges::next(res.begin(), map_version_t::map_version_name_chars); 66 | it = memutil::unaligned_store(it, expected_version_major); 67 | it = memutil::unaligned_store(it, expected_version_minor); 68 | it = memutil::unaligned_store(it, expected_version_major); 69 | memutil::unaligned_store(it, expected_cmp_minor); 70 | return res; 71 | } 72 | static constexpr std::array 73 | polska_6_1451_6_18{ make_version_data("Polska", "2403", 1451, 18)}; 74 | ``` 75 | #### shared mem utils 76 | example using static vector, basic_static_string between processes with memory offset table declaration 77 | ```C++ 78 | //used types between processes 79 | struct foo 80 | { 81 | int a,a_; 82 | double b; 83 | int64_t c; 84 | }; 85 | 86 | using message = coll::static_u8string<512>; 87 | using vector_type = coll::static_vector; 88 | 89 | // memory offset table 90 | using foo_obj_decl = ip::shared_type_decl; 91 | using shared_vector_decl = ip::shared_type_decl; 92 | using ref_obj_decl = ip::shared_type_decl; 93 | using message_decl = ip::shared_type_decl; 94 | 95 | bip::mapped_region region ( shm, bip::read_write ); 96 | 97 | // construct objects in main process 98 | foo & foo_obj{*ip::construct_at(region, foo{.a=1,.a_=0,.b=0.5, .c=0xffffffff })}; 99 | auto & ref_obj{*ip::construct_at(region, 2u)}; 100 | auto & ref_string { *ip::construct_at(region, u8"message hello world"sv) }; 101 | vector_type & vector_obj{ *ip::construct_at(region) }; 102 | resize(vector_obj,1); 103 | front(vector_obj) = 2; 104 | 105 | //alter data at forked process 106 | auto child = ip::fork([](std::string_view shared_mem_name ) 107 | { 108 | bip::shared_memory_object shm_obj{ bip::open_only, shared_mem_name.data() , bip::read_write }; 109 | bip::mapped_region cregion{ shm_obj, bip::read_write }; 110 | 111 | //reference shared objects 112 | foo & cfoo_obj{ ip::ref(cregion) }; 113 | vector_type & vector { ip::ref(cregion) }; 114 | auto & cref_string { ip::ref(cregion) }; 115 | auto & cref_obj{ip::ref(cregion)}; 116 | 117 | //read write data 118 | ut::expect( cfoo_obj.a == 1 ); 119 | ut::expect( cfoo_obj.b == 0.5 ); 120 | ut::expect( cfoo_obj.c == 0xffffffff ); 121 | cfoo_obj.a = 2; 122 | cfoo_obj.b = 1.5; 123 | cfoo_obj.c = -0x1ffffffff; 124 | 125 | ut::expect(size(vector) == 1u ); 126 | ut::expect(front(vector) == 2u ); 127 | ut::expect(resize(vector,128) == coll::vector_outcome_e::no_error ) >> ut::fatal; 128 | pop_back(vector); 129 | std::iota(begin(vector), end(vector), 2); 130 | 131 | ut::expect( cref_string.view() == u8"message hello world"sv ); 132 | cref_string = u8"hello world from child"sv; 133 | cref_obj += 2; 134 | 135 | return true; 136 | }, 137 | shmem_name ); 138 | 139 | // check modified data at forked process 140 | ut::expect(child->join()) >> ut::fatal; 141 | ut::expect( foo_obj.a == 2 ); 142 | ut::expect( foo_obj.b == 1.5 ); 143 | ut::expect( foo_obj.c == -0x1ffffffff ); 144 | 145 | ut::expect( ref_string.view() == u8"hello world from child"sv ); 146 | 147 | ut::expect(ref_obj == 4 ); 148 | 149 | ut::expect(size(vector_obj) == 127u ); 150 | ut::expect(front(vector_obj) == 2 ); 151 | ut::expect(back(vector_obj) == 128 ); 152 | ``` 153 | 154 | #### meta_packed_struct 155 | ```C++ 156 | enum struct mbs_fields 157 | { 158 | field_1, field_2, field_3, field_4 159 | }; 160 | enum struct example_enum_value : uint8_t 161 | { value0 = 0, value1, value2, value3 }; 162 | 163 | using enum mbs_fields; 164 | // pack bit struct 165 | using mixed_bitfiled_struct3 = 166 | meta_packed_struct< 167 | member, 168 | member, 169 | member, 170 | member 171 | >; 172 | mixed_bitfiled_struct3 mbs; 173 | get(mbs) = (0x1llu<<56)-1; 174 | constexpr_test(get(mbs) == 0 ); 175 | constexpr_test(get(mbs) == false ); 176 | constexpr_test(get(mbs) == (0x1llu<<56)-1 ); 177 | constexpr_test(get(mbs) == example_enum_value{} ); 178 | 179 | auto packed_value = pack_value(mbs); 180 | constexpr_test(packed_value == 0b00'11111111111111111111111111111111111111111111111111111111'0'0000 ); 181 | 182 | // unpack bitstruct 183 | using mixed_bitfiled_struct2 = 184 | meta_packed_struct< 185 | member, 186 | member, 187 | member, 188 | member 189 | >; 190 | constexpr auto fcount = filed_count(); 191 | constexpr_test(fcount == 4); 192 | constexpr auto s_bit_width = bit_width(); 193 | constexpr_test(s_bit_width == 24); 194 | uint32_t packed_value{ 0b011000011111111000010010 }; 195 | auto mbs{ unpack_value(packed_value) }; 196 | 197 | constexpr_test(get(mbs) == 0x02 ); 198 | constexpr_test(get(mbs) == true ); 199 | constexpr_test(get(mbs) == 0x0ff0 ); 200 | constexpr_test(get(mbs) == value3 ); 201 | ``` 202 | ## Tested Compilers (as of v2.4.2) 203 | 204 | ### Make Workflows Tested 205 | - `cmake --workflow --preset="clang-18-libc++release"` 206 | - `cmake --workflow --preset="clang-18-release"` using GNU libstdc++ on Linux 207 | - `cmake --workflow --preset="gcc-14-release"` 208 | 209 | ### MSVC 210 | - Tested intermittently 211 | -------------------------------------------------------------------------------- /.cmake-format.yaml: -------------------------------------------------------------------------------- 1 | _help_parse: Options affecting listfile parsing 2 | parse: 3 | _help_additional_commands: 4 | - Specify structure for custom cmake functions 5 | additional_commands: 6 | foo: 7 | flags: 8 | - BAR 9 | - BAZ 10 | kwargs: 11 | HEADERS: '*' 12 | SOURCES: '*' 13 | DEPENDS: '*' 14 | _help_override_spec: 15 | - Override configurations per-command where available 16 | override_spec: {} 17 | _help_vartags: 18 | - Specify variable tags. 19 | vartags: [] 20 | _help_proptags: 21 | - Specify property tags. 22 | proptags: [] 23 | _help_format: Options affecting formatting. 24 | format: 25 | _help_disable: 26 | - Disable formatting entirely, making cmake-format a no-op 27 | disable: false 28 | _help_line_width: 29 | - How wide to allow formatted cmake files 30 | line_width: 120 31 | _help_tab_size: 32 | - How many spaces to tab for indent 33 | tab_size: 2 34 | _help_use_tabchars: 35 | - If true, lines are indented using tab characters (utf-8 36 | - 0x09) instead of space characters (utf-8 0x20). 37 | - In cases where the layout would require a fractional tab 38 | - character, the behavior of the fractional indentation is 39 | - governed by 40 | use_tabchars: false 41 | _help_fractional_tab_policy: 42 | - If is True, then the value of this variable 43 | - indicates how fractional indentions are handled during 44 | - whitespace replacement. If set to 'use-space', fractional 45 | - indentation is left as spaces (utf-8 0x20). If set to 46 | - '`round-up` fractional indentation is replaced with a single' 47 | - tab character (utf-8 0x09) effectively shifting the column 48 | - to the next tabstop 49 | fractional_tab_policy: use-space 50 | _help_max_subgroups_hwrap: 51 | - If an argument group contains more than this many sub-groups 52 | - (parg or kwarg groups) then force it to a vertical layout. 53 | max_subgroups_hwrap: 2 54 | _help_max_pargs_hwrap: 55 | - If a positional argument group contains more than this many 56 | - arguments, then force it to a vertical layout. 57 | max_pargs_hwrap: 2 58 | _help_max_rows_cmdline: 59 | - If a cmdline positional group consumes more than this many 60 | - lines without nesting, then invalidate the layout (and nest) 61 | max_rows_cmdline: 1 62 | _help_separate_ctrl_name_with_space: 63 | - If true, separate flow control names from their parentheses 64 | - with a space 65 | separate_ctrl_name_with_space: false 66 | _help_separate_fn_name_with_space: 67 | - If true, separate function names from parentheses with a 68 | - space 69 | separate_fn_name_with_space: false 70 | _help_dangle_parens: 71 | - If a statement is wrapped to more than one line, than dangle 72 | - the closing parenthesis on its own line. 73 | dangle_parens: false 74 | _help_dangle_align: 75 | - If the trailing parenthesis must be 'dangled' on its on 76 | - 'line, then align it to this reference: `prefix`: the start' 77 | - 'of the statement, `prefix-indent`: the start of the' 78 | - 'statement, plus one indentation level, `child`: align to' 79 | - the column of the arguments 80 | dangle_align: prefix 81 | _help_min_prefix_chars: 82 | - If the statement spelling length (including space and 83 | - parenthesis) is smaller than this amount, then force reject 84 | - nested layouts. 85 | min_prefix_chars: 4 86 | _help_max_prefix_chars: 87 | - If the statement spelling length (including space and 88 | - parenthesis) is larger than the tab width by more than this 89 | - amount, then force reject un-nested layouts. 90 | max_prefix_chars: 10 91 | _help_max_lines_hwrap: 92 | - If a candidate layout is wrapped horizontally but it exceeds 93 | - this many lines, then reject the layout. 94 | max_lines_hwrap: 2 95 | _help_line_ending: 96 | - What style line endings to use in the output. 97 | line_ending: unix 98 | _help_command_case: 99 | - Format command names consistently as 'lower' or 'upper' case 100 | command_case: canonical 101 | _help_keyword_case: 102 | - Format keywords consistently as 'lower' or 'upper' case 103 | keyword_case: unchanged 104 | _help_always_wrap: 105 | - A list of command names which should always be wrapped 106 | always_wrap: [] 107 | _help_enable_sort: 108 | - If true, the argument lists which are known to be sortable 109 | - will be sorted lexicographicall 110 | enable_sort: true 111 | _help_autosort: 112 | - If true, the parsers may infer whether or not an argument 113 | - list is sortable (without annotation). 114 | autosort: false 115 | _help_require_valid_layout: 116 | - By default, if cmake-format cannot successfully fit 117 | - everything into the desired linewidth it will apply the 118 | - last, most agressive attempt that it made. If this flag is 119 | - True, however, cmake-format will print error, exit with non- 120 | - zero status code, and write-out nothing 121 | require_valid_layout: false 122 | _help_layout_passes: 123 | - A dictionary mapping layout nodes to a list of wrap 124 | - decisions. See the documentation for more information. 125 | layout_passes: {} 126 | _help_markup: Options affecting comment reflow and formatting. 127 | markup: 128 | _help_bullet_char: 129 | - What character to use for bulleted lists 130 | bullet_char: '*' 131 | _help_enum_char: 132 | - What character to use as punctuation after numerals in an 133 | - enumerated list 134 | enum_char: . 135 | _help_first_comment_is_literal: 136 | - If comment markup is enabled, don't reflow the first comment 137 | - block in each listfile. Use this to preserve formatting of 138 | - your copyright/license statements. 139 | first_comment_is_literal: false 140 | _help_literal_comment_pattern: 141 | - If comment markup is enabled, don't reflow any comment block 142 | - which matches this (regex) pattern. Default is `None` 143 | - (disabled). 144 | literal_comment_pattern: null 145 | _help_fence_pattern: 146 | - Regular expression to match preformat fences in comments 147 | - default= ``r'^\s*([`~]{3}[`~]*)(.*)$'`` 148 | fence_pattern: ^\s*([`~]{3}[`~]*)(.*)$ 149 | _help_ruler_pattern: 150 | - Regular expression to match rulers in comments default= 151 | - '``r''^\s*[^\w\s]{3}.*[^\w\s]{3}$''``' 152 | ruler_pattern: ^\s*[^\w\s]{3}.*[^\w\s]{3}$ 153 | _help_explicit_trailing_pattern: 154 | - If a comment line matches starts with this pattern then it 155 | - is explicitly a trailing comment for the preceeding 156 | - argument. Default is '#<' 157 | explicit_trailing_pattern: '#<' 158 | _help_hashruler_min_length: 159 | - If a comment line starts with at least this many consecutive 160 | - hash characters, then don't lstrip() them off. This allows 161 | - for lazy hash rulers where the first hash char is not 162 | - separated by space 163 | hashruler_min_length: 10 164 | _help_canonicalize_hashrulers: 165 | - If true, then insert a space between the first hash char and 166 | - remaining hash chars in a hash ruler, and normalize its 167 | - length to fill the column 168 | canonicalize_hashrulers: true 169 | _help_enable_markup: 170 | - enable comment markup parsing and reflow 171 | enable_markup: true 172 | _help_lint: Options affecting the linter 173 | lint: 174 | _help_disabled_codes: 175 | - a list of lint codes to disable 176 | disabled_codes: [] 177 | _help_function_pattern: 178 | - regular expression pattern describing valid function names 179 | function_pattern: '[0-9a-z_]+' 180 | _help_macro_pattern: 181 | - regular expression pattern describing valid macro names 182 | macro_pattern: '[0-9A-Z_]+' 183 | _help_global_var_pattern: 184 | - regular expression pattern describing valid names for 185 | - variables with global (cache) scope 186 | global_var_pattern: '[A-Z][0-9A-Z_]+' 187 | _help_internal_var_pattern: 188 | - regular expression pattern describing valid names for 189 | - variables with global scope (but internal semantic) 190 | internal_var_pattern: _[A-Z][0-9A-Z_]+ 191 | _help_local_var_pattern: 192 | - regular expression pattern describing valid names for 193 | - variables with local scope 194 | local_var_pattern: '[a-z][a-z0-9_]+' 195 | _help_private_var_pattern: 196 | - regular expression pattern describing valid names for 197 | - privatedirectory variables 198 | private_var_pattern: _[0-9a-z_]+ 199 | _help_public_var_pattern: 200 | - regular expression pattern describing valid names for public 201 | - directory variables 202 | public_var_pattern: '[A-Z][0-9A-Z_]+' 203 | _help_argument_var_pattern: 204 | - regular expression pattern describing valid names for 205 | - function/macro arguments and loop variables. 206 | argument_var_pattern: '[a-z][a-z0-9_]+' 207 | _help_keyword_pattern: 208 | - regular expression pattern describing valid names for 209 | - keywords used in functions or macros 210 | keyword_pattern: '[A-Z][0-9A-Z_]+' 211 | _help_max_conditionals_custom_parser: 212 | - In the heuristic for C0201, how many conditionals to match 213 | - within a loop in before considering the loop a parser. 214 | max_conditionals_custom_parser: 2 215 | _help_min_statement_spacing: 216 | - Require at least this many newlines between statements 217 | min_statement_spacing: 1 218 | _help_max_statement_spacing: 219 | - Require no more than this many newlines between statements 220 | max_statement_spacing: 2 221 | max_returns: 6 222 | max_branches: 12 223 | max_arguments: 5 224 | max_localvars: 15 225 | max_statements: 50 226 | _help_encode: Options affecting file encoding 227 | encode: 228 | _help_emit_byteorder_mark: 229 | - If true, emit the unicode byte-order mark (BOM) at the start 230 | - of the file 231 | emit_byteorder_mark: false 232 | _help_input_encoding: 233 | - Specify the encoding of the input file. Defaults to utf-8 234 | input_encoding: utf-8 235 | _help_output_encoding: 236 | - Specify the encoding of the output file. Defaults to utf-8. 237 | - Note that cmake only claims to support utf-8 so be careful 238 | - when using anything else 239 | output_encoding: utf-8 240 | _help_misc: Miscellaneous configurations options. 241 | misc: 242 | _help_per_command: 243 | - A dictionary containing any per-command configuration 244 | - overrides. Currently only `command_case` is supported. 245 | per_command: {} 246 | -------------------------------------------------------------------------------- /include/small_vectors/inclass_storage.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2024 Artur Bać 2 | // SPDX-License-Identifier: MIT 3 | // SPDX-PackageHomePage: https://github.com/arturbac/small_vectors 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace small_vectors::inline v3_3 16 | { 17 | namespace concepts 18 | { 19 | template 20 | concept complete_type = requires { sizeof(T); }; 21 | 22 | template 23 | concept same_as_inclass_storage = requires { 24 | // clang-format off 25 | typename T::value_type; 26 | { T::storage_size } -> std::convertible_to; 27 | { T::alignment } -> std::convertible_to; 28 | requires sizeof(typename T::value_type) == sizeof(std::byte[T::storage_size]) && alignof(typename T::value_type) == T::alignment; 29 | // clang-format on 30 | }; 31 | } // namespace concepts 32 | 33 | template 34 | struct inclass_storage_t 35 | { 36 | using value_type = ValueType; 37 | static constexpr std::size_t storage_size{StorageSize}; 38 | static constexpr std::size_t alignment{Alignment}; 39 | 40 | alignas(alignment) std::byte data[storage_size]; 41 | }; 42 | 43 | namespace inclass_storage 44 | { 45 | template 46 | constexpr auto ptr(storage_type & store) noexcept -> typename storage_type::value_type * 47 | { 48 | return std::launder(reinterpret_cast(&store.data[0])); 49 | } 50 | 51 | template 52 | constexpr auto ptr(storage_type const & store) noexcept -> typename storage_type::value_type const * 53 | { 54 | return std::launder(reinterpret_cast(&store.data[0])); 55 | } 56 | 57 | template 58 | requires concepts::complete_type 59 | && std::default_initializable 60 | constexpr auto default_construct() noexcept(std::is_nothrow_default_constructible_v 61 | ) -> storage_type 62 | 63 | { 64 | if constexpr(!std::is_trivially_default_constructible_v) 65 | { 66 | storage_type storage; 67 | std::construct_at(ptr(storage)); 68 | return storage; 69 | } 70 | else 71 | return storage_type{}; 72 | } 73 | 74 | template 75 | requires std::destructible 76 | constexpr void 77 | destroy(storage_type & storage) noexcept(std::is_nothrow_destructible_v) 78 | { 79 | if constexpr(!std::is_trivially_destructible_v) 80 | std::destroy_at(ptr(storage)); 81 | } 82 | 83 | template 84 | requires concepts::complete_type 85 | && std::constructible_from 86 | constexpr auto construct_from(Args &&... args) -> storage_type 87 | { 88 | storage_type storage; 89 | std::construct_at(ptr(storage), std::forward(args)...); 90 | return storage; 91 | } 92 | 93 | template 94 | requires std::copy_constructible 95 | constexpr auto copy_construct( 96 | storage_type const & other 97 | ) noexcept(std::is_nothrow_copy_constructible_v) -> storage_type 98 | { 99 | storage_type storage; 100 | if constexpr(std::is_trivially_copy_constructible_v) 101 | std::memcpy(&storage, &other, sizeof(typename storage_type::value_type)); 102 | else 103 | std::construct_at(ptr(storage), *ptr(other)); 104 | return storage; 105 | } 106 | 107 | template 108 | requires concepts::complete_type 109 | && std::move_constructible 110 | constexpr auto move_construct( 111 | storage_type && other 112 | ) noexcept(std::is_nothrow_move_constructible_v) -> storage_type 113 | { 114 | storage_type storage; 115 | if constexpr(std::is_trivially_move_constructible_v) 116 | std::memcpy(&storage, &other, sizeof(typename storage_type::value_type)); 117 | else 118 | std::construct_at(ptr(storage), std::move(*ptr(other))); 119 | return storage; 120 | } 121 | 122 | template 123 | requires concepts::complete_type 124 | && std::copyable 125 | constexpr void copy_assign( 126 | storage_type & that, storage_type const & other 127 | ) noexcept(std::is_nothrow_copy_assignable_v) 128 | { 129 | if(&that != &other) 130 | { 131 | if constexpr(std::is_trivially_copy_assignable_v) 132 | std::memcpy(&that, &other, sizeof(typename storage_type::value_type)); 133 | else 134 | *ptr(that) = *ptr(other); 135 | } 136 | } 137 | 138 | template 139 | requires concepts::complete_type 140 | && std::movable 141 | constexpr void move_assign( 142 | storage_type & that, storage_type && other 143 | ) noexcept(std::is_nothrow_move_assignable_v) 144 | { 145 | if(&that != &other) 146 | { 147 | if constexpr(std::is_trivially_move_assignable_v) 148 | std::memcpy(&that, &other, sizeof(typename storage_type::value_type)); 149 | else 150 | *ptr(that) = std::move(*ptr(other)); 151 | } 152 | } 153 | 154 | } // namespace inclass_storage 155 | 156 | template< 157 | typename ValueType, 158 | std::size_t StorageSize = sizeof(ValueType), 159 | std::size_t Alignment = std::alignment_of_v> 160 | struct inclass_store_t 161 | { 162 | using value_type = ValueType; 163 | static constexpr std::size_t storage_size{StorageSize}; 164 | 165 | struct store_t 166 | { 167 | alignas(Alignment) std::byte data[storage_size]; 168 | }; 169 | 170 | store_t store_; 171 | 172 | private: 173 | constexpr auto ptr() noexcept -> value_type * 174 | { 175 | return std::launder(reinterpret_cast(&store_.data[0])); 176 | } 177 | 178 | constexpr auto ptr() const noexcept -> value_type const * 179 | { 180 | return std::launder(reinterpret_cast(&store_.data[0])); 181 | } 182 | 183 | public: 184 | constexpr inclass_store_t() noexcept(std::is_nothrow_default_constructible_v) 185 | requires concepts::complete_type && std::default_initializable 186 | { 187 | if constexpr(std::is_trivially_default_constructible_v) 188 | store_ = {}; 189 | else 190 | std::construct_at(ptr()); 191 | } 192 | 193 | constexpr inclass_store_t(inclass_store_t const & other) noexcept(std::is_nothrow_copy_constructible_v) 194 | requires std::copy_constructible 195 | { 196 | if constexpr(std::is_trivially_copy_constructible_v) 197 | store_ = other.store_; 198 | else 199 | std::construct_at(ptr(), *other.ptr()); 200 | } 201 | 202 | constexpr inclass_store_t(inclass_store_t && other) noexcept(std::is_nothrow_move_constructible_v) 203 | requires concepts::complete_type && std::move_constructible 204 | { 205 | if constexpr(std::is_trivially_move_constructible_v) 206 | store_ = other.store_; 207 | else 208 | std::construct_at(ptr(), std::move(*other.ptr())); 209 | } 210 | 211 | constexpr auto operator=(inclass_store_t const & other) noexcept(std::is_nothrow_copy_assignable_v) 212 | -> inclass_store_t & 213 | requires concepts::complete_type && std::copyable // Requires value_type to be copyable 214 | { 215 | if(this != &other) 216 | { 217 | if constexpr(std::is_trivially_copy_assignable_v) 218 | store_ = other.store_; 219 | else 220 | *ptr() = *other.ptr(); 221 | } 222 | return *this; 223 | } 224 | 225 | constexpr auto operator=(inclass_store_t && other) noexcept(std::is_nothrow_move_assignable_v) 226 | -> inclass_store_t & 227 | requires concepts::complete_type && std::movable // Requires value_type to be movable 228 | { 229 | if(this != &other) 230 | { 231 | if constexpr(not std::is_trivially_destructible_v) 232 | std::destroy_at(ptr()); 233 | if constexpr(std::is_trivially_move_assignable_v) 234 | store_ = other.store_; 235 | else 236 | std::construct_at(ptr(), std::move(*other.ptr())); 237 | } 238 | return *this; 239 | } 240 | 241 | template 242 | constexpr inclass_store_t(Args &&... args) 243 | requires concepts::complete_type && std::constructible_from 244 | { 245 | std::construct_at(ptr(), std::forward(args)...); 246 | } 247 | 248 | constexpr ~inclass_store_t() noexcept(std::is_nothrow_destructible_v) 249 | requires std::destructible and (not std::is_trivially_destructible_v) 250 | { 251 | std::destroy_at(ptr()); 252 | } 253 | 254 | constexpr ~inclass_store_t() noexcept 255 | requires std::is_trivially_destructible_v 256 | = default; 257 | 258 | constexpr auto operator*() noexcept -> value_type & { return *ptr(); } 259 | 260 | constexpr auto operator*() const noexcept -> value_type const & { return *ptr(); } 261 | 262 | constexpr auto operator->() noexcept -> value_type * { return ptr(); } 263 | 264 | constexpr auto operator->() const noexcept -> value_type const * { return ptr(); } 265 | }; 266 | 267 | } // namespace small_vectors::inline v3_3 268 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 6, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 23, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "cfg-common", 11 | "hidden": true, 12 | "binaryDir": "${sourceDir}/build/${presetName}" 13 | }, 14 | { 15 | "name": "cfg-ninja", 16 | "hidden": true, 17 | "generator": "Ninja", 18 | "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } 19 | }, 20 | { 21 | "name": "cfg-c++20", 22 | "hidden": true, 23 | "cacheVariables": { "CMAKE_CXX_STANDARD" : "20" } 24 | }, 25 | { 26 | "name": "cfg-c++23", 27 | "hidden": true, 28 | "cacheVariables": { "CMAKE_CXX_STANDARD" : "23" } 29 | }, 30 | { 31 | "name": "cfg-c++26", 32 | "hidden": true, 33 | "cacheVariables": { "CMAKE_CXX_STANDARD" : "26" } 34 | }, 35 | { 36 | "name": "cfg-libc++", 37 | "hidden": true, 38 | "generator": "Ninja", 39 | "cacheVariables": { 40 | "CMAKE_CXX_FLAGS" : "-stdlib=libc++", 41 | "SMALL_VECTORS_ENABLE_LLD_LINKER" : "ON" 42 | } 43 | }, 44 | { 45 | "name": "clang-debug", 46 | "generator": "Ninja", 47 | "inherits": [ "cfg-ninja", "cfg-c++26" ], 48 | "cacheVariables": { "CMAKE_CXX_COMPILER" : "clang++", "CMAKE_BUILD_TYPE": "Debug" } 49 | }, 50 | { 51 | "name": "clang-20-release", 52 | "inherits": [ "cfg-common", "cfg-ninja", "cfg-c++26" ], 53 | "cacheVariables": { "CMAKE_CXX_COMPILER" : "clang++-20" } 54 | }, 55 | { 56 | "name": "clang-19-release", 57 | "inherits": [ "cfg-common", "cfg-ninja", "cfg-c++26" ], 58 | "cacheVariables": { "CMAKE_CXX_COMPILER" : "clang++-19" } 59 | }, 60 | { 61 | "name": "clang-19-libc++release", 62 | "inherits": ["clang-19-release", "cfg-libc++"] 63 | }, 64 | { 65 | "name": "clang-18-release", 66 | "inherits": [ "cfg-common", "cfg-ninja", "cfg-c++23" ], 67 | "cacheVariables": { "CMAKE_CXX_COMPILER" : "clang++-18" } 68 | }, 69 | { 70 | "name": "clang-18-libc++release", 71 | "inherits": ["clang-18-release", "cfg-libc++"] 72 | }, 73 | { 74 | "name": "clang-19-libc++release-asan", 75 | "inherits": ["clang-19-release", "cfg-libc++"], 76 | "cacheVariables": { 77 | "CMAKE_CXX_FLAGS": "-ggdb -fvisibility=default -fno-omit-frame-pointer -fsanitize=address -fsanitize-address-use-after-scope -fsanitize=undefined", 78 | "CMAKE_EXE_LINKER_FLAGS": "-fsanitize=address -fsanitize=undefined", 79 | "ENABLE_FUZZ_TARGETS" : "ON" 80 | } 81 | }, 82 | { 83 | "name": "clang-17-release", 84 | "inherits": [ "cfg-common", "cfg-ninja", "cfg-c++23" ], 85 | "cacheVariables": { "CMAKE_CXX_COMPILER" : "clang++-17" } 86 | }, 87 | { 88 | "name": "clang-17-libc++release", 89 | "inherits": ["clang-17-release", "cfg-libc++"] 90 | }, 91 | { 92 | "name": "gcc-13-release", 93 | "inherits": [ "cfg-common", "cfg-ninja", "cfg-c++23" ], 94 | "cacheVariables": { 95 | "CMAKE_CXX_COMPILER": "g++-13", 96 | "CMAKE_BUILD_TYPE": "RelWithDebInfo" 97 | } 98 | }, 99 | { 100 | "name": "gcc-14-release", 101 | "inherits": [ "cfg-common", "cfg-ninja", "cfg-c++23" ], 102 | "cacheVariables": { 103 | "CMAKE_CXX_COMPILER": "g++-14", 104 | "CMAKE_BUILD_TYPE": "RelWithDebInfo" 105 | } 106 | }, 107 | { 108 | "name": "clangcl-release", 109 | "inherits": "cfg-common", 110 | "generator": "Visual Studio 17 2022", 111 | "toolset": "ClangCL" 112 | }, 113 | { 114 | "name": "msvc-release", 115 | "inherits": "cfg-common", 116 | "generator": "Visual Studio 17 2022" 117 | } 118 | ], 119 | "buildPresets": [ 120 | { 121 | "name": "clang-19-release", 122 | "configurePreset": "clang-19-release" 123 | }, 124 | { 125 | "name": "clang-19-libc++release", 126 | "configurePreset": "clang-19-libc++release" 127 | }, 128 | { 129 | "name": "clang-18-release", 130 | "configurePreset": "clang-18-release" 131 | }, 132 | { 133 | "name": "clang-18-libc++release", 134 | "configurePreset": "clang-18-libc++release" 135 | }, 136 | { 137 | "name": "clang-19-libc++release-asan", 138 | "configurePreset": "clang-19-libc++release-asan" 139 | }, 140 | { 141 | "name": "clang-17-release", 142 | "configurePreset": "clang-17-release" 143 | }, 144 | { 145 | "name": "clang-17-libc++release", 146 | "configurePreset": "clang-17-libc++release" 147 | }, 148 | { 149 | "name": "gcc-13-release", 150 | "configurePreset": "gcc-13-release" 151 | }, 152 | { 153 | "name": "gcc-14-release", 154 | "configurePreset": "gcc-14-release" 155 | } 156 | 157 | ], 158 | "testPresets": [ 159 | { 160 | "name": "clang-19-release", 161 | "configurePreset": "clang-19-release", 162 | "output": {"outputOnFailure": true}, 163 | "execution": {"noTestsAction": "error", "stopOnFailure": true} 164 | }, 165 | { 166 | "name": "clang-19-libc++release", 167 | "configurePreset": "clang-19-libc++release", 168 | "output": {"outputOnFailure": true}, 169 | "execution": {"noTestsAction": "error", "stopOnFailure": true} 170 | }, 171 | { 172 | "name": "clang-18-release", 173 | "configurePreset": "clang-18-release", 174 | "output": {"outputOnFailure": true}, 175 | "execution": {"noTestsAction": "error", "stopOnFailure": true} 176 | }, 177 | { 178 | "name": "clang-18-libc++release", 179 | "configurePreset": "clang-18-libc++release", 180 | "output": {"outputOnFailure": true}, 181 | "execution": {"noTestsAction": "error", "stopOnFailure": true} 182 | }, 183 | { 184 | "name": "clang-19-libc++release-asan", 185 | "configurePreset": "clang-19-libc++release-asan", 186 | "output": {"outputOnFailure": true}, 187 | "execution": {"noTestsAction": "error", "stopOnFailure": true} 188 | }, 189 | { 190 | "name": "clang-17-release", 191 | "configurePreset": "clang-17-release", 192 | "output": {"outputOnFailure": true}, 193 | "execution": {"noTestsAction": "error", "stopOnFailure": true} 194 | }, 195 | { 196 | "name": "clang-17-libc++release", 197 | "configurePreset": "clang-17-libc++release", 198 | "output": {"outputOnFailure": true}, 199 | "execution": {"noTestsAction": "error", "stopOnFailure": true} 200 | }, 201 | { 202 | "name": "gcc-13-release", 203 | "configurePreset": "gcc-13-release", 204 | "output": {"outputOnFailure": true}, 205 | "execution": {"noTestsAction": "error", "stopOnFailure": true} 206 | }, 207 | { 208 | "name": "gcc-14-release", 209 | "configurePreset": "gcc-14-release", 210 | "output": {"outputOnFailure": true}, 211 | "execution": {"noTestsAction": "error", "stopOnFailure": true} 212 | } 213 | ], 214 | "workflowPresets": [ 215 | { 216 | "name": "clang-19-release", 217 | "steps": [ 218 | { 219 | "type": "configure", 220 | "name": "clang-19-release" 221 | }, 222 | { 223 | "type": "build", 224 | "name": "clang-19-release" 225 | }, 226 | { 227 | "type": "test", 228 | "name": "clang-19-release" 229 | } 230 | ] 231 | }, 232 | { 233 | "name": "clang-19-libc++release", 234 | "steps": [ 235 | { 236 | "type": "configure", 237 | "name": "clang-19-libc++release" 238 | }, 239 | { 240 | "type": "build", 241 | "name": "clang-19-libc++release" 242 | }, 243 | { 244 | "type": "test", 245 | "name": "clang-19-libc++release" 246 | } 247 | ] 248 | }, 249 | { 250 | "name": "clang-18-release", 251 | "steps": [ 252 | { 253 | "type": "configure", 254 | "name": "clang-18-release" 255 | }, 256 | { 257 | "type": "build", 258 | "name": "clang-18-release" 259 | }, 260 | { 261 | "type": "test", 262 | "name": "clang-18-release" 263 | } 264 | ] 265 | }, 266 | { 267 | "name": "clang-18-libc++release", 268 | "steps": [ 269 | { 270 | "type": "configure", 271 | "name": "clang-18-libc++release" 272 | }, 273 | { 274 | "type": "build", 275 | "name": "clang-18-libc++release" 276 | }, 277 | { 278 | "type": "test", 279 | "name": "clang-18-libc++release" 280 | } 281 | ] 282 | }, 283 | { 284 | "name": "clang-19-libc++release-asan", 285 | "steps": [ 286 | { 287 | "type": "configure", 288 | "name": "clang-19-libc++release-asan" 289 | }, 290 | { 291 | "type": "build", 292 | "name": "clang-19-libc++release-asan" 293 | }, 294 | { 295 | "type": "test", 296 | "name": "clang-19-libc++release-asan" 297 | } 298 | ] 299 | }, 300 | { 301 | "name": "clang-17-release", 302 | "steps": [ 303 | { 304 | "type": "configure", 305 | "name": "clang-17-release" 306 | }, 307 | { 308 | "type": "build", 309 | "name": "clang-17-release" 310 | }, 311 | { 312 | "type": "test", 313 | "name": "clang-17-release" 314 | } 315 | ] 316 | }, 317 | { 318 | "name": "clang-17-libc++release", 319 | "steps": [ 320 | { 321 | "type": "configure", 322 | "name": "clang-17-libc++release" 323 | }, 324 | { 325 | "type": "build", 326 | "name": "clang-17-libc++release" 327 | }, 328 | { 329 | "type": "test", 330 | "name": "clang-17-libc++release" 331 | } 332 | ] 333 | }, 334 | { 335 | "name": "gcc-13-release", 336 | "steps": [ 337 | { 338 | "type": "configure", 339 | "name": "gcc-13-release" 340 | }, 341 | { 342 | "type": "build", 343 | "name": "gcc-13-release" 344 | }, 345 | { 346 | "type": "test", 347 | "name": "gcc-13-release" 348 | } 349 | ] 350 | }, 351 | { 352 | "name": "gcc-14-release", 353 | "steps": [ 354 | { 355 | "type": "configure", 356 | "name": "gcc-14-release" 357 | }, 358 | { 359 | "type": "build", 360 | "name": "gcc-14-release" 361 | }, 362 | { 363 | "type": "test", 364 | "name": "gcc-14-release" 365 | } 366 | ] 367 | } 368 | ] 369 | } 370 | 371 | -------------------------------------------------------------------------------- /unit_tests/stack_buffer_ut.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | struct destruction_only 13 | { 14 | template 15 | #if defined(__cpp_static_call_operator) 16 | static 17 | #endif 18 | void 19 | operator()(T * addr) 20 | #if !defined(__cpp_static_call_operator) 21 | const 22 | #endif 23 | noexcept 24 | { 25 | addr->~T(); 26 | } 27 | }; 28 | namespace ip = small_vectors::ip; 29 | namespace ut = boost::ut; 30 | using boost::ut::operator""_test; 31 | using namespace ut::operators::terse; 32 | using metatests::constexpr_test; 33 | 34 | namespace bip = boost::interprocess; 35 | using namespace std::string_view_literals; 36 | 37 | int main() 38 | { 39 | using ip::push_status; 40 | using enum ip::push_status; 41 | using ip::pop_status; 42 | using enum ip::pop_status; 43 | metatests::test_result result; 44 | namespace ranges = std::ranges; 45 | 46 | "stack_buffer_basic"_test = [&] 47 | { 48 | auto fn_test = []() -> metatests::test_result 49 | { 50 | using stack_type = ip::stack_buffer_t<32, ip::constexpr_index>; 51 | stack_type stack; 52 | constexpr_test(ip::empty(stack)); 53 | { 54 | std::array value{'a', 'b', 'c', 'd'}; 55 | push_status res{ip::push(stack, value.begin(), value.end())}; 56 | constexpr_test(res == succeed); 57 | auto [range, index] = ip::top(stack); 58 | constexpr_test(ranges::equal(range, value)); 59 | constexpr_test(!ip::empty(stack)); 60 | pop_status popres = ip::pop(stack, index); 61 | constexpr_test(popres == succeed_range_valid); 62 | constexpr_test(ip::empty(stack)); 63 | res = ip::push(stack, value.begin(), value.end()); 64 | constexpr_test(res == succeed); 65 | std::tie(range, index) = ip::top(stack); 66 | constexpr_test(ranges::equal(range, value)); 67 | constexpr_test(!ip::empty(stack)); 68 | } 69 | 70 | { 71 | std::array value{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'}; 72 | push_status res{ip::push(stack, value.begin(), value.end())}; 73 | constexpr_test(res == succeed); 74 | auto [range, index] = ip::top(stack); 75 | constexpr_test(ranges::equal(range, value)); 76 | pop_status popres = ip::pop(stack, index); 77 | constexpr_test(popres == succeed_range_valid); 78 | } 79 | { 80 | std::array value{'a', 'b', 'c', 'd'}; 81 | auto [range, index] = ip::top(stack); 82 | constexpr_test(ranges::equal(range, value)); 83 | pop_status popres = ip::pop(stack, index); 84 | constexpr_test(popres == succeed_range_valid); 85 | constexpr_test(ip::empty(stack)); 86 | } 87 | { 88 | auto [range, index] = ip::top(stack); 89 | constexpr_test(range.empty()); 90 | } 91 | { 92 | std::array value{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'}; 93 | push_status res{ip::push(stack, value.begin(), value.end())}; 94 | constexpr_test(res == succeed); 95 | auto [range, index] = ip::top(stack); 96 | constexpr_test(ranges::equal(range, value)); 97 | constexpr_test(!ip::empty(stack)); 98 | } 99 | { 100 | std::array value{'a', 'b', 'c', 'd'}; 101 | push_status res{ip::push(stack, value.begin(), value.end())}; 102 | constexpr_test(res == error_not_enough_space); 103 | auto [range, index] = ip::top(stack); 104 | pop_status popres = ip::pop(stack, index); 105 | constexpr_test(popres == succeed_range_valid); 106 | constexpr_test(ip::empty(stack)); 107 | } 108 | { 109 | std::array value{'a', 'b', 'c', 'd'}; 110 | push_status res{ip::push(stack, value.begin(), value.end())}; 111 | constexpr_test(res == succeed); 112 | auto [range, index] = ip::top(stack); 113 | res = ip::push(stack, value.begin(), value.end()); 114 | constexpr_test(res == succeed); 115 | pop_status popres = ip::pop(stack, index); 116 | constexpr_test(popres == failed_stack_changed); 117 | } 118 | return {}; 119 | }; 120 | result |= metatests::run_constexpr_test(fn_test); 121 | result |= metatests::run_consteval_test(fn_test); 122 | }; 123 | 124 | constexpr auto shmem_name{"shmem test for " SMALL_VECTORS_COMPILER_INFO}; 125 | bip::shared_memory_object shm{bip::open_or_create, shmem_name, bip::read_write}; 126 | shm.truncate(8192); 127 | 128 | "test_semaphore"_test = [&] 129 | { 130 | using stack_type = ip::stack_buffer_t<32, ip::atomic_index>; 131 | 132 | using stack_decl = ip::shared_type_decl; 133 | using semaphore_decl = ip::shared_type_decl; 134 | using semaphore2_decl = ip::shared_type_decl; 135 | using writter_done_decl = ip::shared_type_decl; 136 | using namespace std::chrono_literals; 137 | 138 | bip::mapped_region region(shm, bip::read_write); 139 | 140 | std::unique_ptr stack_obj{ip::construct_at(region)}; 141 | std::unique_ptr reader_finished_semaphore{ 142 | ip::construct_at(region, 0) 143 | }, 144 | writter_finished_semaphore{ip::construct_at(region, 0)}; 145 | auto writter_done{ip::construct_at(region, false)}; 146 | 147 | auto writter = ip::fork( 148 | [](std::string_view shared_mem_name) 149 | { 150 | bip::shared_memory_object shm_obj{bip::open_only, shared_mem_name.data(), bip::read_write}; 151 | bip::mapped_region cregion{shm_obj, bip::read_write}; 152 | decltype(auto) stack{ip::ref(cregion)}; 153 | decltype(auto) writter_finished_semaphore_obj{ip::ref(cregion)}; 154 | decltype(auto) writter_done_obj{ip::ref(cregion)}; 155 | std::array value{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'}; 156 | auto delay{4us}; 157 | for(auto i = 1000; i != 0; --i) 158 | for(auto j = 1u; j != value.size(); ++j) 159 | { 160 | ip::push_status push_res{}; 161 | do 162 | { 163 | push_res = ip::push(stack, value.begin(), ranges::next(value.begin(), j)); 164 | if(push_res == push_status::succeed) 165 | { 166 | std::cout << "delay w" << delay.count() << std::endl; 167 | delay = 4us; 168 | } 169 | else if(push_res == error_not_enough_space) 170 | { 171 | // std::this_thread::sleep_for(2000us); 172 | std::this_thread::sleep_for(delay); 173 | delay = delay + 3us; 174 | // std::cout << "push error_not_enough_space" << std::endl; 175 | } 176 | else if(push_res == error_stack_changed) 177 | std::cout << "push error_stack_changed" << std::endl; 178 | else if(push_res == logic_error_not_enough_space) 179 | std::cout << "push logic_error_not_enough_space" << std::endl; 180 | } while(push_res != succeed); 181 | } 182 | std::cout << "writter done" << std::endl; 183 | writter_done_obj = true; 184 | writter_finished_semaphore_obj.post(); 185 | return true; 186 | }, 187 | shmem_name 188 | ); 189 | 190 | auto reader = ip::fork( 191 | [](std::string_view shared_mem_name) 192 | { 193 | bip::shared_memory_object shm_obj{bip::open_only, shared_mem_name.data(), bip::read_write}; 194 | bip::mapped_region cregion{shm_obj, bip::read_write}; 195 | decltype(auto) stack{ip::ref(cregion)}; 196 | decltype(auto) reader_finished_semaphore_obj{ip::ref(cregion)}; 197 | decltype(auto) writter_done_obj{ip::ref(cregion)}; 198 | std::array value{}; 199 | std::array refvalue{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k'}; 200 | int i{}; 201 | auto it_beg{ranges::begin(value)}; 202 | auto delay{3us}; 203 | while(!writter_done_obj || !ip::empty(stack)) 204 | { 205 | if(ip::empty(stack)) 206 | { 207 | // std::this_thread::sleep_for(500us); 208 | std::this_thread::sleep_for(delay); 209 | delay = delay + 2us; 210 | // std::cout << "stack empty" << std::endl; 211 | } 212 | else 213 | { 214 | std::cout << "delay r" << delay.count() << std::endl; 215 | delay = 3us; 216 | auto [range, index] = ip::top(stack); 217 | auto it_end{ranges::copy(range, it_beg).out}; 218 | pop_status popres{ip::pop(stack, index)}; 219 | if(succeed_range_valid == popres) 220 | { 221 | small_vectors_clang_unsafe_buffer_usage_begin // 222 | std::span indata{it_beg, it_end}; 223 | small_vectors_clang_unsafe_buffer_usage_end // 224 | ranges::subrange refsubrange{ 225 | ranges::begin(refvalue), ranges::next(ranges::begin(refvalue), static_cast(indata.size())) 226 | }; 227 | auto match{ranges::equal(indata, refsubrange)}; 228 | constexpr_test(match); 229 | if(!match) 230 | { 231 | std::cout << i << " pop_status " << ranges::size(indata); 232 | for(auto c: indata) 233 | std::cout << c; 234 | std::cout << std::endl; 235 | } 236 | ++i; 237 | } 238 | else 239 | std::cout << "pop_status failed_stack_changed" << std::endl; 240 | } 241 | } 242 | std::cout << "reader_finished" << std::endl; 243 | // writter_finished_semaphore_obj.wait(); 244 | constexpr_test(writter_done_obj); 245 | constexpr_test(ip::empty(stack)); 246 | reader_finished_semaphore_obj.post(); 247 | return true; 248 | }, 249 | shmem_name 250 | ); 251 | 252 | reader_finished_semaphore->wait(); 253 | writter_finished_semaphore->wait(); 254 | ut::expect(writter->join()) >> ut::fatal; 255 | ut::expect(reader->join()) >> ut::fatal; 256 | constexpr_test(*writter_done); 257 | constexpr_test(ip::empty(*stack_obj)); 258 | }; 259 | 260 | bip::shared_memory_object::remove(shmem_name); 261 | } 262 | 263 | -------------------------------------------------------------------------------- /unit_tests/strong_type_ut.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #if defined(__cpp_lib_format) && __cpp_lib_format >= 202110L 4 | #include 5 | #endif 6 | #include 7 | #include 8 | 9 | namespace ut = boost::ut; 10 | using test_types = metatests::type_list; 11 | using namespace metatests; 12 | using boost::ut::operator""_test; 13 | using namespace ut::operators::terse; 14 | using boost::ut::eq; 15 | using boost::ut::neq; 16 | 17 | namespace small_vectors::utils 18 | { 19 | struct test_tag : public strong_type_default_traits 20 | { 21 | }; 22 | 23 | static_assert(small_vectors::concepts::stream_insertable>); 24 | 25 | //---------------------------------------------------------------------------------------------------------------------- 26 | namespace concepts 27 | { 28 | struct tag_with_hash 29 | { 30 | static constexpr bool enable_hash_specialization = true; 31 | }; 32 | 33 | struct tag_with_hash_neg 34 | { 35 | static constexpr bool enable_hash_specialization = false; 36 | }; 37 | 38 | struct tag_empty 39 | { 40 | }; 41 | 42 | static_assert(tag_hash_specialization); 43 | static_assert(!tag_hash_specialization); 44 | static_assert(!tag_hash_specialization); 45 | 46 | struct tag_with_arithemtic 47 | { 48 | static constexpr bool enable_arithemtic = true; 49 | }; 50 | 51 | struct tag_with_arithemtic_neg 52 | { 53 | static constexpr bool enable_arithemtic = false; 54 | }; 55 | 56 | static_assert(tag_arithemtic); 57 | static_assert(!tag_arithemtic); 58 | static_assert(!tag_arithemtic); 59 | 60 | struct tag_with_comparison 61 | { 62 | static constexpr bool enable_comparison = true; 63 | }; 64 | 65 | struct tag_with_comparison_neg 66 | { 67 | static constexpr bool enable_comparison = false; 68 | }; 69 | 70 | static_assert(tag_comparison); 71 | static_assert(!tag_comparison); 72 | static_assert(!tag_comparison); 73 | 74 | struct tag_with_binary_operators 75 | { 76 | static constexpr bool enable_binary_operators = true; 77 | }; 78 | 79 | struct tag_with_binary_operators_neg 80 | { 81 | static constexpr bool enable_binary_operators = false; 82 | }; 83 | 84 | static_assert(tag_binary_operators); 85 | static_assert(!tag_binary_operators); 86 | static_assert(!tag_binary_operators); 87 | } // namespace concepts 88 | #if defined(__cpp_lib_format) && __cpp_lib_format >= 202110L 89 | static ut::suite<"strong_type_formatter"> strong_type_formatter = [] 90 | { 91 | using namespace ut; 92 | using namespace std::string_view_literals; 93 | 94 | "Default format test"_test = [] 95 | { 96 | small_vectors::utils::strong_type strong_int{42}; 97 | expect(eq(std::format("{}", strong_int), "42"sv)); 98 | }; 99 | 100 | "Custom format - width and padding test"_test = [] 101 | { 102 | small_vectors::utils::strong_type strong_int{7}; 103 | expect(eq(std::format("{:04}", strong_int), "0007"sv)); 104 | }; 105 | 106 | "Negative number formatting test"_test = [] 107 | { 108 | small_vectors::utils::strong_type strong_int{-123}; 109 | expect(eq(std::format("{}", strong_int), "-123"sv)); 110 | }; 111 | 112 | "Hexadecimal format test"_test = [] 113 | { 114 | small_vectors::utils::strong_type strong_int{255}; 115 | expect(eq(std::format("{:x}", strong_int), "ff"sv)); 116 | }; 117 | 118 | // More tests can be added here following the same pattern 119 | }; 120 | #endif 121 | static void strong_type_suite() 122 | { 123 | test_result result; 124 | "test_strong_type_basic"_test = [&result] 125 | { 126 | auto fn_tmpl = [](value_type const *) -> metatests::test_result 127 | { 128 | test_result tr; 129 | using test_type = strong_type; 130 | test_type tst{value_type{0x55}}; 131 | tr |= constexpr_test(*tst == 0x55) | constexpr_test(tst.value() == 0x55); 132 | 133 | test_type tst2{tst}; 134 | tr |= constexpr_test(*tst2 == 0x55); 135 | 136 | test_type tst3{value_type{0x15}}; 137 | tr |= constexpr_test(*tst3 == 0x15); 138 | tst3 = tst2; 139 | tr |= constexpr_test(*tst3 == 0x55); 140 | 141 | test_type tst4{std::numeric_limits::max()}; 142 | tr |= constexpr_test(*tst4 == std::numeric_limits::max()); 143 | tst3 = std::move(tst4); 144 | tr |= constexpr_test(*tst3 == std::numeric_limits::max()); 145 | return tr; 146 | }; 147 | result |= run_consteval_test(fn_tmpl); 148 | result |= run_constexpr_test(fn_tmpl); 149 | }; 150 | 151 | //---------------------------------------------------------------------------------------------------------------------- 152 | "test_strong_type_arithemtic"_test = [&result] 153 | { 154 | auto fn_tmpl = [](value_type const *) -> metatests::test_result 155 | { 156 | using test_type = strong_type; 157 | 158 | test_type tst{value_type{0x55}}; 159 | test_result tr = constexpr_test(*++tst == 0x56); 160 | tr |= constexpr_test(*tst++ == 0x56); 161 | tr |= constexpr_test(*tst == 0x57); 162 | 163 | tr |= constexpr_test(*--tst == 0x56); 164 | tr |= constexpr_test(*tst-- == 0x56); 165 | tr |= constexpr_test(*tst == 0x55); 166 | 167 | tr |= constexpr_test(*(tst + test_type(value_type{1})) == 0x56); 168 | tr |= constexpr_test(*(tst - test_type(value_type{1})) == 0x54); 169 | 170 | tst = test_type{value_type{13}}; 171 | tr |= constexpr_test(*(tst * test_type(value_type{2})) == 26); 172 | 173 | tst = test_type{value_type{44}}; 174 | tr |= constexpr_test(*(tst / test_type(value_type{2})) == 22); 175 | 176 | tst = test_type{value_type{22}}; 177 | tr |= constexpr_test(*(tst % test_type(value_type(10))) == 2); 178 | 179 | tst = test_type{value_type{0x55}}; 180 | tr |= constexpr_test(*(tst += test_type(value_type(1))) == 0x56); 181 | 182 | tst = test_type{value_type{0x55}}; 183 | tr |= constexpr_test(*(tst -= test_type(value_type(1))) == 0x54); 184 | 185 | tst = test_type{value_type{13}}; 186 | tr |= constexpr_test(*(tst *= test_type(value_type(2))) == 26); 187 | 188 | tst = test_type{value_type{44}}; 189 | tr |= constexpr_test(*(tst /= test_type(value_type(2))) == 22); 190 | 191 | tst = test_type{value_type{22}}; 192 | tr |= constexpr_test(*(tst %= test_type(value_type(10))) == 2); 193 | 194 | return tr; 195 | }; 196 | result |= run_consteval_test(fn_tmpl); 197 | result |= run_constexpr_test(fn_tmpl); 198 | }; 199 | 200 | //---------------------------------------------------------------------------------------------------------------------- 201 | "test_strong_type_comparison"_test = [&result] 202 | { 203 | auto fn_tmpl = [](value_type const *) -> metatests::test_result 204 | { 205 | using test_type = strong_type; 206 | 207 | test_type tst0{value_type{0x55}}; 208 | test_type tst1{value_type{0x55}}; 209 | test_type tst2{value_type{0x56}}; 210 | 211 | test_result tr 212 | = constexpr_test(tst0 == tst0) | constexpr_test(tst0 == tst1) | constexpr_test(!(tst0 == tst2)) 213 | 214 | | constexpr_test(!(tst0 != tst0)) | constexpr_test(!(tst0 != tst1)) | constexpr_test(tst0 != tst2) 215 | 216 | | constexpr_test((tst0 <=> tst0) == std::strong_ordering::equal) | constexpr_test(!(tst0 < tst0)) 217 | | constexpr_test(!(tst0 < tst1)) | constexpr_test(tst0 < tst2) 218 | | constexpr_test((tst0 <=> tst2) == std::strong_ordering::less) | constexpr_test(!(tst2 < tst0)) 219 | 220 | | constexpr_test(tst0 <= tst0) | constexpr_test(tst0 <= tst1) | constexpr_test(tst0 <= tst2) 221 | | constexpr_test(!(tst2 <= tst0)) 222 | 223 | | constexpr_test(!(tst0 > tst0)) | constexpr_test(!(tst0 > tst1)) | constexpr_test(!(tst0 > tst2)) 224 | | constexpr_test(tst2 > tst0) | constexpr_test((tst2 <=> tst0) == std::strong_ordering::greater) 225 | 226 | | constexpr_test(tst0 >= tst0) | constexpr_test(tst0 >= tst1) | constexpr_test(!(tst0 >= tst2)) 227 | | constexpr_test(tst2 >= tst0) 228 | 229 | | constexpr_test((tst1 <=> tst2) == std::strong_ordering::less) 230 | | constexpr_test((tst1 <=> tst0) == std::strong_ordering::equal); 231 | 232 | return tr; 233 | }; 234 | result |= run_consteval_test(fn_tmpl); 235 | result |= run_constexpr_test(fn_tmpl); 236 | }; 237 | 238 | //---------------------------------------------------------------------------------------------------------------------- 239 | "test_strong_strong_type_binary"_test = [&result] 240 | { 241 | auto fn_tmpl = [](value_type const *) -> metatests::test_result 242 | { 243 | using test_type = strong_type; 244 | test_type tst0{value_type{0b01010101}}; 245 | test_type tst1{value_type{0b00111100}}; 246 | 247 | test_result tr = constexpr_test((tst0 ^ tst1) == test_type{value_type{0b01101001}}) 248 | | constexpr_test((tst0 | tst1) == test_type{value_type{0b01111101}}) 249 | | constexpr_test((tst0 & tst1) == test_type{value_type{0b00010100}}) 250 | 251 | | constexpr_test((tst0 >> 1u) == test_type{value_type{0b00101010}}) 252 | | constexpr_test((tst0 >> test_type{value_type{1}}) == test_type{value_type{0b00101010}}); 253 | 254 | tst0 = test_type{value_type{0b00101010}}; 255 | tr |= constexpr_test((tst0 << 1u) == test_type{value_type{0b01010100}}) 256 | | constexpr_test((tst0 << test_type{value_type{1}}) == test_type{value_type{0b01010100}}); 257 | 258 | tst0 = test_type{value_type{0b01010101}}; 259 | tr |= constexpr_test((tst0 ^= tst1) == test_type{value_type{0b01101001}}); 260 | 261 | tst0 = test_type{value_type{0b01010101}}; 262 | tr |= constexpr_test((tst0 |= tst1) == test_type{value_type{0b01111101}}); 263 | 264 | tst0 = test_type{value_type{0b01010101}}; 265 | tr |= constexpr_test((tst0 &= tst1) == test_type{value_type{0b00010100}}); 266 | 267 | tst0 = test_type{value_type{0b01010101}}; 268 | tr |= constexpr_test((tst0 >>= test_type{value_type{1}}) == test_type{value_type{0b00101010}}); 269 | 270 | tst0 = test_type{value_type{0b00101010}}; 271 | tr |= constexpr_test((tst0 <<= test_type{value_type{1}}) == test_type{value_type{0b01010100}}); 272 | 273 | return tr; 274 | }; 275 | result |= run_consteval_test(fn_tmpl); 276 | result |= run_constexpr_test(fn_tmpl); 277 | }; 278 | } 279 | 280 | } // namespace small_vectors::utils 281 | 282 | int main() { small_vectors::utils::strong_type_suite(); } 283 | -------------------------------------------------------------------------------- /include/small_vectors/utils/meta_packed_struct.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace small_vectors::inline v3_3::utils 12 | { 13 | 14 | namespace detail 15 | { 16 | template 17 | concept enum_struct = std::is_enum_v; 18 | 19 | template 20 | concept underlaying_integral = std::integral || enum_struct; 21 | 22 | template 23 | requires enum_struct 24 | struct tag_with_value 25 | { 26 | T value; 27 | }; 28 | 29 | template 30 | requires enum_struct && ((sizeof(value_type) * 8) >= BitWidth) 31 | struct member 32 | { 33 | using value_t = value_type; 34 | using tag_type = decltype(tag_value); 35 | 36 | static constexpr tag_type tag() noexcept { return tag_value; } 37 | 38 | static constexpr uint8_t bit_width() noexcept { return BitWidth; } 39 | 40 | value_type value{}; 41 | 42 | constexpr member() noexcept = default; 43 | 44 | template 45 | constexpr member(tag_with_value obj) noexcept : value{obj.value} 46 | { 47 | } 48 | }; 49 | 50 | template 51 | constexpr auto & get(member & m) noexcept 52 | { 53 | return m.value; 54 | } 55 | 56 | template 57 | constexpr auto const & get(member const & m) noexcept 58 | { 59 | return m.value; 60 | } 61 | 62 | template 63 | struct sub_member_t 64 | { 65 | using member_type = Member; 66 | using next_member_t = sub_member_t<(counter + 1), Members...>; 67 | 68 | static constexpr unsigned index() noexcept { return counter; } 69 | }; 70 | 71 | template 72 | struct sub_member_t 73 | { 74 | using member_type = Member; 75 | using next_member_t = void; 76 | 77 | static constexpr unsigned index() noexcept { return counter; } 78 | }; 79 | 80 | template 81 | requires enum_struct 82 | struct arg_type 83 | { 84 | template 85 | constexpr auto operator=(T value) const noexcept 86 | { 87 | return tag_with_value{value}; 88 | } 89 | }; 90 | 91 | template 92 | struct params : tags_and_values... 93 | { 94 | constexpr params(tags_and_values... args) noexcept : tags_and_values(args)... {} 95 | }; 96 | 97 | template 98 | params(tags_and_values... args) -> params; 99 | 100 | template 101 | struct meta_packed_struct_impl : Members... 102 | { 103 | using first_member_t = detail::sub_member_t<0, Members...>; 104 | 105 | constexpr meta_packed_struct_impl() noexcept = default; 106 | 107 | template 108 | constexpr meta_packed_struct_impl(Params p) noexcept : Members{p}... 109 | { 110 | } 111 | }; 112 | 113 | template 114 | consteval unsigned filed_count() noexcept 115 | { 116 | using next_member_t = typename sub_member_type::next_member_t; 117 | if constexpr(std::is_same_v) 118 | return sub_member_type::index() + 1; 119 | else 120 | return filed_count(); 121 | } 122 | 123 | template 124 | consteval unsigned bit_width() noexcept 125 | { 126 | using next_member_t = typename sub_member_type::next_member_t; 127 | using member_type = typename sub_member_type::member_type; 128 | unsigned my_bit_width = member_type::bit_width(); 129 | if constexpr(std::is_same_v) 130 | return my_bit_width; 131 | else 132 | return bit_width() + my_bit_width; 133 | } 134 | 135 | /// \brief compile time bitmsk calculating 136 | template 137 | struct bitmask_m 138 | { 139 | static T constexpr value = bitmask_m::value | T(T(1u) << (N - 1)); 140 | }; 141 | 142 | template 143 | struct bitmask_m 144 | { 145 | static T constexpr value = T(0u); 146 | }; 147 | } // namespace detail 148 | 149 | /// \brief compile time bitmsk calculating 150 | template 151 | constexpr T bitmask_v = detail::bitmask_m::value; 152 | 153 | namespace detail 154 | { 155 | template 156 | constexpr auto compress_value(value_type value) noexcept -> value_type 157 | { 158 | constexpr value_type smask{bitmask_v}; 159 | return static_cast(static_cast(value) & smask); 160 | } 161 | 162 | template 163 | constexpr auto compress_value(value_type value) noexcept -> std::make_unsigned_t 164 | { 165 | return compress_value(static_cast>(value)); 166 | } 167 | 168 | template 169 | constexpr auto compress_value(value_type value) noexcept -> std::make_unsigned_t> 170 | { 171 | return compress_value(cxx23::to_underlying(value)); 172 | } 173 | 174 | template 175 | constexpr auto pack_value(unsigned offset, meta_packed_struct const & ms) -> pack_type 176 | { 177 | using next_member_t = typename sub_member_type::next_member_t; 178 | using member_type = typename sub_member_type::member_type; 179 | 180 | // cast meta to exactly my self inherited type 181 | member_type const & self = static_cast(ms); 182 | constexpr unsigned bit_width = member_type::bit_width(); 183 | auto value(compress_value(self.value)); 184 | static_assert(sizeof(decltype(value)) <= sizeof(pack_type)); 185 | auto my_value_packed = static_cast(value) << offset; 186 | if constexpr(std::is_same_v) 187 | return static_cast(my_value_packed); 188 | else 189 | return static_cast(pack_value(offset + bit_width, ms) | my_value_packed); 190 | } 191 | 192 | template 193 | constexpr auto uncompress_value(pack_type value) noexcept -> value_type 194 | { 195 | return static_cast(value & bitmask_v); 196 | } 197 | 198 | template 199 | constexpr auto uncompress_value(pack_type value) noexcept -> value_type 200 | { 201 | using U = std::make_unsigned_t; 202 | U v{uncompress_value(value)}; 203 | 204 | constexpr auto neg_fill_mask{static_cast(~bitmask_v)}; 205 | 206 | if constexpr(neg_fill_mask == 0) 207 | return static_cast(v); 208 | else 209 | { 210 | bool const sign_bit{(v & (1u << (bit_width - 1))) != 0}; 211 | if(!sign_bit) 212 | return static_cast(v); 213 | else 214 | return static_cast(neg_fill_mask | v); 215 | } 216 | } 217 | 218 | template 219 | constexpr auto uncompress_value(pack_type value) noexcept -> value_type 220 | { 221 | using U = std::underlying_type_t; 222 | return static_cast(uncompress_value(value)); 223 | } 224 | 225 | template 226 | constexpr void unpack_value(meta_packed_struct & mps, unsigned offset, pack_type pack) 227 | { 228 | using next_member_t = typename sub_member_type::next_member_t; 229 | using member_type = typename sub_member_type::member_type; 230 | using value_type = typename member_type::value_t; 231 | 232 | constexpr unsigned bit_width = member_type::bit_width(); 233 | constexpr auto mask{bitmask_v}; 234 | 235 | pack = pack >> offset; 236 | auto & value{detail::get(mps)}; 237 | 238 | value = uncompress_value(static_cast(pack & mask)); 239 | 240 | if constexpr(!std::is_same_v) 241 | return unpack_value(mps, bit_width, pack); 242 | } 243 | } // namespace detail 244 | 245 | using detail::member; 246 | template 247 | requires detail::enum_struct 248 | inline constexpr auto arg = detail::arg_type{}; 249 | 250 | template 251 | struct meta_packed_struct : detail::meta_packed_struct_impl 252 | { 253 | using base_type = detail::meta_packed_struct_impl; 254 | 255 | constexpr meta_packed_struct() noexcept = default; 256 | 257 | template 258 | constexpr meta_packed_struct(tag_and_values... args) noexcept : base_type(detail::params(args...)) 259 | { 260 | } 261 | 262 | template 263 | requires detail::enum_struct 264 | constexpr decltype(auto) get() noexcept 265 | { 266 | return detail::get(*this); 267 | } 268 | 269 | template 270 | requires detail::enum_struct 271 | constexpr decltype(auto) get() const noexcept 272 | { 273 | return detail::get(*this); 274 | } 275 | }; 276 | 277 | template 278 | consteval unsigned filed_count() noexcept 279 | { 280 | return detail::filed_count(); 281 | } 282 | 283 | template 284 | consteval unsigned bit_width() noexcept 285 | { 286 | return detail::bit_width(); 287 | } 288 | 289 | ///\brief packs all members into continous integral 290 | template 291 | requires(sizeof(pack_type) * 8 >= bit_width()) 292 | constexpr auto pack_value(meta_packed_struct const & ms) noexcept 293 | { 294 | return detail::pack_value(0, ms); 295 | } 296 | 297 | template 298 | constexpr auto unpack_value(pack_type pack) 299 | { 300 | meta_packed_struct mps{}; 301 | detail::unpack_value(mps, 0, pack); 302 | return mps; 303 | } 304 | 305 | template 306 | requires detail::enum_struct 307 | constexpr decltype(auto) get(meta_packed_struct && s) 308 | { 309 | return detail::get(std::forward(s)); 310 | } 311 | 312 | } // namespace small_vectors::inline v3_3::utils 313 | --------------------------------------------------------------------------------