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-03-02 22:26:03 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             451 : formatter<core::string_view>::
      45                 : parse(format_parse_context& ctx)
      46                 : {
      47             451 :     char const* it = ctx.begin();
      48             451 :     char const* end = ctx.end();
      49             451 :     BOOST_ASSERT(it != end);
      50                 : 
      51                 :     // fill / align
      52             451 :     if (end - it > 2)
      53                 :     {
      54             121 :         if (*it != '{' &&
      55             121 :             *it != '}' &&
      56              23 :             (*(it + 1) == '<' ||
      57              21 :              *(it + 1) == '>' ||
      58               9 :              *(it + 1) == '^'))
      59                 :         {
      60              18 :             fill = *it;
      61              18 :             align = *(it + 1);
      62              18 :             it += 2;
      63                 :         }
      64                 :     }
      65                 : 
      66                 :     // align
      67             451 :     if (align == '\0' &&
      68             433 :         (*it == '<' ||
      69             433 :          *it == '>' ||
      70             433 :          *it == '^'))
      71                 :     {
      72               4 :         align = *it++;
      73                 :     }
      74                 : 
      75                 :     // width
      76             451 :     char const* it0 = it;
      77             451 :     constexpr auto width_rule =
      78                 :         grammar::variant_rule(
      79                 :              grammar::unsigned_rule<std::size_t>{},
      80                 :              grammar::tuple_rule(
      81                 :                  grammar::squelch(
      82                 :                      grammar::delim_rule('{')),
      83                 :                  grammar::optional_rule(
      84                 :                     arg_id_rule),
      85                 :                  grammar::squelch(
      86                 :                      grammar::delim_rule('}'))));
      87             451 :     auto rw = grammar::parse(it, end, width_rule);
      88             451 :     if (!rw)
      89                 :     {
      90                 :         // rewind
      91             429 :         it = it0;
      92                 :     }
      93              22 :     else if (align != '\0')
      94                 :     {
      95                 :         // width is ignored when align is '\0'
      96              22 :         if (rw->index() == 0)
      97                 :         {
      98                 :             // unsigned_rule
      99              12 :             width = variant2::get<0>(*rw);
     100                 :         }
     101                 :         else
     102                 :         {
     103                 :             // arg_id: store the id idx or string
     104              10 :             auto& arg_id = variant2::get<1>(*rw);
     105              10 :             if (!arg_id)
     106                 :             {
     107                 :                 // empty arg_id, use and consume
     108                 :                 // the next arg idx
     109               2 :                 width_idx = ctx.next_arg_id();
     110                 :             }
     111               8 :             else if (arg_id->index() == 0)
     112                 :             {
     113                 :                 // string identifier
     114               4 :                 width_name = variant2::get<0>(*arg_id);
     115                 :             }
     116                 :             else
     117                 :             {
     118                 :                 // integer identifier: use the
     119                 :                 // idx of this format_arg
     120               4 :                 width_idx = variant2::get<1>(*arg_id);
     121                 :             }
     122                 :         }
     123                 :     }
     124                 : 
     125                 :     // type is parsed but doesn't have to
     126                 :     // be stored for strings
     127             451 :     if (*it == 'c' ||
     128             448 :         *it == 's')
     129                 :     {
     130              25 :         ++it;
     131                 :     }
     132                 : 
     133                 :     // we should have arrived at the end now
     134             451 :     if (*it != '}')
     135                 :     {
     136               1 :         urls::detail::throw_invalid_argument();
     137                 :     }
     138                 : 
     139             450 :     return it;
     140                 : }
     141                 : 
     142                 : std::size_t
     143             226 : formatter<core::string_view>::
     144                 : measure(
     145                 :     core::string_view str,
     146                 :     measure_context& ctx,
     147                 :     grammar::lut_chars const& cs) const
     148                 : {
     149             226 :     std::size_t w = width;
     150             449 :     if (width_idx != std::size_t(-1) ||
     151             223 :         !width_name.empty())
     152                 :     {
     153               5 :         get_width_from_args(
     154               5 :             width_idx, width_name, ctx.args(), w);
     155                 :     }
     156                 : 
     157             226 :     std::size_t n = ctx.out();
     158             226 :     if (str.size() < w)
     159              10 :         n += measure_one(fill, cs) * (w - str.size());
     160                 : 
     161             226 :     return n + encoded_size(str, cs);
     162                 : }
     163                 : 
     164                 : char*
     165             224 : formatter<core::string_view>::
     166                 : format(core::string_view str, format_context& ctx, grammar::lut_chars const& cs) const
     167                 : {
     168             224 :     std::size_t w = width;
     169             445 :     if (width_idx != std::size_t(-1) ||
     170             221 :         !width_name.empty())
     171                 :     {
     172               5 :         get_width_from_args(
     173               5 :             width_idx, width_name, ctx.args(), w);
     174                 :     }
     175                 : 
     176             224 :     std::size_t lpad = 0;
     177             224 :     std::size_t rpad = 0;
     178             224 :     if (str.size() < w)
     179                 :     {
     180              10 :         std::size_t pad = w - str.size();
     181              10 :         switch (align)
     182                 :         {
     183               1 :         case '<':
     184               1 :             rpad = pad;
     185               1 :             break;
     186               6 :         case '>':
     187               6 :             lpad = pad;
     188               6 :             break;
     189               3 :         case '^':
     190               3 :             lpad = pad / 2;
     191               3 :             rpad = pad - lpad;
     192               3 :             break;
     193                 :         }
     194                 :     }
     195                 : 
     196                 :     // unsafe `encode`, assuming `out` has
     197                 :     // enough capacity
     198             224 :     char* out = ctx.out();
     199             252 :     for (std::size_t i = 0; i < lpad; ++i)
     200              28 :         encode_one(out, fill, cs);
     201            1252 :     for (char c: str)
     202            1028 :         encode_one(out, c, cs);
     203             232 :     for (std::size_t i = 0; i < rpad; ++i)
     204               8 :         encode_one(out, fill, cs);
     205             224 :     return out;
     206                 : }
     207                 : 
     208                 : void
     209              28 : get_width_from_args(
     210                 :     std::size_t arg_idx,
     211                 :     core::string_view arg_name,
     212                 :     format_args args,
     213                 :     std::size_t& w)
     214                 : {
     215                 :     // check arg_id
     216              28 :     format_arg warg;
     217              28 :     if (arg_idx != std::size_t(-1))
     218                 :     {
     219                 :         // identifier
     220              18 :         warg = args.get(arg_idx);
     221                 :     }
     222                 :     else
     223                 :     {
     224                 :         // unsigned integer
     225              10 :         warg = args.get(arg_name);
     226                 :     }
     227                 : 
     228                 :     // get unsigned int value from that format arg
     229              28 :     w = warg.value();
     230              28 : }
     231                 : 
     232                 : char const*
     233             115 : integer_formatter_impl::
     234                 : parse(format_parse_context& ctx)
     235                 : {
     236             115 :     char const* it = ctx.begin();
     237             115 :     char const* end = ctx.end();
     238             115 :     BOOST_ASSERT(it != end);
     239                 : 
     240                 :     // fill / align
     241             115 :     if (end - it > 2)
     242                 :     {
     243              57 :         if (*it != '{' &&
     244              57 :             *it != '}' &&
     245              53 :             (*(it + 1) == '<' ||
     246              49 :              *(it + 1) == '>' ||
     247              27 :              *(it + 1) == '^'))
     248                 :         {
     249              30 :             fill = *it;
     250              30 :             align = *(it + 1);
     251              30 :             it += 2;
     252                 :         }
     253                 :     }
     254                 : 
     255                 :     // align
     256             115 :     if (align == '\0' &&
     257              85 :         (*it == '<' ||
     258              85 :          *it == '>' ||
     259              77 :          *it == '^'))
     260                 :     {
     261              12 :         align = *it++;
     262                 :     }
     263                 : 
     264                 :     // sign
     265             115 :     if (*it == '+' ||
     266             109 :         *it == '-' ||
     267             109 :         *it == ' ')
     268                 :     {
     269              12 :         sign = *it++;
     270                 :     }
     271                 : 
     272                 :     // #
     273             115 :     if (*it == '#')
     274                 :     {
     275                 :         // alternate form not supported
     276               2 :         ++it;
     277                 :     }
     278                 : 
     279                 :     // 0
     280             115 :     if (*it == '0')
     281                 :     {
     282               8 :         zeros = *it++;
     283                 :     }
     284                 : 
     285                 :     // width
     286             115 :     char const* it0 = it;
     287             115 :     constexpr auto width_rule = grammar::variant_rule(
     288                 :         grammar::unsigned_rule<std::size_t>{},
     289                 :         grammar::tuple_rule(
     290                 :             grammar::squelch(
     291                 :                 grammar::delim_rule('{')),
     292                 :             grammar::optional_rule(
     293                 :                 arg_id_rule),
     294                 :             grammar::squelch(
     295                 :                 grammar::delim_rule('}'))));
     296             115 :     auto rw = grammar::parse(it, end, width_rule);
     297             115 :     if (!rw)
     298                 :     {
     299                 :         // rewind
     300              73 :         it = it0;
     301                 :     }
     302              42 :     else if (align != '\0')
     303                 :     {
     304                 :         // width is ignored when align is '\0'
     305              42 :         if (rw->index() == 0)
     306                 :         {
     307                 :             // unsigned_rule
     308              24 :             width = variant2::get<0>(*rw);
     309                 :         }
     310                 :         else
     311                 :         {
     312                 :             // arg_id: store the id idx or string
     313              18 :             auto& arg_id = variant2::get<1>(*rw);
     314              18 :             if (!arg_id)
     315                 :             {
     316                 :                 // empty arg_id, use and consume
     317                 :                 // the next arg idx
     318               4 :                 width_idx = ctx.next_arg_id();
     319                 :             }
     320              14 :             else if (arg_id->index() == 0)
     321                 :             {
     322                 :                 // string identifier
     323               6 :                 width_name = variant2::get<0>(*arg_id);
     324                 :             }
     325                 :             else
     326                 :             {
     327                 :                 // integer identifier: use the
     328                 :                 // idx of this format_arg
     329               8 :                 width_idx = variant2::get<1>(*arg_id);
     330                 :             }
     331                 :         }
     332                 :     }
     333                 : 
     334                 :     // type is parsed but doesn't have to
     335                 :     // be stored for strings
     336             115 :     if (*it == 'd')
     337                 :     {
     338                 :         // we don't include other presentation
     339                 :         // modes for integers as they are not
     340                 :         // recommended or generally used in
     341                 :         // urls
     342              55 :         ++it;
     343                 :     }
     344                 : 
     345                 :     // we should have arrived at the end now
     346             115 :     if (*it != '}')
     347                 :     {
     348               1 :         urls::detail::throw_invalid_argument();
     349                 :     }
     350                 : 
     351             114 :     return it;
     352                 : }
     353                 : 
     354                 : std::size_t
     355              43 : integer_formatter_impl::
     356                 : measure(
     357                 :     long long int v,
     358                 :     measure_context& ctx,
     359                 :     grammar::lut_chars const& cs) const
     360                 : {
     361              43 :     std::size_t dn = 0;
     362              43 :     std::size_t n = 0;
     363              43 :     if (v < 0)
     364                 :     {
     365               2 :         dn += measure_one('-', cs);
     366               2 :         ++n;
     367                 :     }
     368              41 :     else if (sign != '-')
     369                 :     {
     370               4 :         dn += measure_one(sign, cs);
     371               4 :         ++n;
     372                 :     }
     373                 :     // Use unsigned to avoid UB when v == LLONG_MIN
     374              43 :     unsigned long long int uv = v < 0
     375              43 :         ? 0ull - static_cast<unsigned long long int>(v)
     376                 :         : static_cast<unsigned long long int>(v);
     377                 :     do
     378                 :     {
     379             102 :         int d = static_cast<int>(uv % 10);
     380             102 :         uv /= 10;
     381             102 :         dn += measure_one('0' + static_cast<char>(d), cs);
     382             102 :         ++n;
     383                 :     }
     384             102 :     while (uv > 0);
     385                 : 
     386              43 :     std::size_t w = width;
     387              83 :     if (width_idx != std::size_t(-1) ||
     388              40 :         !width_name.empty())
     389                 :     {
     390               5 :         get_width_from_args(
     391               5 :             width_idx, width_name, ctx.args(), w);
     392                 :     }
     393              43 :     if (w > n)
     394                 :     {
     395              12 :         if (!zeros)
     396               9 :             dn += measure_one(fill, cs) * (w - n);
     397                 :         else
     398               3 :             dn += measure_one('0', cs) * (w - n);
     399                 :     }
     400              86 :     return ctx.out() + dn;
     401                 : }
     402                 : 
     403                 : std::size_t
     404              14 : integer_formatter_impl::
     405                 : measure(
     406                 :     unsigned long long int v,
     407                 :     measure_context& ctx,
     408                 :     grammar::lut_chars const& cs) const
     409                 : {
     410              14 :     std::size_t dn = 0;
     411              14 :     std::size_t n = 0;
     412              14 :     if (sign != '-')
     413                 :     {
     414               2 :         dn += measure_one(sign, cs);
     415               2 :         ++n;
     416                 :     }
     417                 :     do
     418                 :     {
     419              53 :         int d = v % 10;
     420              53 :         v /= 10;
     421              53 :         dn += measure_one('0' + static_cast<char>(d), cs);
     422              53 :         ++n;
     423                 :     }
     424              53 :     while (v != 0);
     425                 : 
     426              14 :     std::size_t w = width;
     427              25 :     if (width_idx != std::size_t(-1) ||
     428              11 :         !width_name.empty())
     429                 :     {
     430               4 :         get_width_from_args(
     431               4 :             width_idx, width_name, ctx.args(), w);
     432                 :     }
     433              14 :     if (w > n)
     434                 :     {
     435               8 :         if (!zeros)
     436               7 :             dn += measure_one(fill, cs) * (w - n);
     437                 :         else
     438               1 :             dn += measure_one('0', cs) * (w - n);
     439                 :     }
     440              28 :     return ctx.out() + dn;
     441                 : }
     442                 : 
     443                 : char*
     444              43 : integer_formatter_impl::
     445                 : format(
     446                 :     long long int v,
     447                 :     format_context& ctx,
     448                 :     grammar::lut_chars const& cs) const
     449                 : {
     450                 :     // get n digits
     451                 :     // Use unsigned to avoid UB when v == LLONG_MIN
     452              43 :     bool const neg = v < 0;
     453              43 :     unsigned long long int uv = neg
     454              43 :         ? 0ull - static_cast<unsigned long long int>(v)
     455                 :         : static_cast<unsigned long long int>(v);
     456              43 :     unsigned long long int uv0 = uv;
     457              43 :     unsigned long long int p = 1;
     458              43 :     std::size_t n = 0;
     459              43 :     if (neg || sign != '-')
     460                 :     {
     461               6 :         ++n;
     462                 :     }
     463                 :     do
     464                 :     {
     465             102 :         if (uv >= 10)
     466              59 :             p *= 10;
     467             102 :         uv /= 10;
     468             102 :         ++n;
     469                 :     }
     470             102 :     while (uv > 0);
     471                 :     static constexpr auto m =
     472                 :         std::numeric_limits<long long int>::digits10;
     473              43 :     BOOST_ASSERT(n <= m + 2);
     474                 :     ignore_unused(m);
     475                 : 
     476                 :     // get pad
     477              43 :     std::size_t w = width;
     478              83 :     if (width_idx != std::size_t(-1) ||
     479              40 :         !width_name.empty())
     480                 :     {
     481               5 :         get_width_from_args(
     482               5 :             width_idx, width_name, ctx.args(), w);
     483                 :     }
     484              43 :     std::size_t lpad = 0;
     485              43 :     std::size_t rpad = 0;
     486              43 :     if (w > n)
     487                 :     {
     488              12 :         std::size_t pad = w - n;
     489              12 :         if (zeros)
     490                 :         {
     491               3 :             lpad = pad;
     492                 :         }
     493                 :         else
     494                 :         {
     495               9 :             switch (align)
     496                 :             {
     497               1 :             case '<':
     498               1 :                 rpad = pad;
     499               1 :                 break;
     500               6 :             case '>':
     501               6 :                 lpad = pad;
     502               6 :                 break;
     503               2 :             case '^':
     504               2 :                 lpad = pad / 2;
     505               2 :                 rpad = pad - lpad;
     506               2 :                 break;
     507                 :             }
     508                 :         }
     509                 :     }
     510                 : 
     511                 :     // write
     512              43 :     uv = uv0;
     513              43 :     char* out = ctx.out();
     514              43 :     if (!zeros)
     515                 :     {
     516              68 :         for (std::size_t i = 0; i < lpad; ++i)
     517              28 :             encode_one(out, fill, cs);
     518                 :     }
     519              43 :     if (neg)
     520                 :     {
     521               2 :         encode_one(out, '-', cs);
     522               2 :         --n;
     523                 :     }
     524              41 :     else if (sign != '-')
     525                 :     {
     526               4 :         encode_one(out, sign, cs);
     527               4 :         --n;
     528                 :     }
     529              43 :     if (zeros)
     530                 :     {
     531              13 :         for (std::size_t i = 0; i < lpad; ++i)
     532              10 :             encode_one(out, '0', cs);
     533                 :     }
     534             145 :     while (n)
     535                 :     {
     536             102 :         unsigned long long int d = uv / p;
     537             102 :         encode_one(out, '0' + static_cast<char>(d), cs);
     538             102 :         --n;
     539             102 :         uv %= p;
     540             102 :         p /= 10;
     541                 :     }
     542              43 :     if (!zeros)
     543                 :     {
     544              48 :         for (std::size_t i = 0; i < rpad; ++i)
     545               8 :             encode_one(out, fill, cs);
     546                 :     }
     547              43 :     return out;
     548                 : }
     549                 : 
     550                 : char*
     551              14 : integer_formatter_impl::
     552                 : format(
     553                 : unsigned long long int v,
     554                 : format_context& ctx,
     555                 : grammar::lut_chars const& cs) const
     556                 : {
     557                 :     // get n digits
     558              14 :     unsigned long long int v0 = v;
     559              14 :     unsigned long long int p = 1;
     560              14 :     std::size_t n = 0;
     561              14 :     if (sign != '-')
     562                 :     {
     563               2 :         ++n;
     564                 :     }
     565                 :     do
     566                 :     {
     567              53 :         if (v >= 10)
     568              39 :             p *= 10;
     569              53 :         v /= 10;
     570              53 :         ++n;
     571                 :     }
     572              53 :     while (v > 0);
     573                 :     static constexpr auto m =
     574                 :         std::numeric_limits<unsigned long long int>::digits10;
     575              14 :     BOOST_ASSERT(n <= m + 2);
     576                 :     ignore_unused(m);
     577                 : 
     578                 :     // get pad
     579              14 :     std::size_t w = width;
     580              25 :     if (width_idx != std::size_t(-1) ||
     581              11 :         !width_name.empty())
     582                 :     {
     583               4 :         get_width_from_args(
     584               4 :             width_idx, width_name, ctx.args(), w);
     585                 :     }
     586              14 :     std::size_t lpad = 0;
     587              14 :     std::size_t rpad = 0;
     588              14 :     if (w > n)
     589                 :     {
     590               8 :         std::size_t pad = w - n;
     591               8 :         if (zeros)
     592                 :         {
     593               1 :             lpad = pad;
     594                 :         }
     595                 :         else
     596                 :         {
     597               7 :             switch (align)
     598                 :             {
     599               1 :             case '<':
     600               1 :                 rpad = pad;
     601               1 :                 break;
     602               5 :             case '>':
     603               5 :                 lpad = pad;
     604               5 :                 break;
     605               1 :             case '^':
     606               1 :                 lpad = pad / 2;
     607               1 :                 rpad = pad - lpad;
     608               1 :                 break;
     609                 :             }
     610                 :         }
     611                 :     }
     612                 : 
     613                 :     // write
     614              14 :     v = v0;
     615              14 :     char* out = ctx.out();
     616              14 :     if (!zeros)
     617                 :     {
     618              35 :         for (std::size_t i = 0; i < lpad; ++i)
     619              22 :             encode_one(out, fill, cs);
     620                 :     }
     621              14 :     if (sign != '-')
     622                 :     {
     623               2 :         encode_one(out, sign, cs);
     624               2 :         --n;
     625                 :     }
     626              14 :     if (zeros)
     627                 :     {
     628               5 :         for (std::size_t i = 0; i < lpad; ++i)
     629               4 :             encode_one(out, '0', cs);
     630                 :     }
     631              67 :     while (n)
     632                 :     {
     633              53 :         unsigned long long int d = v / p;
     634              53 :         encode_one(out, '0' + static_cast<char>(d), cs);
     635              53 :         --n;
     636              53 :         v %= p;
     637              53 :         p /= 10;
     638                 :     }
     639              14 :     if (!zeros)
     640                 :     {
     641              19 :         for (std::size_t i = 0; i < rpad; ++i)
     642               6 :             encode_one(out, fill, cs);
     643                 :     }
     644              14 :     return out;
     645                 : }
     646                 : 
     647                 : } // detail
     648                 : } // urls
     649                 : } // boost
     650                 : 
        

Generated by: LCOV version 2.3