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