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

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

Generated by: LCOV version 2.1