LibQuicR
message.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (c) 2026 Cisco Systems
2 // SPDX-License-Identifier: BSD-2-Clause
3 
4 #pragma once
5 
9 
10 #include <cstdint>
11 #include <memory>
12 #include <span>
13 #include <vector>
14 
15 namespace quicr::messages {
22  class Message
23  {
24  public:
30  Message(std::shared_ptr<std::vector<std::uint8_t>> buffer = std::make_shared<std::vector<std::uint8_t>>())
31  : _bytes(std::move(buffer))
32  {
33  if (_bytes == nullptr) {
34  throw std::invalid_argument("messages::Message buffer cannot be nullptr");
35  }
36  }
37 
42  constexpr bool HasPrependedLength() const noexcept { return _length_bytes_offset.has_value(); }
43 
54  template<typename T>
55  requires std::is_enum_v<T> || std::is_integral_v<T>
56  inline Message& PrependType(T type)
57  {
58  UintVar type_bytes(static_cast<std::uint64_t>(type));
59  _bytes->insert(_bytes->begin(), type_bytes.begin(), type_bytes.end());
60 
61  if (_length_bytes_offset.has_value()) {
62  _length_bytes_offset = type_bytes.size();
63  }
64 
65  return *this;
66  }
67 
73  {
74  if (_length_bytes_offset.has_value()) {
75  return *this;
76  }
77 
78  if (_bytes->empty()) {
79  _length_bytes_offset = 0;
80  } else {
81  _length_bytes_offset = UintVar::Size(_bytes->front());
82  }
83 
84  _bytes->insert(std::next(_bytes->begin(), _length_bytes_offset.value()), { 0, 0 });
85 
86  return *this;
87  }
88 
94  inline Message& Append(std::uint8_t byte) noexcept
95  {
96  _bytes->push_back(byte);
97  return *this;
98  }
99 
105  inline Message& Append(std::span<const std::uint8_t> bytes) noexcept
106  {
107  _bytes->insert(_bytes->end(), bytes.begin(), bytes.end());
108  return *this;
109  }
110 
117  template<typename T>
118  requires requires(std::vector<std::uint8_t>& buffer, const T& value) { buffer << value; }
119  inline Message& Append(const T& value) noexcept(noexcept(*_bytes << value))
120  {
121  *_bytes << value;
122  return *this;
123  }
124 
133  inline const std::shared_ptr<std::vector<uint8_t>>& ToBytes() const noexcept
134  {
135  if (_length_bytes_offset.has_value()) {
136  const std::uint16_t payload_size = SwapBytes(
137  static_cast<std::uint16_t>(_bytes->size() - _length_bytes_offset.value() - sizeof(std::uint16_t)));
138  std::memcpy(_bytes->data() + _length_bytes_offset.value(), &payload_size, sizeof(std::uint16_t));
139  }
140 
141  return _bytes;
142  }
143 
148  inline std::span<const std::uint8_t> ToByteSpan() const noexcept { return *ToBytes(); }
149 
151  void Clear() noexcept
152  {
153  _bytes->clear();
154  _length_bytes_offset = std::nullopt;
155  }
156 
164  template<typename Field>
165  [[nodiscard]] static inline Field ParseField(std::span<const std::uint8_t>& buffer)
166  {
167  Field field{};
168  buffer = buffer >> field;
169  return field;
170  }
171 
172  template<typename Field>
173  [[nodiscard]] static inline std::optional<Field> ParseOptionalField(std::span<const std::uint8_t>& buffer)
174  {
175  if (buffer.empty()) {
176  return std::nullopt;
177  }
178 
179  Field field{};
180  buffer = buffer >> field;
181  return field;
182  }
183 
184  private:
185  std::optional<std::size_t> _length_bytes_offset;
186  mutable std::shared_ptr<std::vector<std::uint8_t>> _bytes;
187  };
188 }
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 std::size_t size() const noexcept
Definition: uintvar.h:160
constexpr auto begin() const noexcept
Definition: uintvar.h:163
Generic message buffer formatting class.
Definition: message.h:23
void Clear() noexcept
Removes all bytes and any reserved-length state; shared storage remains owned.
Definition: message.h:151
requires std::is_enum_v< T > std::is_integral_v< T > Message & PrependType(T type)
Prepends a message type at the front of the byte buffer.
Definition: message.h:56
static std::optional< Field > ParseOptionalField(std::span< const std::uint8_t > &buffer)
Definition: message.h:173
Message & Append(std::uint8_t byte) noexcept
Definition: message.h:94
static Field ParseField(std::span< const std::uint8_t > &buffer)
Definition: message.h:165
const std::shared_ptr< std::vector< uint8_t > > & ToBytes() const noexcept
Definition: message.h:133
Message & ReserveLength()
Inserts a std::uint16_t near the front of the byte buffer for the payload length.
Definition: message.h:72
Message(std::shared_ptr< std::vector< std::uint8_t >> buffer=std::make_shared< std::vector< std::uint8_t >>())
Definition: message.h:30
requires requires(std::vector< std::uint8_t > &buffer, const T &value)
Definition: message.h:118
Message & Append(const T &value) noexcept(noexcept(*_bytes<< value))
Definition: message.h:119
Message & Append(std::span< const std::uint8_t > bytes) noexcept
Definition: message.h:105
constexpr bool HasPrependedLength() const noexcept
Checks if the message was built with the payload length written near the front.
Definition: message.h:42
std::span< const std::uint8_t > ToByteSpan() const noexcept
Definition: message.h:148
Definition: ctrl_message_types.h:38
constexpr std::uint16_t SwapBytes(const std::uint16_t value)
Converts a 16-bit value between native and big-endian byte order.
Definition: bytes.h:106