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 877 : 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 877 : std::size_t n = 0;
46 877 : auto it = s.data();
47 877 : auto const last = it + s.size();
48 :
49 877 : if (!opt.space_as_plus)
50 : {
51 3310 : while (it != last)
52 : {
53 2625 : char const c = *it;
54 2625 : if (allowed(c))
55 : {
56 2501 : ++n;
57 : }
58 : else
59 : {
60 124 : n += 3;
61 : }
62 2625 : ++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 192 : FNT takes_one_char =
74 384 : allowed('+') ?
75 190 : (allowed(' ') ?
76 4 : FNT([](CS const& allowed, char c){ return allowed(c) && c != '+'; }) :
77 1620 : 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 1632 : while (it != last)
82 : {
83 1440 : char const c = *it;
84 1440 : if (takes_one_char(allowed, c))
85 : {
86 1394 : ++n;
87 : }
88 : else
89 : {
90 46 : n += 3;
91 : }
92 1440 : ++it;
93 : }
94 : }
95 877 : return n;
96 : }
97 :
98 : //------------------------------------------------
99 :
100 : template<BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
101 : std::size_t
102 560 : 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 560 : BOOST_ASSERT(!allowed('%'));
119 :
120 560 : char const* const hex =
121 560 : detail::hexdigs[opt.lower_case];
122 675 : auto const encode = [hex](
123 : char*& dest,
124 : unsigned char c) noexcept
125 : {
126 115 : *dest++ = '%';
127 115 : *dest++ = hex[c>>4];
128 115 : *dest++ = hex[c&0xf];
129 : };
130 :
131 560 : auto it = s.data();
132 560 : auto const end = dest + size;
133 560 : auto const last = it + s.size();
134 560 : auto const dest0 = dest;
135 560 : auto const end3 = end - 3;
136 :
137 560 : if (!opt.space_as_plus)
138 : {
139 1831 : while(it != last)
140 : {
141 1481 : char const c = *it;
142 1481 : if (allowed(c))
143 : {
144 1389 : if(dest == end)
145 3 : return dest - dest0;
146 1386 : *dest++ = c;
147 1386 : ++it;
148 1386 : continue;
149 : }
150 92 : if (dest > end3)
151 15 : return dest - dest0;
152 77 : encode(dest, c);
153 77 : ++it;
154 : }
155 350 : return dest - dest0;
156 : }
157 : else
158 : {
159 1602 : while (it != last)
160 : {
161 1424 : char const c = *it;
162 1424 : if (c == ' ')
163 : {
164 25 : if(dest == end)
165 2 : return dest - dest0;
166 23 : *dest++ = '+';
167 23 : ++it;
168 23 : continue;
169 : }
170 2748 : else if (
171 1399 : allowed(c) &&
172 : c != '+')
173 : {
174 1352 : if(dest == end)
175 3 : return dest - dest0;
176 1349 : *dest++ = c;
177 1349 : ++it;
178 1349 : continue;
179 : }
180 47 : if(dest > end3)
181 9 : return dest - dest0;
182 38 : encode(dest, c);
183 38 : ++it;
184 : }
185 : }
186 178 : return dest - dest0;
187 : }
188 :
189 : //------------------------------------------------
190 :
191 : // unsafe encode just
192 : // asserts on the output buffer
193 : //
194 : template<BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
195 : std::size_t
196 181 : encode_unsafe(
197 : char* dest,
198 : std::size_t size,
199 : core::string_view s,
200 : CS const& allowed,
201 : encoding_opts opt)
202 : {
203 : BOOST_CORE_STATIC_ASSERT(
204 : grammar::is_charset<CS>::value);
205 :
206 : // '%' must be reserved
207 181 : BOOST_ASSERT(!allowed('%'));
208 :
209 181 : auto it = s.data();
210 181 : auto const last = it + s.size();
211 181 : auto const end = dest + size;
212 : ignore_unused(end);
213 :
214 181 : char const* const hex =
215 181 : detail::hexdigs[opt.lower_case];
216 283 : auto const encode = [end, hex](
217 : char*& dest,
218 : unsigned char c) noexcept
219 : {
220 51 : ignore_unused(end);
221 51 : *dest++ = '%';
222 51 : BOOST_ASSERT(dest != end);
223 51 : *dest++ = hex[c>>4];
224 51 : BOOST_ASSERT(dest != end);
225 51 : *dest++ = hex[c&0xf];
226 : };
227 :
228 181 : auto const dest0 = dest;
229 181 : if (!opt.space_as_plus)
230 : {
231 623 : while(it != last)
232 : {
233 455 : BOOST_ASSERT(dest != end);
234 455 : char const c = *it;
235 455 : if(allowed(c))
236 : {
237 412 : *dest++ = c;
238 : }
239 : else
240 : {
241 43 : encode(dest, c);
242 : }
243 455 : ++it;
244 : }
245 : }
246 : else
247 : {
248 53 : while(it != last)
249 : {
250 40 : BOOST_ASSERT(dest != end);
251 40 : char const c = *it;
252 40 : if (c == ' ')
253 : {
254 9 : *dest++ = '+';
255 : }
256 31 : else if (
257 31 : allowed(c) &&
258 : c != '+')
259 : {
260 23 : *dest++ = c;
261 : }
262 : else
263 : {
264 8 : encode(dest, c);
265 : }
266 40 : ++it;
267 : }
268 : }
269 181 : return dest - dest0;
270 : }
271 :
272 : //------------------------------------------------
273 :
274 : template<
275 : BOOST_URL_CONSTRAINT(string_token::StringToken) StringToken,
276 : BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
277 : BOOST_URL_STRTOK_RETURN
278 28 : encode(
279 : core::string_view s,
280 : CS const& allowed,
281 : encoding_opts opt,
282 : StringToken&& token) noexcept
283 : {
284 : BOOST_CORE_STATIC_ASSERT(
285 : grammar::is_charset<CS>::value);
286 :
287 28 : auto const n = encoded_size(
288 : s, allowed, opt);
289 28 : auto p = token.prepare(n);
290 28 : if(n > 0)
291 26 : encode_unsafe(
292 : p, n, s, allowed, opt);
293 28 : return token.result();
294 : }
295 :
296 : } // urls
297 : } // boost
298 :
299 : #endif
|