LibQuicR
parameters.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 
7 
8 #include <algorithm>
9 #include <initializer_list>
10 #include <vector>
11 
12 namespace quicr::messages {
13 
15  enum struct ParameterType : uint64_t
16  {
17  // Delivery and Timeouts
18  kDeliveryTimeout = 0x02,
19  kAuthorizationToken = 0x03,
20  kRendezvousTimeout = 0x04,
22  kFillTimeout = 0x0A,
23 
24  // Subscription and Track State
25  kExpires = 0x08,
26  kLargestObject = 0x09,
27  kForward = 0x10,
28 
29  // Priority and Filtering
30  kSubscriberPriority = 0x20,
31  kSubscriptionFilter = 0x21,
32  kGroupOrder = 0x22,
33 
34  // Dynamic Group Management
35  kNewGroupRequest = 0x32,
36  kTrackNamespacePrefix = 0x34,
37 
38  // Filters
39  kTrackFilter = 0x29,
40 
41  /*===================================================================*/
42  // Internal Use
43  /*===================================================================*/
44 
45  kInvalid = 0xFF,
46  };
47 
50 
53 
57  {
58  switch (type) {
63  default:
65  }
66  }
67 
75  {
76  switch (type) {
81 
82  default:
83  throw std::invalid_argument("parameter type is not a valid filter type");
84  }
85  }
86 
90  {
91  return ToParameterFilterType(GetFilterType(filter));
92  }
93 
100  inline Parameter SerializeFilter(FilterType filter_type, const Filter& filter)
101  {
102  auto param = std::visit(
103  [&](auto&& f) {
104  using T = std::decay_t<decltype(f)>;
105 
106  if constexpr (std::is_same_v<std::monostate, T>) {
107  return Parameter{ ToParameterFilterType(filter_type), Bytes{} };
108  } else {
109  Bytes bytes;
110  return Parameter{ ToParameterFilterType(filter_type), bytes << f };
111  }
112  },
113  filter);
114 
115  return param;
116  }
117 
119  enum class ParameterEncoding
120  {
121  kByte,
122  kVarint,
123  kLocation,
124  kBytes
125  };
126 
134  {
135  static const std::map<ParameterType, ParameterEncoding> encodings = {
150  };
151 
152  const auto it = encodings.find(type);
153  if (it == encodings.end()) {
155  "Unknown Message Parameter type (type=" + std::to_string(static_cast<std::uint64_t>(type)) + ")");
156  }
157  return it->second;
158  }
159 
164  template<typename T>
166  HasByteStreamOperators<std::decay_t<T>> || ByteParameter<std::decay_t<T>> ||
167  std::is_same_v<std::decay_t<T>, Location> || std::is_same_v<std::decay_t<T>, Bytes> || requires(T v) {
168  { UintVar(v) };
169  };
170 
181  template<typename Type = ParameterType>
182  requires std::is_convertible_v<Type, std::uint64_t> ||
183  std::is_same_v<std::underlying_type_t<Type>, std::uint64_t>
185  {
186  public:
188  ParameterList() = default;
189 
192  ParameterList(const ParameterList& other) = default;
193 
196  ParameterList(ParameterList&& other) = default;
197 
201  ParameterList& operator=(const ParameterList& other) = default;
202 
206  ParameterList& operator=(ParameterList&& other) = default;
207 
216  template<typename T>
217  requires HasByteStreamOperators<T> || std::is_convertible_v<T, std::uint8_t> ||
218  std::is_same_v<T, Location> || requires(T v) {
219  { UintVar(v) };
220  }
221  ParameterList& Add(Type type, const T& value)
222  {
223  const std::uint64_t key = static_cast<std::uint64_t>(type);
224  switch (GetParameterEncoding(type)) {
226  if constexpr (ByteParameter<T>) {
227  parameters[key].push_back(static_cast<std::uint8_t>(value));
228  break;
229  } else {
231  "Given parameter type must be u8 (type=" + std::to_string(key) + ")");
232  }
234  if constexpr (requires { UintVar(value); }) {
235  parameters[key] << UintVar(value);
236  break;
237  } else {
239  "Given parameter type must be uvarint(u64) (type=" + std::to_string(key) + ")");
240  }
241  default:
242  if constexpr (std::is_same_v<T, Bytes> || std::is_same_v<T, BytesSpan>) {
243  parameters[key].insert(parameters[key].end(), value.begin(), value.end());
244  } else if constexpr (HasByteStreamOperators<T>) {
245  parameters[key] << value;
246  } else {
248  "Given parameter type must be Location or Bytes (type=" + std::to_string(key) + ")");
249  }
250  break;
251  }
252 
253  return *this;
254  }
255 
263  template<ParameterValueType T>
264  ParameterList& AddOptional(Type type, const std::optional<T>& value)
265  {
266  if (value.has_value()) {
267  Add<T>(type, value.value());
268  }
269 
270  return *this;
271  }
272 
274  auto begin() const noexcept { return parameters.begin(); }
275 
277  auto end() const noexcept { return parameters.end(); }
278 
281  bool Contains(Type type) const { return parameters.contains(static_cast<std::uint64_t>(type)); }
282 
289  std::optional<BytesSpan> Find(Type type) const
290  {
291  const auto it = parameters.find(static_cast<std::uint64_t>(type));
292  if (it == parameters.end()) {
293  return std::nullopt;
294  }
295 
296  return it->second;
297  }
298 
300  void Remove(Type type) { parameters.erase(static_cast<std::uint64_t>(type)); }
301 
310  template<ParameterValueType T>
311  std::optional<T> Get(Type type) const
312  {
313  const auto bytes_opt = Find(type);
314  if (!bytes_opt.has_value()) {
315  return std::nullopt;
316  }
317  const auto bytes = *bytes_opt;
318  if (bytes.empty()) {
319  throw ProtocolViolationException("Parameter value must not be empty");
320  }
321 
322  switch (GetParameterEncoding(type)) {
324  if constexpr (ByteParameter<T>) {
325  return static_cast<T>(bytes[0]);
326  } else {
327  throw ProtocolViolationException("Given parameter type must be u8 (type=" +
328  std::to_string(static_cast<std::uint64_t>(type)) + ")");
329  }
331  if constexpr (requires(T v) { UintVar(v); }) {
332  return static_cast<T>(UintVar(bytes).Get());
333  } else {
334  throw ProtocolViolationException("Given parameter type must be uvarint(u64) (type=" +
335  std::to_string(static_cast<std::uint64_t>(type)) + ")");
336  }
337  default:
338  if constexpr (std::is_same_v<T, Bytes> || std::is_same_v<T, BytesSpan>) {
339  return Bytes{ bytes.begin(), bytes.end() };
340  } else if constexpr (HasByteStreamOperators<T>) {
341  T result;
342  bytes >> result;
343  return result;
344  } else {
345  throw ProtocolViolationException("Given parameter type must be Location or Bytes (type=" +
346  std::to_string(static_cast<std::uint64_t>(type)) + ")");
347  }
348  }
349  }
350 
358  {
359  if constexpr (!std::is_same_v<ParameterType, Type>) {
360  return std::monostate{};
361  }
362 
363  const auto bytes = Get<Bytes>(ToParameterFilterType(type));
364  if (!bytes.has_value()) {
365  return std::monostate{};
366  }
367  return DeserializeFilter(type, *bytes);
368  }
369 
373  auto operator<=>(const ParameterList& other) const = default;
374 
375  std::map<std::uint64_t, Bytes> parameters;
376  };
377 
388  template<typename Type = ParameterType>
390  {
391  std::uint64_t num = 0;
392  buffer = buffer >> num;
393 
394  std::uint64_t prev_type = 0;
395  std::uint64_t delta = 0;
396  for (std::uint64_t i = 0; i < num; ++i) {
397  buffer = buffer >> delta;
398 
399  const std::uint64_t type = prev_type += delta;
400  Bytes& value = msg.parameters[type];
401 
402  switch (GetParameterEncoding(static_cast<Type>(type))) {
404  std::uint8_t byte = 0;
405  buffer = buffer >> byte;
406  value.push_back(byte);
407  break;
408  }
410  UintVar uval(buffer);
411  value << uval;
412  buffer = buffer.subspan(uval.size());
413  break;
414  }
416  Location loc{};
417  buffer = buffer >> loc;
418  value << loc;
419  break;
420  }
422  buffer = buffer >> value;
423  break;
424  }
425  }
426  }
427 
428  return buffer;
429  }
430 
440  template<typename Type = ParameterType>
442  {
443  buffer << UintVar(msg.parameters.size());
444 
445  std::uint64_t prev_type = 0;
446  for (const auto& [type, value] : msg.parameters) {
447  buffer << UintVar(type - prev_type);
448  prev_type = type;
449 
450  switch (GetParameterEncoding(static_cast<Type>(type))) {
452  buffer << value.front();
453  break;
455  [[fallthrough]];
457  buffer.insert(buffer.end(), value.begin(), value.end());
458  break;
460  buffer << value;
461  break;
462  }
463  }
464 
465  return buffer;
466  }
467 
470 
479  inline void ValidateParameters(const Parameters& params, std::initializer_list<ParameterType> allowed)
480  {
481  std::vector<ParameterType> seen;
482  for (const auto& [type_value, value] : params) {
483  const auto type = static_cast<ParameterType>(type_value);
484  if (std::ranges::find(allowed, type) == allowed.end()) {
485  throw ProtocolViolationException("Parameter not valid for this message type");
486  }
487 
489  if (std::ranges::find(seen, type) != seen.end()) {
490  throw ProtocolViolationException("Unexpected duplicate parameter");
491  }
492  seen.push_back(type);
493  }
494  }
495  }
496 
504  inline std::vector<Token> CollectAuthTokens(const Parameters& params)
505  {
506  std::vector<Token> tokens;
507  for (const auto& [type_value, value] : params) {
508  if (static_cast<ParameterType>(type_value) != ParameterType::kAuthorizationToken) {
509  continue;
510  }
511  Token token{};
512  const BytesSpan span{ value };
513  span >> token;
514  tokens.push_back(std::move(token));
515  }
516  return tokens;
517  }
518 
526  inline bool ResolveForward(const Parameters& params, bool default_value)
527  {
528  if (!params.Contains(ParameterType::kForward)) {
529  return default_value;
530  }
531  const auto value = params.Get<std::uint8_t>(ParameterType::kForward);
532  if (value > 1) {
533  throw ProtocolViolationException("FORWARD parameter must be 0 or 1");
534  }
535  return value == 1;
536  }
537 
544  inline std::optional<GroupOrder> ResolveGroupOrder(const Parameters& params)
545  {
546  const auto value = params.Get<std::uint8_t>(ParameterType::kGroupOrder);
547  if (!value.has_value()) {
548  return std::nullopt;
549  }
550  if (value != static_cast<std::uint8_t>(GroupOrder::kAscending) &&
551  value != static_cast<std::uint8_t>(GroupOrder::kDescending)) {
552  throw ProtocolViolationException("GROUP_ORDER parameter must be Ascending or Descending");
553  }
554  return static_cast<GroupOrder>(*value);
555  }
556 
559  inline std::optional<std::uint64_t> ResolveExpires(const Parameters& params)
560  {
561  const auto expires = params.Get<std::uint64_t>(ParameterType::kExpires);
562  return (expires.has_value() && expires.value() != 0) ? expires : std::nullopt;
563  }
564 
565  // Filters.
567  inline constexpr FilterType kFilterTypes[] = {
570  };
571 
574  inline bool ContainsAnyFilter(const Parameters& params)
575  {
576  return std::ranges::any_of(
577  kFilterTypes, [&](const auto filter_type) { return params.Contains(ToParameterFilterType(filter_type)); });
578  }
579 
585  inline Filter ResolveFilter(const Parameters& params)
586  {
587  for (const auto filter_type : kFilterTypes) {
588  if (params.Contains(ToParameterFilterType(filter_type))) {
589  return params.GetFilter(filter_type);
590  }
591  }
592  return std::monostate{};
593  }
594 
595 } // namespace quicr::messages
Definition: uintvar.h:50
constexpr std::uint64_t Get() const noexcept
Definition: uintvar.h:128
constexpr std::size_t size() const noexcept
Definition: uintvar.h:160
Definition: parameters.h:185
ParameterList & operator=(ParameterList &&other)=default
std::optional< BytesSpan > Find(Type type) const
Definition: parameters.h:289
ParameterList(ParameterList &&other)=default
ParameterList(const ParameterList &other)=default
ParameterList()=default
Creates an empty list.
auto operator<=>(const ParameterList &other) const =default
auto begin() const noexcept
Definition: parameters.h:274
auto end() const noexcept
Definition: parameters.h:277
Filter GetFilter(FilterType type) const
Definition: parameters.h:357
ParameterList & AddOptional(Type type, const std::optional< T > &value)
Definition: parameters.h:264
requires HasByteStreamOperators< T > std::is_convertible_v< T, std::uint8_t > std::is_same_v< T, Location > requires(T v)
Definition: parameters.h:218
bool Contains(Type type) const
Definition: parameters.h:281
std::map< std::uint64_t, Bytes > parameters
Encoded values keyed by absolute numeric parameter type.
Definition: parameters.h:375
ParameterList & operator=(const ParameterList &other)=default
ParameterList & Add(Type type, const T &value)
Definition: parameters.h:221
std::optional< T > Get(Type type) const
Definition: parameters.h:311
void Remove(Type type)
Definition: parameters.h:300
Definition: ctrl_message_types.h:38
bool ResolveForward(const Parameters &params, bool default_value)
Definition: parameters.h:526
std::vector< Token > CollectAuthTokens(const Parameters &params)
Definition: parameters.h:504
constexpr FilterType kFilterTypes[]
Filter kinds searched by ContainsAnyFilter() and ResolveFilter(), in precedence order.
Definition: parameters.h:567
Parameter SerializeFilter(FilterType filter_type, const Filter &filter)
Definition: parameters.h:100
ParameterEncoding
Physical value encoding selected by a parameter type.
Definition: parameters.h:120
@ kByte
Exactly one raw byte.
@ kBytes
Unsigned-varint length followed by bytes.
@ kLocation
Two unsigned variable-length integers.
@ kVarint
One unsigned variable-length integer.
constexpr FilterType ToFilterType(ParameterType type)
Definition: parameters.h:74
Bytes & operator<<(Bytes &buffer, const Bytes &bytes)
Filter DeserializeFilter(FilterType filter_type, BytesSpan bytes)
Definition: ctrl_message_types.h:732
ParameterEncoding GetParameterEncoding(ParameterType type)
Definition: parameters.h:133
bool ContainsAnyFilter(const Parameters &params)
Definition: parameters.h:574
constexpr ParameterType ToParameterFilterType(FilterType type)
Definition: parameters.h:56
FilterType GetFilterType(const Filter &filter)
Definition: ctrl_message_types.h:681
concept ParameterValueType
Definition: parameters.h:165
ParameterType GetFilterParameterType(const Filter &filter)
Definition: parameters.h:89
ParameterType
Message-parameter type codes used as delta-encoded map keys on the wire.
Definition: parameters.h:16
@ kLargestObject
Group/object location encoded as two unsigned varints.
@ kNewGroupRequest
New-group request encoded as an unsigned varint.
@ kTrackNamespacePrefix
Length-prefixed opaque namespace-prefix bytes.
@ kAuthorizationToken
Sensitive opaque token bytes; never log the value.
@ kExpires
Expiry value; zero is resolved as absent.
@ kTrackFilter
Length-prefixed track filter bytes.
@ kSubscriberPriority
One-byte subscriber priority.
@ kDeliveryTimeout
Delivery timeout, encoded as an unsigned varint.
@ kFillTimeout
Fill timeout, encoded as an unsigned varint.
@ kSubscriptionFilter
Length-prefixed location-filter bytes.
@ kInvalid
Internal sentinel; not a valid wire parameter.
@ kRendezvousTimeout
Rendezvous timeout, encoded as an unsigned varint.
@ kGroupOrder
One-byte GroupOrder value.
@ kForward
One-byte Boolean forwarding flag.
@ kSubgroupDeliveryTimeout
Subgroup delivery timeout, encoded as an unsigned varint.
BytesSpan operator>>(BytesSpan buffer, Bytes &value)
std::variant< std::monostate, SubscriptionFilter, TrackFilter > Filter
Any supported filter representation; monostate means no/unsupported filter.
Definition: ctrl_message_types.h:677
std::optional< std::uint64_t > ResolveExpires(const Parameters &params)
Definition: parameters.h:559
void ValidateParameters(const Parameters &params, std::initializer_list< ParameterType > allowed)
Definition: parameters.h:479
FilterType
In-memory filter discriminator.
Definition: ctrl_message_types.h:546
@ kTrackFilter
Track-selection constraints.
@ kSubscriptionFilter
Location/subscription filter.
std::optional< GroupOrder > ResolveGroupOrder(const Parameters &params)
Definition: parameters.h:544
GroupOrder
Requested group traversal order.
Definition: ctrl_message_types.h:532
@ kAscending
Increasing group identifiers.
@ kDescending
Decreasing group identifiers.
Filter ResolveFilter(const Parameters &params)
Definition: parameters.h:585
std::vector< Byte > Bytes
Owning contiguous byte buffer.
Definition: bytes.h:18
std::span< const Byte > BytesSpan
Non-owning, read-only view of contiguous bytes.
Definition: bytes.h:21
Definition: ctrl_message_types.h:231
Absolute group/object location ordered lexicographically by group then object.
Definition: ctrl_message_types.h:171
Exception indicating that received or constructed data violates the MoQ protocol.
Definition: ctrl_message_types.h:71
Definition: ctrl_message_types.h:425