From d4b1c5f3abef19cb0efa6499d3ef519673a6dd13 Mon Sep 17 00:00:00 2001 From: Cyril Sharma Date: Thu, 5 Dec 2024 16:55:12 -0800 Subject: [PATCH] Add FixedMapRawView --- BUILD.bazel | 23 +++ CMakeLists.txt | 2 + .../fixed_containers/fixed_map_raw_view.hpp | 164 ++++++++++++++++++ .../fixed_unordered_map_raw_view.hpp | 39 +---- .../fixed_containers/map_entry_raw_view.hpp | 41 +++++ test/fixed_map_raw_view_test.cpp | 87 ++++++++++ 6 files changed, 318 insertions(+), 38 deletions(-) create mode 100644 include/fixed_containers/fixed_map_raw_view.hpp create mode 100644 include/fixed_containers/map_entry_raw_view.hpp create mode 100644 test/fixed_map_raw_view_test.cpp diff --git a/BUILD.bazel b/BUILD.bazel index 11681179..b83f6297 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -347,6 +347,19 @@ cc_library( copts = ["-std=c++20"], ) +cc_library( + name = "fixed_map_raw_view", + hdrs = ["include/fixed_containers/fixed_map_raw_view.hpp",], + includes = includes_config(), + strip_include_prefix = strip_include_prefix_config(), + deps = [ + ":fixed_red_black_tree", + ":map_entry_raw_view", + + ], + copts = ["-std=c++20"], +) + cc_library( name = "wyhash", hdrs = ["include/fixed_containers/wyhash.hpp"], @@ -409,6 +422,15 @@ cc_library( ] ) +cc_library( + name = "fixed_map_raw_view", + hdrs = ["include/fixed_containers/map_entry_raw_view.hpp",], + includes = includes_config(), + strip_include_prefix = strip_include_prefix_config(), + deps = [], + copts = ["-std=c++20"], +) + cc_library( name = "fixed_unordered_map_raw_view", hdrs = ["include/fixed_containers/fixed_unordered_map_raw_view.hpp"], @@ -417,6 +439,7 @@ cc_library( deps = [ ":fixed_doubly_linked_list_raw_view", ":forward_iterator", + ":fixed_map_raw_view" ], copts = ["-std=c++20"], ) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3510fc38..6ccc2539 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,6 +169,8 @@ if(BUILD_TESTS) add_test_dependencies(fixed_list_test) add_executable(fixed_map_test test/fixed_map_test.cpp) add_test_dependencies(fixed_map_test) + add_executable(fixed_map_raw_view_test test/fixed_map_raw_view_test.cpp) + add_test_dependencies(fixed_map_raw_view_test) add_executable(fixed_map_perf_test test/fixed_map_perf_test.cpp) add_test_dependencies(fixed_map_perf_test) add_executable(fixed_red_black_tree_test test/fixed_red_black_tree_test.cpp) diff --git a/include/fixed_containers/fixed_map_raw_view.hpp b/include/fixed_containers/fixed_map_raw_view.hpp new file mode 100644 index 00000000..c11853f2 --- /dev/null +++ b/include/fixed_containers/fixed_map_raw_view.hpp @@ -0,0 +1,164 @@ +#pragma once + +#include "fixed_containers/fixed_red_black_tree_nodes.hpp" +#include "fixed_containers/fixed_red_black_tree_types.hpp" +#include "fixed_containers/fixed_red_black_tree_view.hpp" +#include "fixed_containers/map_entry_raw_view.hpp" + +#include +#include + +namespace fixed_containers +{ + +class FixedMapRawView { + using Compactness = fixed_red_black_tree_detail::RedBlackTreeNodeColorCompactness; + using StorageType = fixed_red_black_tree_detail::RedBlackTreeStorageType; + using NodeIndex = fixed_red_black_tree_detail::NodeIndex; + +public: + class Iterator + { + private: + FixedRedBlackTreeRawView::Iterator base_iterator_; + std::ptrdiff_t value_offset_; + + // We need this constructor to avoid default initializing the base_iterator_. + Iterator(MapEntryRawView map_entry, + const std::byte* ptr, + std::size_t value_size_bytes, + std::size_t max_size_bytes, + Compactness compactness, + StorageType storage_type, + bool end = false) noexcept : + base_iterator_( + ptr, + static_cast(map_entry.value_offset()) + value_size_bytes, + max_size_bytes, compactness, storage_type, end + ), + value_offset_(map_entry.value_offset()) {} + + public: + // Define iterator traits + using value_type = const std::byte*; + using difference_type = std::ptrdiff_t; + using pointer = value_type*; + using const_pointer = const value_type*; + using reference = value_type&; + using const_reference = const value_type&; + using iterator_category = std::forward_iterator_tag; + + // Constructor that initializes the base iterator + Iterator(const std::byte* ptr, + std::size_t key_size_bytes, + std::size_t key_align_bytes, + std::size_t value_size_bytes, + std::size_t value_align_bytes, + std::size_t max_size_bytes, + Compactness compactness, + StorageType storage_type, + bool end = false) noexcept : + Iterator( + MapEntryRawView(ptr, key_size_bytes, key_align_bytes, value_size_bytes, value_align_bytes), + ptr, key_size_bytes, max_size_bytes, + compactness, storage_type, end) {} + + Iterator() noexcept: base_iterator_(), value_offset_(0) {} + Iterator(const Iterator&) noexcept = default; + Iterator(Iterator&&) noexcept = default; + Iterator& operator=(const Iterator&) noexcept = default; + Iterator& operator=(Iterator&&) noexcept = default; + + Iterator& operator++() + { + ++base_iterator_; + return *this; + } + + Iterator operator++(int) & noexcept + { + Iterator tmp = *this; + ++base_iterator_; + return tmp; + } + + const_reference operator*() const + { + return *base_iterator_; + } + + const_pointer operator->() const + { + return base_iterator_.operator->(); + } + + bool operator==(const Iterator& other) const + { + return base_iterator_ == other.base_iterator_; + } + + bool operator!=(const Iterator& other) const + { + return !(*this == other); + } + + [[nodiscard]] const std::byte* key() const { return *base_iterator_; } + + [[nodiscard]] const std::byte* value() const { return std::next(*base_iterator_, value_offset_); } + + [[nodiscard]] std::size_t size() const { return base_iterator_.size(); } + }; + +private: + const std::byte* tree_ptr_; + const std::size_t key_size_bytes_; + const std::size_t key_align_bytes_; + const std::size_t value_size_bytes_; + const std::size_t value_align_bytes_; + const std::size_t max_size_bytes_; + const Compactness compactness_; + const StorageType storage_type_; + +public: + FixedMapRawView(const void* tree_ptr, + std::size_t key_size_bytes, + std::size_t key_align_bytes, + std::size_t value_size_bytes, + std::size_t value_align_bytes, + std::size_t max_size_bytes, + Compactness compactness, + StorageType storage_type): + tree_ptr_{reinterpret_cast(tree_ptr)} + , key_size_bytes_{key_size_bytes} + , key_align_bytes_{key_align_bytes} + , value_size_bytes_{value_size_bytes} + , value_align_bytes_{value_align_bytes} + , max_size_bytes_{max_size_bytes} + , compactness_{compactness} + , storage_type_{storage_type} + { + } + + [[nodiscard]] Iterator begin() const + { + return Iterator( + tree_ptr_, + key_size_bytes_, key_align_bytes_, value_size_bytes_, value_align_bytes_, + max_size_bytes_, compactness_, storage_type_ + ); + } + + [[nodiscard]] Iterator end() const + { + return Iterator( + tree_ptr_, + key_size_bytes_, key_align_bytes_, value_size_bytes_, value_align_bytes_, + max_size_bytes_, compactness_, storage_type_, + true + ); + } + + [[nodiscard]] std::size_t size() const { return end().size(); } +}; + +} // namespace fixed_containers diff --git a/include/fixed_containers/fixed_unordered_map_raw_view.hpp b/include/fixed_containers/fixed_unordered_map_raw_view.hpp index 91a28ffb..a0dfad8f 100644 --- a/include/fixed_containers/fixed_unordered_map_raw_view.hpp +++ b/include/fixed_containers/fixed_unordered_map_raw_view.hpp @@ -2,49 +2,12 @@ #include "fixed_containers/fixed_doubly_linked_list_raw_view.hpp" #include "fixed_containers/forward_iterator.hpp" - +#include "fixed_containers/map_entry_raw_view.hpp" #include namespace fixed_containers { -class MapEntryRawView -{ -private: - const std::byte* base_ptr_; - const std::ptrdiff_t value_offs_; - -public: - static constexpr std::ptrdiff_t get_value_offs(std::size_t key_size, - std::size_t /*key_alignment*/, - std::size_t /*value_size*/, - std::size_t value_alignment) - { - std::size_t value_offs = key_size; - // align the value start addr to the correct alignment - if (value_offs % value_alignment != 0) - { - value_offs += value_alignment - value_offs % value_alignment; - } - return static_cast(value_offs); - } - -public: - MapEntryRawView(const void* ptr, - std::size_t key_size, - std::size_t key_alignment, - std::size_t value_size, - std::size_t value_alignment) - : base_ptr_{reinterpret_cast(ptr)} - , value_offs_{get_value_offs(key_size, key_alignment, value_size, value_alignment)} - { - } - - [[nodiscard]] const std::byte* key() const { return base_ptr_; } - - [[nodiscard]] const std::byte* value() const { return std::next(base_ptr_, value_offs_); } -}; - class FixedUnorderedMapRawView { private: diff --git a/include/fixed_containers/map_entry_raw_view.hpp b/include/fixed_containers/map_entry_raw_view.hpp new file mode 100644 index 00000000..147f355a --- /dev/null +++ b/include/fixed_containers/map_entry_raw_view.hpp @@ -0,0 +1,41 @@ +#include +class MapEntryRawView +{ +private: + const std::byte* base_ptr_; + const std::ptrdiff_t value_offs_; + +public: + static constexpr std::ptrdiff_t get_value_offs(std::size_t key_size, + std::size_t /*key_alignment*/, + std::size_t /*value_size*/, + std::size_t value_alignment) + { + std::size_t value_offs = key_size; + // align the value start addr to the correct alignment + if (value_offs % value_alignment != 0) + { + value_offs += value_alignment - value_offs % value_alignment; + } + return static_cast(value_offs); + } + +public: + MapEntryRawView(const void* ptr, + std::size_t key_size, + std::size_t key_alignment, + std::size_t value_size, + std::size_t value_alignment) + : base_ptr_{reinterpret_cast(ptr)} + , value_offs_{get_value_offs(key_size, key_alignment, value_size, value_alignment)} + { + } + + [[nodiscard]] const std::byte* key() const { return base_ptr_; } + + [[nodiscard]] const std::byte* value() const { return std::next(base_ptr_, value_offs_); } + + [[nodiscard]] std::ptrdiff_t value_offset() const { return value_offs_; } + + +}; diff --git a/test/fixed_map_raw_view_test.cpp b/test/fixed_map_raw_view_test.cpp new file mode 100644 index 00000000..10acaea2 --- /dev/null +++ b/test/fixed_map_raw_view_test.cpp @@ -0,0 +1,87 @@ +#include "fixed_containers/fixed_map_raw_view.hpp" + +#include "mock_testing_types.hpp" +#include "test_utilities_common.hpp" +#include "fixed_containers/fixed_map.hpp" +#include + +#include +#include +#include +#include +#include + +namespace fixed_containers +{ +namespace +{ + +static_assert(std::forward_iterator); +static_assert(std::ranges::forward_range); + +template +T get_from_ptr(const std::byte* ptr) +{ + return *reinterpret_cast(ptr); +} + +template +void test_and_increment(auto& map_it, auto& view_it) +{ + EXPECT_EQ(map_it->first, get_from_ptr(view_it.key())); + EXPECT_EQ(map_it->second, get_from_ptr(view_it.value())); + ++map_it; + ++view_it; +} + +template +FixedMapRawView get_view_of_map(const Map& map) +{ + return FixedMapRawView(&map, + sizeof(typename Map::key_type), + alignof(typename Map::key_type), + sizeof(typename Map::mapped_type), + alignof(typename Map::mapped_type), + map.max_size(), + // Ideally we'd grab these from the map too. + fixed_red_black_tree_detail::RedBlackTreeNodeColorCompactness::EMBEDDED_COLOR, + fixed_red_black_tree_detail::RedBlackTreeStorageType::FIXED_INDEX_POOL); +} + + +TEST(FixedMapRawView, IntIntMap) +{ + const auto map = make_fixed_map({{1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 0}}); + const FixedMapRawView view = get_view_of_map(map); + EXPECT_EQ(map.size(), view.size()); + auto map_it = map.begin(); + FixedMapRawView::Iterator view_it = view.begin(); + for (std::size_t i = 0; i < map.size(); i++) + { + test_and_increment(map_it, view_it); + } + EXPECT_EQ(map_it, map.end()); + EXPECT_EQ(view_it, view.end()); +} + +TEST(FixedMapRawView, CharCharMap) +{ + FixedMap map{}; + map['a'] = 'A'; + map['b'] = 'B'; + map['c'] = 'C'; + map['z'] = 'Z'; + + const FixedMapRawView view = get_view_of_map(map); + EXPECT_EQ(map.size(), view.size()); + auto map_it = map.begin(); + auto view_it = view.begin(); + for (std::size_t i = 0; i < map.size(); i++) + { + test_and_increment(map_it, view_it); + } + EXPECT_EQ(map_it, map.end()); + EXPECT_EQ(view_it, view.end()); +} +} +} // namespace fixed_containers