Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Shared Memory for incoming data. #350

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions benchmark/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ add_executable(quicr_benchmark
time_queue.cpp
uintvar.cpp
hash.cpp
data_storage.cpp
)

target_link_libraries(quicr_benchmark PRIVATE quicr benchmark::benchmark_main)
Expand Down
32 changes: 32 additions & 0 deletions benchmark/data_storage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// SPDX-FileCopyrightText: Copyright (c) 2024 Cisco Systems
// SPDX-License-Identifier: BSD-2-Clause

#include <quicr/data_storage.h>
#include <quicr/detail/uintvar.h>

#include <benchmark/benchmark.h>

static void
DataStorage_Construct(benchmark::State& state)
{
for ([[maybe_unused]] const auto& _ : state) {
auto buffer = quicr::DataStorage<>::Create();
benchmark::DoNotOptimize(buffer);
benchmark::ClobberMemory();
}
}

static void
DataStorage_Push(benchmark::State& state)
{
auto buffer = quicr::DataStorage<>::Create();
uint64_t value = 0;
auto bytes = quicr::AsBytes(value);

for ([[maybe_unused]] const auto& _ : state) {
buffer->Push(bytes);
}
}

BENCHMARK(DataStorage_Construct);
BENCHMARK(DataStorage_Push);
132 changes: 132 additions & 0 deletions include/quicr/data_storage.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// SPDX-FileCopyrightText: Copyright (c) 2024 Cisco Systems
// SPDX-License-Identifier: BSD-2-Clause

#pragma once

#include "detail/span.h"

#include <cstdint>
#include <memory>
#include <optional>
#include <vector>

namespace quicr {
template<class T, std::enable_if_t<std::is_standard_layout_v<T>, bool> = true>
inline Span<const uint8_t> AsBytes(const T& value)
{
return Span{ reinterpret_cast<const std::uint8_t*>(&value), sizeof(T) };
}

template<>
inline Span<const uint8_t> AsBytes<std::string>(const std::string& value)
{
return Span{ reinterpret_cast<const std::uint8_t*>(value.data()), value.size() };
}

template<class Allocator = std::allocator<std::uint8_t>>
class DataStorage
{
template<class It, class SpanIt>
class IteratorImpl
{
public:
// NOLINTBEGIN(readability-identifier-naming)
using value_type = std::uint8_t;
using difference_type = std::ptrdiff_t;
using pointer = std::uint8_t*;
using reference = std::uint8_t&;
using iterator_category = std::forward_iterator_tag;
// NOLINTEND(readability-identifier-naming)

constexpr IteratorImpl() noexcept = default;
constexpr IteratorImpl(const It& it, const It& end_it) noexcept
: it_(it)
, end_it_(end_it)
, span_it_(it == end_it_ ? std::nullopt : std::optional<SpanIt>{ (*it_)->begin() })
{
}

constexpr IteratorImpl(const IteratorImpl&) = default;

IteratorImpl& operator=(const IteratorImpl&) = default;

constexpr const std::uint8_t& operator*() const noexcept { return **span_it_; }
constexpr const std::uint8_t* operator->() const noexcept { return span_it_->operator->(); }

constexpr IteratorImpl& operator++() noexcept
{
if (++*span_it_ == (*it_)->end()) {
span_it_ = ++it_ == end_it_ ? std::nullopt : std::optional<SpanIt>{ (*it_)->begin() };
}

return *this;
}

friend constexpr bool operator==(const IteratorImpl& lhs, const IteratorImpl& rhs)
{
return lhs.it_ == rhs.it_ && lhs.span_it_ == rhs.span_it_;
}

friend constexpr bool operator!=(const IteratorImpl& lhs, const IteratorImpl& rhs) { return !(lhs == rhs); }

private:
It it_;
It end_it_;
std::optional<SpanIt> span_it_;
};

using SliceType = std::shared_ptr<std::vector<uint8_t, Allocator>>;
using BufferType = std::vector<SliceType>;

DataStorage() = default;
DataStorage(SliceType slice)
: buffer_{ std::move(slice) }
{
}

public:
static std::shared_ptr<DataStorage> Create() noexcept
{
return std::shared_ptr<DataStorage>(new DataStorage());
}

static std::shared_ptr<DataStorage> Create(SliceType slice) noexcept
{
return std::shared_ptr<DataStorage>(new DataStorage(std::move(slice)));
}

bool Empty() const noexcept { return buffer_.empty(); }
const SliceType& First() const noexcept { return buffer_.front(); }
const SliceType& Last() const noexcept { return buffer_.back(); }

void Push(Span<const uint8_t> bytes)
{
auto slice = std::make_shared<typename SliceType::element_type>();
slice->assign(bytes.begin(), bytes.end());

buffer_.push_back(std::move(slice));
}

void Push(SliceType slice) { buffer_.push_back(std::move(slice)); }

friend DataStorage& operator<<(DataStorage& buffer, Span<const uint8_t> value)
{
buffer.Push(value);
return buffer;
}

// NOLINTBEGIN(readability-identifier-naming)
using iterator = IteratorImpl<typename BufferType::iterator, typename SliceType::element_type::iterator>;
using const_iterator =
IteratorImpl<typename BufferType::const_iterator, typename SliceType::element_type::iterator>;

iterator begin() noexcept { return iterator(buffer_.begin(), buffer_.end()); }
iterator end() noexcept { return iterator(buffer_.end(), buffer_.end()); }
const_iterator begin() const noexcept { return const_iterator(buffer_.begin(), buffer_.end()); }
const_iterator end() const noexcept { return const_iterator(buffer_.end(), buffer_.end()); }
// NOLINTEND(readability-identifier-naming)

private:
BufferType buffer_;
};
}
1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_executable(quicr_test
client.cpp
tick_service.cpp
track_namespace.cpp
data_storage.cpp
)
target_include_directories(quicr_test PRIVATE ${PROJECT_SOURCE_DIR}/src)

