10#ifndef UPA_URL_PERCENT_ENCODE_H
11#define UPA_URL_PERCENT_ENCODE_H
21# include <initializer_list>
23# include <type_traits>
58 arr_[c >> 3] &= ~(1u << (c & 0x07));
64 arr_[c >> 3] |= (1u << (c & 0x07));
69 constexpr void exclude(std::initializer_list<std::uint8_t> clist) {
76 constexpr void include(std::initializer_list<std::uint8_t> clist) {
83 constexpr void include(std::uint8_t from, std::uint8_t to) {
84 for (
auto c = from; c <= to; ++c)
90 template <
typename CharT>
92 const auto uc = util::to_unsigned(c);
93 return is_8bit(uc) && (arr_[uc >> 3] & (1u << (uc & 0x07))) != 0;
98 static constexpr bool is_8bit(
unsigned char)
noexcept {
102 template <
typename CharT>
103 static constexpr bool is_8bit(CharT c)
noexcept {
108 std::array<std::uint8_t, 32> arr_{};
117 self.include(0x20, 0x7E);
118 self.exclude({ 0x20, 0x22, 0x3C, 0x3E, 0x60 });
124 self.include(0x20, 0x7E);
125 self.exclude({ 0x20, 0x22, 0x23, 0x3C, 0x3E });
139 self.exclude({ 0x3F, 0x5E, 0x60, 0x7B, 0x7D });
153 self.exclude({ 0x3A, 0x5C, 0x7C });
160 self.exclude({ 0x2F, 0x3A, 0x3B, 0x3D, 0x40, 0x5B, 0x5C, 0x5D, 0x7C });
167 self.exclude({ 0x24, 0x25, 0x26, 0x2B, 0x2C });
176enum CP_SET : std::uint8_t {
177 ASCII_DOMAIN_SET = 0x01,
178 DOMAIN_FORBIDDEN_SET = 0x02,
179 HOST_FORBIDDEN_SET = 0x04,
180 HEX_DIGIT_SET = 0x08,
181 IPV4_CHAR_SET = 0x10,
185class code_points_multiset {
187 constexpr code_points_multiset() {
193 include(
static_cast<CP_SET
>(HOST_FORBIDDEN_SET | DOMAIN_FORBIDDEN_SET), {
194 0x00, 0x09, 0x0A, 0x0D, 0x20, 0x23, 0x2F, 0x3A, 0x3C, 0x3E, 0x3F, 0x40, 0x5B,
195 0x5C, 0x5D, 0x5E, 0x7C });
200 include(DOMAIN_FORBIDDEN_SET, 0x00, 0x1F);
201 include(DOMAIN_FORBIDDEN_SET, { 0x25, 0x7F });
206 include(ASCII_DOMAIN_SET, 0x20, 0x7F);
208 exclude(ASCII_DOMAIN_SET, {
209 0x00, 0x09, 0x0A, 0x0D, 0x20, 0x23, 0x2F, 0x3A, 0x3C, 0x3E, 0x3F, 0x40, 0x5B,
210 0x5C, 0x5D, 0x5E, 0x7C });
212 exclude(ASCII_DOMAIN_SET, { 0x25, 0x7F });
215 include(
static_cast<CP_SET
>(HEX_DIGIT_SET | IPV4_CHAR_SET),
'0',
'9');
216 include(
static_cast<CP_SET
>(HEX_DIGIT_SET | IPV4_CHAR_SET),
'A',
'F');
217 include(
static_cast<CP_SET
>(HEX_DIGIT_SET | IPV4_CHAR_SET),
'a',
'f');
220 include(IPV4_CHAR_SET, {
'.',
'X',
'x' });
225 include(SCHEME_SET,
'0',
'9');
226 include(SCHEME_SET,
'A',
'Z');
227 include(SCHEME_SET,
'a',
'z');
228 include(SCHEME_SET, { 0x2B, 0x2D, 0x2E });
234 template <
typename CharT>
235 [[nodiscard]]
constexpr bool char_in_set(CharT c, CP_SET cps)
const {
236 const auto uc = util::to_unsigned(c);
237 return is_8bit(uc) && (arr_[uc] & cps);
244 constexpr void include(CP_SET cpsbits, std::uint8_t c) {
251 constexpr void include(CP_SET cpsbits, std::initializer_list<std::uint8_t> clist) {
259 constexpr void include(CP_SET cpsbits, std::uint8_t from, std::uint8_t to) {
260 for (
auto c = from; c <= to; ++c)
267 constexpr void exclude(CP_SET cpsbits, std::uint8_t c) {
274 constexpr void exclude(CP_SET cpsbits, std::initializer_list<std::uint8_t> clist) {
280 static constexpr bool is_8bit(
unsigned char)
noexcept {
284 template <
typename CharT>
285 static constexpr bool is_8bit(CharT c)
noexcept {
290 std::array<std::uint8_t, 256> arr_{};
293inline constexpr code_points_multiset code_points;
298template <
typename CharT>
299constexpr bool is_char_in_set(CharT c,
const code_point_set& cpset) {
303template <
typename CharT>
304constexpr bool is_ipv4_char(CharT c) {
305 return code_points.char_in_set(c, IPV4_CHAR_SET);
308template <
typename CharT>
309constexpr bool is_hex_char(CharT c) {
310 return code_points.char_in_set(c, HEX_DIGIT_SET);
313template <
typename CharT>
314constexpr bool is_scheme_char(CharT c) {
315 return code_points.char_in_set(c, SCHEME_SET);
318template <
typename CharT>
319constexpr bool is_forbidden_domain_char(CharT c) {
320 return code_points.char_in_set(c, DOMAIN_FORBIDDEN_SET);
323template <
typename CharT>
324constexpr bool is_forbidden_host_char(CharT c) {
325 return code_points.char_in_set(c, HOST_FORBIDDEN_SET);
328template <
typename CharT>
329constexpr bool is_ascii_domain_char(CharT c) {
330 return code_points.char_in_set(c, ASCII_DOMAIN_SET);
335template <
typename CharT>
336constexpr bool is_ascii_digit(CharT ch)
noexcept {
337 return ch <=
'9' && ch >=
'0';
340template <
typename CharT>
341constexpr bool is_ascii_alpha(CharT ch)
noexcept {
342 return (ch >=
'a' && ch <=
'z') || (ch >=
'A' && ch <=
'Z');
350inline constexpr char kHexCharLookup[0x10] = {
351 '0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
352 '8',
'9',
'A',
'B',
'C',
'D',
'E',
'F',
363inline constexpr char kCharToHexLookup[8] = {
375constexpr unsigned char hex_char_to_num(
unsigned char c)
noexcept {
376 return c - kCharToHexLookup[c / 0x20];
390template <
typename CharT>
391constexpr bool decode_hex_to_byte(
const CharT*& first,
const CharT* last,
unsigned char& unescaped_value) {
392 if (last - first < 2 ||
393 !is_hex_char(first[0]) || !is_hex_char(first[1])) {
399 const auto uc1 =
static_cast<unsigned char>(first[0]);
400 const auto uc2 =
static_cast<unsigned char>(first[1]);
401 unescaped_value = (hex_char_to_num(uc1) << 4) + hex_char_to_num(uc2);
412UPA_CONSTEXPR_20
void append_percent_encoded_byte(
unsigned char uc, std::string& output) {
413 output.push_back(
'%');
414 output.push_back(kHexCharLookup[uc >> 4]);
415 output.push_back(kHexCharLookup[uc & 0xf]);
422template <
typename CharT>
423UPA_CONSTEXPR_20
bool append_utf8_percent_encoded_char(
const CharT*& first,
const CharT* last, std::string& output) {
427 const auto cp_res = url_utf::read_utf_char(first, last);
429 url_utf::append_utf8<std::string, append_percent_encoded_byte>(cp_res.value, output);
430 return cp_res.result;
437template<
typename CharT>
438UPA_CONSTEXPR_20
void append_utf8_percent_encoded(
const CharT* first,
const CharT* last,
const code_point_set& cpset, std::string& output) {
439 using UCharT = std::make_unsigned_t<CharT>;
441 for (
auto it = first; it < last; ) {
442 const auto uch =
static_cast<UCharT
>(*it);
445 append_utf8_percent_encoded_char(it, last, output);
448 const auto uc =
static_cast<unsigned char>(uch);
449 if (is_char_in_set(uc, cpset)) {
450 output.push_back(uc);
453 append_percent_encoded_byte(uc, output);
469template <
class StrT, enable_if_str_arg_t<StrT> = 0>
470inline void append_percent_decoded(StrT&& str, std::string& output) {
471 const auto inp = make_str_arg(std::forward<StrT>(str));
472 const auto* first = inp.begin();
473 const auto* last = inp.end();
475 for (
auto it = first; it != last;) {
476 const auto uch = util::to_unsigned(*it); ++it;
479 output.push_back(
static_cast<char>(uch));
484 if (decode_hex_to_byte(it, last, uc8)) {
486 output.push_back(
static_cast<char>(uc8));
490 std::string buff_utf8;
491 buff_utf8.push_back(
static_cast<char>(uc8));
492 while (it != last && *it ==
'%') {
494 if (!decode_hex_to_byte(it, last, uc8))
496 buff_utf8.push_back(
static_cast<char>(uc8));
498 url_utf::check_fix_utf8(buff_utf8);
503 output.push_back(
'%');
506 url_utf::read_char_append_utf8(it, last, output);
525template <
class StrT, enable_if_str_arg_t<StrT> = 0>
528 detail::append_percent_decoded(std::forward<StrT>(str), out);
543template <
class StrT, enable_if_str_arg_t<StrT> = 0>
545 const auto inp = make_str_arg(std::forward<StrT>(str));
548 detail::append_utf8_percent_encoded(inp.begin(), inp.end(), no_encode_set, out);
562template <
class StrT, enable_if_str_arg_t<StrT> = 0>
Represents code point set.
constexpr void include(std::uint8_t c)
include c code point to set
constexpr void exclude(std::initializer_list< std::uint8_t > clist)
exclude list of code points from set
constexpr code_point_set(void(*fun)(code_point_set &self))
constructor for code point set initialization
constexpr void include(std::initializer_list< std::uint8_t > clist)
include code points from list
constexpr void include(std::uint8_t from, std::uint8_t to)
include range of code points to set
constexpr bool operator[](CharT c) const
test code point set contains code point c
constexpr void copy(const code_point_set &other)
copy code points from other set
constexpr void exclude(std::uint8_t c)
exclude c code point from set
constexpr code_point_set query_no_encode_set
std::string encode_url_component(StrT &&str)
UTF-8 percent encode input string using component percent encode set.
std::string percent_decode(StrT &&str)
Percent decode input string.
std::string percent_encode(StrT &&str, const code_point_set &no_encode_set)
UTF-8 percent encode input string using specified percent encode set.
constexpr code_point_set component_no_encode_set
constexpr code_point_set special_query_no_encode_set
constexpr code_point_set raw_path_no_encode_set
constexpr code_point_set path_no_encode_set
constexpr code_point_set fragment_no_encode_set
constexpr code_point_set userinfo_no_encode_set
constexpr code_point_set posix_path_no_encode_set