Line | Branch | Exec | Source |
---|---|---|---|
1 | // | ||
2 | // Copyright (c) 2021 Vinnie Falco (vinnie.falco@gmail.com) | ||
3 | // | ||
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | ||
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | ||
6 | // | ||
7 | // Official repository: https://github.com/cppalliance/http_proto | ||
8 | // | ||
9 | |||
10 | #include <boost/http_proto/fields_base.hpp> | ||
11 | |||
12 | #include <boost/http_proto/error.hpp> | ||
13 | #include <boost/http_proto/field.hpp> | ||
14 | #include <boost/http_proto/header_limits.hpp> | ||
15 | #include <boost/http_proto/rfc/detail/rules.hpp> | ||
16 | #include <boost/http_proto/rfc/token_rule.hpp> | ||
17 | |||
18 | #include <boost/http_proto/detail/config.hpp> | ||
19 | #include <boost/http_proto/detail/except.hpp> | ||
20 | #include <boost/http_proto/detail/header.hpp> | ||
21 | |||
22 | #include <boost/assert.hpp> | ||
23 | #include <boost/assert/source_location.hpp> | ||
24 | |||
25 | #include <boost/core/detail/string_view.hpp> | ||
26 | |||
27 | #include <boost/system/result.hpp> | ||
28 | |||
29 | #include <boost/url/grammar/ci_string.hpp> | ||
30 | #include <boost/url/grammar/error.hpp> | ||
31 | #include <boost/url/grammar/parse.hpp> | ||
32 | #include <boost/url/grammar/token_rule.hpp> | ||
33 | |||
34 | #include "detail/align_up.hpp" | ||
35 | #include "detail/move_chars.hpp" | ||
36 | #include "rfc/detail/rules.hpp" | ||
37 | |||
38 | namespace boost { | ||
39 | namespace http_proto { | ||
40 | |||
41 | static | ||
42 | system::result<core::string_view> | ||
43 | 233 | verify_field_name( | |
44 | core::string_view name) | ||
45 | { | ||
46 | auto rv = | ||
47 | 233 | grammar::parse(name, detail::field_name_rule); | |
48 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 227 times.
|
233 | if( rv.has_error() ) |
49 | { | ||
50 | 6 | auto ec = rv.error(); | |
51 |
2/2✓ Branch 2 taken 3 times.
✓ Branch 3 taken 3 times.
|
6 | if( ec == urls::grammar::error::leftover ) |
52 | 3 | return error::bad_field_name; | |
53 |
2/2✓ Branch 2 taken 1 times.
✓ Branch 3 taken 2 times.
|
3 | if( ec == condition::need_more_input ) |
54 | 1 | return error::bad_field_name; | |
55 | } | ||
56 | 229 | return rv; | |
57 | } | ||
58 | |||
59 | static | ||
60 | system::result<typename detail::field_value_rule_t::value_type> | ||
61 | 273 | verify_field_value( | |
62 | core::string_view value) | ||
63 | { | ||
64 | 273 | auto it = value.begin(); | |
65 | 273 | auto end = value.end(); | |
66 | auto rv = | ||
67 | 273 | grammar::parse(it, end, detail::field_value_rule); | |
68 |
2/2✓ Branch 1 taken 5 times.
✓ Branch 2 taken 268 times.
|
273 | if( rv.has_error() ) |
69 | { | ||
70 |
1/2✓ Branch 3 taken 5 times.
✗ Branch 4 not taken.
|
5 | if( rv.error() == condition::need_more_input ) |
71 | 5 | return error::bad_field_value; | |
72 | ✗ | return rv.error(); | |
73 | } | ||
74 | |||
75 |
2/2✓ Branch 1 taken 7 times.
✓ Branch 2 taken 261 times.
|
268 | if( rv->has_crlf ) |
76 | 7 | return error::bad_field_smuggle; | |
77 | |||
78 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 254 times.
|
261 | if( it != end ) |
79 | 7 | return error::bad_field_value; | |
80 | |||
81 | 254 | return rv; | |
82 | } | ||
83 | |||
84 | class fields_base:: | ||
85 | op_t | ||
86 | { | ||
87 | fields_base& self_; | ||
88 | core::string_view* s0_; | ||
89 | core::string_view* s1_; | ||
90 | char* buf_ = nullptr; | ||
91 | char const* cbuf_ = nullptr; | ||
92 | std::size_t cap_ = 0; | ||
93 | |||
94 | public: | ||
95 | explicit | ||
96 | 901 | op_t( | |
97 | fields_base& self, | ||
98 | core::string_view* s0 = nullptr, | ||
99 | core::string_view* s1 = nullptr) noexcept | ||
100 | 901 | : self_(self) | |
101 | , s0_(s0) | ||
102 | 901 | , s1_(s1) | |
103 | { | ||
104 | 901 | } | |
105 | |||
106 | 901 | ~op_t() | |
107 | 901 | { | |
108 |
2/2✓ Branch 0 taken 110 times.
✓ Branch 1 taken 791 times.
|
901 | if(buf_) |
109 |
1/2✓ Branch 0 taken 110 times.
✗ Branch 1 not taken.
|
110 | delete[] buf_; |
110 | 901 | } | |
111 | |||
112 | char const* | ||
113 | 12 | buf() const noexcept | |
114 | { | ||
115 | 12 | return buf_; | |
116 | } | ||
117 | |||
118 | char const* | ||
119 | 240 | cbuf() const noexcept | |
120 | { | ||
121 | 240 | return cbuf_; | |
122 | } | ||
123 | |||
124 | char* | ||
125 | 12 | end() const noexcept | |
126 | { | ||
127 | 12 | return buf_ + cap_; | |
128 | } | ||
129 | |||
130 | table | ||
131 | 6 | tab() const noexcept | |
132 | { | ||
133 | 6 | return table(end()); | |
134 | } | ||
135 | |||
136 | static | ||
137 | std::size_t | ||
138 | growth( | ||
139 | std::size_t n0, | ||
140 | std::size_t m) noexcept; | ||
141 | |||
142 | bool | ||
143 | reserve(std::size_t bytes); | ||
144 | |||
145 | bool | ||
146 | grow( | ||
147 | std::size_t extra_char, | ||
148 | std::size_t extra_field); | ||
149 | |||
150 | void | ||
151 | copy_prefix( | ||
152 | std::size_t n, | ||
153 | std::size_t i) noexcept; | ||
154 | |||
155 | void | ||
156 | move_chars( | ||
157 | char* dest, | ||
158 | char const* src, | ||
159 | std::size_t n) const noexcept; | ||
160 | }; | ||
161 | |||
162 | /* Growth functions for containers | ||
163 | |||
164 | N1 = g( N0, M ); | ||
165 | |||
166 | g = growth function | ||
167 | M = minimum capacity | ||
168 | N0 = old size | ||
169 | N1 = new size | ||
170 | */ | ||
171 | std::size_t | ||
172 | 1631 | fields_base:: | |
173 | op_t:: | ||
174 | growth( | ||
175 | std::size_t n0, | ||
176 | std::size_t m) noexcept | ||
177 | { | ||
178 | auto const m1 = | ||
179 | 1631 | detail::align_up(m, alignof(entry)); | |
180 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1631 times.
|
1631 | BOOST_ASSERT(m1 >= m); |
181 |
2/2✓ Branch 0 taken 1149 times.
✓ Branch 1 taken 482 times.
|
1631 | if(n0 == 0) |
182 | { | ||
183 | // exact | ||
184 | 1149 | return m1; | |
185 | } | ||
186 |
2/2✓ Branch 0 taken 213 times.
✓ Branch 1 taken 269 times.
|
482 | if(m1 > n0) |
187 | 213 | return m1; | |
188 | 269 | return n0; | |
189 | } | ||
190 | |||
191 | bool | ||
192 | 884 | fields_base:: | |
193 | op_t:: | ||
194 | reserve( | ||
195 | std::size_t bytes) | ||
196 | { | ||
197 |
2/2✓ Branch 1 taken 34 times.
✓ Branch 2 taken 850 times.
|
884 | if(bytes > self_.max_capacity_in_bytes()) |
198 | { | ||
199 | // max capacity exceeded | ||
200 | 34 | detail::throw_length_error(); | |
201 | } | ||
202 | 850 | auto n = growth( | |
203 | 850 | self_.h_.cap, bytes); | |
204 |
2/2✓ Branch 0 taken 152 times.
✓ Branch 1 taken 698 times.
|
850 | if(n <= self_.h_.cap) |
205 | 152 | return false; | |
206 | 698 | auto buf = new char[n]; | |
207 | 698 | buf_ = self_.h_.buf; | |
208 | 698 | cbuf_ = self_.h_.cbuf; | |
209 | 698 | cap_ = self_.h_.cap; | |
210 | 698 | self_.h_.buf = buf; | |
211 | 698 | self_.h_.cbuf = buf; | |
212 | 698 | self_.h_.cap = n; | |
213 | 698 | return true; | |
214 | } | ||
215 | |||
216 | bool | ||
217 | 783 | fields_base:: | |
218 | op_t:: | ||
219 | grow( | ||
220 | std::size_t extra_char, | ||
221 | std::size_t extra_field) | ||
222 | { | ||
223 | // extra_field is naturally limited | ||
224 | // by max_offset, since each field | ||
225 | // is at least 4 bytes: "X:\r\n" | ||
226 |
2/4✓ Branch 0 taken 783 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 783 times.
✗ Branch 3 not taken.
|
783 | BOOST_ASSERT( |
227 | extra_field <= max_offset && | ||
228 | extra_field <= static_cast< | ||
229 | std::size_t>( | ||
230 | max_offset - self_.h_.count)); | ||
231 |
2/2✓ Branch 0 taken 781 times.
✓ Branch 1 taken 2 times.
|
783 | if( extra_char > max_offset || |
232 | 781 | extra_char > static_cast<std::size_t>( | |
233 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 781 times.
|
781 | max_offset - self_.h_.size)) |
234 | 2 | detail::throw_length_error(); | |
235 | 1562 | auto n1 = growth( | |
236 | 781 | self_.h_.cap, | |
237 | detail::header::bytes_needed( | ||
238 | 781 | self_.h_.size + extra_char, | |
239 | 781 | self_.h_.count + extra_field)); | |
240 | 781 | return reserve(n1); | |
241 | } | ||
242 | |||
243 | void | ||
244 | ✗ | fields_base:: | |
245 | op_t:: | ||
246 | copy_prefix( | ||
247 | std::size_t n, | ||
248 | std::size_t i) noexcept | ||
249 | { | ||
250 | // copy first n chars | ||
251 | ✗ | std::memcpy( | |
252 | ✗ | self_.h_.buf, | |
253 | ✗ | cbuf_, | |
254 | n); | ||
255 | // copy first i entries | ||
256 | ✗ | if(i > 0) | |
257 | ✗ | std::memcpy( | |
258 | ✗ | self_.h_.tab_() - i, | |
259 | reinterpret_cast<entry*>( | ||
260 | ✗ | buf_ + cap_) - i, | |
261 | i * sizeof(entry)); | ||
262 | ✗ | } | |
263 | |||
264 | void | ||
265 | 133 | fields_base:: | |
266 | op_t:: | ||
267 | move_chars( | ||
268 | char* dest, | ||
269 | char const* src, | ||
270 | std::size_t n) const noexcept | ||
271 | { | ||
272 | 133 | detail::move_chars( | |
273 | 133 | dest, src, n, s0_, s1_); | |
274 | 133 | } | |
275 | |||
276 | //------------------------------------------------ | ||
277 | |||
278 | 228 | fields_base:: | |
279 | fields_base( | ||
280 | ✗ | detail::kind k) noexcept | |
281 | 228 | : fields_base(k, 0) | |
282 | { | ||
283 | 228 | } | |
284 | |||
285 | 252 | fields_base:: | |
286 | fields_base( | ||
287 | detail::kind k, | ||
288 | ✗ | std::size_t size) | |
289 | ✗ | : fields_view_base(&h_) | |
290 | 252 | , h_(k) | |
291 | { | ||
292 |
2/2✓ Branch 0 taken 9 times.
✓ Branch 1 taken 117 times.
|
252 | if( size > 0 ) |
293 | { | ||
294 | 18 | h_.max_cap = detail::align_up( | |
295 | size, alignof(detail::header::entry)); | ||
296 | 18 | reserve_bytes(size); | |
297 | } | ||
298 | 252 | } | |
299 | |||
300 | 48 | fields_base:: | |
301 | fields_base( | ||
302 | detail::kind k, | ||
303 | std::size_t size, | ||
304 | ✗ | std::size_t max_size) | |
305 | ✗ | : fields_view_base(&h_) | |
306 | 48 | , h_(k) | |
307 | { | ||
308 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 18 times.
|
48 | if( size > max_size ) |
309 | 12 | detail::throw_length_error(); | |
310 | |||
311 | 36 | h_.max_cap = detail::align_up( | |
312 | max_size, alignof(detail::header::entry)); | ||
313 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 3 times.
|
36 | if( size > 0 ) |
314 | { | ||
315 | 30 | reserve_bytes(size); | |
316 | } | ||
317 | 36 | } | |
318 | |||
319 | // copy s and parse it | ||
320 | 1058 | fields_base:: | |
321 | fields_base( | ||
322 | detail::kind k, | ||
323 | ✗ | core::string_view s) | |
324 | ✗ | : fields_view_base(&h_) | |
325 | 1058 | , h_(detail::empty{k}) | |
326 | { | ||
327 | 1058 | auto n = detail::header::count_crlf(s); | |
328 |
2/2✓ Branch 0 taken 241 times.
✓ Branch 1 taken 288 times.
|
1058 | if(h_.kind == detail::kind::fields) |
329 | { | ||
330 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 240 times.
|
482 | if(n < 1) |
331 | 2 | detail::throw_invalid_argument(); | |
332 | 480 | n -= 1; | |
333 | } | ||
334 | else | ||
335 | { | ||
336 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 286 times.
|
576 | if(n < 2) |
337 | 4 | detail::throw_invalid_argument(); | |
338 | 572 | n -= 2; | |
339 | } | ||
340 | 2104 | op_t op(*this); | |
341 |
1/2✓ Branch 2 taken 526 times.
✗ Branch 3 not taken.
|
1052 | op.grow(s.size(), n); |
342 |
1/2✓ Branch 2 taken 526 times.
✗ Branch 3 not taken.
|
1052 | s.copy(h_.buf, s.size()); |
343 | 1052 | system::error_code ec; | |
344 | // VFALCO This is using defaults? | ||
345 | 1052 | header_limits lim; | |
346 | 1052 | h_.parse(s.size(), lim, ec); | |
347 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 526 times.
|
1052 | if(ec.failed()) |
348 | ✗ | detail::throw_system_error(ec); | |
349 | 1052 | } | |
350 | |||
351 | // construct a complete copy of h | ||
352 | 52 | fields_base:: | |
353 | fields_base( | ||
354 | 28 | detail::header const& h) | |
355 | 28 | : fields_view_base(&h_) | |
356 | 52 | , h_(h.kind) | |
357 | { | ||
358 |
2/2✓ Branch 1 taken 8 times.
✓ Branch 2 taken 18 times.
|
52 | if(h.is_default()) |
359 | { | ||
360 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
16 | BOOST_ASSERT(h.cap == 0); |
361 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 8 times.
|
16 | BOOST_ASSERT(h.buf == nullptr); |
362 | 16 | h_ = h; | |
363 | 16 | return; | |
364 | } | ||
365 | |||
366 | // allocate and copy the buffer | ||
367 | 72 | op_t op(*this); | |
368 |
1/2✓ Branch 1 taken 18 times.
✗ Branch 2 not taken.
|
36 | op.grow(h.size, h.count); |
369 | 36 | h.assign_to(h_); | |
370 | 36 | std::memcpy( | |
371 | 36 | h_.buf, h.cbuf, h.size); | |
372 | 36 | h.copy_table(h_.buf + h_.cap); | |
373 | } | ||
374 | |||
375 | //------------------------------------------------ | ||
376 | |||
377 | 1392 | fields_base:: | |
378 | 1420 | ~fields_base() | |
379 | { | ||
380 |
2/2✓ Branch 0 taken 608 times.
✓ Branch 1 taken 88 times.
|
1392 | if(h_.buf) |
381 |
1/2✓ Branch 0 taken 608 times.
✗ Branch 1 not taken.
|
1216 | delete[] h_.buf; |
382 | 1392 | } | |
383 | |||
384 | //------------------------------------------------ | ||
385 | // | ||
386 | // Capacity | ||
387 | // | ||
388 | //------------------------------------------------ | ||
389 | |||
390 | void | ||
391 | 10 | fields_base:: | |
392 | clear() noexcept | ||
393 | { | ||
394 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 5 times.
|
10 | if(! h_.buf) |
395 | 5 | return; | |
396 | using H = | ||
397 | detail::header; | ||
398 | auto const& h = | ||
399 | 5 | *H::get_default( | |
400 | 5 | h_.kind); | |
401 | 5 | h.assign_to(h_); | |
402 | 5 | std::memcpy( | |
403 | 5 | h_.buf, | |
404 | 5 | h.cbuf, | |
405 | 5 | h_.size); | |
406 | } | ||
407 | |||
408 | void | ||
409 | 103 | fields_base:: | |
410 | reserve_bytes( | ||
411 | std::size_t n) | ||
412 | { | ||
413 | 134 | op_t op(*this); | |
414 |
4/4✓ Branch 1 taken 72 times.
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 34 times.
✓ Branch 4 taken 38 times.
|
103 | if(! op.reserve(n)) |
415 | 34 | return; | |
416 | 76 | std::memcpy( | |
417 | 38 | h_.buf, op.cbuf(), h_.size); | |
418 | 38 | auto const nt = | |
419 | 38 | sizeof(entry) * h_.count; | |
420 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 32 times.
|
38 | if(nt > 0) |
421 | 6 | std::memcpy( | |
422 | 6 | h_.buf + h_.cap - nt, | |
423 | 6 | op.end() - nt, | |
424 | nt); | ||
425 | } | ||
426 | |||
427 | void | ||
428 | 7 | fields_base:: | |
429 | shrink_to_fit() noexcept | ||
430 | { | ||
431 | 14 | if(detail::header::bytes_needed( | |
432 | 7 | h_.size, h_.count) >= | |
433 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 4 times.
|
7 | h_.cap) |
434 | 3 | return; | |
435 | 8 | fields_base tmp(h_); | |
436 | 4 | tmp.h_.swap(h_); | |
437 | } | ||
438 | |||
439 | //------------------------------------------------ | ||
440 | // | ||
441 | // Modifiers | ||
442 | // | ||
443 | //------------------------------------------------ | ||
444 | |||
445 | std::size_t | ||
446 | 24 | fields_base:: | |
447 | erase( | ||
448 | field id) noexcept | ||
449 | { | ||
450 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 24 times.
|
24 | BOOST_ASSERT( |
451 | id != field::unknown); | ||
452 | #if 1 | ||
453 | 24 | auto const end_ = end(); | |
454 | 24 | auto it = find_last(end_, id); | |
455 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 21 times.
|
24 | if(it == end_) |
456 | 3 | return 0; | |
457 | 21 | std::size_t n = 1; | |
458 | 21 | auto const begin_ = begin(); | |
459 | 21 | raw_erase(it.i_); | |
460 |
2/2✓ Branch 1 taken 36 times.
✓ Branch 2 taken 21 times.
|
57 | while(it != begin_) |
461 | { | ||
462 | 36 | --it; | |
463 |
2/2✓ Branch 2 taken 25 times.
✓ Branch 3 taken 11 times.
|
36 | if(it->id == id) |
464 | { | ||
465 | 25 | raw_erase(it.i_); | |
466 | 25 | ++n; | |
467 | } | ||
468 | } | ||
469 | 21 | h_.on_erase_all(id); | |
470 | 21 | return n; | |
471 | #else | ||
472 | std::size_t n = 0; | ||
473 | auto it0 = find(id); | ||
474 | auto const end_ = end(); | ||
475 | if(it0 != end_) | ||
476 | { | ||
477 | auto it1 = it0; | ||
478 | std::size_t total = 0; | ||
479 | std::size_t size = 0; | ||
480 | // [it0, it1) run of id | ||
481 | for(;;) | ||
482 | { | ||
483 | size += length(it1.i_); | ||
484 | ++it1; | ||
485 | if(it1 == end_) | ||
486 | goto finish; | ||
487 | if(it1->id != id) | ||
488 | break; | ||
489 | } | ||
490 | std::memmove( | ||
491 | h_.buf + offset(it0.i_), | ||
492 | h_.buf + offset(it1.i_), | ||
493 | h_.size - offset(it2.i_)); | ||
494 | |||
495 | finish: | ||
496 | h_.size -= size; | ||
497 | h_.count -= n; | ||
498 | } | ||
499 | return n; | ||
500 | #endif | ||
501 | } | ||
502 | |||
503 | std::size_t | ||
504 | 18 | fields_base:: | |
505 | erase( | ||
506 | core::string_view name) noexcept | ||
507 | { | ||
508 | 18 | auto it0 = find(name); | |
509 | 18 | auto const end_ = end(); | |
510 |
2/2✓ Branch 1 taken 3 times.
✓ Branch 2 taken 15 times.
|
18 | if(it0 == end_) |
511 | 3 | return 0; | |
512 | 15 | auto it = end_; | |
513 | 15 | std::size_t n = 1; | |
514 | 15 | auto const id = it0->id; | |
515 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 9 times.
|
15 | if(id == field::unknown) |
516 | { | ||
517 | // fix self-intersection | ||
518 | 6 | name = it0->name; | |
519 | |||
520 | for(;;) | ||
521 | { | ||
522 | 24 | --it; | |
523 |
2/2✓ Branch 1 taken 6 times.
✓ Branch 2 taken 18 times.
|
24 | if(it == it0) |
524 | 6 | break; | |
525 | 18 | if(grammar::ci_is_equal( | |
526 |
2/2✓ Branch 2 taken 9 times.
✓ Branch 3 taken 9 times.
|
36 | it->name, name)) |
527 | { | ||
528 | 9 | raw_erase(it.i_); | |
529 | 9 | ++n; | |
530 | } | ||
531 | } | ||
532 | 6 | raw_erase(it.i_); | |
533 | } | ||
534 | else | ||
535 | { | ||
536 | for(;;) | ||
537 | { | ||
538 | 21 | --it; | |
539 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 12 times.
|
21 | if(it == it0) |
540 | 9 | break; | |
541 |
2/2✓ Branch 2 taken 6 times.
✓ Branch 3 taken 6 times.
|
12 | if(it->id == id) |
542 | { | ||
543 | 6 | raw_erase(it.i_); | |
544 | 6 | ++n; | |
545 | } | ||
546 | } | ||
547 | 9 | raw_erase(it.i_); | |
548 | 9 | h_.on_erase_all(id); | |
549 | } | ||
550 | 15 | return n; | |
551 | } | ||
552 | |||
553 | //------------------------------------------------ | ||
554 | |||
555 | system::result<void> | ||
556 | 23 | fields_base:: | |
557 | set( | ||
558 | iterator it, | ||
559 | core::string_view value) | ||
560 | { | ||
561 |
1/2✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
23 | auto rv = verify_field_value(value); |
562 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 21 times.
|
23 | if( rv.has_error() ) |
563 | 2 | return rv.error(); | |
564 | |||
565 | 21 | value = rv->value; | |
566 | 21 | bool has_obs_fold = rv->has_obs_fold; | |
567 | |||
568 | 21 | auto const i = it.i_; | |
569 | 21 | auto tab = h_.tab(); | |
570 | 21 | auto const& e0 = tab[i]; | |
571 | 21 | auto const pos0 = offset(i); | |
572 | 21 | auto const pos1 = offset(i + 1); | |
573 | std::ptrdiff_t dn = | ||
574 | 21 | value.size() - | |
575 | 21 | it->value.size(); | |
576 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 21 times.
|
21 | if( value.empty() && |
577 |
1/4✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 21 times.
|
21 | ! it->value.empty()) |
578 | ✗ | --dn; // remove SP | |
579 | 21 | else if( | |
580 |
2/4✗ Branch 3 not taken.
✓ Branch 4 taken 21 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 21 times.
|
21 | it->value.empty() && |
581 | ✗ | ! value.empty()) | |
582 | ✗ | ++dn; // add SP | |
583 | |||
584 | 42 | op_t op(*this, &value); | |
585 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 15 times.
|
27 | if( dn > 0 && |
586 |
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() - |
587 |
2/2✓ Branch 3 taken 6 times.
✓ Branch 4 taken 15 times.
|
27 | it->value.size(), 0)) |
588 | { | ||
589 | // reallocated | ||
590 | 6 | auto dest = h_.buf + | |
591 | 6 | pos0 + e0.nn + 1; | |
592 | 12 | std::memcpy( | |
593 | 6 | h_.buf, | |
594 | 6 | op.buf(), | |
595 | 6 | dest - h_.buf); | |
596 |
1/2✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
|
6 | if(! value.empty()) |
597 | { | ||
598 | 6 | *dest++ = ' '; | |
599 |
1/2✓ Branch 2 taken 6 times.
✗ Branch 3 not taken.
|
6 | value.copy( |
600 | dest, | ||
601 | value.size()); | ||
602 |
2/2✓ Branch 0 taken 3 times.
✓ Branch 1 taken 3 times.
|
6 | if( has_obs_fold ) |
603 | 3 | detail::remove_obs_fold( | |
604 | 3 | dest, dest + value.size()); | |
605 | 6 | dest += value.size(); | |
606 | } | ||
607 | 6 | *dest++ = '\r'; | |
608 | 6 | *dest++ = '\n'; | |
609 | 12 | std::memcpy( | |
610 | 6 | h_.buf + pos1 + dn, | |
611 | 12 | op.buf() + pos1, | |
612 | 6 | h_.size - pos1); | |
613 | 12 | std::memcpy( | |
614 | 6 | h_.buf + h_.cap - | |
615 | 6 | sizeof(entry) * h_.count, | |
616 | 6 | &op.tab()[h_.count - 1], | |
617 | 6 | sizeof(entry) * h_.count); | |
618 | } | ||
619 | else | ||
620 | { | ||
621 | // copy the value first | ||
622 | 30 | auto dest = h_.buf + pos0 + | |
623 | 15 | it->name.size() + 1; | |
624 |
1/2✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
|
15 | if(! value.empty()) |
625 | { | ||
626 | 15 | *dest++ = ' '; | |
627 |
1/2✓ Branch 2 taken 15 times.
✗ Branch 3 not taken.
|
15 | value.copy( |
628 | dest, | ||
629 | value.size()); | ||
630 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 15 times.
|
15 | if( has_obs_fold ) |
631 | ✗ | detail::remove_obs_fold( | |
632 | ✗ | dest, dest + value.size()); | |
633 | 15 | dest += value.size(); | |
634 | } | ||
635 | 15 | op.move_chars( | |
636 | 15 | h_.buf + pos1 + dn, | |
637 | 15 | h_.buf + pos1, | |
638 | 15 | h_.size - pos1); | |
639 | 15 | *dest++ = '\r'; | |
640 | 15 | *dest++ = '\n'; | |
641 | } | ||
642 | { | ||
643 | // update tab | ||
644 | 21 | auto ft = h_.tab(); | |
645 | 28 | for(std::size_t j = h_.count - 1; | |
646 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 21 times.
|
28 | j > i; --j) |
647 | 7 | ft[j] = ft[j] + dn; | |
648 | 21 | auto& e = ft[i]; | |
649 | 42 | e.vp = e.np + e.nn + | |
650 | 21 | 1 + ! value.empty(); | |
651 | 21 | e.vn = static_cast< | |
652 | 21 | offset_type>(value.size()); | |
653 | 21 | h_.size = static_cast< | |
654 | 21 | offset_type>(h_.size + dn); | |
655 | } | ||
656 | 21 | auto const id = it->id; | |
657 |
2/2✓ Branch 1 taken 9 times.
✓ Branch 2 taken 12 times.
|
21 | if(h_.is_special(id)) |
658 | { | ||
659 | // replace first char of name | ||
660 | // with null to hide metadata | ||
661 | 9 | char saved = h_.buf[pos0]; | |
662 | 9 | auto& e = h_.tab()[i]; | |
663 | 9 | e.id = field::unknown; | |
664 | 9 | h_.buf[pos0] = '\0'; | |
665 |
1/2✓ Branch 1 taken 9 times.
✗ Branch 2 not taken.
|
9 | h_.on_erase(id); |
666 | 9 | h_.buf[pos0] = saved; // restore | |
667 | 9 | e.id = id; | |
668 |
1/2✓ Branch 3 taken 9 times.
✗ Branch 4 not taken.
|
9 | h_.on_insert(id, it->value); |
669 | } | ||
670 | 21 | return {}; | |
671 | } | ||
672 | |||
673 | // erase existing fields with id | ||
674 | // and then add the field with value | ||
675 | system::result<void> | ||
676 | 23 | fields_base:: | |
677 | set( | ||
678 | field id, | ||
679 | core::string_view value) | ||
680 | { | ||
681 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
|
23 | BOOST_ASSERT( |
682 | id != field::unknown); | ||
683 | |||
684 |
1/2✓ Branch 1 taken 23 times.
✗ Branch 2 not taken.
|
23 | auto rv = verify_field_value(value); |
685 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 21 times.
|
23 | if( rv.has_error() ) |
686 | 2 | return rv.error(); | |
687 | |||
688 | 21 | value = rv->value; | |
689 | 21 | bool has_obs_fold = rv->has_obs_fold; | |
690 | |||
691 | 21 | auto const i0 = h_.find(id); | |
692 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 6 times.
|
21 | if(i0 != h_.count) |
693 | { | ||
694 | // field exists | ||
695 | 15 | auto const ft = h_.tab(); | |
696 | { | ||
697 | // provide strong guarantee | ||
698 | auto const n0 = | ||
699 | 15 | h_.size - length(i0); | |
700 | auto const n = | ||
701 | 15 | ft[i0].nn + 2 + | |
702 | 15 | value.size() + 2; | |
703 | // VFALCO missing overflow check | ||
704 |
1/2✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
|
15 | reserve_bytes(n0 + n); |
705 | } | ||
706 | 15 | erase_all_impl(i0, id); | |
707 | } | ||
708 | |||
709 |
1/2✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
|
21 | insert_impl_unchecked( |
710 |
1/2✓ Branch 1 taken 21 times.
✗ Branch 2 not taken.
|
21 | id, to_string(id), value, h_.count, has_obs_fold); |
711 | 21 | return {}; | |
712 | } | ||
713 | |||
714 | // erase existing fields with name | ||
715 | // and then add the field with value | ||
716 | system::result<void> | ||
717 | 24 | fields_base:: | |
718 | set( | ||
719 | core::string_view name, | ||
720 | core::string_view value) | ||
721 | { | ||
722 | { | ||
723 |
1/2✓ Branch 1 taken 24 times.
✗ Branch 2 not taken.
|
24 | auto rv = verify_field_name(name); |
724 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 22 times.
|
24 | if( rv.has_error() ) |
725 | 2 | return rv.error(); | |
726 | } | ||
727 | |||
728 |
1/2✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
|
22 | auto rv = verify_field_value(value); |
729 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 20 times.
|
22 | if( rv.has_error() ) |
730 | 2 | return rv.error(); | |
731 | |||
732 | 20 | value = rv->value; | |
733 | 20 | bool has_obs_fold = rv->has_obs_fold; | |
734 | |||
735 | 20 | auto const i0 = h_.find(name); | |
736 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 5 times.
|
20 | if(i0 != h_.count) |
737 | { | ||
738 | // field exists | ||
739 | 15 | auto const ft = h_.tab(); | |
740 | 15 | auto const id = ft[i0].id; | |
741 | { | ||
742 | // provide strong guarantee | ||
743 | auto const n0 = | ||
744 | 15 | h_.size - length(i0); | |
745 | auto const n = | ||
746 | 15 | ft[i0].nn + 2 + | |
747 | 15 | value.size() + 2; | |
748 | // VFALCO missing overflow check | ||
749 |
1/2✓ Branch 1 taken 15 times.
✗ Branch 2 not taken.
|
15 | reserve_bytes(n0 + n); |
750 | } | ||
751 | // VFALCO simple algorithm but | ||
752 | // costs one extra memmove | ||
753 | 15 | erase_all_impl(i0, id); | |
754 | } | ||
755 |
2/2✓ Branch 1 taken 19 times.
✓ Branch 2 taken 1 times.
|
20 | insert_impl_unchecked( |
756 | string_to_field(name), | ||
757 | 20 | name, value, h_.count, has_obs_fold); | |
758 | 19 | return {}; | |
759 | } | ||
760 | |||
761 | //------------------------------------------------ | ||
762 | // | ||
763 | // (implementation) | ||
764 | // | ||
765 | //------------------------------------------------ | ||
766 | |||
767 | // copy start line and fields | ||
768 | void | ||
769 | 17 | fields_base:: | |
770 | copy_impl( | ||
771 | detail::header const& h) | ||
772 | { | ||
773 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
|
17 | BOOST_ASSERT( |
774 | h.kind == ph_->kind); | ||
775 |
2/2✓ Branch 1 taken 14 times.
✓ Branch 2 taken 3 times.
|
17 | if(! h.is_default()) |
776 | { | ||
777 | auto const n = | ||
778 | 14 | detail::header::bytes_needed( | |
779 | 14 | h.size, h.count); | |
780 |
2/2✓ Branch 0 taken 7 times.
✓ Branch 1 taken 7 times.
|
14 | if(n <= h_.cap) |
781 | { | ||
782 | // no realloc | ||
783 | 7 | h.assign_to(h_); | |
784 | 7 | h.copy_table( | |
785 | 7 | h_.buf + h_.cap); | |
786 | 7 | std::memcpy( | |
787 | 7 | h_.buf, | |
788 | 7 | h.cbuf, | |
789 | 7 | h.size); | |
790 | 7 | return; | |
791 | } | ||
792 | } | ||
793 |
1/2✓ Branch 1 taken 10 times.
✗ Branch 2 not taken.
|
20 | fields_base tmp(h); |
794 | 10 | tmp.h_.swap(h_); | |
795 | } | ||
796 | |||
797 | void | ||
798 | 233 | fields_base:: | |
799 | insert_impl_unchecked( | ||
800 | field id, | ||
801 | core::string_view name, | ||
802 | core::string_view value, | ||
803 | std::size_t before, | ||
804 | bool has_obs_fold) | ||
805 | { | ||
806 | 233 | auto const tab0 = h_.tab_(); | |
807 | 233 | auto const pos = offset(before); | |
808 | auto const n = | ||
809 | 233 | name.size() + // name | |
810 | 233 | 1 + // ':' | |
811 | 233 | ! value.empty() + // [SP] | |
812 | 233 | value.size() + // value | |
813 | 233 | 2; // CRLF | |
814 | |||
815 | 466 | op_t op(*this, &name, &value); | |
816 |
4/4✓ Branch 1 taken 228 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 110 times.
✓ Branch 4 taken 118 times.
|
233 | if(op.grow(n, 1)) |
817 | { | ||
818 | // reallocated | ||
819 |
2/2✓ Branch 0 taken 92 times.
✓ Branch 1 taken 18 times.
|
110 | if(pos > 0) |
820 | 92 | std::memcpy( | |
821 | 92 | h_.buf, | |
822 | 92 | op.cbuf(), | |
823 | pos); | ||
824 |
2/2✓ Branch 0 taken 37 times.
✓ Branch 1 taken 73 times.
|
110 | if(before > 0) |
825 | 74 | std::memcpy( | |
826 | 37 | h_.tab_() - before, | |
827 | 37 | tab0 - before, | |
828 | before * sizeof(entry)); | ||
829 | 220 | std::memcpy( | |
830 | 110 | h_.buf + pos + n, | |
831 | 110 | op.cbuf() + pos, | |
832 | 110 | h_.size - pos); | |
833 | } | ||
834 | else | ||
835 | { | ||
836 | 118 | op.move_chars( | |
837 | 118 | h_.buf + pos + n, | |
838 | 118 | h_.buf + pos, | |
839 | 118 | h_.size - pos); | |
840 | } | ||
841 | |||
842 | // serialize | ||
843 | { | ||
844 | 228 | auto dest = h_.buf + pos; | |
845 |
1/2✓ Branch 2 taken 228 times.
✗ Branch 3 not taken.
|
228 | name.copy(dest, name.size()); |
846 | 228 | dest += name.size(); | |
847 | 228 | *dest++ = ':'; | |
848 |
2/2✓ Branch 1 taken 216 times.
✓ Branch 2 taken 12 times.
|
228 | if(! value.empty()) |
849 | { | ||
850 | 216 | *dest++ = ' '; | |
851 |
1/2✓ Branch 2 taken 216 times.
✗ Branch 3 not taken.
|
216 | value.copy( |
852 | dest, value.size()); | ||
853 |
2/2✓ Branch 0 taken 15 times.
✓ Branch 1 taken 201 times.
|
216 | if( has_obs_fold ) |
854 | 15 | detail::remove_obs_fold( | |
855 | 15 | dest, dest + value.size()); | |
856 | 216 | dest += value.size(); | |
857 | } | ||
858 | 228 | *dest++ = '\r'; | |
859 | 228 | *dest = '\n'; | |
860 | } | ||
861 | |||
862 | // update table | ||
863 | 228 | auto const tab = h_.tab_(); | |
864 | { | ||
865 | 228 | auto i = h_.count - before; | |
866 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 174 times.
|
228 | if(i > 0) |
867 | { | ||
868 | 54 | auto p0 = tab0 - h_.count; | |
869 | 54 | auto p = tab - h_.count - 1; | |
870 | 54 | do | |
871 | { | ||
872 | 108 | *p++ = *p0++ + n; | |
873 | } | ||
874 |
2/2✓ Branch 0 taken 54 times.
✓ Branch 1 taken 54 times.
|
108 | while(--i); |
875 | } | ||
876 | } | ||
877 | 228 | auto& e = tab[0 - static_cast<std::ptrdiff_t>(before) - 1]; | |
878 | 228 | e.np = static_cast<offset_type>( | |
879 | 228 | pos - h_.prefix); | |
880 | 228 | e.nn = static_cast< | |
881 | 228 | offset_type>(name.size()); | |
882 | 228 | e.vp = static_cast<offset_type>( | |
883 | 456 | pos - h_.prefix + | |
884 | 228 | name.size() + 1 + | |
885 | 228 | ! value.empty()); | |
886 | 228 | e.vn = static_cast< | |
887 | 228 | offset_type>(value.size()); | |
888 | 228 | e.id = id; | |
889 | |||
890 | // update container | ||
891 | 228 | h_.count++; | |
892 | 228 | h_.size = static_cast< | |
893 | 228 | offset_type>(h_.size + n); | |
894 |
2/2✓ Branch 0 taken 198 times.
✓ Branch 1 taken 30 times.
|
228 | if( id != field::unknown) |
895 |
1/2✓ Branch 1 taken 198 times.
✗ Branch 2 not taken.
|
198 | h_.on_insert(id, value); |
896 | 228 | } | |
897 | |||
898 | system::result<void> | ||
899 | 209 | fields_base:: | |
900 | insert_impl( | ||
901 | field id, | ||
902 | core::string_view name, | ||
903 | core::string_view value, | ||
904 | std::size_t before) | ||
905 | { | ||
906 | { | ||
907 |
1/2✓ Branch 1 taken 209 times.
✗ Branch 2 not taken.
|
209 | auto rv = verify_field_name(name); |
908 |
2/2✓ Branch 1 taken 4 times.
✓ Branch 2 taken 205 times.
|
209 | if( rv.has_error() ) |
909 | 4 | return rv.error(); | |
910 | } | ||
911 | |||
912 |
1/2✓ Branch 1 taken 205 times.
✗ Branch 2 not taken.
|
205 | auto rv = verify_field_value(value); |
913 |
2/2✓ Branch 1 taken 13 times.
✓ Branch 2 taken 192 times.
|
205 | if( rv.has_error() ) |
914 | 13 | return rv.error(); | |
915 | |||
916 |
2/2✓ Branch 1 taken 188 times.
✓ Branch 2 taken 4 times.
|
192 | insert_impl_unchecked( |
917 | 192 | id, name, rv->value, before, rv->has_obs_fold); | |
918 | 188 | return {}; | |
919 | } | ||
920 | |||
921 | // erase i and update metadata | ||
922 | void | ||
923 | 31 | fields_base:: | |
924 | erase_impl( | ||
925 | std::size_t i, | ||
926 | field id) noexcept | ||
927 | { | ||
928 | 31 | raw_erase(i); | |
929 |
1/2✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
|
31 | if(id != field::unknown) |
930 | 31 | h_.on_erase(id); | |
931 | 31 | } | |
932 | |||
933 | //------------------------------------------------ | ||
934 | |||
935 | void | ||
936 | 155 | fields_base:: | |
937 | raw_erase( | ||
938 | std::size_t i) noexcept | ||
939 | { | ||
940 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
|
155 | BOOST_ASSERT(i < h_.count); |
941 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
|
155 | BOOST_ASSERT(h_.buf != nullptr); |
942 | 155 | auto const p0 = offset(i); | |
943 | 155 | auto const p1 = offset(i + 1); | |
944 | 155 | std::memmove( | |
945 | 155 | h_.buf + p0, | |
946 | 155 | h_.buf + p1, | |
947 | 155 | h_.size - p1); | |
948 | 155 | auto const n = p1 - p0; | |
949 | 155 | --h_.count; | |
950 | 155 | auto ft = h_.tab(); | |
951 |
2/2✓ Branch 0 taken 79 times.
✓ Branch 1 taken 155 times.
|
234 | for(;i < h_.count; ++i) |
952 | 79 | ft[i] = ft[i + 1] - n; | |
953 | 155 | h_.size = static_cast< | |
954 | 155 | offset_type>(h_.size - n); | |
955 | 155 | } | |
956 | |||
957 | //------------------------------------------------ | ||
958 | |||
959 | // erase all fields with id | ||
960 | // and update metadata | ||
961 | std::size_t | ||
962 | 30 | fields_base:: | |
963 | erase_all_impl( | ||
964 | std::size_t i0, | ||
965 | field id) noexcept | ||
966 | { | ||
967 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 30 times.
|
30 | BOOST_ASSERT( |
968 | id != field::unknown); | ||
969 | 30 | std::size_t n = 1; | |
970 | 30 | std::size_t i = h_.count - 1; | |
971 | 30 | auto const ft = h_.tab(); | |
972 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 30 times.
|
58 | while(i > i0) |
973 | { | ||
974 |
2/2✓ Branch 1 taken 13 times.
✓ Branch 2 taken 15 times.
|
28 | if(ft[i].id == id) |
975 | { | ||
976 | 13 | raw_erase(i); | |
977 | 13 | ++n; | |
978 | } | ||
979 | // go backwards to | ||
980 | // reduce memmoves | ||
981 | 28 | --i; | |
982 | } | ||
983 | 30 | raw_erase(i0); | |
984 | 30 | h_.on_erase_all(id); | |
985 | 30 | return n; | |
986 | } | ||
987 | |||
988 | // return i-th field absolute offset | ||
989 | std::size_t | ||
990 | 645 | fields_base:: | |
991 | offset( | ||
992 | std::size_t i) const noexcept | ||
993 | { | ||
994 |
2/2✓ Branch 0 taken 259 times.
✓ Branch 1 taken 386 times.
|
645 | if(i == 0) |
995 | 259 | return h_.prefix; | |
996 |
2/2✓ Branch 0 taken 191 times.
✓ Branch 1 taken 195 times.
|
386 | if(i < h_.count) |
997 | 382 | return h_.prefix + | |
998 | 191 | h_.tab_()[0-(static_cast<std::ptrdiff_t>(i) + 1)].np; | |
999 | // make final CRLF the last "field" | ||
1000 | //BOOST_ASSERT(i == h_.count); | ||
1001 | 195 | return h_.size - 2; | |
1002 | } | ||
1003 | |||
1004 | // return i-th field absolute length | ||
1005 | std::size_t | ||
1006 | 30 | fields_base:: | |
1007 | length( | ||
1008 | std::size_t i) const noexcept | ||
1009 | { | ||
1010 | return | ||
1011 | 30 | offset(i + 1) - | |
1012 | 30 | offset(i); | |
1013 | } | ||
1014 | |||
1015 | //------------------------------------------------ | ||
1016 | |||
1017 | // erase n fields matching id | ||
1018 | // without updating metadata | ||
1019 | void | ||
1020 | 4 | fields_base:: | |
1021 | raw_erase_n( | ||
1022 | field id, | ||
1023 | std::size_t n) noexcept | ||
1024 | { | ||
1025 | // iterate in reverse | ||
1026 | 4 | auto e = &h_.tab()[h_.count]; | |
1027 | 4 | auto const e0 = &h_.tab()[0]; | |
1028 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
|
10 | while(n > 0) |
1029 | { | ||
1030 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
|
6 | BOOST_ASSERT(e != e0); |
1031 | 6 | ++e; // decrement | |
1032 |
2/2✓ Branch 0 taken 5 times.
✓ Branch 1 taken 1 times.
|
6 | if(e->id == id) |
1033 | { | ||
1034 | 5 | raw_erase(e0 - e); | |
1035 | 5 | --n; | |
1036 | } | ||
1037 | } | ||
1038 | 4 | } | |
1039 | |||
1040 | } // http_proto | ||
1041 | } // boost | ||
1042 |