Expand Down
11 changes: 0 additions & 11 deletions test/cache_buffer.cpp

This file was deleted.

54 changes: 54 additions & 0 deletions test/data_storage.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-FileCopyrightText: Copyright (c) 2024 Cisco Systems
// SPDX-License-Identifier: BSD-2-Clause

#include <doctest/doctest.h>

#include "quicr/data_storage.h"

TEST_CASE("DataStorage Construct")
{
CHECK_NOTHROW(quicr::DataStorage<>::Create());

auto storage = quicr::DataStorage<>::Create();
CHECK_NOTHROW(quicr::DataStorage<>::Create());
}

TEST_CASE("DataStorage Push")
{
auto buffer = quicr::DataStorage<>::Create();

uint64_t value = 0;
auto bytes = quicr::AsBytes(value);
CHECK_NOTHROW(buffer->Push(bytes));
}

TEST_CASE("DataStorage Read")
{
auto buffer = quicr::DataStorage<>::Create();

uint64_t value = 0x0102030405060708;
CHECK_NOTHROW(buffer->Push(quicr::AsBytes(value)));

std::vector<uint8_t> v(buffer->begin(), buffer->end());

CHECK_EQ(v.size(), 8);
CHECK_EQ(v, std::vector<uint8_t>{ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01 });
}

TEST_CASE("DataStorage Multiples")
{
auto buffer = quicr::DataStorage<>::Create();

std::string s1 = "one";
std::string s2 = " two";
std::string s3 = " three";

buffer->Push(quicr::AsBytes(s1));
buffer->Push(quicr::AsBytes(s2));
buffer->Push(quicr::AsBytes(s3));

std::vector<uint8_t> v(buffer->begin(), buffer->end());

CHECK_EQ(v.size(), s1.size() + s2.size() + s3.size());
CHECK_EQ(v.at(5), 'w');
}
Loading