LCOV - code coverage report
Current view: top level - libs/url/src/detail - format_args.cpp (source / functions) Hit Total Coverage
Test: coverage_filtered.info Lines: 305 305 100.0 %
Date: 2024-02-29 20:02:55 Functions: 11 11 100.0 %

          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

Generated by: LCOV version 1.15