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