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 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 369 : formatter<core::string_view>::
45 : parse(format_parse_context& ctx)
46 : {
47 369 : char const* it = ctx.begin();
48 369 : char const* end = ctx.end();
49 369 : BOOST_ASSERT(it != end);
50 :
51 : // fill / align
52 369 : if (end - it > 2)
53 : {
54 107 : if (*it != '{' &&
55 107 : *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 369 : if (align == '\0' &&
68 353 : (*it == '<' ||
69 353 : *it == '>' ||
70 353 : *it == '^'))
71 : {
72 4 : align = *it++;
73 : }
74 :
75 : // width
76 369 : char const* it0 = it;
77 369 : 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 369 : auto rw = grammar::parse(it, end, width_rule);
88 369 : if (!rw)
89 : {
90 : // rewind
91 349 : 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 369 : if (*it == 'c' ||
128 366 : *it == 's')
129 : {
130 23 : ++it;
131 : }
132 :
133 : // we should have arrived at the end now
134 369 : if (*it != '}')
135 : {
136 1 : urls::detail::throw_invalid_argument();
137 : }
138 :
139 368 : return it;
140 369 : }
141 :
142 : std::size_t
143 185 : formatter<core::string_view>::
144 : measure(
145 : core::string_view str,
146 : measure_context& ctx,
147 : grammar::lut_chars const& cs) const
148 : {
149 185 : std::size_t w = width;
150 367 : if (width_idx != std::size_t(-1) ||
151 182 : !width_name.empty())
152 : {
153 5 : get_width_from_args(
154 5 : width_idx, width_name, ctx.args(), w);
155 : }
156 :
157 185 : std::size_t n = ctx.out();
158 185 : if (str.size() < w)
159 9 : n += measure_one(fill, cs) * (w - str.size());
160 :
161 185 : return n + encoded_size(str, cs);
162 : }
163 :
164 : char*
165 183 : formatter<core::string_view>::
166 : format(core::string_view str, format_context& ctx, grammar::lut_chars const& cs) const
167 : {
168 183 : std::size_t w = width;
169 363 : if (width_idx != std::size_t(-1) ||
170 180 : !width_name.empty())
171 : {
172 5 : get_width_from_args(
173 5 : width_idx, width_name, ctx.args(), w);
174 : }
175 :
176 183 : std::size_t lpad = 0;
177 183 : std::size_t rpad = 0;
178 183 : 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 183 : char* out = ctx.out();
199 210 : for (std::size_t i = 0; i < lpad; ++i)
200 27 : encode_one(out, fill, cs);
201 878 : for (char c: str)
202 695 : encode_one(out, c, cs);
203 190 : for (std::size_t i = 0; i < rpad; ++i)
204 7 : encode_one(out, fill, cs);
205 183 : 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 97 : integer_formatter_impl::
234 : parse(format_parse_context& ctx)
235 : {
236 97 : char const* it = ctx.begin();
237 97 : char const* end = ctx.end();
238 97 : BOOST_ASSERT(it != end);
239 :
240 : // fill / align
241 97 : 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 97 : if (align == '\0' &&
257 67 : (*it == '<' ||
258 67 : *it == '>' ||
259 59 : *it == '^'))
260 : {
261 12 : align = *it++;
262 : }
263 :
264 : // sign
265 97 : if (*it == '+' ||
266 91 : *it == '-' ||
267 91 : *it == ' ')
268 : {
269 12 : sign = *it++;
270 : }
271 :
272 : // #
273 97 : if (*it == '#')
274 : {
275 : // alternate form not supported
276 2 : ++it;
277 : }
278 :
279 : // 0
280 97 : if (*it == '0')
281 : {
282 8 : zeros = *it++;
283 : }
284 :
285 : // width
286 97 : char const* it0 = it;
287 97 : 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 97 : auto rw = grammar::parse(it, end, width_rule);
297 97 : if (!rw)
298 : {
299 : // rewind
300 55 : 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 97 : 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 97 : if (*it != '}')
347 : {
348 1 : urls::detail::throw_invalid_argument();
349 : }
350 :
351 96 : return it;
352 97 : }
353 :
354 : std::size_t
355 34 : integer_formatter_impl::
356 : measure(
357 : long long int v,
358 : measure_context& ctx,
359 : grammar::lut_chars const& cs) const
360 : {
361 34 : std::size_t dn = 0;
362 34 : std::size_t n = 0;
363 34 : if (v < 0)
364 : {
365 1 : dn += measure_one('-', cs);
366 1 : ++n;
367 1 : v *= -1;
368 : }
369 33 : else if (sign != '-')
370 : {
371 4 : dn += measure_one(sign, cs);
372 4 : ++n;
373 : }
374 : do
375 : {
376 67 : int d = v % 10;
377 67 : v /= 10;
378 67 : dn += measure_one('0' + static_cast<char>(d), cs);
379 67 : ++n;
380 : }
381 67 : while (v > 0);
382 :
383 34 : std::size_t w = width;
384 65 : if (width_idx != std::size_t(-1) ||
385 31 : !width_name.empty())
386 : {
387 5 : get_width_from_args(
388 5 : width_idx, width_name, ctx.args(), w);
389 : }
390 34 : 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 34 : 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 14 : return ctx.out() + dn;
438 : }
439 :
440 : char*
441 34 : 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 34 : long long int v0 = v;
449 34 : long long int p = 1;
450 34 : std::size_t n = 0;
451 34 : if (v < 0)
452 : {
453 1 : v *= - 1;
454 1 : ++n;
455 : }
456 33 : else if (sign != '-')
457 : {
458 4 : ++n;
459 : }
460 : do
461 : {
462 67 : if (v >= 10)
463 33 : p *= 10;
464 67 : v /= 10;
465 67 : ++n;
466 : }
467 67 : while (v > 0);
468 : static constexpr auto m =
469 : std::numeric_limits<long long int>::digits10;
470 34 : BOOST_ASSERT(n <= m + 1);
471 : ignore_unused(m);
472 :
473 : // get pad
474 34 : std::size_t w = width;
475 65 : if (width_idx != std::size_t(-1) ||
476 31 : !width_name.empty())
477 : {
478 5 : get_width_from_args(
479 5 : width_idx, width_name, ctx.args(), w);
480 : }
481 34 : std::size_t lpad = 0;
482 34 : std::size_t rpad = 0;
483 34 : 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 34 : v = v0;
510 34 : char* out = ctx.out();
511 34 : if (!zeros)
512 : {
513 59 : for (std::size_t i = 0; i < lpad; ++i)
514 28 : encode_one(out, fill, cs);
515 : }
516 34 : if (v < 0)
517 : {
518 1 : encode_one(out, '-', cs);
519 1 : v *= -1;
520 1 : --n;
521 : }
522 33 : else if (sign != '-')
523 : {
524 4 : encode_one(out, sign, cs);
525 4 : --n;
526 : }
527 34 : if (zeros)
528 : {
529 13 : for (std::size_t i = 0; i < lpad; ++i)
530 10 : encode_one(out, '0', cs);
531 : }
532 101 : while (n)
533 : {
534 67 : unsigned long long int d = v / p;
535 67 : encode_one(out, '0' + static_cast<char>(d), cs);
536 67 : --n;
537 67 : v %= p;
538 67 : p /= 10;
539 : }
540 34 : if (!zeros)
541 : {
542 39 : for (std::size_t i = 0; i < rpad; ++i)
543 8 : encode_one(out, fill, cs);
544 : }
545 34 : 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 :
|