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