src/detail/format_args.cpp

100.0% Lines (302/302) 100.0% List of functions (11/11)
format_args.cpp
f(x) Functions (11)
Function Calls Lines Blocks
boost::urls::detail::get_uvalue(boost::core::basic_string_view<char>) :26 68x 100.0% 100.0% boost::urls::detail::get_uvalue(char) :37 68x 100.0% 100.0% boost::urls::detail::formatter<boost::core::basic_string_view<char>, void>::parse(boost::urls::detail::format_parse_context&) :44 451x 100.0% 98.0% boost::urls::detail::formatter<boost::core::basic_string_view<char>, void>::measure(boost::core::basic_string_view<char>, boost::urls::detail::measure_context&, boost::urls::grammar::lut_chars const&) const :143 226x 100.0% 100.0% boost::urls::detail::formatter<boost::core::basic_string_view<char>, void>::format(boost::core::basic_string_view<char>, boost::urls::detail::format_context&, boost::urls::grammar::lut_chars const&) const :165 224x 100.0% 100.0% boost::urls::detail::get_width_from_args(unsigned long, boost::core::basic_string_view<char>, boost::urls::detail::format_args, unsigned long&) :209 28x 100.0% 100.0% boost::urls::detail::integer_formatter_impl::parse(boost::urls::detail::format_parse_context&) :233 115x 100.0% 98.0% boost::urls::detail::integer_formatter_impl::measure(long long, boost::urls::detail::measure_context&, boost::urls::grammar::lut_chars const&) const :355 43x 100.0% 100.0% boost::urls::detail::integer_formatter_impl::measure(unsigned long long, boost::urls::detail::measure_context&, boost::urls::grammar::lut_chars const&) const :404 14x 100.0% 100.0% boost::urls::detail::integer_formatter_impl::format(long long, boost::urls::detail::format_context&, boost::urls::grammar::lut_chars const&) const :444 43x 100.0% 98.0% boost::urls::detail::integer_formatter_impl::format(unsigned long long, boost::urls::detail::format_context&, boost::urls::grammar::lut_chars const&) const :551 14x 100.0% 98.0%
Line TLA Hits Source Code
1 //
2 // Copyright (c) 2022 Alan de Freitas (alandefreitas@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/boostorg/url
8 //
9
10
11 #include <boost/url/detail/config.hpp>
12 #include <boost/url/encode.hpp>
13 #include <boost/url/detail/format_args.hpp>
14 #include "boost/url/detail/replacement_field_rule.hpp"
15 #include <boost/url/grammar/delim_rule.hpp>
16 #include <boost/url/grammar/optional_rule.hpp>
17 #include <boost/url/grammar/parse.hpp>
18 #include <boost/url/grammar/tuple_rule.hpp>
19 #include <boost/url/grammar/unsigned_rule.hpp>
20
21 namespace boost {
22 namespace urls {
23 namespace detail {
24
25 std::size_t
26 68x get_uvalue( core::string_view a )
27 {
28 68x core::string_view str(a);
29 68x auto rv = grammar::parse(
30 68x str, grammar::unsigned_rule<std::size_t>{});
31 68x if (rv)
32 2x return *rv;
33 66x return 0;
34 }
35
36 std::size_t
37 68x get_uvalue( char a )
38 {
39 68x core::string_view str(&a, 1);
40 136x return get_uvalue(str);
41 }
42
43 char const*
44 451x formatter<core::string_view>::
45 parse(format_parse_context& ctx)
46 {
47 451x char const* it = ctx.begin();
48 451x char const* end = ctx.end();
49 451x BOOST_ASSERT(it != end);
50
51 // fill / align
52 451x if (end - it > 2)
53 {
54 121x if (*it != '{' &&
55 121x *it != '}' &&
56 23x (*(it + 1) == '<' ||
57 21x *(it + 1) == '>' ||
58 9x *(it + 1) == '^'))
59 {
60 18x fill = *it;
61 18x align = *(it + 1);
62 18x it += 2;
63 }
64 }
65
66 // align
67 451x if (align == '\0' &&
68 433x (*it == '<' ||
69 433x *it == '>' ||
70 433x *it == '^'))
71 {
72 4x align = *it++;
73 }
74
75 // width
76 451x char const* it0 = it;
77 451x constexpr auto width_rule =
78 grammar::variant_rule(
79 grammar::unsigned_rule<std::size_t>{},
80 grammar::tuple_rule(
81 grammar::squelch(
82 grammar::delim_rule('{')),
83 grammar::optional_rule(
84 arg_id_rule),
85 grammar::squelch(
86 grammar::delim_rule('}'))));
87 451x auto rw = grammar::parse(it, end, width_rule);
88 451x if (!rw)
89 {
90 // rewind
91 429x it = it0;
92 }
93 22x else if (align != '\0')
94 {
95 // width is ignored when align is '\0'
96 22x if (rw->index() == 0)
97 {
98 // unsigned_rule
99 12x width = variant2::get<0>(*rw);
100 }
101 else
102 {
103 // arg_id: store the id idx or string
104 10x auto& arg_id = variant2::get<1>(*rw);
105 10x if (!arg_id)
106 {
107 // empty arg_id, use and consume
108 // the next arg idx
109 2x width_idx = ctx.next_arg_id();
110 }
111 8x else if (arg_id->index() == 0)
112 {
113 // string identifier
114 4x width_name = variant2::get<0>(*arg_id);
115 }
116 else
117 {
118 // integer identifier: use the
119 // idx of this format_arg
120 4x width_idx = variant2::get<1>(*arg_id);
121 }
122 }
123 }
124
125 // type is parsed but doesn't have to
126 // be stored for strings
127 451x if (*it == 'c' ||
128 448x *it == 's')
129 {
130 25x ++it;
131 }
132
133 // we should have arrived at the end now
134 451x if (*it != '}')
135 {
136 1x urls::detail::throw_invalid_argument();
137 }
138
139 450x return it;
140 }
141
142 std::size_t
143 226x formatter<core::string_view>::
144 measure(
145 core::string_view str,
146 measure_context& ctx,
147 grammar::lut_chars const& cs) const
148 {
149 226x std::size_t w = width;
150 449x if (width_idx != std::size_t(-1) ||
151 223x !width_name.empty())
152 {
153 5x get_width_from_args(
154 5x width_idx, width_name, ctx.args(), w);
155 }
156
157 226x std::size_t n = ctx.out();
158 226x if (str.size() < w)
159 10x n += measure_one(fill, cs) * (w - str.size());
160
161 226x return n + encoded_size(str, cs);
162 }
163
164 char*
165 224x formatter<core::string_view>::
166 format(core::string_view str, format_context& ctx, grammar::lut_chars const& cs) const
167 {
168 224x std::size_t w = width;
169 445x if (width_idx != std::size_t(-1) ||
170 221x !width_name.empty())
171 {
172 5x get_width_from_args(
173 5x width_idx, width_name, ctx.args(), w);
174 }
175
176 224x std::size_t lpad = 0;
177 224x std::size_t rpad = 0;
178 224x if (str.size() < w)
179 {
180 10x std::size_t pad = w - str.size();
181 10x switch (align)
182 {
183 1x case '<':
184 1x rpad = pad;
185 1x break;
186 6x case '>':
187 6x lpad = pad;
188 6x break;
189 3x case '^':
190 3x lpad = pad / 2;
191 3x rpad = pad - lpad;
192 3x break;
193 }
194 }
195
196 // unsafe `encode`, assuming `out` has
197 // enough capacity
198 224x char* out = ctx.out();
199 252x for (std::size_t i = 0; i < lpad; ++i)
200 28x encode_one(out, fill, cs);
201 1252x for (char c: str)
202 1028x encode_one(out, c, cs);
203 232x for (std::size_t i = 0; i < rpad; ++i)
204 8x encode_one(out, fill, cs);
205 224x return out;
206 }
207
208 void
209 28x get_width_from_args(
210 std::size_t arg_idx,
211 core::string_view arg_name,
212 format_args args,
213 std::size_t& w)
214 {
215 // check arg_id
216 28x format_arg warg;
217 28x if (arg_idx != std::size_t(-1))
218 {
219 // identifier
220 18x warg = args.get(arg_idx);
221 }
222 else
223 {
224 // unsigned integer
225 10x warg = args.get(arg_name);
226 }
227
228 // get unsigned int value from that format arg
229 28x w = warg.value();
230 28x }
231
232 char const*
233 115x integer_formatter_impl::
234 parse(format_parse_context& ctx)
235 {
236 115x char const* it = ctx.begin();
237 115x char const* end = ctx.end();
238 115x BOOST_ASSERT(it != end);
239
240 // fill / align
241 115x if (end - it > 2)
242 {
243 57x if (*it != '{' &&
244 57x *it != '}' &&
245 53x (*(it + 1) == '<' ||
246 49x *(it + 1) == '>' ||
247 27x *(it + 1) == '^'))
248 {
249 30x fill = *it;
250 30x align = *(it + 1);
251 30x it += 2;
252 }
253 }
254
255 // align
256 115x if (align == '\0' &&
257 85x (*it == '<' ||
258 85x *it == '>' ||
259 77x *it == '^'))
260 {
261 12x align = *it++;
262 }
263
264 // sign
265 115x if (*it == '+' ||
266 109x *it == '-' ||
267 109x *it == ' ')
268 {
269 12x sign = *it++;
270 }
271
272 // #
273 115x if (*it == '#')
274 {
275 // alternate form not supported
276 2x ++it;
277 }
278
279 // 0
280 115x if (*it == '0')
281 {
282 8x zeros = *it++;
283 }
284
285 // width
286 115x char const* it0 = it;
287 115x constexpr auto width_rule = grammar::variant_rule(
288 grammar::unsigned_rule<std::size_t>{},
289 grammar::tuple_rule(
290 grammar::squelch(
291 grammar::delim_rule('{')),
292 grammar::optional_rule(
293 arg_id_rule),
294 grammar::squelch(
295 grammar::delim_rule('}'))));
296 115x auto rw = grammar::parse(it, end, width_rule);
297 115x if (!rw)
298 {
299 // rewind
300 73x it = it0;
301 }
302 42x else if (align != '\0')
303 {
304 // width is ignored when align is '\0'
305 42x if (rw->index() == 0)
306 {
307 // unsigned_rule
308 24x width = variant2::get<0>(*rw);
309 }
310 else
311 {
312 // arg_id: store the id idx or string
313 18x auto& arg_id = variant2::get<1>(*rw);
314 18x if (!arg_id)
315 {
316 // empty arg_id, use and consume
317 // the next arg idx
318 4x width_idx = ctx.next_arg_id();
319 }
320 14x else if (arg_id->index() == 0)
321 {
322 // string identifier
323 6x width_name = variant2::get<0>(*arg_id);
324 }
325 else
326 {
327 // integer identifier: use the
328 // idx of this format_arg
329 8x width_idx = variant2::get<1>(*arg_id);
330 }
331 }
332 }
333
334 // type is parsed but doesn't have to
335 // be stored for strings
336 115x if (*it == 'd')
337 {
338 // we don't include other presentation
339 // modes for integers as they are not
340 // recommended or generally used in
341 // urls
342 55x ++it;
343 }
344
345 // we should have arrived at the end now
346 115x if (*it != '}')
347 {
348 1x urls::detail::throw_invalid_argument();
349 }
350
351 114x return it;
352 }
353
354 std::size_t
355 43x integer_formatter_impl::
356 measure(
357 long long int v,
358 measure_context& ctx,
359 grammar::lut_chars const& cs) const
360 {
361 43x std::size_t dn = 0;
362 43x std::size_t n = 0;
363 43x if (v < 0)
364 {
365 2x dn += measure_one('-', cs);
366 2x ++n;
367 }
368 41x else if (sign != '-')
369 {
370 4x dn += measure_one(sign, cs);
371 4x ++n;
372 }
373 // Use unsigned to avoid UB when v == LLONG_MIN
374 43x unsigned long long int uv = v < 0
375 43x ? 0ull - static_cast<unsigned long long int>(v)
376 : static_cast<unsigned long long int>(v);
377 do
378 {
379 102x int d = static_cast<int>(uv % 10);
380 102x uv /= 10;
381 102x dn += measure_one('0' + static_cast<char>(d), cs);
382 102x ++n;
383 }
384 102x while (uv > 0);
385
386 43x std::size_t w = width;
387 83x if (width_idx != std::size_t(-1) ||
388 40x !width_name.empty())
389 {
390 5x get_width_from_args(
391 5x width_idx, width_name, ctx.args(), w);
392 }
393 43x if (w > n)
394 {
395 12x if (!zeros)
396 9x dn += measure_one(fill, cs) * (w - n);
397 else
398 3x dn += measure_one('0', cs) * (w - n);
399 }
400 86x return ctx.out() + dn;
401 }
402
403 std::size_t
404 14x integer_formatter_impl::
405 measure(
406 unsigned long long int v,
407 measure_context& ctx,
408 grammar::lut_chars const& cs) const
409 {
410 14x std::size_t dn = 0;
411 14x std::size_t n = 0;
412 14x if (sign != '-')
413 {
414 2x dn += measure_one(sign, cs);
415 2x ++n;
416 }
417 do
418 {
419 53x int d = v % 10;
420 53x v /= 10;
421 53x dn += measure_one('0' + static_cast<char>(d), cs);
422 53x ++n;
423 }
424 53x while (v != 0);
425
426 14x std::size_t w = width;
427 25x if (width_idx != std::size_t(-1) ||
428 11x !width_name.empty())
429 {
430 4x get_width_from_args(
431 4x width_idx, width_name, ctx.args(), w);
432 }
433 14x if (w > n)
434 {
435 8x if (!zeros)
436 7x dn += measure_one(fill, cs) * (w - n);
437 else
438 1x dn += measure_one('0', cs) * (w - n);
439 }
440 28x return ctx.out() + dn;
441 }
442
443 char*
444 43x integer_formatter_impl::
445 format(
446 long long int v,
447 format_context& ctx,
448 grammar::lut_chars const& cs) const
449 {
450 // get n digits
451 // Use unsigned to avoid UB when v == LLONG_MIN
452 43x bool const neg = v < 0;
453 43x unsigned long long int uv = neg
454 43x ? 0ull - static_cast<unsigned long long int>(v)
455 : static_cast<unsigned long long int>(v);
456 43x unsigned long long int uv0 = uv;
457 43x unsigned long long int p = 1;
458 43x std::size_t n = 0;
459 43x if (neg || sign != '-')
460 {
461 6x ++n;
462 }
463 do
464 {
465 102x if (uv >= 10)
466 59x p *= 10;
467 102x uv /= 10;
468 102x ++n;
469 }
470 102x while (uv > 0);
471 static constexpr auto m =
472 std::numeric_limits<long long int>::digits10;
473 43x BOOST_ASSERT(n <= m + 2);
474 ignore_unused(m);
475
476 // get pad
477 43x std::size_t w = width;
478 83x if (width_idx != std::size_t(-1) ||
479 40x !width_name.empty())
480 {
481 5x get_width_from_args(
482 5x width_idx, width_name, ctx.args(), w);
483 }
484 43x std::size_t lpad = 0;
485 43x std::size_t rpad = 0;
486 43x if (w > n)
487 {
488 12x std::size_t pad = w - n;
489 12x if (zeros)
490 {
491 3x lpad = pad;
492 }
493 else
494 {
495 9x switch (align)
496 {
497 1x case '<':
498 1x rpad = pad;
499 1x break;
500 6x case '>':
501 6x lpad = pad;
502 6x break;
503 2x case '^':
504 2x lpad = pad / 2;
505 2x rpad = pad - lpad;
506 2x break;
507 }
508 }
509 }
510
511 // write
512 43x uv = uv0;
513 43x char* out = ctx.out();
514 43x if (!zeros)
515 {
516 68x for (std::size_t i = 0; i < lpad; ++i)
517 28x encode_one(out, fill, cs);
518 }
519 43x if (neg)
520 {
521 2x encode_one(out, '-', cs);
522 2x --n;
523 }
524 41x else if (sign != '-')
525 {
526 4x encode_one(out, sign, cs);
527 4x --n;
528 }
529 43x if (zeros)
530 {
531 13x for (std::size_t i = 0; i < lpad; ++i)
532 10x encode_one(out, '0', cs);
533 }
534 145x while (n)
535 {
536 102x unsigned long long int d = uv / p;
537 102x encode_one(out, '0' + static_cast<char>(d), cs);
538 102x --n;
539 102x uv %= p;
540 102x p /= 10;
541 }
542 43x if (!zeros)
543 {
544 48x for (std::size_t i = 0; i < rpad; ++i)
545 8x encode_one(out, fill, cs);
546 }
547 43x return out;
548 }
549
550 char*
551 14x integer_formatter_impl::
552 format(
553 unsigned long long int v,
554 format_context& ctx,
555 grammar::lut_chars const& cs) const
556 {
557 // get n digits
558 14x unsigned long long int v0 = v;
559 14x unsigned long long int p = 1;
560 14x std::size_t n = 0;
561 14x if (sign != '-')
562 {
563 2x ++n;
564 }
565 do
566 {
567 53x if (v >= 10)
568 39x p *= 10;
569 53x v /= 10;
570 53x ++n;
571 }
572 53x while (v > 0);
573 static constexpr auto m =
574 std::numeric_limits<unsigned long long int>::digits10;
575 14x BOOST_ASSERT(n <= m + 2);
576 ignore_unused(m);
577
578 // get pad
579 14x std::size_t w = width;
580 25x if (width_idx != std::size_t(-1) ||
581 11x !width_name.empty())
582 {
583 4x get_width_from_args(
584 4x width_idx, width_name, ctx.args(), w);
585 }
586 14x std::size_t lpad = 0;
587 14x std::size_t rpad = 0;
588 14x if (w > n)
589 {
590 8x std::size_t pad = w - n;
591 8x if (zeros)
592 {
593 1x lpad = pad;
594 }
595 else
596 {
597 7x switch (align)
598 {
599 1x case '<':
600 1x rpad = pad;
601 1x break;
602 5x case '>':
603 5x lpad = pad;
604 5x break;
605 1x case '^':
606 1x lpad = pad / 2;
607 1x rpad = pad - lpad;
608 1x break;
609 }
610 }
611 }
612
613 // write
614 14x v = v0;
615 14x char* out = ctx.out();
616 14x if (!zeros)
617 {
618 35x for (std::size_t i = 0; i < lpad; ++i)
619 22x encode_one(out, fill, cs);
620 }
621 14x if (sign != '-')
622 {
623 2x encode_one(out, sign, cs);
624 2x --n;
625 }
626 14x if (zeros)
627 {
628 5x for (std::size_t i = 0; i < lpad; ++i)
629 4x encode_one(out, '0', cs);
630 }
631 67x while (n)
632 {
633 53x unsigned long long int d = v / p;
634 53x encode_one(out, '0' + static_cast<char>(d), cs);
635 53x --n;
636 53x v %= p;
637 53x p /= 10;
638 }
639 14x if (!zeros)
640 {
641 19x for (std::size_t i = 0; i < rpad; ++i)
642 6x encode_one(out, fill, cs);
643 }
644 14x return out;
645 }
646
647 } // detail
648 } // urls
649 } // boost
650
651