LibQuicR
data_storage.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (c) 2024 Cisco Systems
2 // SPDX-License-Identifier: BSD-2-Clause
3 
4 #pragma once
5 
7 
8 #include <cstdint>
9 #include <deque>
10 #include <memory>
11 #include <optional>
12 #include <span>
13 #include <vector>
14 
15 namespace quicr {
16 
23  template<class Allocator = std::allocator<std::uint8_t>>
24  class DataStorage : public std::enable_shared_from_this<DataStorage<Allocator>>
25  {
26  template<class It, class SpanIt>
27  class Iterator
28  {
29  public:
30  // NOLINTBEGIN(readability-identifier-naming)
32  using value_type = std::uint8_t;
33 
35  using difference_type = std::ptrdiff_t;
36 
38  using pointer = std::uint8_t*;
39 
41  using reference = std::uint8_t&;
42 
44  using iterator_category = std::bidirectional_iterator_tag;
45  // NOLINTEND(readability-identifier-naming)
46 
48  constexpr Iterator() noexcept = default;
49 
56  constexpr Iterator(const It& it, const It& end_it) noexcept
57  : it_(it)
58  , end_it_(end_it)
59  , span_it_(it == end_it_ ? std::nullopt : std::optional<SpanIt>{ (*it_)->begin() })
60  {
61  }
62 
64  constexpr Iterator(const Iterator& other) = default;
65 
67  Iterator& operator=(const Iterator& other) = default;
68 
70  constexpr const std::uint8_t& operator*() const noexcept { return **span_it_; }
71 
73  constexpr const std::uint8_t* operator->() const noexcept { return span_it_->operator->(); }
74 
76  constexpr Iterator& operator++() noexcept
77  {
78  if (!span_it_.has_value() || ++*span_it_ == (*it_)->end()) {
79  span_it_ = ++it_ == end_it_ ? std::nullopt : std::optional<SpanIt>{ (*it_)->begin() };
80  }
81 
82  return *this;
83  }
84 
86  constexpr Iterator& operator--() noexcept
87  {
88  if (!span_it_.has_value() || --*span_it_ == std::prev((*it_)->begin())) {
89  span_it_ = std::optional<SpanIt>{ std::prev((*--it_)->end()) };
90  }
91 
92  return *this;
93  }
94 
96  friend constexpr bool operator==(const Iterator& lhs, const Iterator& rhs)
97  {
98  return lhs.it_ == rhs.it_ && lhs.span_it_ == rhs.span_it_;
99  }
100 
102  friend constexpr bool operator!=(const Iterator& lhs, const Iterator& rhs) { return !(lhs == rhs); }
103 
104  private:
105  It it_;
106  It end_it_;
107  std::optional<SpanIt> span_it_;
108  };
109 
110  protected:
112  using SliceType = std::shared_ptr<std::vector<uint8_t, Allocator>>;
113 
115  using BufferType = std::deque<SliceType>;
116 
118  DataStorage() = default;
119 
126  : buffer_{ std::move(slice) }
127  {
128  }
129 
130  public:
132  static std::shared_ptr<DataStorage> Create() noexcept
133  {
134  return std::shared_ptr<DataStorage>(new DataStorage());
135  }
136 
142  static std::shared_ptr<DataStorage> Create(SliceType slice) noexcept
143  {
144  return std::shared_ptr<DataStorage>(new DataStorage(std::move(slice)));
145  }
146 
148  bool Empty() const noexcept { return buffer_.empty(); }
149 
154  const SliceType& First() const noexcept { return *(this->begin()); }
155 
160  const SliceType& Last() const noexcept { return *std::prev(this->end()); }
161 
166  const SliceType GetLast() const noexcept { return *std::prev(this->end()); }
167 
174  void Push(std::span<const uint8_t> bytes)
175  {
176  auto slice = std::make_shared<typename SliceType::element_type>();
177  slice->assign(bytes.begin(), bytes.end());
178 
179  buffer_.push_back(std::move(slice));
180  }
181 
187  void Push(SliceType slice) { buffer_.push_back(std::move(slice)); }
188 
195  friend DataStorage& operator<<(DataStorage& buffer, std::span<const uint8_t> value)
196  {
197  buffer.Push(value);
198  return buffer;
199  }
200 
201  // NOLINTBEGIN(readability-identifier-naming)
203  using iterator = Iterator<typename BufferType::iterator, typename SliceType::element_type::iterator>;
204 
207  Iterator<typename BufferType::const_iterator, typename SliceType::element_type::const_iterator>;
208 
210  std::size_t size() const noexcept
211  {
212  std::size_t value = 0;
213  for (const auto& buf : buffer_) {
214  value += buf->size();
215  }
216  return value;
217  }
218 
226  std::size_t EraseFront(std::size_t len) noexcept
227  {
228  for (const auto& s : buffer_) {
229  if (s == nullptr) {
230  return 0;
231  }
232 
233  const auto size = s->size();
234  if (len >= size) {
235  buffer_.pop_front();
236  len -= size;
237  } else {
238  return len;
239  }
240  }
241 
242  return 0;
243  }
244 
246  iterator begin() noexcept { return iterator(buffer_.begin(), buffer_.end()); }
247 
249  iterator end() noexcept { return iterator(buffer_.end(), buffer_.end()); }
250 
252  const_iterator begin() const noexcept { return const_iterator(buffer_.begin(), buffer_.end()); }
253 
255  const_iterator end() const noexcept { return const_iterator(buffer_.end(), buffer_.end()); }
256 
258  const_iterator cbegin() const noexcept { return const_iterator(buffer_.begin(), buffer_.end()); }
259 
261  const_iterator cend() const noexcept { return const_iterator(buffer_.end(), buffer_.end()); }
262  // NOLINTEND(readability-identifier-naming)
263 
264  private:
265  BufferType buffer_;
266  };
267 
274  {
275  public:
284  DataStorageDynView(std::shared_ptr<DataStorage<>> storage,
285  std::size_t start_pos = 0,
286  std::optional<std::size_t> end_pos = std::nullopt)
287  : storage_(std::move(storage))
288  , start_pos_(start_pos)
289  , end_pos_(end_pos)
290  {
291  }
292 
294  DataStorageDynView(const DataStorageDynView& other) = default;
295 
298 
305 
308 
317  DataStorageDynView Subspan(std::size_t start_pos,
318  std::optional<std::size_t> end_pos = std::nullopt) const noexcept
319  {
320  if (not end_pos.has_value() || *end_pos > storage_->size()) {
321  end_pos = storage_->size();
322  }
323 
324  return DataStorageDynView(storage_, start_pos, *end_pos);
325  }
326 
327  // NOLINTBEGIN(readability-identifier-naming)
329  std::size_t size() const noexcept { return (end_pos_.has_value() ? *end_pos_ : storage_->size()) - start_pos_; }
330 
332  DataStorage<>::iterator begin() noexcept { return std::next(storage_->begin(), start_pos_); }
333 
335  DataStorage<>::iterator end() noexcept { return std::next(this->begin(), size()); }
336 
338  DataStorage<>::const_iterator begin() const noexcept { return std::next(storage_->cbegin(), start_pos_); }
339 
341  DataStorage<>::const_iterator end() const noexcept { return std::next(this->begin(), size()); }
342  // NOLINTEND(readability-identifier-naming)
343 
344  private:
345  std::shared_ptr<DataStorage<>> storage_;
346  std::size_t start_pos_ = 0;
347  std::optional<std::size_t> end_pos_;
348  };
349 }
Non-owning-position view that shares ownership of a DataStorage<>.
Definition: data_storage.h:274
DataStorageDynView & operator=(DataStorageDynView &&other)=default
Moves the view.
std::size_t size() const noexcept
Definition: data_storage.h:329
DataStorageDynView(DataStorageDynView &&other)=default
Moves the view.
DataStorageDynView Subspan(std::size_t start_pos, std::optional< std::size_t > end_pos=std::nullopt) const noexcept
Creates another absolute view of the same storage.
Definition: data_storage.h:317
DataStorage ::iterator begin() noexcept
Definition: data_storage.h:332
DataStorage ::const_iterator end() const noexcept
Definition: data_storage.h:341
DataStorage ::iterator end() noexcept
Definition: data_storage.h:335
DataStorageDynView(std::shared_ptr< DataStorage<>> storage, std::size_t start_pos=0, std::optional< std::size_t > end_pos=std::nullopt)
Constructs a view over an absolute half-open range.
Definition: data_storage.h:284
DataStorageDynView(const DataStorageDynView &other)=default
Copies the view and shares ownership of its storage.
DataStorage ::const_iterator begin() const noexcept
Definition: data_storage.h:338
DataStorageDynView & operator=(const DataStorageDynView &other)=default
Copies the view and shares ownership of its storage.
Shared, segmented byte storage with a flattened bidirectional iterator.
Definition: data_storage.h:25
static std::shared_ptr< DataStorage > Create(SliceType slice) noexcept
Definition: data_storage.h:142
std::shared_ptr< std::vector< uint8_t, Allocator > > SliceType
Shared ownership of one contiguous byte slice.
Definition: data_storage.h:112
const SliceType GetLast() const noexcept
Definition: data_storage.h:166
std::size_t size() const noexcept
Definition: data_storage.h:210
void Push(SliceType slice)
Appends a shared slice without copying its bytes.
Definition: data_storage.h:187
const_iterator cbegin() const noexcept
Definition: data_storage.h:258
const SliceType & Last() const noexcept
Definition: data_storage.h:160
void Push(std::span< const uint8_t > bytes)
Copies bytes into a new owned slice at the back.
Definition: data_storage.h:174
DataStorage()=default
Constructs empty storage; use Create() from callers.
iterator end() noexcept
Definition: data_storage.h:249
const SliceType & First() const noexcept
Definition: data_storage.h:154
Iterator< typename BufferType::iterator, typename SliceType::element_type::iterator > iterator
Mutable flattened byte iterator type.
Definition: data_storage.h:203
std::deque< SliceType > BufferType
Sequence of shared slices.
Definition: data_storage.h:115
friend DataStorage & operator<<(DataStorage &buffer, std::span< const uint8_t > value)
Appends a copied byte span.
Definition: data_storage.h:195
Iterator< typename BufferType::const_iterator, typename SliceType::element_type::const_iterator > const_iterator
Read-only flattened byte iterator type.
Definition: data_storage.h:207
std::size_t EraseFront(std::size_t len) noexcept
Removes complete slices from the front whose cumulative size does not exceed len.
Definition: data_storage.h:226
const_iterator end() const noexcept
Definition: data_storage.h:255
const_iterator cend() const noexcept
Definition: data_storage.h:261
iterator begin() noexcept
Definition: data_storage.h:246
bool Empty() const noexcept
Definition: data_storage.h:148
const_iterator begin() const noexcept
Definition: data_storage.h:252
static std::shared_ptr< DataStorage > Create() noexcept
Definition: data_storage.h:132
DataStorage(SliceType slice)
Constructs storage containing one shared slice.
Definition: data_storage.h:125
Definition: attributes.h:13