LCOV - code coverage report
Current view: top level - /jenkins/workspace/boost-root/libs/url/src/detail - format_args.cpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 100.0 % 302 302
Test Date: 2026-02-25 21:00:01 Functions: 100.0 % 11 11

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

Generated by: LCOV version 2.3