// // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com) // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // Official repository: https://github.com/boostorg/url // #ifndef BOOST_URL_DETAIL_IMPL_FORMAT_ARGS_IPP #define BOOST_URL_DETAIL_IMPL_FORMAT_ARGS_IPP #include #include #include #include namespace boost { namespace urls { namespace detail { std::size_t get_uvalue( string_view a ) { string_view str(a); auto rv = grammar::parse( str, grammar::unsigned_rule{}); if (rv) return *rv; return 0; } std::size_t get_uvalue( char a ) { string_view str(&a, 1); return get_uvalue(str); } char const* formatter:: parse(format_parse_context& ctx) { char const* it = ctx.begin(); char const* end = ctx.end(); BOOST_ASSERT(it != end); // fill / align if (end - it > 2) { if (*it != '{' && *it != '}' && (*(it + 1) == '<' || *(it + 1) == '>' || *(it + 1) == '^')) { fill = *it; align = *(it + 1); it += 2; } } // align if (align == '\0' && (*it == '<' || *it == '>' || *it == '^')) { align = *it++; } // width char const* it0 = it; constexpr auto width_rule = grammar::variant_rule( grammar::unsigned_rule{}, grammar::tuple_rule( grammar::squelch( grammar::delim_rule('{')), grammar::optional_rule( arg_id_rule), grammar::squelch( grammar::delim_rule('}')))); auto rw = grammar::parse(it, end, width_rule); if (!rw) { // rewind it = it0; } else if (align != '\0') { // width is ignored when align is '\0' if (rw->index() == 0) { // unsigned_rule width = variant2::get<0>(*rw); } else { // arg_id: store the id idx or string auto& arg_id = variant2::get<1>(*rw); if (!arg_id) { // empty arg_id, use and consume // the next arg idx width_idx = ctx.next_arg_id(); } else if (arg_id->index() == 0) { // string identifier width_name = variant2::get<0>(*arg_id); } else { // integer identifier: use the // idx of this format_arg width_idx = variant2::get<1>(*arg_id); } } } // type is parsed but doesn't have to // be stored for strings if (*it == 'c' || *it == 's') { ++it; } // we should have arrived at the end now if (*it != '}') { urls::detail::throw_invalid_argument(); } return it; } std::size_t formatter:: measure( string_view str, measure_context& ctx, grammar::lut_chars const& cs) const { std::size_t w = width; if (width_idx != std::size_t(-1) || !width_name.empty()) { get_width_from_args( width_idx, width_name, ctx.args(), w); } std::size_t n = ctx.out(); if (str.size() < w) n += measure_one(fill, cs) * (w - str.size()); return n + encoded_size(str, cs); } char* formatter:: format(string_view str, format_context& ctx, grammar::lut_chars const& cs) const { std::size_t w = width; if (width_idx != std::size_t(-1) || !width_name.empty()) { get_width_from_args( width_idx, width_name, ctx.args(), w); } std::size_t lpad = 0; std::size_t rpad = 0; if (str.size() < w) { std::size_t pad = w - str.size(); switch (align) { case '<': rpad = pad; break; case '>': lpad = pad; break; case '^': lpad = w / 2; rpad = pad - lpad; break; } } // unsafe `encode`, assuming `out` has // enough capacity char* out = ctx.out(); for (std::size_t i = 0; i < lpad; ++i) encode_one(out, fill, cs); for (char c: str) encode_one(out, c, cs); for (std::size_t i = 0; i < rpad; ++i) encode_one(out, fill, cs); return out; } void get_width_from_args( std::size_t arg_idx, string_view arg_name, format_args args, std::size_t& w) { // check arg_id format_arg warg; if (arg_idx != std::size_t(-1)) { // identifier warg = args.get(arg_idx); } else { // unsigned integer warg = args.get(arg_name); } // get unsigned int value from that format arg w = warg.value(); } char const* integer_formatter_impl:: parse(format_parse_context& ctx) { char const* it = ctx.begin(); char const* end = ctx.end(); BOOST_ASSERT(it != end); // fill / align if (end - it > 2) { if (*it != '{' && *it != '}' && (*(it + 1) == '<' || *(it + 1) == '>' || *(it + 1) == '^')) { fill = *it; align = *(it + 1); it += 2; } } // align if (align == '\0' && (*it == '<' || *it == '>' || *it == '^')) { align = *it++; } // sign if (*it == '+' || *it == '-' || *it == ' ') { sign = *it++; } // # if (*it == '#') { // alternate form not supported ++it; } // 0 if (*it == '0') { zeros = *it++; } // width char const* it0 = it; constexpr auto width_rule = grammar::variant_rule( grammar::unsigned_rule{}, grammar::tuple_rule( grammar::squelch( grammar::delim_rule('{')), grammar::optional_rule( arg_id_rule), grammar::squelch( grammar::delim_rule('}')))); auto rw = grammar::parse(it, end, width_rule); if (!rw) { // rewind it = it0; } else if (align != '\0') { // width is ignored when align is '\0' if (rw->index() == 0) { // unsigned_rule width = variant2::get<0>(*rw); } else { // arg_id: store the id idx or string auto& arg_id = variant2::get<1>(*rw); if (!arg_id) { // empty arg_id, use and consume // the next arg idx width_idx = ctx.next_arg_id(); } else if (arg_id->index() == 0) { // string identifier width_name = variant2::get<0>(*arg_id); } else { // integer identifier: use the // idx of this format_arg width_idx = variant2::get<1>(*arg_id); } } } // type is parsed but doesn't have to // be stored for strings if (*it == 'd') { // we don't include other presentation // modes for integers as they are not // recommended or generally used in // urls ++it; } // we should have arrived at the end now if (*it != '}') { urls::detail::throw_invalid_argument(); } return it; } std::size_t integer_formatter_impl:: measure( long long int v, measure_context& ctx, grammar::lut_chars const& cs) const { std::size_t dn = 0; std::size_t n = 0; if (v < 0) { dn += measure_one('-', cs); ++n; v *= -1; } else if (sign != '-') { dn += measure_one(sign, cs); ++n; } while (v > 0) { int d = v % 10; v /= 10; dn += measure_one('0' + static_cast(d), cs); ++n; } std::size_t w = width; if (width_idx != std::size_t(-1) || !width_name.empty()) { get_width_from_args( width_idx, width_name, ctx.args(), w); } if (w > n) { if (!zeros) dn += measure_one(fill, cs) * (w - n); else dn += measure_one('0', cs) * (w - n); } return ctx.out() + dn; } std::size_t integer_formatter_impl:: measure( unsigned long long int v, measure_context& ctx, grammar::lut_chars const& cs) const { std::size_t dn = 0; std::size_t n = 0; if (sign != '-') { dn += measure_one(sign, cs); ++n; } while (v != 0) { int d = v % 10; v /= 10; dn += measure_one('0' + static_cast(d), cs); ++n; } std::size_t w = width; if (width_idx != std::size_t(-1) || !width_name.empty()) { get_width_from_args( width_idx, width_name, ctx.args(), w); } if (w > n) { if (!zeros) dn += measure_one(fill, cs) * (w - n); else dn += measure_one('0', cs) * (w - n); } return ctx.out() + dn; } char* integer_formatter_impl:: format( long long int v, format_context& ctx, grammar::lut_chars const& cs) const { // get n digits long long int v0 = v; long long int p = 1; std::size_t n = 0; if (v < 0) { v *= - 1; ++n; } else if (sign != '-') { ++n; } while (v > 0) { if (v >= 10) p *= 10; v /= 10; ++n; } static constexpr auto m = std::numeric_limits::digits10; BOOST_ASSERT(n <= m + 1); ignore_unused(m); // get pad std::size_t w = width; if (width_idx != std::size_t(-1) || !width_name.empty()) { get_width_from_args( width_idx, width_name, ctx.args(), w); } std::size_t lpad = 0; std::size_t rpad = 0; if (w > n) { std::size_t pad = w - n; if (zeros) { lpad = pad; } else { switch (align) { case '<': rpad = pad; break; case '>': lpad = pad; break; case '^': lpad = pad / 2; rpad = pad - lpad; break; } } } // write v = v0; char* out = ctx.out(); if (!zeros) { for (std::size_t i = 0; i < lpad; ++i) encode_one(out, fill, cs); } if (v < 0) { encode_one(out, '-', cs); v *= -1; --n; } else if (sign != '-') { encode_one(out, sign, cs); --n; } if (zeros) { for (std::size_t i = 0; i < lpad; ++i) encode_one(out, '0', cs); } while (n) { unsigned long long int d = v / p; encode_one(out, '0' + static_cast(d), cs); --n; v %= p; p /= 10; } if (!zeros) { for (std::size_t i = 0; i < rpad; ++i) encode_one(out, fill, cs); } return out; } char* integer_formatter_impl:: format( unsigned long long int v, format_context& ctx, grammar::lut_chars const& cs) const { // get n digits unsigned long long int v0 = v; unsigned long long int p = 1; std::size_t n = 0; if (sign != '-') { ++n; } while (v > 0) { if (v >= 10) p *= 10; v /= 10; ++n; } static constexpr auto m = std::numeric_limits::digits10; BOOST_ASSERT(n <= m + 1); ignore_unused(m); // get pad std::size_t w = width; if (width_idx != std::size_t(-1) || !width_name.empty()) { get_width_from_args( width_idx, width_name, ctx.args(), w); } std::size_t lpad = 0; std::size_t rpad = 0; if (w > n) { std::size_t pad = w - n; if (zeros) { lpad = pad; } else { switch (align) { case '<': rpad = pad; break; case '>': lpad = pad; break; case '^': lpad = pad / 2; rpad = pad - lpad; break; } } } // write v = v0; char* out = ctx.out(); if (!zeros) { for (std::size_t i = 0; i < lpad; ++i) encode_one(out, fill, cs); } if (sign != '-') { encode_one(out, sign, cs); --n; } if (zeros) { for (std::size_t i = 0; i < lpad; ++i) encode_one(out, '0', cs); } while (n) { unsigned long long int d = v / p; encode_one(out, '0' + static_cast(d), cs); --n; v %= p; p /= 10; } if (!zeros) { for (std::size_t i = 0; i < rpad; ++i) encode_one(out, fill, cs); } return out; } } // detail } // urls } // boost #endif