206 lines
4.7 KiB
C++
206 lines
4.7 KiB
C++
//
|
|
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
|
|
// 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_ENCODE_HPP
|
|
#define BOOST_URL_DETAIL_ENCODE_HPP
|
|
|
|
#include <boost/url/encoding_opts.hpp>
|
|
#include <boost/url/pct_string_view.hpp>
|
|
#include <boost/url/grammar/hexdig_chars.hpp>
|
|
#include <boost/core/ignore_unused.hpp>
|
|
#include <cstdlib>
|
|
|
|
namespace boost {
|
|
namespace urls {
|
|
namespace detail {
|
|
|
|
constexpr
|
|
char const* const hexdigs[] = {
|
|
"0123456789ABCDEF",
|
|
"0123456789abcdef" };
|
|
|
|
//------------------------------------------------
|
|
|
|
// re-encode is to percent-encode a
|
|
// string that can already contain
|
|
// escapes. Characters not in the
|
|
// unreserved set are escaped, and
|
|
// escapes are passed through unchanged.
|
|
//
|
|
template<class CharSet>
|
|
std::size_t
|
|
re_encoded_size_unsafe(
|
|
string_view s,
|
|
CharSet const& unreserved,
|
|
encoding_opts opt) noexcept
|
|
{
|
|
std::size_t n = 0;
|
|
auto const end = s.end();
|
|
auto it = s.begin();
|
|
if(opt.space_as_plus)
|
|
{
|
|
while(it != end)
|
|
{
|
|
if(*it != '%')
|
|
{
|
|
if( unreserved(*it)
|
|
|| *it == ' ')
|
|
n += 1;
|
|
else
|
|
n += 3;
|
|
++it;
|
|
}
|
|
else
|
|
{
|
|
BOOST_ASSERT(end - it >= 3);
|
|
BOOST_ASSERT(
|
|
grammar::hexdig_value(
|
|
it[1]) >= 0);
|
|
BOOST_ASSERT(
|
|
grammar::hexdig_value(
|
|
it[2]) >= 0);
|
|
n += 3;
|
|
it += 3;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while(it != end)
|
|
{
|
|
if(*it != '%')
|
|
{
|
|
if(unreserved(*it))
|
|
n += 1;
|
|
else
|
|
n += 3;
|
|
++it;
|
|
}
|
|
else
|
|
{
|
|
BOOST_ASSERT(end - it >= 3);
|
|
BOOST_ASSERT(
|
|
grammar::hexdig_value(
|
|
it[1]) >= 0);
|
|
BOOST_ASSERT(
|
|
grammar::hexdig_value(
|
|
it[2]) >= 0);
|
|
n += 3;
|
|
it += 3;
|
|
}
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
// unchecked
|
|
// returns decoded size
|
|
template<class CharSet>
|
|
std::size_t
|
|
re_encode_unsafe(
|
|
char*& dest_,
|
|
char const* const end,
|
|
string_view s,
|
|
CharSet const& unreserved,
|
|
encoding_opts opt) noexcept
|
|
{
|
|
char const* const hex =
|
|
detail::hexdigs[opt.lower_case];
|
|
auto const encode = [end, hex](
|
|
char*& dest,
|
|
unsigned char c) noexcept
|
|
{
|
|
ignore_unused(end);
|
|
*dest++ = '%';
|
|
BOOST_ASSERT(dest != end);
|
|
*dest++ = hex[c>>4];
|
|
BOOST_ASSERT(dest != end);
|
|
*dest++ = hex[c&0xf];
|
|
};
|
|
ignore_unused(end);
|
|
|
|
auto dest = dest_;
|
|
auto const dest0 = dest;
|
|
auto const last = s.end();
|
|
std::size_t dn = 0;
|
|
auto it = s.begin();
|
|
|
|
if(opt.space_as_plus)
|
|
{
|
|
while(it != last)
|
|
{
|
|
BOOST_ASSERT(dest != end);
|
|
if(*it != '%')
|
|
{
|
|
if(*it == ' ')
|
|
{
|
|
*dest++ = '+';
|
|
}
|
|
else if(unreserved(*it))
|
|
{
|
|
*dest++ = *it;
|
|
}
|
|
else
|
|
{
|
|
encode(dest, *it);
|
|
dn += 2;
|
|
}
|
|
++it;
|
|
}
|
|
else
|
|
{
|
|
*dest++ = *it++;
|
|
BOOST_ASSERT(dest != end);
|
|
*dest++ = *it++;
|
|
BOOST_ASSERT(dest != end);
|
|
*dest++ = *it++;
|
|
dn += 2;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while(it != last)
|
|
{
|
|
BOOST_ASSERT(dest != end);
|
|
if(*it != '%')
|
|
{
|
|
if(unreserved(*it))
|
|
{
|
|
*dest++ = *it;
|
|
}
|
|
else
|
|
{
|
|
encode(dest, *it);
|
|
dn += 2;
|
|
}
|
|
++it;
|
|
}
|
|
else
|
|
{
|
|
*dest++ = *it++;
|
|
BOOST_ASSERT(dest != end);
|
|
*dest++ = *it++;
|
|
BOOST_ASSERT(dest != end);
|
|
*dest++ = *it++;
|
|
dn += 2;
|
|
}
|
|
}
|
|
}
|
|
dest_ = dest;
|
|
return dest - dest0 - dn;
|
|
}
|
|
|
|
} // detail
|
|
} // urls
|
|
} // boost
|
|
|
|
#endif
|