ext-boost/boost/url/impl/encode.hpp
2023-11-04 21:30:42 +02:00

279 lines
6.3 KiB
C++

//
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@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_IMPL_ENCODE_HPP
#define BOOST_URL_IMPL_ENCODE_HPP
#include <boost/url/detail/encode.hpp>
#include <boost/url/detail/except.hpp>
#include <boost/url/encoding_opts.hpp>
#include <boost/url/grammar/charset.hpp>
#include <boost/url/grammar/hexdig_chars.hpp>
#include <boost/url/grammar/type_traits.hpp>
#include <boost/assert.hpp>
#include <boost/static_assert.hpp>
namespace boost {
namespace urls {
//------------------------------------------------
template<class CharSet>
std::size_t
encoded_size(
core::string_view s,
CharSet const& unreserved,
encoding_opts opt) noexcept
{
/* If you get a compile error here, it
means that the value you passed does
not meet the requirements stated in
the documentation.
*/
static_assert(
grammar::is_charset<CharSet>::value,
"Type requirements not met");
std::size_t n = 0;
auto it = s.data();
auto const last = it + s.size();
if(! opt.space_as_plus ||
unreserved(' '))
{
while(it != last)
{
if(unreserved(*it))
n += 1;
else
n += 3;
++it;
}
}
else
{
while(it != last)
{
auto c = *it;
if(unreserved(c))
++n;
else if(c == ' ')
++n;
else
n += 3;
++it;
}
}
return n;
}
//------------------------------------------------
template<class CharSet>
std::size_t
encode(
char* dest,
std::size_t size,
core::string_view s,
CharSet const& unreserved,
encoding_opts opt)
{
/* If you get a compile error here, it
means that the value you passed does
not meet the requirements stated in
the documentation.
*/
static_assert(
grammar::is_charset<CharSet>::value,
"Type requirements not met");
// '%' must be reserved
BOOST_ASSERT(! unreserved('%'));
char const* const hex =
detail::hexdigs[opt.lower_case];
auto const encode = [hex](
char*& dest,
unsigned char c) noexcept
{
*dest++ = '%';
*dest++ = hex[c>>4];
*dest++ = hex[c&0xf];
};
auto it = s.data();
auto const end = dest + size;
auto const last = it + s.size();
auto const dest0 = dest;
auto const end3 = end - 3;
if(! opt.space_as_plus)
{
while(it != last)
{
if(unreserved(*it))
{
if(dest == end)
return dest - dest0;
*dest++ = *it++;
continue;
}
if(dest > end3)
return dest - dest0;
encode(dest, *it++);
}
return dest - dest0;
}
else if(! unreserved(' '))
{
// VFALCO space is usually reserved,
// and we depend on this for an
// optimization. if this assert
// goes off we can split the loop
// below into two versions.
BOOST_ASSERT(! unreserved(' '));
while(it != last)
{
if(unreserved(*it))
{
if(dest == end)
return dest - dest0;
*dest++ = *it++;
continue;
}
if(*it == ' ')
{
if(dest == end)
return dest - dest0;
*dest++ = '+';
++it;
continue;
}
if(dest > end3)
return dest - dest0;
encode(dest, *it++);
}
}
return dest - dest0;
}
//------------------------------------------------
// unsafe encode just
// asserts on the output buffer
//
template<class CharSet>
std::size_t
encode_unsafe(
char* dest,
std::size_t size,
core::string_view s,
CharSet const& unreserved,
encoding_opts opt)
{
// '%' must be reserved
BOOST_ASSERT(! unreserved('%'));
auto it = s.data();
auto const last = it + s.size();
auto const end = dest + size;
ignore_unused(end);
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];
};
auto const dest0 = dest;
if(! opt.space_as_plus)
{
while(it != last)
{
BOOST_ASSERT(dest != end);
if(unreserved(*it))
*dest++ = *it++;
else
encode(dest, *it++);
}
}
else
{
// VFALCO space is usually reserved,
// and we depend on this for an
// optimization. if this assert
// goes off we can split the loop
// below into two versions.
BOOST_ASSERT(! unreserved(' '));
while(it != last)
{
BOOST_ASSERT(dest != end);
if(unreserved(*it))
{
*dest++ = *it++;
}
else if(*it == ' ')
{
*dest++ = '+';
++it;
}
else
{
encode(dest, *it++);
}
}
}
return dest - dest0;
}
//------------------------------------------------
template<
class StringToken,
class CharSet>
BOOST_URL_STRTOK_RETURN
encode(
core::string_view s,
CharSet const& unreserved,
encoding_opts opt,
StringToken&& token) noexcept
{
/* If you get a compile error here, it
means that the value you passed does
not meet the requirements stated in
the documentation.
*/
static_assert(
grammar::is_charset<CharSet>::value,
"Type requirements not met");
auto const n = encoded_size(
s, unreserved, opt);
auto p = token.prepare(n);
if(n > 0)
encode_unsafe(
p, n, s, unreserved, opt);
return token.result();
}
} // urls
} // boost
#endif