src/detail/format_args.cpp

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