1  
//
1  
//
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3  
//
3  
//
4  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
5  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6  
//
6  
//
7  
// Official repository: https://github.com/boostorg/url
7  
// Official repository: https://github.com/boostorg/url
8  
//
8  
//
9  

9  

10  

10  

11  
#include <boost/url/detail/config.hpp>
11  
#include <boost/url/detail/config.hpp>
12  
#include <boost/url/detail/decode.hpp>
12  
#include <boost/url/detail/decode.hpp>
13  
#include <boost/url/grammar/charset.hpp>
13  
#include <boost/url/grammar/charset.hpp>
14  
#include <boost/url/grammar/hexdig_chars.hpp>
14  
#include <boost/url/grammar/hexdig_chars.hpp>
15  
#include <memory>
15  
#include <memory>
16  

16  

17  
namespace boost {
17  
namespace boost {
18  
namespace urls {
18  
namespace urls {
19  
namespace detail {
19  
namespace detail {
20  

20  

21  
char
21  
char
22  
decode_one(
22  
decode_one(
23  
    char const* const it) noexcept
23  
    char const* const it) noexcept
24  
{
24  
{
25  
    auto d0 = grammar::hexdig_value(it[0]);
25  
    auto d0 = grammar::hexdig_value(it[0]);
26  
    auto d1 = grammar::hexdig_value(it[1]);
26  
    auto d1 = grammar::hexdig_value(it[1]);
27  
    return static_cast<char>(
27  
    return static_cast<char>(
28  
        ((static_cast<
28  
        ((static_cast<
29  
            unsigned char>(d0) << 4) +
29  
            unsigned char>(d0) << 4) +
30  
        (static_cast<
30  
        (static_cast<
31  
            unsigned char>(d1))));
31  
            unsigned char>(d1))));
32  
}
32  
}
33  

33  

34  
std::size_t
34  
std::size_t
35  
decode_bytes_unsafe(
35  
decode_bytes_unsafe(
36  
    core::string_view s) noexcept
36  
    core::string_view s) noexcept
37  
{
37  
{
38  
    auto p = s.begin();
38  
    auto p = s.begin();
39  
    auto const end = s.end();
39  
    auto const end = s.end();
40  
    std::size_t dn = 0;
40  
    std::size_t dn = 0;
41  
    if(s.size() >= 3)
41  
    if(s.size() >= 3)
42  
    {
42  
    {
43  
        auto const safe_end = end - 2;
43  
        auto const safe_end = end - 2;
44  
        while(p < safe_end)
44  
        while(p < safe_end)
45  
        {
45  
        {
46  
            if(*p != '%')
46  
            if(*p != '%')
47  
                p += 1;
47  
                p += 1;
48  
            else
48  
            else
49  
                p += 3;
49  
                p += 3;
50  
            ++dn;
50  
            ++dn;
51  
        }
51  
        }
52  
    }
52  
    }
53  
    dn += end - p;
53  
    dn += end - p;
54  
    return dn;
54  
    return dn;
55  
}
55  
}
56  

56  

57  
template <bool SpaceAsPlus>
57  
template <bool SpaceAsPlus>
58  
std::size_t
58  
std::size_t
59  
decode_unsafe_is_plus_impl(char c);
59  
decode_unsafe_is_plus_impl(char c);
60  

60  

61  
template <>
61  
template <>
62  
std::size_t
62  
std::size_t
63  
decode_unsafe_is_plus_impl<true>(char c)
63  
decode_unsafe_is_plus_impl<true>(char c)
64  
{
64  
{
65  
    return c == '+';
65  
    return c == '+';
66  
}
66  
}
67  

67  

68  
template <>
68  
template <>
69  
std::size_t
69  
std::size_t
70  
decode_unsafe_is_plus_impl<false>(char)
70  
decode_unsafe_is_plus_impl<false>(char)
71  
{
71  
{
72  
    return false;
72  
    return false;
73  
}
73  
}
74  

74  

75  

75  

76  
template <bool SpaceAsPlus>
76  
template <bool SpaceAsPlus>
77  
std::size_t
77  
std::size_t
78  
decode_unsafe_impl(
78  
decode_unsafe_impl(
79  
    char* const dest0,
79  
    char* const dest0,
80  
    char const* end,
80  
    char const* end,
81  
    core::string_view s) noexcept
81  
    core::string_view s) noexcept
82  
{
82  
{
83  
    auto it = s.data();
83  
    auto it = s.data();
84  
    auto const last = it + s.size();
84  
    auto const last = it + s.size();
85  
    auto dest = dest0;
85  
    auto dest = dest0;
86  

86  

87  
    while(it != last)
87  
    while(it != last)
88  
    {
88  
    {
89  
        // LCOV_EXCL_START
89  
        // LCOV_EXCL_START
90  
        if(dest == end)
90  
        if(dest == end)
91  
        {
91  
        {
92  
            /*
92  
            /*
93  
             * dest too small: unreachable
93  
             * dest too small: unreachable
94  
             * public functions always pass
94  
             * public functions always pass
95  
             * a buffer of sufficient size
95  
             * a buffer of sufficient size
96  
             */
96  
             */
97  
            return dest - dest0;
97  
            return dest - dest0;
98  
        }
98  
        }
99  
        // LCOV_EXCL_STOP
99  
        // LCOV_EXCL_STOP
100  
        if(decode_unsafe_is_plus_impl<SpaceAsPlus>(*it))
100  
        if(decode_unsafe_is_plus_impl<SpaceAsPlus>(*it))
101  
        {
101  
        {
102  
            // plus to space
102  
            // plus to space
103  
            *dest++ = ' ';
103  
            *dest++ = ' ';
104  
            ++it;
104  
            ++it;
105  
            continue;
105  
            continue;
106  
        }
106  
        }
107  
        if(*it == '%')
107  
        if(*it == '%')
108  
        {
108  
        {
109  
            // escaped
109  
            // escaped
110  
            ++it;
110  
            ++it;
111  
            // LCOV_EXCL_START
111  
            // LCOV_EXCL_START
112  
            if(last - it < 2)
112  
            if(last - it < 2)
113  
            {
113  
            {
114  
                // `%` not followed by two hex digits
114  
                // `%` not followed by two hex digits
115  
                // invalid input: unreachable
115  
                // invalid input: unreachable
116  
                // public functions always pass
116  
                // public functions always pass
117  
                // a valid string_view.
117  
                // a valid string_view.
118  
                // initialize output
118  
                // initialize output
119  
                std::memset(dest,
119  
                std::memset(dest,
120  
                    0, end - dest);
120  
                    0, end - dest);
121  
                return dest - dest0;
121  
                return dest - dest0;
122  
            }
122  
            }
123  
            // LCOV_EXCL_STOP
123  
            // LCOV_EXCL_STOP
124  
            *dest++ = decode_one(it);
124  
            *dest++ = decode_one(it);
125  
            it += 2;
125  
            it += 2;
126  
            continue;
126  
            continue;
127  
        }
127  
        }
128  
        // unescaped
128  
        // unescaped
129  
        *dest++ = *it++;
129  
        *dest++ = *it++;
130  
    }
130  
    }
131  
    return dest - dest0;
131  
    return dest - dest0;
132  
}
132  
}
133  

133  

134  
std::size_t
134  
std::size_t
135  
decode_unsafe(
135  
decode_unsafe(
136  
    char* const dest0,
136  
    char* const dest0,
137  
    char const* end,
137  
    char const* end,
138  
    core::string_view s,
138  
    core::string_view s,
139  
    encoding_opts opt) noexcept
139  
    encoding_opts opt) noexcept
140  
{
140  
{
141  
    if(opt.space_as_plus)
141  
    if(opt.space_as_plus)
142  
    {
142  
    {
143  
        return decode_unsafe_impl<true>(
143  
        return decode_unsafe_impl<true>(
144  
            dest0, end, s);
144  
            dest0, end, s);
145  
    }
145  
    }
146  
    return decode_unsafe_impl<false>(
146  
    return decode_unsafe_impl<false>(
147  
        dest0, end, s);
147  
        dest0, end, s);
148  
}
148  
}
149  

149  

150  
} // detail
150  
} // detail
151  
} // urls
151  
} // urls
152  
} // boost
152  
} // boost