LibQuicR
stream_buffer.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 <algorithm>
9 #include <any>
10 #include <cstring>
11 #include <optional>
12 #include <span>
13 #include <type_traits>
14 #include <vector>
15 
16 namespace quicr {
17 
26  template<typename T, class Allocator = std::allocator<T>>
28  {
29  using BufferT = std::vector<T, Allocator>;
30 
31  public:
36  StreamBuffer(std::size_t compact_threshold = 4096)
37  : compact_threshold_(compact_threshold)
38  {
39  }
40 
49  template<typename D>
50  void InitAny()
51  {
52  parsed_data_.emplace<D>();
53  }
54 
60  template<typename D>
61  void InitAnyB()
62  {
63  parsed_dataB_.emplace<D>();
64  }
65 
72  template<typename D>
73  void InitAny(uint64_t type)
74  {
75  parsed_data_.emplace<D>();
76  parsed_data_type_ = type;
77  }
78 
89  template<typename D>
90  D& GetAny()
91  {
92  return std::any_cast<D&>(parsed_data_);
93  }
94 
101  template<typename D>
102  D& GetAnyB()
103  {
104  return std::any_cast<D&>(parsed_dataB_);
105  }
106 
111  std::optional<uint64_t> GetAnyType() { return parsed_data_type_; }
112 
117  void SetAnyType(uint64_t type) { parsed_data_type_ = type; }
118 
123  void Clear()
124  {
125  ResetAny();
126  buffer_.clear();
127  read_offset_ = 0;
128  }
129 
131  void ResetAny()
132  {
133  parsed_data_.reset();
134  parsed_dataB_.reset();
135  parsed_data_type_ = std::nullopt;
136  }
137 
139  void ResetAnyB() { parsed_dataB_.reset(); }
140 
146  template<class D>
147  void ResetAnyB()
148  {
149  ResetAnyB();
150  InitAnyB<D>();
151  }
152 
154  bool AnyHasValue() { return parsed_data_.has_value(); }
155 
157  bool AnyHasValueB() { return parsed_dataB_.has_value(); }
158 
160  bool Empty() const noexcept { return read_offset_ >= buffer_.size(); }
161 
163  size_t Size() noexcept { return read_offset_ >= buffer_.size() ? 0 : buffer_.size() - read_offset_; }
164 
169  std::span<const T> Front() noexcept
170  {
171  if (Empty()) {
172  return {};
173  }
174 
175  return { buffer_.data() + read_offset_, 1 };
176  }
177 
186  std::span<const T> Front(std::size_t length) noexcept
187  {
188  if (Empty()) {
189  return {};
190  }
191 
192  const auto logical_size = buffer_.size() - read_offset_;
193  return logical_size < length ? std::span<const T>{}
194  : std::span<const T>{ buffer_.data() + read_offset_, length };
195  }
196 
202  std::span<const T> Data() noexcept
203  {
204  if (Empty()) {
205  return {};
206  }
207 
208  return { buffer_.data() + read_offset_, buffer_.size() - read_offset_ };
209  }
210 
224  BufferT TakeAll()
225  {
226  BufferT taken;
227  if (read_offset_ == 0) {
228  taken = std::move(buffer_);
229  } else {
230  taken.assign(buffer_.begin() + static_cast<std::ptrdiff_t>(read_offset_), buffer_.end());
231  }
232 
233  buffer_.clear();
234  read_offset_ = 0;
235 
236  return taken;
237  }
238 
240  void Pop()
241  {
242  if (Empty()) {
243  return;
244  }
245 
246  PopInternal(1);
247  }
248 
254  void Pop(std::size_t length)
255  {
256  if (length == 0 || Empty()) {
257  return;
258  }
259 
260  PopInternal(length);
261  }
262 
270  bool Available(std::size_t length) const noexcept
271  {
272  return length == 0 || (read_offset_ < buffer_.size() && buffer_.size() - read_offset_ >= length);
273  }
274 
280  void Push(const T& value)
281  {
282  CompactFrontIfNeeded();
283 
284  buffer_.push_back(value);
285  }
286 
292  void Push(T&& value)
293  {
294  CompactFrontIfNeeded();
295 
296  buffer_.push_back(std::move(value));
297  }
298 
305  void Push(std::span<const T> value)
306  {
307  CompactFrontIfNeeded();
308 
309  buffer_.insert(buffer_.end(), value.begin(), value.end());
310  }
311 
318  void PushLengthBytes(std::span<const T> value)
319  {
320  CompactFrontIfNeeded();
321 
322  UintVar len(static_cast<uint64_t>(value.size()));
323  buffer_.insert(buffer_.end(), len.begin(), len.end());
324  buffer_.insert(buffer_.end(), value.begin(), value.end());
325  }
326 
337  std::optional<uint64_t> DecodeUintV(bool pop = true)
338  {
339  const auto uintv = ReadUintV(pop);
340  if (uintv) {
341  return uint64_t(*uintv);
342  }
343  return std::nullopt;
344  }
345 
356  std::optional<UintVar> ReadUintV(bool pop = true)
357  {
358  if (Empty()) {
359  return std::nullopt;
360  }
361 
362  const auto& uv_msb = buffer_[read_offset_];
363  std::size_t uv_len = UintVar::Size(uv_msb);
364 
365  const auto logical_size = buffer_.size() - read_offset_;
366  if (logical_size >= uv_len) {
367  auto val = UintVar(std::span<const T>{ buffer_.data() + read_offset_, uv_len });
368  if (pop) {
369  PopInternal(uv_len);
370  }
371 
372  return val;
373  }
374 
375  return std::nullopt;
376  }
377 
390  std::optional<std::vector<uint8_t>> DecodeBytes()
391  {
392  if (Empty()) {
393  return std::nullopt;
394  }
395 
396  const auto& uv_msb = buffer_[read_offset_];
397  std::size_t uv_len = UintVar::Size(uv_msb);
398 
399  auto logical_size = buffer_.size() - read_offset_;
400  if (logical_size >= uv_len) {
401  auto len = UintVar(std::span<const T>{ buffer_.data() + read_offset_, uv_len });
402  if (logical_size >= uv_len + uint64_t(len)) {
403  PopInternal(uv_len);
404  auto v = std::span<const T>{ buffer_.data() + read_offset_, uint64_t(len) };
405  auto bytes = std::vector<uint8_t>(v.begin(), v.end());
406  PopInternal(uint64_t(len));
407 
408  return bytes;
409  }
410  }
411 
412  return std::nullopt;
413  }
414 
415  private:
416  void CompactFrontIfNeeded()
417  {
418 
419  if (read_offset_ == 0) {
420  return;
421  }
422 
423  const std::size_t logical_size = buffer_.size() - read_offset_;
424 
425  if (read_offset_ < compact_threshold_ && read_offset_ <= logical_size) {
426  return;
427  }
428 
429  if (read_offset_ >= buffer_.size()) {
430  buffer_.clear();
431  read_offset_ = 0;
432  return;
433  }
434 
435  if constexpr (std::is_trivially_copyable_v<T>) {
436  std::memmove(buffer_.data(), buffer_.data() + read_offset_, logical_size * sizeof(T));
437  } else {
438  std::move(buffer_.begin() + static_cast<std::ptrdiff_t>(read_offset_), buffer_.end(), buffer_.begin());
439  }
440 
441  buffer_.resize(logical_size);
442  read_offset_ = 0;
443  }
444 
445  void PopInternal(std::size_t length)
446  {
447  if (read_offset_ >= buffer_.size() || length >= buffer_.size() - read_offset_) {
448  buffer_.clear();
449  read_offset_ = 0;
450  return;
451  }
452 
453  read_offset_ += length;
454  }
455 
456  private:
457  BufferT buffer_;
458  std::any parsed_data_;
459  std::any parsed_dataB_;
460  std::optional<uint64_t> parsed_data_type_;
461  std::size_t read_offset_{ 0 };
462  std::size_t compact_threshold_{ 4096 };
463  };
464 
465 }
Contiguous append-only stream accumulator with a logical read offset and parser scratch state.
Definition: stream_buffer.h:28
void Push(std::span< const T > value)
Appends a copy of a contiguous value range.
Definition: stream_buffer.h:305
std::optional< std::vector< uint8_t > > DecodeBytes()
Definition: stream_buffer.h:390
std::optional< uint64_t > DecodeUintV(bool pop=true)
Definition: stream_buffer.h:337
void PushLengthBytes(std::span< const T > value)
Appends a QUIC variable-length count followed by the supplied values.
Definition: stream_buffer.h:318
void Push(T &&value)
Moves one value to the back of the stream.
Definition: stream_buffer.h:292
void Clear()
Clears unread data and all parser scratch state.
Definition: stream_buffer.h:123
std::span< const T > Front() noexcept
Get the first data byte in stream buffer.
Definition: stream_buffer.h:169
bool Available(std::size_t length) const noexcept
Checks if length bytes are available for front.
Definition: stream_buffer.h:270
StreamBuffer(std::size_t compact_threshold=4096)
Constructs an empty stream buffer.
Definition: stream_buffer.h:36
void ResetAnyB()
Replaces the secondary parser scratch object with a default-constructed D.
Definition: stream_buffer.h:147
void ResetAny()
Clears both parser scratch objects and the optional parser type without changing stream data.
Definition: stream_buffer.h:131
void ResetAnyB()
Clears only the secondary parser scratch object.
Definition: stream_buffer.h:139
bool Empty() const noexcept
Definition: stream_buffer.h:160
bool AnyHasValueB()
Definition: stream_buffer.h:157
void InitAny()
Initialize the parsed data.
Definition: stream_buffer.h:50
std::span< const T > Front(std::size_t length) noexcept
Front length number of data bytes.
Definition: stream_buffer.h:186
void SetAnyType(uint64_t type)
Set the user-defined parsed data type value.
Definition: stream_buffer.h:117
void Pop(std::size_t length)
Consumes up to length unread elements.
Definition: stream_buffer.h:254
std::optional< uint64_t > GetAnyType()
Get the user defined parsed type value.
Definition: stream_buffer.h:111
void InitAnyB()
Initializes the secondary parser scratch object as D.
Definition: stream_buffer.h:61
BufferT TakeAll()
Surrender everything not yet read, leaving the buffer empty.
Definition: stream_buffer.h:224
std::optional< UintVar > ReadUintV(bool pop=true)
Definition: stream_buffer.h:356
void InitAny(uint64_t type)
Initialize the parsed data and type.
Definition: stream_buffer.h:73
bool AnyHasValue()
Definition: stream_buffer.h:154
std::span< const T > Data() noexcept
All readable data bytes in the stream buffer.
Definition: stream_buffer.h:202
D & GetAny()
Get the parsed data.
Definition: stream_buffer.h:90
size_t Size() noexcept
Definition: stream_buffer.h:163
D & GetAnyB()
Gets the secondary parser scratch object.
Definition: stream_buffer.h:102
void Pop()
Consumes one unread element, or does nothing when empty.
Definition: stream_buffer.h:240
void Push(const T &value)
Copies one value to the back of the stream.
Definition: stream_buffer.h:280
Definition: uintvar.h:50
constexpr auto end() const noexcept
Definition: uintvar.h:166
constexpr std::size_t Size() const noexcept
Definition: uintvar.h:153
constexpr auto begin() const noexcept
Definition: uintvar.h:163
Definition: attributes.h:13