Line data Source code
1 : // 2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) 3 : // 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) 6 : // 7 : // Official repository: https://github.com/boostorg/url 8 : // 9 : 10 : 11 : #include <boost/url/detail/config.hpp> 12 : #include "decode.hpp" 13 : #include <boost/url/grammar/charset.hpp> 14 : #include <boost/url/grammar/hexdig_chars.hpp> 15 : #include <memory> 16 : 17 : namespace boost { 18 : namespace urls { 19 : namespace detail { 20 : 21 : char 22 794 : decode_one( 23 : char const* const it) noexcept 24 : { 25 794 : auto d0 = grammar::hexdig_value(it[0]); 26 794 : auto d1 = grammar::hexdig_value(it[1]); 27 : return static_cast<char>( 28 794 : ((static_cast< 29 794 : unsigned char>(d0) << 4) + 30 794 : (static_cast< 31 794 : unsigned char>(d1)))); 32 : } 33 : 34 : std::size_t 35 1977 : decode_bytes_unsafe( 36 : core::string_view s) noexcept 37 : { 38 1977 : auto p = s.begin(); 39 1977 : auto const end = s.end(); 40 1977 : std::size_t dn = 0; 41 1977 : if(s.size() >= 3) 42 : { 43 826 : auto const safe_end = end - 2; 44 7303 : while(p < safe_end) 45 : { 46 6477 : if(*p != '%') 47 6218 : p += 1; 48 : else 49 259 : p += 3; 50 6477 : ++dn; 51 : } 52 : } 53 1977 : dn += end - p; 54 1977 : return dn; 55 : } 56 : 57 : template <bool SpaceAsPlus> 58 : std::size_t 59 : decode_unsafe_is_plus_impl(char c); 60 : 61 : template <> 62 : std::size_t 63 4247 : decode_unsafe_is_plus_impl<true>(char c) 64 : { 65 4247 : return c == '+'; 66 : } 67 : 68 : template <> 69 : std::size_t 70 10648 : decode_unsafe_is_plus_impl<false>(char) 71 : { 72 10648 : return false; 73 : } 74 : 75 : 76 : template <bool SpaceAsPlus> 77 : std::size_t 78 2711 : decode_unsafe_impl( 79 : char* const dest0, 80 : char const* end, 81 : core::string_view s) noexcept 82 : { 83 2711 : auto it = s.data(); 84 2711 : auto const last = it + s.size(); 85 2711 : auto dest = dest0; 86 : 87 17606 : while(it != last) 88 : { 89 : // LCOV_EXCL_START 90 : if(dest == end) 91 : { 92 : /* 93 : * dest too small: unreachable 94 : * public functions always pass 95 : * a buffer of sufficient size 96 : */ 97 : return dest - dest0; 98 : } 99 : // LCOV_EXCL_STOP 100 14895 : if(decode_unsafe_is_plus_impl<SpaceAsPlus>(*it)) 101 : { 102 : // plus to space 103 5 : *dest++ = ' '; 104 5 : ++it; 105 5 : continue; 106 : } 107 14890 : if(*it == '%') 108 : { 109 : // escaped 110 686 : ++it; 111 : // LCOV_EXCL_START 112 : if(last - it < 2) 113 : { 114 : // `%` not followed by two hex digits 115 : // invalid input: unreachable 116 : // public functions always pass 117 : // a valid string_view. 118 : // initialize output 119 : std::memset(dest, 120 : 0, end - dest); 121 : return dest - dest0; 122 : } 123 : // LCOV_EXCL_STOP 124 686 : *dest++ = decode_one(it); 125 686 : it += 2; 126 686 : continue; 127 : } 128 : // unescaped 129 14204 : *dest++ = *it++; 130 : } 131 2711 : return dest - dest0; 132 : } 133 : 134 : std::size_t 135 2711 : decode_unsafe( 136 : char* const dest0, 137 : char const* end, 138 : core::string_view s, 139 : encoding_opts opt) noexcept 140 : { 141 2711 : if(opt.space_as_plus) 142 : { 143 709 : return decode_unsafe_impl<true>( 144 709 : dest0, end, s); 145 : } 146 2002 : return decode_unsafe_impl<false>( 147 2002 : dest0, end, s); 148 : } 149 : 150 : } // detail 151 : } // urls 152 : } // boost 153 :