TLA 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 : #ifndef BOOST_URL_IMPL_ENCODE_HPP
11 : #define BOOST_URL_IMPL_ENCODE_HPP
12 :
13 : #include <boost/url/grammar/token_rule.hpp>
14 : #include <boost/assert.hpp>
15 : #include <boost/core/detail/static_assert.hpp>
16 : #include <boost/url/detail/encode.hpp>
17 : #include <boost/url/detail/except.hpp>
18 : #include <boost/url/encoding_opts.hpp>
19 : #include <boost/url/grammar/charset.hpp>
20 : #include <boost/url/grammar/hexdig_chars.hpp>
21 : #include <boost/url/grammar/string_token.hpp>
22 : #include <boost/url/grammar/type_traits.hpp>
23 :
24 : namespace boost {
25 : namespace urls {
26 :
27 : //------------------------------------------------
28 :
29 : template<BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
30 : std::size_t
31 HIT 5523 : encoded_size(
32 : core::string_view s,
33 : CS const& allowed,
34 : encoding_opts opt) noexcept
35 : {
36 : /*
37 : If you get a compilation error here, it
38 : means that the value you passed does
39 : not meet the requirements stated in
40 : the documentation.
41 : */
42 : BOOST_CORE_STATIC_ASSERT(
43 : grammar::is_charset<CS>::value);
44 :
45 5523 : std::size_t n = 0;
46 5523 : auto it = s.data();
47 5523 : auto const last = it + s.size();
48 :
49 5523 : if (!opt.space_as_plus)
50 : {
51 25592 : while (it != last)
52 : {
53 22571 : char const c = *it;
54 22571 : if (allowed(c))
55 : {
56 19533 : ++n;
57 : }
58 : else
59 : {
60 3038 : n += 3;
61 : }
62 22571 : ++it;
63 : }
64 : }
65 : else
66 : {
67 : // '+' is always encoded (thus
68 : // spending 3 chars) even if
69 : // allowed because "%2B" and
70 : // "+" have different meanings
71 : // when space as plus is enabled
72 : using FNT = bool (*)(CS const& allowed, char);
73 2502 : FNT takes_one_char =
74 5004 : allowed('+') ?
75 2500 : (allowed(' ') ?
76 4 : FNT([](CS const& allowed, char c){ return allowed(c) && c != '+'; }) :
77 22612 : FNT([](CS const& allowed, char c){ return (allowed(c) || c == ' ') && c != '+'; })) :
78 2 : (allowed(' ') ?
79 4 : FNT([](CS const& allowed, char c){ return allowed(c); }) :
80 4 : FNT([](CS const& allowed, char c){ return allowed(c) || c == ' '; }));
81 22624 : while (it != last)
82 : {
83 20122 : char const c = *it;
84 20122 : if (takes_one_char(allowed, c))
85 : {
86 16975 : ++n;
87 : }
88 : else
89 : {
90 3147 : n += 3;
91 : }
92 20122 : ++it;
93 : }
94 : }
95 5523 : return n;
96 : }
97 :
98 : //------------------------------------------------
99 :
100 : template<BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
101 : std::size_t
102 21818 : encode(
103 : char* dest,
104 : std::size_t size,
105 : core::string_view s,
106 : CS const& allowed,
107 : encoding_opts opt)
108 : {
109 : /* If you get a compilation error here, it
110 : means that the value you passed does
111 : not meet the requirements stated in
112 : the documentation.
113 : */
114 : BOOST_CORE_STATIC_ASSERT(
115 : grammar::is_charset<CS>::value);
116 :
117 : // '%' must be reserved
118 21818 : BOOST_ASSERT(!allowed('%'));
119 :
120 21818 : char const* const hex =
121 21818 : detail::hexdigs[opt.lower_case];
122 266468 : auto const encode = [hex](
123 : char*& dest,
124 : unsigned char c) noexcept
125 : {
126 244650 : *dest++ = '%';
127 244650 : *dest++ = hex[c>>4];
128 244650 : *dest++ = hex[c&0xf];
129 : };
130 :
131 21818 : auto it = s.data();
132 21818 : auto const end = dest + size;
133 21818 : auto const last = it + s.size();
134 21818 : auto const dest0 = dest;
135 :
136 21818 : if (!opt.space_as_plus)
137 : {
138 1303128 : while(it != last)
139 : {
140 1294002 : char const c = *it;
141 1294002 : if (allowed(c))
142 : {
143 1048403 : if(dest == end)
144 6102 : return dest - dest0;
145 1042301 : *dest++ = c;
146 1042301 : ++it;
147 1042301 : continue;
148 : }
149 245599 : if (end - dest < 3)
150 4088 : return dest - dest0;
151 241511 : encode(dest, c);
152 241511 : ++it;
153 : }
154 9126 : return dest - dest0;
155 : }
156 : else
157 : {
158 22594 : while (it != last)
159 : {
160 20106 : char const c = *it;
161 20106 : if (c == ' ')
162 : {
163 262 : if(dest == end)
164 2 : return dest - dest0;
165 260 : *dest++ = '+';
166 260 : ++it;
167 260 : continue;
168 : }
169 36537 : else if (
170 19844 : allowed(c) &&
171 : c != '+')
172 : {
173 16696 : if(dest == end)
174 3 : return dest - dest0;
175 16693 : *dest++ = c;
176 16693 : ++it;
177 16693 : continue;
178 : }
179 3148 : if(end - dest < 3)
180 9 : return dest - dest0;
181 3139 : encode(dest, c);
182 3139 : ++it;
183 : }
184 : }
185 2488 : return dest - dest0;
186 : }
187 :
188 : //------------------------------------------------
189 :
190 : // unsafe encode just
191 : // asserts on the output buffer
192 : //
193 : template<BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
194 : std::size_t
195 1569 : encode_unsafe(
196 : char* dest,
197 : std::size_t size,
198 : core::string_view s,
199 : CS const& allowed,
200 : encoding_opts opt)
201 : {
202 : BOOST_CORE_STATIC_ASSERT(
203 : grammar::is_charset<CS>::value);
204 :
205 : // '%' must be reserved
206 1569 : BOOST_ASSERT(!allowed('%'));
207 :
208 1569 : auto it = s.data();
209 1569 : auto const last = it + s.size();
210 1569 : auto const end = dest + size;
211 : ignore_unused(end);
212 :
213 1569 : char const* const hex =
214 1569 : detail::hexdigs[opt.lower_case];
215 2955 : auto const encode = [end, hex](
216 : char*& dest,
217 : unsigned char c) noexcept
218 : {
219 693 : ignore_unused(end);
220 693 : *dest++ = '%';
221 693 : BOOST_ASSERT(dest != end);
222 693 : *dest++ = hex[c>>4];
223 693 : BOOST_ASSERT(dest != end);
224 693 : *dest++ = hex[c&0xf];
225 : };
226 :
227 1569 : auto const dest0 = dest;
228 1569 : if (!opt.space_as_plus)
229 : {
230 8140 : while(it != last)
231 : {
232 6584 : BOOST_ASSERT(dest != end);
233 6584 : char const c = *it;
234 6584 : if(allowed(c))
235 : {
236 5899 : *dest++ = c;
237 : }
238 : else
239 : {
240 685 : encode(dest, c);
241 : }
242 6584 : ++it;
243 : }
244 : }
245 : else
246 : {
247 53 : while(it != last)
248 : {
249 40 : BOOST_ASSERT(dest != end);
250 40 : char const c = *it;
251 40 : if (c == ' ')
252 : {
253 9 : *dest++ = '+';
254 : }
255 31 : else if (
256 31 : allowed(c) &&
257 : c != '+')
258 : {
259 23 : *dest++ = c;
260 : }
261 : else
262 : {
263 8 : encode(dest, c);
264 : }
265 40 : ++it;
266 : }
267 : }
268 1569 : return dest - dest0;
269 : }
270 :
271 : //------------------------------------------------
272 :
273 : template<
274 : BOOST_URL_CONSTRAINT(string_token::StringToken) StringToken,
275 : BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
276 : BOOST_URL_STRTOK_RETURN
277 28 : encode(
278 : core::string_view s,
279 : CS const& allowed,
280 : encoding_opts opt,
281 : StringToken&& token)
282 : {
283 : BOOST_CORE_STATIC_ASSERT(
284 : grammar::is_charset<CS>::value);
285 :
286 28 : auto const n = encoded_size(
287 : s, allowed, opt);
288 28 : auto p = token.prepare(n);
289 28 : if(n > 0)
290 26 : encode_unsafe(
291 : p, n, s, allowed, opt);
292 28 : return token.result();
293 : }
294 :
295 : } // urls
296 : } // boost
297 :
298 : #endif
|