Line | Branch | Exec | Source |
---|---|---|---|
1 | // | ||
2 | // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) | ||
3 | // Copyright (c) 2025 Mohammad Nejati | ||
4 | // | ||
5 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
6 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
7 | // | ||
8 | // Official repository: https://github.com/cppalliance/http_proto | ||
9 | // | ||
10 | |||
11 | #include <boost/http_proto/detail/config.hpp> | ||
12 | #include <boost/http_proto/detail/except.hpp> | ||
13 | #include <boost/http_proto/detail/header.hpp> | ||
14 | #include <boost/http_proto/error.hpp> | ||
15 | #include <boost/http_proto/field.hpp> | ||
16 | #include <boost/http_proto/fields_base.hpp> | ||
17 | #include <boost/http_proto/header_limits.hpp> | ||
18 | #include <boost/http_proto/rfc/token_rule.hpp> | ||
19 | |||
20 | #include "src/detail/move_chars.hpp" | ||
21 | #include "src/rfc/detail/rules.hpp" | ||
22 | |||
23 | #include <boost/assert.hpp> | ||
24 | #include <boost/assert/source_location.hpp> | ||
25 | #include <boost/core/detail/string_view.hpp> | ||
26 | #include <boost/system/result.hpp> | ||
27 | #include <boost/url/grammar/ci_string.hpp> | ||
28 | #include <boost/url/grammar/error.hpp> | ||
29 | #include <boost/url/grammar/parse.hpp> | ||
30 | #include <boost/url/grammar/token_rule.hpp> | ||
31 | |||
32 | namespace boost { | ||
33 | namespace http_proto { | ||
34 | |||
35 | namespace { | ||
36 | |||
37 | std::size_t | ||
38 | 1066 | align_down( | |
39 | void * ptr, | ||
40 | std::size_t size, | ||
41 | std::size_t alignment) | ||
42 | { | ||
43 | 1066 | auto addr = reinterpret_cast<std::uintptr_t>(ptr); | |
44 | 1066 | auto aligned_end = (addr + size) & ~(alignment - 1); | |
45 | |||
46 |
1/2✓ Branch 0 taken 1066 times.
✗ Branch 1 not taken.
|
1066 | if(aligned_end > addr) |
47 | 1066 | return aligned_end - addr; | |
48 | |||
49 | ✗ | return 0; | |
50 | } | ||
51 | |||
52 | void | ||
53 | 235 | verify_field_name( | |
54 | core::string_view name, | ||
55 | system::error_code& ec) | ||
56 | { | ||
57 | 235 | auto rv = grammar::parse( | |
58 | name, detail::field_name_rule); | ||
59 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 226 times.
|
235 | if(rv.has_error()) |
60 | { | ||
61 | 18 | ec = BOOST_HTTP_PROTO_ERR( | |
62 | error::bad_field_name); | ||
63 | } | ||
64 | 235 | } | |
65 | |||
66 | system::result<detail::field_value_rule_t::value_type> | ||
67 | 364 | verify_field_value( | |
68 | core::string_view value) | ||
69 | { | ||
70 | 364 | auto it = value.begin(); | |
71 | 364 | auto end = value.end(); | |
72 | auto rv = | ||
73 | 364 | grammar::parse(it, end, detail::field_value_rule); | |
74 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 357 times.
|
364 | if( rv.has_error() ) |
75 | { | ||
76 |
1/2✓ Branch 3 taken 7 times.
✗ Branch 4 not taken.
|
7 | if( rv.error() == condition::need_more_input ) |
77 | 7 | return error::bad_field_value; | |
78 | ✗ | return rv.error(); | |
79 | } | ||
80 | |||
81 |
2/2✓ Branch 1 taken 16 times.
✓ Branch 2 taken 341 times.
|
357 | if( rv->has_crlf ) |
82 | 16 | return error::bad_field_smuggle; | |
83 | |||
84 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 334 times.
|
341 | if( it != end ) |
85 | 7 | return error::bad_field_value; | |
86 | |||
87 | 334 | return rv; | |
88 | } | ||
89 | |||
90 | } // namespace | ||
91 | |||
92 | class fields_base:: | ||
93 | op_t | ||
94 | { | ||
95 | fields_base& self_; | ||
96 | core::string_view* s0_; | ||
97 | core::string_view* s1_; | ||
98 | char* buf_ = nullptr; | ||
99 | char const* cbuf_ = nullptr; | ||
100 | std::size_t cap_ = 0; | ||
101 | |||
102 | public: | ||
103 | explicit | ||
104 | 988 | op_t( | |
105 | fields_base& self, | ||
106 | core::string_view* s0 = nullptr, | ||
107 | core::string_view* s1 = nullptr) noexcept | ||
108 | 988 | : self_(self) | |
109 | 988 | , s0_(s0) | |
110 | 988 | , s1_(s1) | |
111 | { | ||
112 | 988 | } | |
113 | |||
114 | 988 | ~op_t() | |
115 | { | ||
116 |
2/2✓ Branch 0 taken 162 times.
✓ Branch 1 taken 826 times.
|
988 | if(buf_) |
117 |
1/2✓ Branch 0 taken 162 times.
✗ Branch 1 not taken.
|
162 | delete[] buf_; |
118 | 988 | } | |
119 | |||
120 | char const* | ||
121 | 12 | buf() const noexcept | |
122 | { | ||
123 | 12 | return buf_; | |
124 | } | ||
125 | |||
126 | char const* | ||
127 | 449 | cbuf() const noexcept | |
128 | { | ||
129 | 449 | return cbuf_; | |
130 | } | ||
131 | |||
132 | char* | ||
133 | 12 | end() const noexcept | |
134 | { | ||
135 | 12 | return buf_ + cap_; | |
136 | } | ||
137 | |||
138 | table | ||
139 | 6 | tab() const noexcept | |
140 | { | ||
141 | 6 | return table(end()); | |
142 | } | ||
143 | |||
144 | bool | ||
145 | reserve(std::size_t n); | ||
146 | |||
147 | bool | ||
148 | grow( | ||
149 | std::size_t extra_char, | ||
150 | std::size_t extra_field); | ||
151 | |||
152 | void | ||
153 | move_chars( | ||
154 | char* dest, | ||
155 | char const* src, | ||
156 | std::size_t n) const noexcept; | ||
157 | }; | ||
158 | |||
159 | bool | ||
160 | 968 | fields_base:: | |
161 | op_t:: | ||
162 | reserve( | ||
163 | std::size_t n) | ||
164 | { | ||
165 | // TODO: consider using a growth factor | ||
166 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 950 times.
|
968 | if(n > self_.max_cap_) |
167 | { | ||
168 | // max capacity exceeded | ||
169 | 18 | detail::throw_length_error(); | |
170 | } | ||
171 |
2/2✓ Branch 0 taken 133 times.
✓ Branch 1 taken 817 times.
|
950 | if(n <= self_.h_.cap) |
172 | 133 | return false; | |
173 | 817 | auto buf = new char[n]; | |
174 | 817 | buf_ = self_.h_.buf; | |
175 | 817 | cbuf_ = self_.h_.cbuf; | |
176 | 817 | cap_ = self_.h_.cap; | |
177 | 817 | self_.h_.buf = buf; | |
178 | 817 | self_.h_.cbuf = buf; | |
179 | 817 | self_.h_.cap = n; | |
180 | 817 | return true; | |
181 | } | ||
182 | |||
183 | bool | ||
184 | 875 | fields_base:: | |
185 | op_t:: | ||
186 | grow( | ||
187 | std::size_t extra_char, | ||
188 | std::size_t extra_field) | ||
189 | { | ||
190 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 875 times.
|
875 | if(extra_field > detail::header::max_offset - self_.h_.count) |
191 | ✗ | detail::throw_length_error(); | |
192 | |||
193 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 873 times.
|
875 | if(extra_char > detail::header::max_offset - self_.h_.size) |
194 | 2 | detail::throw_length_error(); | |
195 | |||
196 | 873 | return reserve( | |
197 | detail::header::bytes_needed( | ||
198 | 873 | self_.h_.size + extra_char, | |
199 | 1741 | self_.h_.count + extra_field)); | |
200 | } | ||
201 | |||
202 | void | ||
203 | 103 | fields_base:: | |
204 | op_t:: | ||
205 | move_chars( | ||
206 | char* dest, | ||
207 | char const* src, | ||
208 | std::size_t n) const noexcept | ||
209 | { | ||
210 | 103 | detail::move_chars( | |
211 | 103 | dest, src, n, s0_, s1_); | |
212 | 103 | } | |
213 | |||
214 | //------------------------------------------------ | ||
215 | |||
216 | 39 | fields_base:: | |
217 | prefix_op_t:: | ||
218 | prefix_op_t( | ||
219 | fields_base& self, | ||
220 | std::size_t new_prefix, | ||
221 | core::string_view* s0, | ||
222 | 39 | core::string_view* s1) | |
223 | 39 | : self_(self) | |
224 | 39 | , new_prefix_(static_cast< | |
225 | 39 | offset_type>(new_prefix)) | |
226 | { | ||
227 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 37 times.
|
39 | if(self.h_.size - self.h_.prefix + new_prefix |
228 | > detail::header::max_offset) | ||
229 | 2 | detail::throw_length_error(); | |
230 | |||
231 | // memmove happens in the destructor | ||
232 | // to avoid overlaping with start line. | ||
233 | 74 | if(new_prefix_ < self_.h_.prefix | |
234 |
5/6✓ Branch 0 taken 5 times.
✓ Branch 1 taken 32 times.
✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 32 times.
|
37 | && !self.h_.is_default()) |
235 | 5 | return; | |
236 | |||
237 | 32 | auto new_size = static_cast<offset_type>( | |
238 | 32 | self.h_.size - self.h_.prefix + new_prefix_); | |
239 | |||
240 | auto bytes_needed = | ||
241 | 32 | detail::header::bytes_needed( | |
242 | new_size, | ||
243 | 32 | self.h_.count); | |
244 | |||
245 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 5 times.
|
32 | if(bytes_needed > self.h_.cap) |
246 | { | ||
247 | // static storage will always throw which is | ||
248 | // intended since they cannot reallocate. | ||
249 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 26 times.
|
27 | if(self.max_cap_ < bytes_needed) |
250 | 1 | detail::throw_length_error(); | |
251 | // TODO: consider using a growth factor | ||
252 | 26 | char* p = new char[bytes_needed]; | |
253 | 26 | std::memcpy( | |
254 | 26 | p + new_prefix_, | |
255 | 26 | self.h_.cbuf + self.h_.prefix, | |
256 | 26 | self.h_.size - self.h_.prefix); | |
257 | 26 | self.h_.copy_table(p + bytes_needed); | |
258 | |||
259 | // old buffer gets released in the destructor | ||
260 | // to avoid invalidating any string_views | ||
261 | // that may still reference it. | ||
262 | 26 | buf_ = self.h_.buf; | |
263 | 26 | self.h_.buf = p; | |
264 | 26 | self.h_.cap = bytes_needed; | |
265 | } | ||
266 | else | ||
267 | { | ||
268 | // memmove to the right and update any | ||
269 | // string_views that reference that region. | ||
270 | 5 | detail::move_chars( | |
271 | 5 | self.h_.buf + new_prefix_, | |
272 | 5 | self.h_.cbuf + self.h_.prefix, | |
273 | 5 | self.h_.size - self.h_.prefix, | |
274 | s0, | ||
275 | s1); | ||
276 | } | ||
277 | |||
278 | 31 | self.h_.cbuf = self.h_.buf; | |
279 | 31 | self.h_.size = new_size; | |
280 | 31 | self.h_.prefix = new_prefix_; | |
281 | } | ||
282 | |||
283 | 36 | fields_base:: | |
284 | prefix_op_t:: | ||
285 | ~prefix_op_t() | ||
286 | { | ||
287 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 31 times.
|
36 | if(new_prefix_ < self_.h_.prefix) |
288 | { | ||
289 | 5 | std::memmove( | |
290 | 5 | self_.h_.buf + new_prefix_, | |
291 | 5 | self_.h_.cbuf + self_.h_.prefix, | |
292 | 5 | self_.h_.size - self_.h_.prefix); | |
293 | |||
294 | 5 | self_.h_.size = | |
295 | 5 | self_.h_.size - self_.h_.prefix + new_prefix_; | |
296 | 5 | self_.h_.prefix = new_prefix_; | |
297 | } | ||
298 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 26 times.
|
31 | else if(buf_) |
299 | { | ||
300 |
1/2✓ Branch 0 taken 5 times.
✗ Branch 1 not taken.
|
5 | delete[] buf_; |
301 | } | ||
302 | 36 | } | |
303 | |||
304 | //------------------------------------------------ | ||
305 | |||
306 | 198 | fields_base:: | |
307 | fields_base( | ||
308 | 198 | detail::kind k) noexcept | |
309 | 198 | : h_(k) | |
310 | { | ||
311 | 198 | } | |
312 | |||
313 | 1066 | fields_base:: | |
314 | fields_base( | ||
315 | detail::kind k, | ||
316 | void* storage, | ||
317 | 1066 | std::size_t cap) noexcept | |
318 | : fields_base( | ||
319 | 1066 | *detail::header::get_default(k), storage, cap) | |
320 | { | ||
321 | 1066 | } | |
322 | |||
323 | // copy s and parse it | ||
324 | 546 | fields_base:: | |
325 | fields_base( | ||
326 | detail::kind k, | ||
327 | 546 | core::string_view s) | |
328 | 546 | : h_(detail::empty{k}) | |
329 | { | ||
330 | 546 | auto n = detail::header::count_crlf(s); | |
331 |
2/2✓ Branch 0 taken 235 times.
✓ Branch 1 taken 311 times.
|
546 | if(h_.kind == detail::kind::fields) |
332 | { | ||
333 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 234 times.
|
235 | if(n < 1) |
334 | 1 | detail::throw_invalid_argument(); | |
335 | 234 | n -= 1; | |
336 | } | ||
337 | else | ||
338 | { | ||
339 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 309 times.
|
311 | if(n < 2) |
340 | 2 | detail::throw_invalid_argument(); | |
341 | 309 | n -= 2; | |
342 | } | ||
343 | 543 | op_t op(*this); | |
344 |
1/2✓ Branch 2 taken 543 times.
✗ Branch 3 not taken.
|
543 | op.grow(s.size(), n); |
345 |
1/2✓ Branch 2 taken 543 times.
✗ Branch 3 not taken.
|
543 | s.copy(h_.buf, s.size()); |
346 | 543 | system::error_code ec; | |
347 | // VFALCO This is using defaults? | ||
348 | 543 | header_limits lim; | |
349 | 543 | h_.parse(s.size(), lim, ec); | |
350 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 543 times.
|
543 | if(ec.failed()) |
351 | ✗ | detail::throw_system_error(ec); | |
352 | 543 | } | |
353 | |||
354 | // construct a complete copy of h | ||
355 | 25 | fields_base:: | |
356 | fields_base( | ||
357 | 25 | detail::header const& h) | |
358 | 25 | : h_(h.kind) | |
359 | { | ||
360 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 16 times.
|
25 | if(h.is_default()) |
361 | 9 | return; | |
362 | |||
363 | // allocate and copy the buffer | ||
364 | 16 | op_t op(*this); | |
365 |
1/2✓ Branch 1 taken 16 times.
✗ Branch 2 not taken.
|
16 | op.grow(h.size, h.count); |
366 | 16 | h.assign_to(h_); | |
367 | 16 | std::memcpy( | |
368 | 16 | h_.buf, h.cbuf, h.size); | |
369 | 16 | h.copy_table(h_.buf + h_.cap); | |
370 | 16 | } | |
371 | |||
372 | // construct a complete copy of h | ||
373 | 1066 | fields_base:: | |
374 | fields_base( | ||
375 | detail::header const& h, | ||
376 | void* storage, | ||
377 | 1066 | std::size_t cap) | |
378 | 1066 | : h_(h.kind) | |
379 | 1066 | , external_storage_(true) | |
380 | { | ||
381 | 1066 | h_.cbuf = static_cast<char*>(storage); | |
382 | 1066 | h_.buf = static_cast<char*>(storage); | |
383 | 1066 | h_.cap = align_down( | |
384 | storage, | ||
385 | cap, | ||
386 | alignof(detail::header::entry)); | ||
387 | 1066 | max_cap_ = h_.cap; | |
388 | |||
389 | 2132 | if(detail::header::bytes_needed( | |
390 | 1066 | h.size, h.count) | |
391 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1066 times.
|
1066 | >= h_.cap) |
392 | ✗ | detail::throw_length_error(); | |
393 | |||
394 | 1066 | h.assign_to(h_); | |
395 | 1066 | std::memcpy( | |
396 | 1066 | h_.buf, h.cbuf, h.size); | |
397 | 1066 | h.copy_table(h_.buf + h_.cap); | |
398 | 1066 | } | |
399 | |||
400 | //------------------------------------------------ | ||
401 | |||
402 | 13 | fields_base:: | |
403 | 13 | fields_base(fields_base const& other) | |
404 | 13 | : fields_base(other.h_) | |
405 | { | ||
406 | 13 | } | |
407 | |||
408 | 1832 | fields_base:: | |
409 | ~fields_base() | ||
410 | { | ||
411 |
4/4✓ Branch 0 taken 1720 times.
✓ Branch 1 taken 112 times.
✓ Branch 2 taken 676 times.
✓ Branch 3 taken 1044 times.
|
1832 | if(h_.buf && !external_storage_) |
412 |
1/2✓ Branch 0 taken 676 times.
✗ Branch 1 not taken.
|
676 | delete[] h_.buf; |
413 | 1832 | } | |
414 | |||
415 | //------------------------------------------------ | ||
416 | // | ||
417 | // Capacity | ||
418 | // | ||
419 | //------------------------------------------------ | ||
420 | |||
421 | void | ||
422 | 10 | fields_base:: | |
423 | clear() noexcept | ||
424 | { | ||
425 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
|
10 | if(! h_.buf) |
426 | 5 | return; | |
427 | using H = | ||
428 | detail::header; | ||
429 | auto const& h = | ||
430 | 5 | *H::get_default( | |
431 | 5 | h_.kind); | |
432 | 5 | h.assign_to(h_); | |
433 | 5 | std::memcpy( | |
434 | 5 | h_.buf, | |
435 | 5 | h.cbuf, | |
436 | 5 | h_.size); | |
437 | } | ||
438 | |||
439 | void | ||
440 | 95 | fields_base:: | |
441 | reserve_bytes( | ||
442 | std::size_t n) | ||
443 | { | ||
444 | 95 | op_t op(*this); | |
445 |
4/4✓ Branch 1 taken 82 times.
✓ Branch 2 taken 13 times.
✓ Branch 3 taken 48 times.
✓ Branch 4 taken 34 times.
|
95 | if(! op.reserve(n)) |
446 | 48 | return; | |
447 | 68 | std::memcpy( | |
448 | 34 | h_.buf, op.cbuf(), h_.size); | |
449 | 34 | auto const nt = | |
450 | 34 | sizeof(entry) * h_.count; | |
451 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 28 times.
|
34 | if(nt > 0) |
452 | 6 | std::memcpy( | |
453 | 6 | h_.buf + h_.cap - nt, | |
454 | 6 | op.end() - nt, | |
455 | nt); | ||
456 |
2/2✓ Branch 1 taken 34 times.
✓ Branch 2 taken 48 times.
|
95 | } |
457 | |||
458 | void | ||
459 | 7 | fields_base:: | |
460 | shrink_to_fit() | ||
461 | { | ||
462 | 14 | if(detail::header::bytes_needed( | |
463 | 7 | h_.size, h_.count) >= | |
464 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
|
7 | h_.cap) |
465 | 3 | return; | |
466 | |||
467 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if(external_storage_) |
468 | ✗ | return; | |
469 | |||
470 |
1/2✓ Branch 1 taken 4 times.
✗ Branch 2 not taken.
|
4 | fields_base tmp(h_); |
471 | 4 | tmp.h_.swap(h_); | |
472 | 4 | } | |
473 | |||
474 | |||
475 | void | ||
476 | 30 | fields_base:: | |
477 | set_max_capacity_in_bytes(std::size_t n) | ||
478 | { | ||
479 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 24 times.
|
30 | if(n < h_.cap) |
480 | 6 | detail::throw_invalid_argument(); | |
481 | 24 | max_cap_ = n; | |
482 | 24 | } | |
483 | |||
484 | //-------------------------------------------- | ||
485 | // | ||
486 | // Observers | ||
487 | // | ||
488 | //-------------------------------------------- | ||
489 | |||
490 | |||
491 | ✗ | fields_base:: | |
492 | value_type:: | ||
493 | value_type( | ||
494 | ✗ | reference const& other) | |
495 | ✗ | : id(other.id) | |
496 | ✗ | , name(other.name) | |
497 | ✗ | , value(other.value) | |
498 | { | ||
499 | ✗ | } | |
500 | |||
501 | //------------------------------------------------ | ||
502 | |||
503 | auto | ||
504 | 1814 | fields_base:: | |
505 | iterator:: | ||
506 | operator*() const noexcept -> | ||
507 | reference | ||
508 | { | ||
509 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1814 times.
|
1814 | BOOST_ASSERT(i_ < ph_->count); |
510 | auto tab = | ||
511 | 1814 | ph_->tab(); | |
512 | auto const& e = | ||
513 | 1814 | tab[i_]; | |
514 | 1814 | auto const* p = | |
515 | 1814 | ph_->cbuf + ph_->prefix; | |
516 | return { | ||
517 | 1814 | (e.id == detail::header::unknown_field) | |
518 |
2/2✓ Branch 0 taken 789 times.
✓ Branch 1 taken 1025 times.
|
1814 | ? optional<field>{} : e.id, |
519 | core::string_view( | ||
520 | 1814 | p + e.np, e.nn), | |
521 | core::string_view( | ||
522 | 1814 | p + e.vp, e.vn) }; | |
523 | } | ||
524 | |||
525 | //------------------------------------------------ | ||
526 | |||
527 | auto | ||
528 | 24 | fields_base:: | |
529 | reverse_iterator:: | ||
530 | operator*() const noexcept -> | ||
531 | reference | ||
532 | { | ||
533 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | BOOST_ASSERT(i_ > 0); |
534 | auto tab = | ||
535 | 24 | ph_->tab(); | |
536 | auto const& e = | ||
537 | 24 | tab[i_-1]; | |
538 | 24 | auto const* p = | |
539 | 24 | ph_->cbuf + ph_->prefix; | |
540 | return { | ||
541 | 24 | (e.id == detail::header::unknown_field) | |
542 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 9 times.
|
24 | ? optional<field>{} : e.id, |
543 | core::string_view( | ||
544 | 24 | p + e.np, e.nn), | |
545 | core::string_view( | ||
546 | 24 | p + e.vp, e.vn) }; | |
547 | } | ||
548 | |||
549 | //------------------------------------------------ | ||
550 | |||
551 | 21 | fields_base:: | |
552 | subrange:: | ||
553 | iterator:: | ||
554 | iterator( | ||
555 | detail::header const* ph, | ||
556 | 21 | std::size_t i) noexcept | |
557 | 21 | : ph_(ph) | |
558 | 21 | , i_(i) | |
559 | { | ||
560 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 21 times.
|
21 | BOOST_ASSERT(i <= ph_->count); |
561 | 21 | } | |
562 | |||
563 | 21 | fields_base:: | |
564 | subrange:: | ||
565 | iterator:: | ||
566 | iterator( | ||
567 | 21 | detail::header const* ph) noexcept | |
568 | 21 | : ph_(ph) | |
569 | 21 | , i_(ph->count) | |
570 | { | ||
571 | 21 | } | |
572 | |||
573 | auto | ||
574 | 11 | fields_base:: | |
575 | subrange:: | ||
576 | iterator:: | ||
577 | operator*() const noexcept -> | ||
578 | reference const | ||
579 | { | ||
580 | auto tab = | ||
581 | 11 | ph_->tab(); | |
582 | auto const& e = | ||
583 | 11 | tab[i_]; | |
584 | 11 | auto const p = | |
585 | 11 | ph_->cbuf + ph_->prefix; | |
586 | 22 | return core::string_view( | |
587 | 11 | p + e.vp, e.vn); | |
588 | } | ||
589 | |||
590 | auto | ||
591 | 27 | fields_base:: | |
592 | subrange:: | ||
593 | iterator:: | ||
594 | operator++() noexcept -> | ||
595 | iterator& | ||
596 | { | ||
597 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 27 times.
|
27 | BOOST_ASSERT(i_ < ph_->count); |
598 | 27 | auto const* e = &ph_->tab()[i_]; | |
599 | 27 | auto const id = e->id; | |
600 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 7 times.
|
27 | if(id != detail::header::unknown_field) |
601 | { | ||
602 | 20 | ++i_; | |
603 | 20 | --e; | |
604 |
2/2✓ Branch 0 taken 26 times.
✓ Branch 1 taken 12 times.
|
38 | while(i_ != ph_->count) |
605 | { | ||
606 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 18 times.
|
26 | if(e->id == id) |
607 | 8 | break; | |
608 | 18 | ++i_; | |
609 | 18 | --e; | |
610 | } | ||
611 | 20 | return *this; | |
612 | } | ||
613 | 7 | auto const p = | |
614 | 7 | ph_->cbuf + ph_->prefix; | |
615 | auto name = core::string_view( | ||
616 | 7 | p + e->np, e->nn); | |
617 | 7 | ++i_; | |
618 | 7 | --e; | |
619 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 4 times.
|
24 | while(i_ != ph_->count) |
620 | { | ||
621 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 17 times.
|
20 | if(grammar::ci_is_equal( |
622 | name, core::string_view( | ||
623 | 20 | p + e->np, e->nn))) | |
624 | 3 | break; | |
625 | 17 | ++i_; | |
626 | 17 | --e; | |
627 | } | ||
628 | 7 | return *this; | |
629 | } | ||
630 | |||
631 | //------------------------------------------------ | ||
632 | // | ||
633 | // fields_base | ||
634 | // | ||
635 | //------------------------------------------------ | ||
636 | |||
637 | core::string_view | ||
638 | 2 | fields_base:: | |
639 | at( | ||
640 | field id) const | ||
641 | { | ||
642 | 2 | auto const it = find(id); | |
643 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
|
2 | if(it == end()) |
644 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | BOOST_THROW_EXCEPTION( |
645 | std::out_of_range{ "field not found" }); | ||
646 | 1 | return it->value; | |
647 | } | ||
648 | |||
649 | core::string_view | ||
650 | 2 | fields_base:: | |
651 | at( | ||
652 | core::string_view name) const | ||
653 | { | ||
654 | 2 | auto const it = find(name); | |
655 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
|
2 | if(it == end()) |
656 |
1/2✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
|
2 | BOOST_THROW_EXCEPTION( |
657 | std::out_of_range{ "field not found" }); | ||
658 | 1 | return it->value; | |
659 | } | ||
660 | |||
661 | bool | ||
662 | 4 | fields_base:: | |
663 | exists( | ||
664 | field id) const noexcept | ||
665 | { | ||
666 | 4 | return find(id) != end(); | |
667 | } | ||
668 | |||
669 | bool | ||
670 | 7 | fields_base:: | |
671 | exists( | ||
672 | core::string_view name) const noexcept | ||
673 | { | ||
674 | 7 | return find(name) != end(); | |
675 | } | ||
676 | |||
677 | std::size_t | ||
678 | 12 | fields_base:: | |
679 | count(field id) const noexcept | ||
680 | { | ||
681 | 12 | std::size_t n = 0; | |
682 |
2/2✓ Branch 5 taken 45 times.
✓ Branch 6 taken 12 times.
|
57 | for(auto v : *this) |
683 |
2/2✓ Branch 1 taken 11 times.
✓ Branch 2 taken 34 times.
|
45 | if(v.id == id) |
684 | 11 | ++n; | |
685 | 12 | return n; | |
686 | } | ||
687 | |||
688 | std::size_t | ||
689 | 14 | fields_base:: | |
690 | count( | ||
691 | core::string_view name) const noexcept | ||
692 | { | ||
693 | 14 | std::size_t n = 0; | |
694 |
2/2✓ Branch 5 taken 62 times.
✓ Branch 6 taken 14 times.
|
76 | for(auto v : *this) |
695 |
2/2✓ Branch 1 taken 19 times.
✓ Branch 2 taken 43 times.
|
62 | if(grammar::ci_is_equal( |
696 | v.name, name)) | ||
697 | 19 | ++n; | |
698 | 14 | return n; | |
699 | } | ||
700 | |||
701 | auto | ||
702 | 94 | fields_base:: | |
703 | find(field id) const noexcept -> | ||
704 | iterator | ||
705 | { | ||
706 | 94 | auto it = begin(); | |
707 | 94 | auto const last = end(); | |
708 |
2/2✓ Branch 1 taken 199 times.
✓ Branch 2 taken 9 times.
|
208 | while(it != last) |
709 | { | ||
710 |
2/2✓ Branch 3 taken 85 times.
✓ Branch 4 taken 114 times.
|
199 | if(it->id == id) |
711 | 85 | break; | |
712 | 114 | ++it; | |
713 | } | ||
714 | 94 | return it; | |
715 | } | ||
716 | |||
717 | auto | ||
718 | 93 | fields_base:: | |
719 | find( | ||
720 | core::string_view name) const noexcept -> | ||
721 | iterator | ||
722 | { | ||
723 | 93 | auto it = begin(); | |
724 | 93 | auto const last = end(); | |
725 |
2/2✓ Branch 1 taken 200 times.
✓ Branch 2 taken 6 times.
|
206 | while(it != last) |
726 | { | ||
727 | 200 | if(grammar::ci_is_equal( | |
728 |
2/2✓ Branch 2 taken 87 times.
✓ Branch 3 taken 113 times.
|
400 | it->name, name)) |
729 | 87 | break; | |
730 | 113 | ++it; | |
731 | } | ||
732 | 93 | return it; | |
733 | } | ||
734 | |||
735 | auto | ||
736 | 2 | fields_base:: | |
737 | find( | ||
738 | iterator from, | ||
739 | field id) const noexcept -> | ||
740 | iterator | ||
741 | { | ||
742 | 2 | auto const last = end(); | |
743 |
2/2✓ Branch 1 taken 10 times.
✓ Branch 2 taken 1 times.
|
11 | while(from != last) |
744 | { | ||
745 |
2/2✓ Branch 3 taken 1 times.
✓ Branch 4 taken 9 times.
|
10 | if(from->id == id) |
746 | 1 | break; | |
747 | 9 | ++from; | |
748 | } | ||
749 | 2 | return from; | |
750 | } | ||
751 | |||
752 | auto | ||
753 | 2 | fields_base:: | |
754 | find( | ||
755 | iterator from, | ||
756 | core::string_view name) const noexcept -> | ||
757 | iterator | ||
758 | { | ||
759 | 2 | auto const last = end(); | |
760 |
2/2✓ Branch 1 taken 11 times.
✓ Branch 2 taken 1 times.
|
12 | while(from != last) |
761 | { | ||
762 | 11 | if(grammar::ci_is_equal( | |
763 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 10 times.
|
22 | name, from->name)) |
764 | 1 | break; | |
765 | 10 | ++from; | |
766 | } | ||
767 | 2 | return from; | |
768 | } | ||
769 | |||
770 | auto | ||
771 | 3 | fields_base:: | |
772 | find_last( | ||
773 | iterator it, | ||
774 | field id) const noexcept -> | ||
775 | iterator | ||
776 | { | ||
777 | 3 | auto const it0 = begin(); | |
778 | for(;;) | ||
779 | { | ||
780 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 9 times.
|
10 | if(it == it0) |
781 | 1 | return end(); | |
782 | 9 | --it; | |
783 |
2/2✓ Branch 3 taken 2 times.
✓ Branch 4 taken 7 times.
|
9 | if(it->id == id) |
784 | 2 | return it; | |
785 | } | ||
786 | } | ||
787 | |||
788 | auto | ||
789 | 3 | fields_base:: | |
790 | find_last( | ||
791 | iterator it, | ||
792 | core::string_view name) const noexcept -> | ||
793 | iterator | ||
794 | { | ||
795 | 3 | auto const it0 = begin(); | |
796 | for(;;) | ||
797 | { | ||
798 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 13 times.
|
14 | if(it == it0) |
799 | 1 | return end(); | |
800 | 13 | --it; | |
801 | 13 | if(grammar::ci_is_equal( | |
802 |
2/2✓ Branch 2 taken 2 times.
✓ Branch 3 taken 11 times.
|
26 | it->name, name)) |
803 | 2 | return it; | |
804 | } | ||
805 | } | ||
806 | |||
807 | core::string_view | ||
808 | 2 | fields_base:: | |
809 | value_or( | ||
810 | field id, | ||
811 | core::string_view s) const noexcept | ||
812 | { | ||
813 | 2 | auto it = find(id); | |
814 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
|
2 | if(it != end()) |
815 | 1 | return it->value; | |
816 | 1 | return s; | |
817 | } | ||
818 | |||
819 | core::string_view | ||
820 | 2 | fields_base:: | |
821 | value_or( | ||
822 | core::string_view name, | ||
823 | core::string_view s) const noexcept | ||
824 | { | ||
825 | 2 | auto it = find(name); | |
826 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
|
2 | if(it != end()) |
827 | 1 | return it->value; | |
828 | 1 | return s; | |
829 | } | ||
830 | |||
831 | //------------------------------------------------ | ||
832 | |||
833 | auto | ||
834 | 16 | fields_base:: | |
835 | find_all( | ||
836 | field id) const noexcept -> | ||
837 | subrange | ||
838 | { | ||
839 | 16 | return subrange( | |
840 | 32 | &h_, find(id).i_); | |
841 | } | ||
842 | |||
843 | auto | ||
844 | 5 | fields_base:: | |
845 | find_all( | ||
846 | core::string_view name) const noexcept -> | ||
847 | subrange | ||
848 | { | ||
849 | 5 | return subrange( | |
850 | 10 | &h_, find(name).i_); | |
851 | } | ||
852 | |||
853 | //------------------------------------------------ | ||
854 | |||
855 | std::ostream& | ||
856 | 1 | operator<<( | |
857 | std::ostream& os, | ||
858 | const fields_base& f) | ||
859 | { | ||
860 | 1 | return os << f.buffer(); | |
861 | } | ||
862 | |||
863 | //------------------------------------------------ | ||
864 | // | ||
865 | // Modifiers | ||
866 | // | ||
867 | //------------------------------------------------ | ||
868 | |||
869 | auto | ||
870 | 30 | fields_base:: | |
871 | erase( | ||
872 | iterator it) noexcept -> iterator | ||
873 | { | ||
874 | 30 | auto const id = it->id.value_or( | |
875 | detail::header::unknown_field); | ||
876 | 30 | raw_erase(it.i_); | |
877 | 30 | h_.on_erase(id); | |
878 | 30 | return it; | |
879 | } | ||
880 | |||
881 | std::size_t | ||
882 | 30 | fields_base:: | |
883 | erase( | ||
884 | field id) noexcept | ||
885 | { | ||
886 | 30 | auto const i0 = h_.find(id); | |
887 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 27 times.
|
30 | if(i0 == h_.count) |
888 | 3 | return 0; | |
889 | 27 | return erase_all(i0, id); | |
890 | } | ||
891 | |||
892 | std::size_t | ||
893 | 18 | fields_base:: | |
894 | erase( | ||
895 | core::string_view name) noexcept | ||
896 | { | ||
897 | 18 | auto const i0 = h_.find(name); | |
898 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 15 times.
|
18 | if(i0 == h_.count) |
899 | 3 | return 0; | |
900 | 15 | auto const ft = h_.tab(); | |
901 | 15 | auto const id = ft[i0].id; | |
902 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 9 times.
|
15 | if(id == detail::header::unknown_field) |
903 | 6 | return erase_all(i0, name); | |
904 | 9 | return erase_all(i0, id); | |
905 | } | ||
906 | |||
907 | //------------------------------------------------ | ||
908 | |||
909 | void | ||
910 | 28 | fields_base:: | |
911 | set( | ||
912 | iterator it, | ||
913 | core::string_view value, | ||
914 | system::error_code& ec) | ||
915 | { | ||
916 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | auto rv = verify_field_value(value); |
917 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 24 times.
|
28 | if(rv.has_error()) |
918 | { | ||
919 | 4 | ec = rv.error(); | |
920 | 4 | return; | |
921 | } | ||
922 | |||
923 | 24 | value = rv->value; | |
924 | 24 | bool has_obs_fold = rv->has_obs_fold; | |
925 | |||
926 | 24 | auto const i = it.i_; | |
927 | 24 | auto tab = h_.tab(); | |
928 | 24 | auto const& e0 = tab[i]; | |
929 | 24 | auto const pos0 = offset(i); | |
930 | 24 | auto const pos1 = offset(i + 1); | |
931 | std::ptrdiff_t dn = | ||
932 | 24 | value.size() - | |
933 | 24 | it->value.size(); | |
934 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 24 times.
|
24 | if( value.empty() && |
935 |
1/4✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 24 times.
|
24 | ! it->value.empty()) |
936 | ✗ | --dn; // remove SP | |
937 | 24 | else if( | |
938 |
2/4✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 24 times.
|
24 | it->value.empty() && |
939 | ✗ | ! value.empty()) | |
940 | ✗ | ++dn; // add SP | |
941 | |||
942 | 24 | op_t op(*this, &value); | |
943 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 18 times.
|
30 | if( dn > 0 && |
944 |
2/4✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 6 times.
✗ Branch 5 not taken.
|
12 | op.grow(value.size() - |
945 |
2/2✓ Branch 3 taken 6 times.
✓ Branch 4 taken 18 times.
|
30 | it->value.size(), 0)) |
946 | { | ||
947 | // reallocated | ||
948 | 6 | auto dest = h_.buf + | |
949 | 6 | pos0 + e0.nn + 1; | |
950 | 12 | std::memcpy( | |
951 | 6 | h_.buf, | |
952 | 6 | op.buf(), | |
953 | 6 | dest - h_.buf); | |
954 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | if(! value.empty()) |
955 | { | ||
956 | 6 | *dest++ = ' '; | |
957 |
1/2✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | value.copy( |
958 | dest, | ||
959 | value.size()); | ||
960 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
|
6 | if( has_obs_fold ) |
961 | 3 | detail::remove_obs_fold( | |
962 | 3 | dest, dest + value.size()); | |
963 | 6 | dest += value.size(); | |
964 | } | ||
965 | 6 | *dest++ = '\r'; | |
966 | 6 | *dest++ = '\n'; | |
967 | 12 | std::memcpy( | |
968 | 6 | h_.buf + pos1 + dn, | |
969 | 12 | op.buf() + pos1, | |
970 | 6 | h_.size - pos1); | |
971 | 12 | std::memcpy( | |
972 | 6 | h_.buf + h_.cap - | |
973 | 6 | sizeof(entry) * h_.count, | |
974 | 6 | &op.tab()[h_.count - 1], | |
975 | 6 | sizeof(entry) * h_.count); | |
976 | } | ||
977 | else | ||
978 | { | ||
979 | // copy the value first | ||
980 | 36 | auto dest = h_.buf + pos0 + | |
981 | 18 | it->name.size() + 1; | |
982 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | if(! value.empty()) |
983 | { | ||
984 | 18 | *dest++ = ' '; | |
985 |
1/2✓ Branch 2 taken 18 times.
✗ Branch 3 not taken.
|
18 | value.copy( |
986 | dest, | ||
987 | value.size()); | ||
988 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 18 times.
|
18 | if( has_obs_fold ) |
989 | ✗ | detail::remove_obs_fold( | |
990 | ✗ | dest, dest + value.size()); | |
991 | 18 | dest += value.size(); | |
992 | } | ||
993 | 18 | op.move_chars( | |
994 | 18 | h_.buf + pos1 + dn, | |
995 | 18 | h_.buf + pos1, | |
996 | 18 | h_.size - pos1); | |
997 | 18 | *dest++ = '\r'; | |
998 | 18 | *dest++ = '\n'; | |
999 | } | ||
1000 | { | ||
1001 | // update tab | ||
1002 | 24 | auto ft = h_.tab(); | |
1003 | 31 | for(std::size_t j = h_.count - 1; | |
1004 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 24 times.
|
31 | j > i; --j) |
1005 | 7 | ft[j] = ft[j] + dn; | |
1006 | 24 | auto& e = ft[i]; | |
1007 | 48 | e.vp = e.np + e.nn + | |
1008 | 24 | 1 + ! value.empty(); | |
1009 | 24 | e.vn = static_cast< | |
1010 | 24 | offset_type>(value.size()); | |
1011 | 24 | h_.size = static_cast< | |
1012 | 24 | offset_type>(h_.size + dn); | |
1013 | } | ||
1014 |
1/2✓ Branch 3 taken 24 times.
✗ Branch 4 not taken.
|
24 | auto const id = it->id.value_or( |
1015 | detail::header::unknown_field); | ||
1016 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 15 times.
|
24 | if(h_.is_special(id)) |
1017 | { | ||
1018 | // replace first char of name | ||
1019 | // with null to hide metadata | ||
1020 | 9 | char saved = h_.buf[pos0]; | |
1021 | 9 | auto& e = h_.tab()[i]; | |
1022 | 9 | e.id = detail::header::unknown_field; | |
1023 | 9 | h_.buf[pos0] = '\0'; | |
1024 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | h_.on_erase(id); |
1025 | 9 | h_.buf[pos0] = saved; // restore | |
1026 | 9 | e.id = id; | |
1027 |
1/2✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
|
9 | h_.on_insert(id, it->value); |
1028 | } | ||
1029 | 24 | } | |
1030 | |||
1031 | // erase existing fields with id | ||
1032 | // and then add the field with value | ||
1033 | void | ||
1034 | 110 | fields_base:: | |
1035 | set( | ||
1036 | field id, | ||
1037 | core::string_view value, | ||
1038 | system::error_code& ec) | ||
1039 | { | ||
1040 |
1/2✓ Branch 1 taken 110 times.
✗ Branch 2 not taken.
|
110 | auto rv = verify_field_value(value); |
1041 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 106 times.
|
110 | if(rv.has_error()) |
1042 | { | ||
1043 | 4 | ec = rv.error(); | |
1044 | 4 | return; | |
1045 | } | ||
1046 | |||
1047 | 106 | auto const i0 = h_.find(id); | |
1048 |
2/2✓ Branch 0 taken 21 times.
✓ Branch 1 taken 85 times.
|
106 | if(i0 != h_.count) |
1049 | { | ||
1050 | // field exists | ||
1051 | 21 | auto const ft = h_.tab(); | |
1052 | { | ||
1053 | // provide strong guarantee | ||
1054 | auto const n0 = | ||
1055 | 21 | h_.size - length(i0); | |
1056 | auto const n = | ||
1057 | 21 | ft[i0].nn + 2 + | |
1058 | 21 | rv->value.size() + 2; | |
1059 | // VFALCO missing overflow check | ||
1060 |
1/2✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
|
21 | reserve_bytes(n0 + n); |
1061 | } | ||
1062 | 21 | erase_all(i0, id); | |
1063 | } | ||
1064 | |||
1065 |
3/6✓ Branch 1 taken 106 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 106 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 106 times.
✗ Branch 8 not taken.
|
106 | insert_unchecked( |
1066 | id, | ||
1067 | to_string(id), | ||
1068 | 106 | rv->value, | |
1069 | 106 | h_.count, | |
1070 | 106 | rv->has_obs_fold); | |
1071 | } | ||
1072 | |||
1073 | // erase existing fields with name | ||
1074 | // and then add the field with value | ||
1075 | void | ||
1076 | 32 | fields_base:: | |
1077 | set( | ||
1078 | core::string_view name, | ||
1079 | core::string_view value, | ||
1080 | system::error_code& ec) | ||
1081 | { | ||
1082 |
1/2✓ Branch 1 taken 32 times.
✗ Branch 2 not taken.
|
32 | verify_field_name(name , ec); |
1083 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 28 times.
|
32 | if(ec.failed()) |
1084 | 8 | return; | |
1085 | |||
1086 |
1/2✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
|
28 | auto rv = verify_field_value(value); |
1087 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 24 times.
|
28 | if(rv.has_error()) |
1088 | { | ||
1089 | 4 | ec = rv.error(); | |
1090 | 4 | return; | |
1091 | } | ||
1092 | |||
1093 | 24 | auto const i0 = h_.find(name); | |
1094 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 6 times.
|
24 | if(i0 != h_.count) |
1095 | { | ||
1096 | // field exists | ||
1097 | 18 | auto const ft = h_.tab(); | |
1098 | 18 | auto const id = ft[i0].id; | |
1099 | { | ||
1100 | // provide strong guarantee | ||
1101 | auto const n0 = | ||
1102 | 18 | h_.size - length(i0); | |
1103 | auto const n = | ||
1104 | 18 | ft[i0].nn + 2 + | |
1105 | 18 | rv->value.size() + 2; | |
1106 | // VFALCO missing overflow check | ||
1107 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
18 | reserve_bytes(n0 + n); |
1108 | } | ||
1109 | // VFALCO simple algorithm but | ||
1110 | // costs one extra memmove | ||
1111 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 3 times.
|
18 | if(id != detail::header::unknown_field) |
1112 | 15 | erase_all(i0, id); | |
1113 | else | ||
1114 | 3 | erase_all(i0, name); | |
1115 | } | ||
1116 |
2/2✓ Branch 2 taken 23 times.
✓ Branch 3 taken 1 times.
|
24 | insert_unchecked( |
1117 | string_to_field(name), | ||
1118 | name, | ||
1119 | 24 | rv->value, | |
1120 | 24 | h_.count, | |
1121 | 24 | rv->has_obs_fold); | |
1122 | } | ||
1123 | |||
1124 | auto | ||
1125 | 26 | fields_base:: | |
1126 | insert( | ||
1127 | iterator before, | ||
1128 | field id, | ||
1129 | core::string_view value) | ||
1130 | -> iterator | ||
1131 | { | ||
1132 | 26 | system::error_code ec; | |
1133 |
1/2✓ Branch 1 taken 26 times.
✗ Branch 2 not taken.
|
26 | auto const it = insert(before, id, value, ec); |
1134 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 25 times.
|
26 | if(ec.failed()) |
1135 | 1 | detail::throw_system_error(ec); | |
1136 | 25 | return it; | |
1137 | } | ||
1138 | |||
1139 | auto | ||
1140 | 33 | fields_base:: | |
1141 | insert( | ||
1142 | iterator before, | ||
1143 | field id, | ||
1144 | core::string_view value, | ||
1145 | system::error_code& ec) | ||
1146 | -> iterator | ||
1147 | { | ||
1148 |
2/4✓ Branch 2 taken 33 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 33 times.
✗ Branch 6 not taken.
|
33 | insert_impl( |
1149 | id, | ||
1150 | to_string(id), | ||
1151 | value, | ||
1152 | before.i_, ec); | ||
1153 | 33 | return before; | |
1154 | } | ||
1155 | |||
1156 | auto | ||
1157 | 13 | fields_base:: | |
1158 | insert( | ||
1159 | iterator before, | ||
1160 | core::string_view name, | ||
1161 | core::string_view value) | ||
1162 | -> iterator | ||
1163 | { | ||
1164 | 13 | system::error_code ec; | |
1165 |
1/2✓ Branch 1 taken 13 times.
✗ Branch 2 not taken.
|
13 | insert(before, name, value, ec); |
1166 |
2/2✓ Branch 1 taken 1 times.
✓ Branch 2 taken 12 times.
|
13 | if(ec.failed()) |
1167 | 1 | detail::throw_system_error(ec); | |
1168 | 12 | return before; | |
1169 | } | ||
1170 | |||
1171 | auto | ||
1172 | 16 | fields_base:: | |
1173 | insert( | ||
1174 | iterator before, | ||
1175 | core::string_view name, | ||
1176 | core::string_view value, | ||
1177 | system::error_code& ec) | ||
1178 | -> iterator | ||
1179 | { | ||
1180 | 16 | insert_impl( | |
1181 | string_to_field(name), | ||
1182 | name, | ||
1183 | value, | ||
1184 | before.i_, | ||
1185 | ec); | ||
1186 | 16 | return before; | |
1187 | } | ||
1188 | |||
1189 | void | ||
1190 | 23 | fields_base:: | |
1191 | set( | ||
1192 | iterator it, | ||
1193 | core::string_view value) | ||
1194 | { | ||
1195 | 23 | system::error_code ec; | |
1196 |
1/2✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
23 | set(it, value, ec); |
1197 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 21 times.
|
23 | if(ec.failed()) |
1198 | 2 | detail::throw_system_error(ec); | |
1199 | 21 | } | |
1200 | |||
1201 | //------------------------------------------------ | ||
1202 | // | ||
1203 | // (implementation) | ||
1204 | // | ||
1205 | //------------------------------------------------ | ||
1206 | |||
1207 | // copy start line and fields | ||
1208 | void | ||
1209 | 16 | fields_base:: | |
1210 | copy_impl( | ||
1211 | detail::header const& h) | ||
1212 | { | ||
1213 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | BOOST_ASSERT( |
1214 | h.kind == h_.kind); | ||
1215 | |||
1216 | auto const n = | ||
1217 | 16 | detail::header::bytes_needed( | |
1218 | 16 | h.size, h.count); | |
1219 |
7/8✓ Branch 0 taken 11 times.
✓ Branch 1 taken 5 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 8 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 3 times.
✓ Branch 7 taken 8 times.
✓ Branch 8 taken 8 times.
|
16 | if(n <= h_.cap && (!h.is_default() || external_storage_)) |
1220 | { | ||
1221 | // no realloc | ||
1222 | 8 | h.assign_to(h_); | |
1223 | 8 | h.copy_table( | |
1224 | 8 | h_.buf + h_.cap); | |
1225 | 8 | std::memcpy( | |
1226 | 8 | h_.buf, | |
1227 | 8 | h.cbuf, | |
1228 | 8 | h.size); | |
1229 | 8 | return; | |
1230 | } | ||
1231 | |||
1232 | // static storages cannot reallocate | ||
1233 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
8 | if(external_storage_) |
1234 | ✗ | detail::throw_length_error(); | |
1235 | |||
1236 |
1/2✓ Branch 1 taken 8 times.
✗ Branch 2 not taken.
|
8 | fields_base tmp(h); |
1237 | 8 | tmp.h_.swap(h_); | |
1238 | 8 | } | |
1239 | |||
1240 | void | ||
1241 | 203 | fields_base:: | |
1242 | insert_impl( | ||
1243 | optional<field> id, | ||
1244 | core::string_view name, | ||
1245 | core::string_view value, | ||
1246 | std::size_t before, | ||
1247 | system::error_code& ec) | ||
1248 | { | ||
1249 |
1/2✓ Branch 1 taken 203 times.
✗ Branch 2 not taken.
|
203 | verify_field_name(name, ec); |
1250 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 2 taken 198 times.
|
203 | if(ec.failed()) |
1251 | 23 | return; | |
1252 | |||
1253 |
1/2✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
|
198 | auto rv = verify_field_value(value); |
1254 |
2/2✓ Branch 1 taken 18 times.
✓ Branch 2 taken 180 times.
|
198 | if(rv.has_error()) |
1255 | { | ||
1256 | 18 | ec = rv.error(); | |
1257 | 18 | return; | |
1258 | } | ||
1259 | |||
1260 |
2/2✓ Branch 1 taken 174 times.
✓ Branch 2 taken 6 times.
|
180 | insert_unchecked( |
1261 | id, | ||
1262 | name, | ||
1263 | 180 | rv->value, | |
1264 | before, | ||
1265 | 180 | rv->has_obs_fold); | |
1266 | } | ||
1267 | |||
1268 | void | ||
1269 | 310 | fields_base:: | |
1270 | insert_unchecked( | ||
1271 | optional<field> id, | ||
1272 | core::string_view name, | ||
1273 | core::string_view value, | ||
1274 | std::size_t before, | ||
1275 | bool has_obs_fold) | ||
1276 | { | ||
1277 | 310 | auto const tab0 = h_.tab_(); | |
1278 | 310 | auto const pos = offset(before); | |
1279 | auto const n = | ||
1280 | 310 | name.size() + // name | |
1281 | 310 | 1 + // ':' | |
1282 | 310 | ! value.empty() + // [SP] | |
1283 | 310 | value.size() + // value | |
1284 | 310 | 2; // CRLF | |
1285 | |||
1286 | 310 | op_t op(*this, &name, &value); | |
1287 |
4/4✓ Branch 1 taken 303 times.
✓ Branch 2 taken 7 times.
✓ Branch 3 taken 218 times.
✓ Branch 4 taken 85 times.
|
310 | if(op.grow(n, 1)) |
1288 | { | ||
1289 | // reallocated | ||
1290 |
2/2✓ Branch 0 taken 197 times.
✓ Branch 1 taken 21 times.
|
218 | if(pos > 0) |
1291 | 197 | std::memcpy( | |
1292 | 197 | h_.buf, | |
1293 | 197 | op.cbuf(), | |
1294 | pos); | ||
1295 |
2/2✓ Branch 0 taken 89 times.
✓ Branch 1 taken 129 times.
|
218 | if(before > 0) |
1296 | 178 | std::memcpy( | |
1297 | 89 | h_.tab_() - before, | |
1298 | 89 | tab0 - before, | |
1299 | before * sizeof(entry)); | ||
1300 | 436 | std::memcpy( | |
1301 | 218 | h_.buf + pos + n, | |
1302 | 218 | op.cbuf() + pos, | |
1303 | 218 | h_.size - pos); | |
1304 | } | ||
1305 | else | ||
1306 | { | ||
1307 | 85 | op.move_chars( | |
1308 | 85 | h_.buf + pos + n, | |
1309 | 85 | h_.buf + pos, | |
1310 | 85 | h_.size - pos); | |
1311 | } | ||
1312 | |||
1313 | // serialize | ||
1314 | { | ||
1315 | 303 | auto dest = h_.buf + pos; | |
1316 |
1/2✓ Branch 2 taken 303 times.
✗ Branch 3 not taken.
|
303 | name.copy(dest, name.size()); |
1317 | 303 | dest += name.size(); | |
1318 | 303 | *dest++ = ':'; | |
1319 |
2/2✓ Branch 1 taken 291 times.
✓ Branch 2 taken 12 times.
|
303 | if(! value.empty()) |
1320 | { | ||
1321 | 291 | *dest++ = ' '; | |
1322 |
1/2✓ Branch 2 taken 291 times.
✗ Branch 3 not taken.
|
291 | value.copy( |
1323 | dest, value.size()); | ||
1324 |
2/2✓ Branch 0 taken 18 times.
✓ Branch 1 taken 273 times.
|
291 | if( has_obs_fold ) |
1325 | 18 | detail::remove_obs_fold( | |
1326 | 18 | dest, dest + value.size()); | |
1327 | 291 | dest += value.size(); | |
1328 | } | ||
1329 | 303 | *dest++ = '\r'; | |
1330 | 303 | *dest = '\n'; | |
1331 | } | ||
1332 | |||
1333 | // update table | ||
1334 | 303 | auto const tab = h_.tab_(); | |
1335 | { | ||
1336 | 303 | auto i = h_.count - before; | |
1337 |
2/2✓ Branch 0 taken 43 times.
✓ Branch 1 taken 260 times.
|
303 | if(i > 0) |
1338 | { | ||
1339 | 43 | auto p0 = tab0 - h_.count; | |
1340 | 43 | auto p = tab - h_.count - 1; | |
1341 | do | ||
1342 | { | ||
1343 | 80 | *p++ = *p0++ + n; | |
1344 | } | ||
1345 |
2/2✓ Branch 0 taken 37 times.
✓ Branch 1 taken 43 times.
|
80 | while(--i); |
1346 | } | ||
1347 | } | ||
1348 | 303 | auto& e = tab[0 - static_cast<std::ptrdiff_t>(before) - 1]; | |
1349 | 303 | e.np = static_cast<offset_type>( | |
1350 | 303 | pos - h_.prefix); | |
1351 | 303 | e.nn = static_cast< | |
1352 | 303 | offset_type>(name.size()); | |
1353 | 303 | e.vp = static_cast<offset_type>( | |
1354 | 606 | pos - h_.prefix + | |
1355 | 303 | name.size() + 1 + | |
1356 | 303 | ! value.empty()); | |
1357 | 303 | e.vn = static_cast< | |
1358 | 303 | offset_type>(value.size()); | |
1359 |
1/2✓ Branch 1 taken 303 times.
✗ Branch 2 not taken.
|
303 | e.id = id.value_or( |
1360 | detail::header::unknown_field); | ||
1361 | |||
1362 | // update container | ||
1363 | 303 | h_.count++; | |
1364 | 303 | h_.size = static_cast< | |
1365 | 303 | offset_type>(h_.size + n); | |
1366 |
1/2✓ Branch 1 taken 303 times.
✗ Branch 2 not taken.
|
303 | h_.on_insert(e.id, value); |
1367 | 310 | } | |
1368 | |||
1369 | void | ||
1370 | 169 | fields_base:: | |
1371 | raw_erase( | ||
1372 | std::size_t i) noexcept | ||
1373 | { | ||
1374 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 169 times.
|
169 | BOOST_ASSERT(i < h_.count); |
1375 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 169 times.
|
169 | BOOST_ASSERT(h_.buf != nullptr); |
1376 | 169 | auto const p0 = offset(i); | |
1377 | 169 | auto const p1 = offset(i + 1); | |
1378 | 169 | std::memmove( | |
1379 | 169 | h_.buf + p0, | |
1380 | 169 | h_.buf + p1, | |
1381 | 169 | h_.size - p1); | |
1382 | 169 | auto const n = p1 - p0; | |
1383 | 169 | --h_.count; | |
1384 | 169 | auto ft = h_.tab(); | |
1385 |
2/2✓ Branch 0 taken 101 times.
✓ Branch 1 taken 169 times.
|
270 | for(;i < h_.count; ++i) |
1386 | 101 | ft[i] = ft[i + 1] - n; | |
1387 | 169 | h_.size = static_cast< | |
1388 | 169 | offset_type>(h_.size - n); | |
1389 | 169 | } | |
1390 | |||
1391 | // erase n fields matching id | ||
1392 | // without updating metadata | ||
1393 | void | ||
1394 | 4 | fields_base:: | |
1395 | raw_erase_n( | ||
1396 | field id, | ||
1397 | std::size_t n) noexcept | ||
1398 | { | ||
1399 | // iterate in reverse | ||
1400 | 4 | auto e = &h_.tab()[h_.count]; | |
1401 | 4 | auto const e0 = &h_.tab()[0]; | |
1402 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
|
10 | while(n > 0) |
1403 | { | ||
1404 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | BOOST_ASSERT(e != e0); |
1405 | 6 | ++e; // decrement | |
1406 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
|
6 | if(e->id == id) |
1407 | { | ||
1408 | 5 | raw_erase(e0 - e); | |
1409 | 5 | --n; | |
1410 | } | ||
1411 | } | ||
1412 | 4 | } | |
1413 | |||
1414 | // erase all fields with id | ||
1415 | // and update metadata | ||
1416 | std::size_t | ||
1417 | 72 | fields_base:: | |
1418 | erase_all( | ||
1419 | std::size_t i0, | ||
1420 | field id) noexcept | ||
1421 | { | ||
1422 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 72 times.
|
72 | BOOST_ASSERT( |
1423 | id != detail::header::unknown_field); | ||
1424 | 72 | std::size_t n = 1; | |
1425 | 72 | std::size_t i = h_.count - 1; | |
1426 | 72 | auto const ft = h_.tab(); | |
1427 |
2/2✓ Branch 0 taken 77 times.
✓ Branch 1 taken 72 times.
|
149 | while(i > i0) |
1428 | { | ||
1429 |
2/2✓ Branch 1 taken 44 times.
✓ Branch 2 taken 33 times.
|
77 | if(ft[i].id == id) |
1430 | { | ||
1431 | 44 | raw_erase(i); | |
1432 | 44 | ++n; | |
1433 | } | ||
1434 | // go backwards to | ||
1435 | // reduce memmoves | ||
1436 | 77 | --i; | |
1437 | } | ||
1438 | 72 | raw_erase(i0); | |
1439 | 72 | h_.on_erase_all(id); | |
1440 | 72 | return n; | |
1441 | } | ||
1442 | |||
1443 | // erase all fields with name | ||
1444 | // when id == detail::header::unknown_field | ||
1445 | std::size_t | ||
1446 | 9 | fields_base:: | |
1447 | erase_all( | ||
1448 | std::size_t i0, | ||
1449 | core::string_view name) noexcept | ||
1450 | { | ||
1451 | 9 | std::size_t n = 1; | |
1452 | 9 | std::size_t i = h_.count - 1; | |
1453 | 9 | auto const ft = h_.tab(); | |
1454 | 9 | auto const* p = h_.cbuf + h_.prefix; | |
1455 |
2/2✓ Branch 0 taken 27 times.
✓ Branch 1 taken 9 times.
|
36 | while(i > i0) |
1456 | { | ||
1457 | core::string_view s( | ||
1458 | 27 | p + ft[i].np, ft[i].nn); | |
1459 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 18 times.
|
27 | if(s == name) |
1460 | { | ||
1461 | 9 | raw_erase(i); | |
1462 | 9 | ++n; | |
1463 | } | ||
1464 | // go backwards to | ||
1465 | // reduce memmoves | ||
1466 | 27 | --i; | |
1467 | } | ||
1468 | 9 | raw_erase(i0); | |
1469 | 9 | return n; | |
1470 | } | ||
1471 | |||
1472 | // return i-th field absolute offset | ||
1473 | std::size_t | ||
1474 | 774 | fields_base:: | |
1475 | offset( | ||
1476 | std::size_t i) const noexcept | ||
1477 | { | ||
1478 |
2/2✓ Branch 0 taken 310 times.
✓ Branch 1 taken 464 times.
|
774 | if(i == 0) |
1479 | 310 | return h_.prefix; | |
1480 |
2/2✓ Branch 0 taken 219 times.
✓ Branch 1 taken 245 times.
|
464 | if(i < h_.count) |
1481 | 219 | return h_.prefix + h_.tab()[i].np; | |
1482 | // make final CRLF the last "field" | ||
1483 | 245 | return h_.size - 2; | |
1484 | } | ||
1485 | |||
1486 | // return i-th field absolute length | ||
1487 | std::size_t | ||
1488 | 39 | fields_base:: | |
1489 | length( | ||
1490 | std::size_t i) const noexcept | ||
1491 | { | ||
1492 | return | ||
1493 | 39 | offset(i + 1) - | |
1494 | 39 | offset(i); | |
1495 | } | ||
1496 | |||
1497 | } // http_proto | ||
1498 | } // boost | ||
1499 |