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 :406 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 :446 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 :555 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 bitwise two's-complement negation to obtain |v| without
374 // tripping signed-overflow (v == LLONG_MIN) or
375 // unsigned-overflow (0ull - x) sanitizers.
376 43x unsigned long long int uv = v < 0
377 43x ? ~static_cast<unsigned long long int>(v) + 1ull
378 : static_cast<unsigned long long int>(v);
379 do
380 {
381 102x int d = static_cast<int>(uv % 10);
382 102x uv /= 10;
383 102x dn += measure_one('0' + static_cast<char>(d), cs);
384 102x ++n;
385 }
386 102x while (uv > 0);
387
388 43x std::size_t w = width;
389 83x if (width_idx != std::size_t(-1) ||
390 40x !width_name.empty())
391 {
392 5x get_width_from_args(
393 5x width_idx, width_name, ctx.args(), w);
394 }
395 43x if (w > n)
396 {
397 12x if (!zeros)
398 9x dn += measure_one(fill, cs) * (w - n);
399 else
400 3x dn += measure_one('0', cs) * (w - n);
401 }
402 86x return ctx.out() + dn;
403 }
404
405 std::size_t
406 14x integer_formatter_impl::
407 measure(
408 unsigned long long int v,
409 measure_context& ctx,
410 grammar::lut_chars const& cs) const
411 {
412 14x std::size_t dn = 0;
413 14x std::size_t n = 0;
414 14x if (sign != '-')
415 {
416 2x dn += measure_one(sign, cs);
417 2x ++n;
418 }
419 do
420 {
421 53x int d = v % 10;
422 53x v /= 10;
423 53x dn += measure_one('0' + static_cast<char>(d), cs);
424 53x ++n;
425 }
426 53x while (v != 0);
427
428 14x std::size_t w = width;
429 25x if (width_idx != std::size_t(-1) ||
430 11x !width_name.empty())
431 {
432 4x get_width_from_args(
433 4x width_idx, width_name, ctx.args(), w);
434 }
435 14x if (w > n)
436 {
437 8x if (!zeros)
438 7x dn += measure_one(fill, cs) * (w - n);
439 else
440 1x dn += measure_one('0', cs) * (w - n);
441 }
442 28x return ctx.out() + dn;
443 }
444
445 char*
446 43x integer_formatter_impl::
447 format(
448 long long int v,
449 format_context& ctx,
450 grammar::lut_chars const& cs) const
451 {
452 // get n digits
453 // Bitwise two's-complement negation to obtain |v| without
454 // tripping signed-overflow (v == LLONG_MIN) or
455 // unsigned-overflow (0ull - x) sanitizers.
456 43x bool const neg = v < 0;
457 43x unsigned long long int uv = neg
458 43x ? ~static_cast<unsigned long long int>(v) + 1ull
459 : static_cast<unsigned long long int>(v);
460 43x unsigned long long int uv0 = uv;
461 43x unsigned long long int p = 1;
462 43x std::size_t n = 0;
463 43x if (neg || sign != '-')
464 {
465 6x ++n;
466 }
467 do
468 {
469 102x if (uv >= 10)
470 59x p *= 10;
471 102x uv /= 10;
472 102x ++n;
473 }
474 102x while (uv > 0);
475 static constexpr auto m =
476 std::numeric_limits<long long int>::digits10;
477 43x BOOST_ASSERT(n <= m + 2);
478 ignore_unused(m);
479
480 // get pad
481 43x std::size_t w = width;
482 83x if (width_idx != std::size_t(-1) ||
483 40x !width_name.empty())
484 {
485 5x get_width_from_args(
486 5x width_idx, width_name, ctx.args(), w);
487 }
488 43x std::size_t lpad = 0;
489 43x std::size_t rpad = 0;
490 43x if (w > n)
491 {
492 12x std::size_t pad = w - n;
493 12x if (zeros)
494 {
495 3x lpad = pad;
496 }
497 else
498 {
499 9x switch (align)
500 {
501 1x case '<':
502 1x rpad = pad;
503 1x break;
504 6x case '>':
505 6x lpad = pad;
506 6x break;
507 2x case '^':
508 2x lpad = pad / 2;
509 2x rpad = pad - lpad;
510 2x break;
511 }
512 }
513 }
514
515 // write
516 43x uv = uv0;
517 43x char* out = ctx.out();
518 43x if (!zeros)
519 {
520 68x for (std::size_t i = 0; i < lpad; ++i)
521 28x encode_one(out, fill, cs);
522 }
523 43x if (neg)
524 {
525 2x encode_one(out, '-', cs);
526 2x --n;
527 }
528 41x else if (sign != '-')
529 {
530 4x encode_one(out, sign, cs);
531 4x --n;
532 }
533 43x if (zeros)
534 {
535 13x for (std::size_t i = 0; i < lpad; ++i)
536 10x encode_one(out, '0', cs);
537 }
538 145x while (n)
539 {
540 102x unsigned long long int d = uv / p;
541 102x encode_one(out, '0' + static_cast<char>(d), cs);
542 102x --n;
543 102x uv %= p;
544 102x p /= 10;
545 }
546 43x if (!zeros)
547 {
548 48x for (std::size_t i = 0; i < rpad; ++i)
549 8x encode_one(out, fill, cs);
550 }
551 43x return out;
552 }
553
554 char*
555 14x integer_formatter_impl::
556 format(
557 unsigned long long int v,
558 format_context& ctx,
559 grammar::lut_chars const& cs) const
560 {
561 // get n digits
562 14x unsigned long long int v0 = v;
563 14x unsigned long long int p = 1;
564 14x std::size_t n = 0;
565 14x if (sign != '-')
566 {
567 2x ++n;
568 }
569 do
570 {
571 53x if (v >= 10)
572 39x p *= 10;
573 53x v /= 10;
574 53x ++n;
575 }
576 53x while (v > 0);
577 static constexpr auto m =
578 std::numeric_limits<unsigned long long int>::digits10;
579 14x BOOST_ASSERT(n <= m + 2);
580 ignore_unused(m);
581
582 // get pad
583 14x std::size_t w = width;
584 25x if (width_idx != std::size_t(-1) ||
585 11x !width_name.empty())
586 {
587 4x get_width_from_args(
588 4x width_idx, width_name, ctx.args(), w);
589 }
590 14x std::size_t lpad = 0;
591 14x std::size_t rpad = 0;
592 14x if (w > n)
593 {
594 8x std::size_t pad = w - n;
595 8x if (zeros)
596 {
597 1x lpad = pad;
598 }
599 else
600 {
601 7x switch (align)
602 {
603 1x case '<':
604 1x rpad = pad;
605 1x break;
606 5x case '>':
607 5x lpad = pad;
608 5x break;
609 1x case '^':
610 1x lpad = pad / 2;
611 1x rpad = pad - lpad;
612 1x break;
613 }
614 }
615 }
616
617 // write
618 14x v = v0;
619 14x char* out = ctx.out();
620 14x if (!zeros)
621 {
622 35x for (std::size_t i = 0; i < lpad; ++i)
623 22x encode_one(out, fill, cs);
624 }
625 14x if (sign != '-')
626 {
627 2x encode_one(out, sign, cs);
628 2x --n;
629 }
630 14x if (zeros)
631 {
632 5x for (std::size_t i = 0; i < lpad; ++i)
633 4x encode_one(out, '0', cs);
634 }
635 67x while (n)
636 {
637 53x unsigned long long int d = v / p;
638 53x encode_one(out, '0' + static_cast<char>(d), cs);
639 53x --n;
640 53x v %= p;
641 53x p /= 10;
642 }
643 14x if (!zeros)
644 {
645 19x for (std::size_t i = 0; i < rpad; ++i)
646 6x encode_one(out, fill, cs);
647 }
648 14x return out;
649 }
650
651 } // detail
652 } // urls
653 } // boost
654
655