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