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-03-05 20:06:56 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             : 
      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             :     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         370 :     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         736 :     return it;
     140             : }
     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          98 :     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         192 :     return it;
     352             : }
     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          33 :     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          39 :     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          33 :     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          39 :     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             : 

Generated by: LCOV version 1.15