TLA 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 :
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 HIT 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 451 : formatter<core::string_view>::
45 : parse(format_parse_context& ctx)
46 : {
47 451 : char const* it = ctx.begin();
48 451 : char const* end = ctx.end();
49 451 : BOOST_ASSERT(it != end);
50 :
51 : // fill / align
52 451 : if (end - it > 2)
53 : {
54 121 : if (*it != '{' &&
55 121 : *it != '}' &&
56 23 : (*(it + 1) == '<' ||
57 21 : *(it + 1) == '>' ||
58 9 : *(it + 1) == '^'))
59 : {
60 18 : fill = *it;
61 18 : align = *(it + 1);
62 18 : it += 2;
63 : }
64 : }
65 :
66 : // align
67 451 : if (align == '\0' &&
68 433 : (*it == '<' ||
69 433 : *it == '>' ||
70 433 : *it == '^'))
71 : {
72 4 : align = *it++;
73 : }
74 :
75 : // width
76 451 : char const* it0 = it;
77 451 : 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 451 : auto rw = grammar::parse(it, end, width_rule);
88 451 : if (!rw)
89 : {
90 : // rewind
91 429 : it = it0;
92 : }
93 22 : else if (align != '\0')
94 : {
95 : // width is ignored when align is '\0'
96 22 : if (rw->index() == 0)
97 : {
98 : // unsigned_rule
99 12 : 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 451 : if (*it == 'c' ||
128 448 : *it == 's')
129 : {
130 25 : ++it;
131 : }
132 :
133 : // we should have arrived at the end now
134 451 : if (*it != '}')
135 : {
136 1 : urls::detail::throw_invalid_argument();
137 : }
138 :
139 450 : return it;
140 : }
141 :
142 : std::size_t
143 226 : formatter<core::string_view>::
144 : measure(
145 : core::string_view str,
146 : measure_context& ctx,
147 : grammar::lut_chars const& cs) const
148 : {
149 226 : std::size_t w = width;
150 449 : if (width_idx != std::size_t(-1) ||
151 223 : !width_name.empty())
152 : {
153 5 : get_width_from_args(
154 5 : width_idx, width_name, ctx.args(), w);
155 : }
156 :
157 226 : std::size_t n = ctx.out();
158 226 : if (str.size() < w)
159 10 : n += measure_one(fill, cs) * (w - str.size());
160 :
161 226 : return n + encoded_size(str, cs);
162 : }
163 :
164 : char*
165 224 : formatter<core::string_view>::
166 : format(core::string_view str, format_context& ctx, grammar::lut_chars const& cs) const
167 : {
168 224 : std::size_t w = width;
169 445 : if (width_idx != std::size_t(-1) ||
170 221 : !width_name.empty())
171 : {
172 5 : get_width_from_args(
173 5 : width_idx, width_name, ctx.args(), w);
174 : }
175 :
176 224 : std::size_t lpad = 0;
177 224 : std::size_t rpad = 0;
178 224 : if (str.size() < w)
179 : {
180 10 : std::size_t pad = w - str.size();
181 10 : switch (align)
182 : {
183 1 : case '<':
184 1 : rpad = pad;
185 1 : break;
186 6 : case '>':
187 6 : lpad = pad;
188 6 : break;
189 3 : case '^':
190 3 : lpad = pad / 2;
191 3 : rpad = pad - lpad;
192 3 : break;
193 : }
194 : }
195 :
196 : // unsafe `encode`, assuming `out` has
197 : // enough capacity
198 224 : char* out = ctx.out();
199 252 : for (std::size_t i = 0; i < lpad; ++i)
200 28 : encode_one(out, fill, cs);
201 1252 : for (char c: str)
202 1028 : encode_one(out, c, cs);
203 232 : for (std::size_t i = 0; i < rpad; ++i)
204 8 : encode_one(out, fill, cs);
205 224 : 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 115 : integer_formatter_impl::
234 : parse(format_parse_context& ctx)
235 : {
236 115 : char const* it = ctx.begin();
237 115 : char const* end = ctx.end();
238 115 : BOOST_ASSERT(it != end);
239 :
240 : // fill / align
241 115 : 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 115 : if (align == '\0' &&
257 85 : (*it == '<' ||
258 85 : *it == '>' ||
259 77 : *it == '^'))
260 : {
261 12 : align = *it++;
262 : }
263 :
264 : // sign
265 115 : if (*it == '+' ||
266 109 : *it == '-' ||
267 109 : *it == ' ')
268 : {
269 12 : sign = *it++;
270 : }
271 :
272 : // #
273 115 : if (*it == '#')
274 : {
275 : // alternate form not supported
276 2 : ++it;
277 : }
278 :
279 : // 0
280 115 : if (*it == '0')
281 : {
282 8 : zeros = *it++;
283 : }
284 :
285 : // width
286 115 : char const* it0 = it;
287 115 : 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 115 : auto rw = grammar::parse(it, end, width_rule);
297 115 : if (!rw)
298 : {
299 : // rewind
300 73 : 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 115 : 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 115 : if (*it != '}')
347 : {
348 1 : urls::detail::throw_invalid_argument();
349 : }
350 :
351 114 : return it;
352 : }
353 :
354 : std::size_t
355 43 : integer_formatter_impl::
356 : measure(
357 : long long int v,
358 : measure_context& ctx,
359 : grammar::lut_chars const& cs) const
360 : {
361 43 : std::size_t dn = 0;
362 43 : std::size_t n = 0;
363 43 : if (v < 0)
364 : {
365 2 : dn += measure_one('-', cs);
366 2 : ++n;
367 : }
368 41 : else if (sign != '-')
369 : {
370 4 : dn += measure_one(sign, cs);
371 4 : ++n;
372 : }
373 : // Use unsigned to avoid UB when v == LLONG_MIN
374 43 : unsigned long long int uv = v < 0
375 43 : ? 0ull - static_cast<unsigned long long int>(v)
376 : : static_cast<unsigned long long int>(v);
377 : do
378 : {
379 102 : int d = static_cast<int>(uv % 10);
380 102 : uv /= 10;
381 102 : dn += measure_one('0' + static_cast<char>(d), cs);
382 102 : ++n;
383 : }
384 102 : while (uv > 0);
385 :
386 43 : std::size_t w = width;
387 83 : if (width_idx != std::size_t(-1) ||
388 40 : !width_name.empty())
389 : {
390 5 : get_width_from_args(
391 5 : width_idx, width_name, ctx.args(), w);
392 : }
393 43 : if (w > n)
394 : {
395 12 : if (!zeros)
396 9 : dn += measure_one(fill, cs) * (w - n);
397 : else
398 3 : dn += measure_one('0', cs) * (w - n);
399 : }
400 86 : return ctx.out() + dn;
401 : }
402 :
403 : std::size_t
404 14 : integer_formatter_impl::
405 : measure(
406 : unsigned long long int v,
407 : measure_context& ctx,
408 : grammar::lut_chars const& cs) const
409 : {
410 14 : std::size_t dn = 0;
411 14 : std::size_t n = 0;
412 14 : if (sign != '-')
413 : {
414 2 : dn += measure_one(sign, cs);
415 2 : ++n;
416 : }
417 : do
418 : {
419 53 : int d = v % 10;
420 53 : v /= 10;
421 53 : dn += measure_one('0' + static_cast<char>(d), cs);
422 53 : ++n;
423 : }
424 53 : while (v != 0);
425 :
426 14 : std::size_t w = width;
427 25 : if (width_idx != std::size_t(-1) ||
428 11 : !width_name.empty())
429 : {
430 4 : get_width_from_args(
431 4 : width_idx, width_name, ctx.args(), w);
432 : }
433 14 : if (w > n)
434 : {
435 8 : if (!zeros)
436 7 : dn += measure_one(fill, cs) * (w - n);
437 : else
438 1 : dn += measure_one('0', cs) * (w - n);
439 : }
440 28 : return ctx.out() + dn;
441 : }
442 :
443 : char*
444 43 : 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 43 : bool const neg = v < 0;
453 43 : unsigned long long int uv = neg
454 43 : ? 0ull - static_cast<unsigned long long int>(v)
455 : : static_cast<unsigned long long int>(v);
456 43 : unsigned long long int uv0 = uv;
457 43 : unsigned long long int p = 1;
458 43 : std::size_t n = 0;
459 43 : if (neg || sign != '-')
460 : {
461 6 : ++n;
462 : }
463 : do
464 : {
465 102 : if (uv >= 10)
466 59 : p *= 10;
467 102 : uv /= 10;
468 102 : ++n;
469 : }
470 102 : while (uv > 0);
471 : static constexpr auto m =
472 : std::numeric_limits<long long int>::digits10;
473 43 : BOOST_ASSERT(n <= m + 2);
474 : ignore_unused(m);
475 :
476 : // get pad
477 43 : std::size_t w = width;
478 83 : if (width_idx != std::size_t(-1) ||
479 40 : !width_name.empty())
480 : {
481 5 : get_width_from_args(
482 5 : width_idx, width_name, ctx.args(), w);
483 : }
484 43 : std::size_t lpad = 0;
485 43 : std::size_t rpad = 0;
486 43 : if (w > n)
487 : {
488 12 : std::size_t pad = w - n;
489 12 : if (zeros)
490 : {
491 3 : lpad = pad;
492 : }
493 : else
494 : {
495 9 : switch (align)
496 : {
497 1 : case '<':
498 1 : rpad = pad;
499 1 : break;
500 6 : case '>':
501 6 : lpad = pad;
502 6 : break;
503 2 : case '^':
504 2 : lpad = pad / 2;
505 2 : rpad = pad - lpad;
506 2 : break;
507 : }
508 : }
509 : }
510 :
511 : // write
512 43 : uv = uv0;
513 43 : char* out = ctx.out();
514 43 : if (!zeros)
515 : {
516 68 : for (std::size_t i = 0; i < lpad; ++i)
517 28 : encode_one(out, fill, cs);
518 : }
519 43 : if (neg)
520 : {
521 2 : encode_one(out, '-', cs);
522 2 : --n;
523 : }
524 41 : else if (sign != '-')
525 : {
526 4 : encode_one(out, sign, cs);
527 4 : --n;
528 : }
529 43 : if (zeros)
530 : {
531 13 : for (std::size_t i = 0; i < lpad; ++i)
532 10 : encode_one(out, '0', cs);
533 : }
534 145 : while (n)
535 : {
536 102 : unsigned long long int d = uv / p;
537 102 : encode_one(out, '0' + static_cast<char>(d), cs);
538 102 : --n;
539 102 : uv %= p;
540 102 : p /= 10;
541 : }
542 43 : if (!zeros)
543 : {
544 48 : for (std::size_t i = 0; i < rpad; ++i)
545 8 : encode_one(out, fill, cs);
546 : }
547 43 : 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 : 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 + 2);
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 :
|