Skip to content

Commit

Permalink
Introduce FixedDequeRawView
Browse files Browse the repository at this point in the history
  • Loading branch information
Cyril Sharma authored and alexkaratarakis committed Nov 15, 2024
1 parent eb39217 commit ea88f2f
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 1 deletion.
29 changes: 28 additions & 1 deletion BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,20 @@ cc_library(
copts = ["-std=c++20"],
)

cc_library(
name = "fixed_deque_raw_view",
hdrs = ["include/fixed_containers/fixed_deque_raw_view.hpp"],
includes = includes_config(),
strip_include_prefix = strip_include_prefix_config(),
deps = [
":fixed_deque",
":forward_iterator",
":integer_range",
],
copts = ["-std=c++20"],
)


cc_library(
name = "fixed_doubly_linked_list",
hdrs = ["include/fixed_containers/fixed_doubly_linked_list.hpp"],
Expand Down Expand Up @@ -432,7 +446,6 @@ cc_library(
],
copts = ["-std=c++20"],
)

cc_library(
name = "fixed_queue",
hdrs = ["include/fixed_containers/fixed_queue.hpp"],
Expand Down Expand Up @@ -1184,6 +1197,20 @@ cc_test(
copts = ["-std=c++20"],
)

cc_test(
name = "fixed_deque_raw_view_test",
srcs = ["test/fixed_deque_raw_view_test.cpp"],
deps = [
":fixed_deque",
":fixed_deque_raw_view",
":mock_testing_types",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
],
copts = ["-std=c++20"],
)


cc_test(
name = "fixed_doubly_linked_list_test",
srcs = ["test/fixed_doubly_linked_list_test.cpp"],
Expand Down
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ if(BUILD_TESTS)
add_executable(fixed_circular_queue_test test/fixed_circular_queue_test.cpp)
add_test_dependencies(fixed_circular_queue_test)
add_executable(fixed_deque_test test/fixed_deque_test.cpp)
add_executable(fixed_deque_raw_view_test test/fixed_deque_raw_view_test.cpp)
add_test_dependencies(fixed_deque_raw_view_test)
add_test_dependencies(fixed_deque_test)
add_executable(fixed_doubly_linked_list_test test/fixed_doubly_linked_list_test.cpp)
add_test_dependencies(fixed_doubly_linked_list_test)
Expand Down
117 changes: 117 additions & 0 deletions include/fixed_containers/fixed_deque_raw_view.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#pragma once

#include "fixed_containers/fixed_deque.hpp"
#include "fixed_containers/forward_iterator.hpp"
#include "fixed_containers/integer_range.hpp"

#include <cstddef>

namespace fixed_containers
{

template <typename IndexType = std::size_t>
class FixedDequeRawView
{
public:
class ReferenceProvider
{
friend class FixedDequeRawView;

private:
const FixedDequeRawView* parent_;

IndexType current_idx_;

explicit constexpr ReferenceProvider(const FixedDequeRawView* parent,
const IndexType idx) noexcept
: parent_{parent}
, current_idx_{idx}
{
}

public:
constexpr ReferenceProvider() noexcept
: parent_{nullptr}
, current_idx_{0}
{
}

constexpr void advance() noexcept { ++current_idx_; }

[[nodiscard]] constexpr const std::byte* get() const noexcept
{
return parent_->value_at(current_idx_);
}

constexpr bool operator==(const ReferenceProvider& other) const noexcept = default;
};

private:
const std::byte* data_ptr_;
std::size_t elem_size_bytes_;
std::size_t elem_align_bytes_;
std::size_t max_elem_count_;

public:
using Iterator =
ForwardIterator<ReferenceProvider, ReferenceProvider, IteratorConstness::CONSTANT_ITERATOR>;
using iterator = Iterator;
using const_iterator = iterator;

FixedDequeRawView(const void* data_ptr,
std::size_t elem_size_bytes,
std::size_t elem_align_bytes,
std::size_t max_elem_count)
: data_ptr_{static_cast<const std::byte*>(data_ptr)}
, elem_size_bytes_{elem_size_bytes}
, elem_align_bytes_{elem_align_bytes}
, max_elem_count_{max_elem_count}
{
}

[[nodiscard]] Iterator begin() const { return Iterator{ReferenceProvider{this, 0}}; }

[[nodiscard]] Iterator end() const
{
auto stats = start_and_distance();
return Iterator{ReferenceProvider{this, stats.distance}};
}

[[nodiscard]] StartingIntegerAndDistance start_and_distance() const
{
// The bookkeeping fields are stored after the data in fixed_deque
return *reinterpret_cast<const StartingIntegerAndDistance*>(
std::next(data_ptr_, value_storage_size()));
}

[[nodiscard]] size_t size() const { return start_and_distance().distance; }

public:
[[nodiscard]] constexpr const std::byte* value_at(IndexType index) const noexcept
{
auto stats = start_and_distance();
auto starting_offset =
fixed_containers::fixed_deque_detail::FIXED_DEQUE_STARTING_OFFSET % max_elem_count_;
auto real_index =
(stats.start + index - starting_offset + max_elem_count_) % max_elem_count_;
return std::next(value_storage_start(),
static_cast<std::ptrdiff_t>(elem_size_bytes_ * real_index));
}

[[nodiscard]] constexpr const std::byte* value_storage_start() const noexcept
{
return data_ptr_;
}

[[nodiscard]] constexpr std::ptrdiff_t value_storage_size() const noexcept
{
auto member_alignment = alignof(StartingIntegerAndDistance);
std::size_t raw_size = max_elem_count_ * elem_size_bytes_;
if (raw_size % member_alignment != 0)
{
raw_size += member_alignment - (raw_size % member_alignment);
}
return static_cast<std::ptrdiff_t>(raw_size);
}
};
} // namespace fixed_containers
95 changes: 95 additions & 0 deletions test/fixed_deque_raw_view_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include "fixed_containers/fixed_deque_raw_view.hpp"

#include "mock_testing_types.hpp"

#include "fixed_containers/fixed_deque.hpp"

#include <gtest/gtest.h>

#include <cstddef>

namespace fixed_containers
{
namespace
{

template <typename T>
T get_from_ptr(const std::byte* ptr)
{
return *reinterpret_cast<const T*>(ptr);
}

template <typename Elem>
void test_and_increment(auto& dq_it, auto& view_it)
{
EXPECT_EQ(*dq_it, get_from_ptr<Elem>(*view_it));
++dq_it;
++view_it;
}
} // namespace

TEST(FixedDequeRawView, IntDeque)
{
auto deque = make_fixed_deque<int>({1, 2, 3, 5, 8});
EXPECT_EQ(sizeof(deque), 40);
const FixedDequeRawView view{&deque, sizeof(int), alignof(int), deque.max_size()};
auto stats = view.start_and_distance();
EXPECT_EQ(stats.start, deque.IMPLEMENTATION_DETAIL_DO_NOT_USE_starting_index_and_size_.start);
EXPECT_EQ(deque.size(), view.size());
EXPECT_EQ(reinterpret_cast<char*>(&deque),
reinterpret_cast<char*>(&deque.IMPLEMENTATION_DETAIL_DO_NOT_USE_array_));
char* member =
reinterpret_cast<char*>(&deque.IMPLEMENTATION_DETAIL_DO_NOT_USE_starting_index_and_size_);
char* array = reinterpret_cast<char*>(&deque.IMPLEMENTATION_DETAIL_DO_NOT_USE_array_);
EXPECT_EQ(member - array, view.value_storage_size());
auto dq_it = deque.begin();
auto view_it = view.begin();
for (std::size_t i = 0; i < deque.size(); i++)
{
test_and_increment<int>(dq_it, view_it);
}
EXPECT_EQ(dq_it, deque.end());
EXPECT_EQ(view_it, view.end());
}

TEST(FixedDequeRawView, StructDeque)
{
FixedDeque<MockAligned64, 10> deque{};
deque.push_back({1});
deque.push_back({2});
deque.push_back({3});
deque.push_back({4});
deque.push_front({5});
deque.push_front({6});
deque.push_front({7});
deque.push_front({8});
const FixedDequeRawView view{
&deque, sizeof(MockAligned64), alignof(MockAligned64), deque.max_size()};
auto stats = view.start_and_distance();
EXPECT_EQ(stats.start, deque.IMPLEMENTATION_DETAIL_DO_NOT_USE_starting_index_and_size_.start);
EXPECT_EQ(deque.size(), view.size());

// Ensure data pointer starts at the beginning of the struct.
EXPECT_EQ(reinterpret_cast<char*>(&deque),
reinterpret_cast<char*>(&deque.IMPLEMENTATION_DETAIL_DO_NOT_USE_array_));

// The deque is a struct with two members, an array and another bookkeeping struct.
// value_storage_size() is needed purely to compute where this bookkeeping struct is.
// This is potentially error prone as there may be padding in between the data portion and the
// bookkeeping struct.
char* member =
reinterpret_cast<char*>(&deque.IMPLEMENTATION_DETAIL_DO_NOT_USE_starting_index_and_size_);
char* array = reinterpret_cast<char*>(&deque.IMPLEMENTATION_DETAIL_DO_NOT_USE_array_);
EXPECT_EQ(member - array, view.value_storage_size());

auto dq_it = deque.begin();
auto view_it = view.begin();
for (std::size_t i = 0; i < deque.size(); i++)
{
test_and_increment<int>(dq_it, view_it);
}
EXPECT_EQ(dq_it, deque.end());
EXPECT_EQ(view_it, view.end());
}

} // namespace fixed_containers

0 comments on commit ea88f2f

Please sign in to comment.