Nexus HTTP/3
A QUIC and HTTP/3 library
fields.hpp
1 #pragma once
2 
3 #include <cctype>
4 #include <memory>
5 #include <string_view>
6 
7 #include <boost/intrusive/set.hpp>
8 #include <boost/intrusive/list.hpp>
9 
10 namespace nexus::h3 {
11 
13 class field : public boost::intrusive::list_base_hook<>,
14  public boost::intrusive::set_base_hook<>
15 {
16  friend class fields;
17  using size_type = uint16_t;
18  size_type name_size;
19  size_type value_size;
20  uint8_t never_index_;
21  char buffer[3]; // accounts for delimiter and null terminator
22 
23  static constexpr auto delim = std::string_view{": "};
24 
25  // private constructor, use the create() factory function instead
26  field(std::string_view name, std::string_view value, uint8_t never_index)
27  : name_size(name.size()), value_size(value.size()),
28  never_index_(never_index) {
29  auto pos = std::copy(name.begin(), name.end(), buffer);
30  pos = std::copy(delim.begin(), delim.end(), pos);
31  pos = std::copy(value.begin(), value.end(), pos);
32  *pos = '\0';
33  }
34  public:
35  field(const field&) = delete;
36  field& operator=(const field&) = delete;
37 
39  std::string_view name() const {
40  return {buffer, name_size};
41  }
43  std::string_view value() const {
44  return {buffer + name_size + delim.size(), value_size};
45  }
46 
48  void never_index(bool value) { never_index_ = value; }
50  bool never_index() const { return never_index_; }
51 
53  const char* c_str() const { return buffer; }
55  const char* data() const { return buffer; }
57  size_type size() const { return name_size + delim.size() + value_size; }
58 
59  private:
60  struct deleter {
61  void operator()(field* f) {
62  const size_t size = sizeof(field) + f->name_size + f->value_size;
63  using Alloc = std::allocator<char>;
64  using Traits = std::allocator_traits<Alloc>;
65  auto alloc = Alloc{};
66  Traits::destroy(alloc, f);
67  Traits::deallocate(alloc, reinterpret_cast<char*>(f), size);
68  }
69  };
70  using ptr = std::unique_ptr<field, deleter>;
71 
72  // allocate just enough memory to hold the given name and value
73  static ptr create(std::string_view name, std::string_view value,
74  bool never_index)
75  {
76  const size_t size = sizeof(field) + name.size() + value.size();
77  using Alloc = std::allocator<char>;
78  using Traits = std::allocator_traits<Alloc>;
79  auto alloc = Alloc{};
80  auto p = Traits::allocate(alloc, size);
81  try {
82  return ptr{new (p) field(name, value, never_index)};
83  } catch (const std::exception&) {
84  Traits::deallocate(alloc, p, size);
85  throw;
86  }
87  }
88 };
89 
90 namespace detail {
91 
92 // case-insensitive field comparison for sorting in multiset
93 struct field_compare {
94  bool operator()(char lhs, char rhs) const {
95  return std::tolower(lhs) < std::tolower(rhs);
96  }
97  bool operator()(std::string_view lhs, std::string_view rhs) const {
98  return std::lexicographical_compare(lhs.begin(), lhs.end(),
99  rhs.begin(), rhs.end(), *this);
100  }
101  bool operator()(const field& lhs, const field& rhs) const {
102  return (*this)(lhs.name(), rhs.name());
103  }
104  bool operator()(std::string_view lhs, const field& rhs) const {
105  return (*this)(lhs, rhs.name());
106  }
107  bool operator()(const field& lhs, std::string_view rhs) const {
108  return (*this)(lhs.name(), rhs);
109  }
110 };
111 
112 struct field_key {
113  using type = std::string_view;
114  type operator()(const field& f) { return f.name(); }
115 };
116 
117 using field_multiset = boost::intrusive::multiset<field,
118  boost::intrusive::compare<field_compare>,
119  boost::intrusive::key_of_value<field_key>>;
120 
121 using field_list = boost::intrusive::list<field,
122  boost::intrusive::constant_time_size<true>,
123  boost::intrusive::cache_last<true>,
124  boost::intrusive::size_type<uint16_t>>;
125 
126 } // namespace detail
127 
130 class fields {
131  using list_type = detail::field_list;
132  list_type list;
133  // maintain an index of the names for efficient searching
134  using multiset_type = detail::field_multiset;
135  multiset_type set;
136  public:
138  fields() = default;
140  fields(fields&& o) = default;
143  clear();
144  list = std::move(o.list);
145  set = std::move(o.set);
146  return *this;
147  }
148  ~fields() { clear(); }
149 
150  using size_type = list_type::size_type;
152  size_type size() const { return list.size(); }
153 
154  bool empty() const { return list.empty(); }
155 
156  using value_type = field;
157  using iterator = list_type::iterator;
158  using const_iterator = list_type::const_iterator;
159 
160  iterator begin() { return list.begin(); }
161  const_iterator begin() const { return list.begin(); }
162  const_iterator cbegin() const { return list.cbegin(); }
163 
164  iterator end() { return list.end(); }
165  const_iterator end() const { return list.end(); }
166  const_iterator cend() const { return list.cend(); }
167 
169  size_type count(std::string_view name) const {
170  return set.count(name);
171  }
172 
174  iterator find(std::string_view name) {
175  if (auto i = set.find(name); i != set.end()) {
176  return list.iterator_to(*i);
177  }
178  return list.end();
179  }
180 
182  const_iterator find(std::string_view name) const {
183  if (auto i = set.find(name); i != set.end()) {
184  return list.iterator_to(*i);
185  }
186  return list.end();
187  }
188 
191  auto equal_range(std::string_view name)
192  -> std::pair<iterator, iterator>
193  {
194  auto lower = set.lower_bound(name);
195  if (lower == set.end()) {
196  return {list.end(), list.end()};
197  }
198  auto upper = set.upper_bound(name);
199  auto list_upper = std::next(list.iterator_to(*std::prev(upper)));
200  return {list.iterator_to(*lower), list_upper};
201  }
202 
205  auto equal_range(std::string_view name) const
206  -> std::pair<const_iterator, const_iterator>
207  {
208  auto lower = set.lower_bound(name);
209  if (lower == set.end()) {
210  return {list.end(), list.end()};
211  }
212  auto upper = set.upper_bound(name);
213  auto list_upper = std::next(list.iterator_to(*std::prev(upper)));
214  return {list.iterator_to(*lower), list_upper};
215  }
216 
219  iterator insert(std::string_view name, std::string_view value,
220  bool never_index = false)
221  {
222  auto ptr = field::create(name, value, never_index);
223 
224  auto lower = set.lower_bound(name);
225  if (lower == set.end()) {
226  set.insert(set.end(), *ptr);
227  return list.insert(list.end(), *ptr.release());
228  }
229  auto upper = set.upper_bound(name);
230  auto list_upper = std::next(list.iterator_to(*std::prev(upper)));
231  set.insert(upper, *ptr);
232  return list.insert(list_upper, *ptr.release());
233  }
234 
237  iterator assign(std::string_view name, std::string_view value,
238  bool never_index = false)
239  {
240  auto ptr = field::create(name, value, never_index);
241 
242  auto lower = set.lower_bound(name);
243  if (lower == set.end()) {
244  set.insert(set.end(), *ptr);
245  return list.insert(list.end(), *ptr.release());
246  }
247  auto upper = set.upper_bound(name);
248  auto list_lower = list.iterator_to(*lower);
249  auto list_upper = std::next(list.iterator_to(*std::prev(upper)));
250  set.erase(lower, upper);
251  list.erase_and_dispose(list_lower, list_upper, field::deleter{});
252 
253  set.insert(set.end(), *ptr);
254  return list.insert(list.end(), *ptr.release());
255  }
256 
258  iterator erase(iterator p) {
259  set.erase(set.iterator_to(*p));
260  return list.erase_and_dispose(p, field::deleter{});
261  }
262 
264  iterator erase(iterator begin, iterator end) {
265  set.erase(set.iterator_to(*begin),
266  std::next(set.iterator_to(*std::prev(end))));
267  return list.erase_and_dispose(begin, end, field::deleter{});
268  }
269 
271  void clear() {
272  set.clear();
273  list.clear_and_dispose(field::deleter{});
274  }
275 };
276 
277 } // namespace nexus::h3
an immutable key/value pair to represent a single header
Definition: fields.hpp:15
std::string_view name() const
return a view of the field name
Definition: fields.hpp:39
size_type size() const
return the string length of c_str()
Definition: fields.hpp:57
const char * c_str() const
return a null-terminated string of the form "<name>: <value>"
Definition: fields.hpp:53
const char * data() const
return a null-terminated string of the form "<name>: <value>"
Definition: fields.hpp:55
void never_index(bool value)
enable or disable the caching of this field for header compression
Definition: fields.hpp:48
bool never_index() const
return whether or not this field can be cached for header compression
Definition: fields.hpp:50
std::string_view value() const
return a view of the field value
Definition: fields.hpp:43
an ordered list of headers for an http request or response. all field name comparisons are case-insen...
Definition: fields.hpp:130
auto equal_range(std::string_view name) -> std::pair< iterator, iterator >
return an iterator pair corresponding to the range of fields that match the given name (the first mat...
Definition: fields.hpp:191
fields(fields &&o)=default
move-construct the fields, leaving o empty
void clear()
erase all fields
Definition: fields.hpp:271
fields & operator=(fields &&o)
move-assign the fields, leaving o empty
Definition: fields.hpp:142
iterator insert(std::string_view name, std::string_view value, bool never_index=false)
insert the given field after the last field that matches its name, or at the end of the list
Definition: fields.hpp:219
const_iterator find(std::string_view name) const
return an iterator to the first field that matches the given name
Definition: fields.hpp:182
iterator find(std::string_view name)
return an iterator to the first field that matches the given name
Definition: fields.hpp:174
fields()=default
construct an empty list of fields
iterator erase(iterator p)
erase the field at the given position
Definition: fields.hpp:258
auto equal_range(std::string_view name) const -> std::pair< const_iterator, const_iterator >
return an iterator pair corresponding to the range of fields that match the given name (the first mat...
Definition: fields.hpp:205
size_type count(std::string_view name) const
return the number of fields that match the given name
Definition: fields.hpp:169
size_type size() const
return the total number of fields in the list
Definition: fields.hpp:152
iterator assign(std::string_view name, std::string_view value, bool never_index=false)
insert the given field at the end of the list, erasing any existing fields with a matching name
Definition: fields.hpp:237
iterator erase(iterator begin, iterator end)
erase all fields in the range [begin,end)
Definition: fields.hpp:264
HTTP/3 library.
Definition: client.hpp:6