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/detail/encode.hpp>
14 : #include <boost/url/detail/except.hpp>
15 : #include <boost/url/encoding_opts.hpp>
16 : #include <boost/url/grammar/charset.hpp>
17 : #include <boost/url/grammar/hexdig_chars.hpp>
18 : #include <boost/url/grammar/type_traits.hpp>
19 : #include <boost/assert.hpp>
20 : #include <boost/static_assert.hpp>
21 :
22 : namespace boost {
23 : namespace urls {
24 :
25 : //------------------------------------------------
26 :
27 : template<class CharSet>
28 : std::size_t
29 826 : encoded_size(
30 : core::string_view s,
31 : CharSet const& unreserved,
32 : encoding_opts opt) noexcept
33 : {
34 : /* If you get a compile error here, it
35 : means that the value you passed does
36 : not meet the requirements stated in
37 : the documentation.
38 : */
39 : static_assert(
40 : grammar::is_charset<CharSet>::value,
41 : "Type requirements not met");
42 :
43 826 : std::size_t n = 0;
44 826 : auto it = s.data();
45 826 : auto const last = it + s.size();
46 :
47 847 : if(! opt.space_as_plus ||
48 21 : unreserved(' '))
49 : {
50 4469 : while(it != last)
51 : {
52 3664 : if(unreserved(*it))
53 3520 : n += 1;
54 : else
55 144 : n += 3;
56 3664 : ++it;
57 : }
58 : }
59 : else
60 : {
61 50 : while(it != last)
62 : {
63 29 : auto c = *it;
64 29 : if(unreserved(c))
65 12 : ++n;
66 17 : else if(c == ' ')
67 8 : ++n;
68 : else
69 9 : n += 3;
70 29 : ++it;
71 : }
72 : }
73 826 : return n;
74 : }
75 :
76 : //------------------------------------------------
77 :
78 : template<class CharSet>
79 : std::size_t
80 542 : encode(
81 : char* dest,
82 : std::size_t size,
83 : core::string_view s,
84 : CharSet const& unreserved,
85 : encoding_opts opt)
86 : {
87 : /* If you get a compile error here, it
88 : means that the value you passed does
89 : not meet the requirements stated in
90 : the documentation.
91 : */
92 : static_assert(
93 : grammar::is_charset<CharSet>::value,
94 : "Type requirements not met");
95 :
96 : // '%' must be reserved
97 542 : BOOST_ASSERT(! unreserved('%'));
98 :
99 542 : char const* const hex =
100 542 : detail::hexdigs[opt.lower_case];
101 651 : auto const encode = [hex](
102 : char*& dest,
103 : unsigned char c) noexcept
104 : {
105 109 : *dest++ = '%';
106 109 : *dest++ = hex[c>>4];
107 109 : *dest++ = hex[c&0xf];
108 : };
109 :
110 542 : auto it = s.data();
111 542 : auto const end = dest + size;
112 542 : auto const last = it + s.size();
113 542 : auto const dest0 = dest;
114 542 : auto const end3 = end - 3;
115 :
116 542 : if(! opt.space_as_plus)
117 : {
118 3274 : while(it != last)
119 : {
120 2776 : if(unreserved(*it))
121 : {
122 2658 : if(dest == end)
123 3 : return dest - dest0;
124 2655 : *dest++ = *it++;
125 2655 : continue;
126 : }
127 118 : if(dest > end3)
128 15 : return dest - dest0;
129 103 : encode(dest, *it++);
130 : }
131 498 : return dest - dest0;
132 : }
133 26 : else if(! unreserved(' '))
134 : {
135 : // VFALCO space is usually reserved,
136 : // and we depend on this for an
137 : // optimization. if this assert
138 : // goes off we can split the loop
139 : // below into two versions.
140 26 : BOOST_ASSERT(! unreserved(' '));
141 :
142 52 : while(it != last)
143 : {
144 40 : if(unreserved(*it))
145 : {
146 16 : if(dest == end)
147 3 : return dest - dest0;
148 13 : *dest++ = *it++;
149 13 : continue;
150 : }
151 24 : if(*it == ' ')
152 : {
153 9 : if(dest == end)
154 2 : return dest - dest0;
155 7 : *dest++ = '+';
156 7 : ++it;
157 7 : continue;
158 : }
159 15 : if(dest > end3)
160 9 : return dest - dest0;
161 6 : encode(dest, *it++);
162 : }
163 : }
164 12 : return dest - dest0;
165 : }
166 :
167 : //------------------------------------------------
168 :
169 : // unsafe encode just
170 : // asserts on the output buffer
171 : //
172 : template<class CharSet>
173 : std::size_t
174 170 : encode_unsafe(
175 : char* dest,
176 : std::size_t size,
177 : core::string_view s,
178 : CharSet const& unreserved,
179 : encoding_opts opt)
180 : {
181 : // '%' must be reserved
182 170 : BOOST_ASSERT(! unreserved('%'));
183 :
184 170 : auto it = s.data();
185 170 : auto const last = it + s.size();
186 170 : auto const end = dest + size;
187 : ignore_unused(end);
188 :
189 170 : char const* const hex =
190 170 : detail::hexdigs[opt.lower_case];
191 338 : auto const encode = [end, hex](
192 : char*& dest,
193 : unsigned char c) noexcept
194 : {
195 42 : ignore_unused(end);
196 42 : *dest++ = '%';
197 42 : BOOST_ASSERT(dest != end);
198 42 : *dest++ = hex[c>>4];
199 42 : BOOST_ASSERT(dest != end);
200 42 : *dest++ = hex[c&0xf];
201 : };
202 :
203 170 : auto const dest0 = dest;
204 170 : if(! opt.space_as_plus)
205 : {
206 567 : while(it != last)
207 : {
208 405 : BOOST_ASSERT(dest != end);
209 405 : if(unreserved(*it))
210 366 : *dest++ = *it++;
211 : else
212 39 : encode(dest, *it++);
213 : }
214 : }
215 : else
216 : {
217 : // VFALCO space is usually reserved,
218 : // and we depend on this for an
219 : // optimization. if this assert
220 : // goes off we can split the loop
221 : // below into two versions.
222 8 : BOOST_ASSERT(! unreserved(' '));
223 :
224 21 : while(it != last)
225 : {
226 13 : BOOST_ASSERT(dest != end);
227 13 : if(unreserved(*it))
228 : {
229 6 : *dest++ = *it++;
230 : }
231 7 : else if(*it == ' ')
232 : {
233 4 : *dest++ = '+';
234 4 : ++it;
235 : }
236 : else
237 : {
238 3 : encode(dest, *it++);
239 : }
240 : }
241 : }
242 170 : return dest - dest0;
243 : }
244 :
245 : //------------------------------------------------
246 :
247 : template<
248 : class StringToken,
249 : class CharSet>
250 : BOOST_URL_STRTOK_RETURN
251 19 : encode(
252 : core::string_view s,
253 : CharSet const& unreserved,
254 : encoding_opts opt,
255 : StringToken&& token) noexcept
256 : {
257 : /* If you get a compile error here, it
258 : means that the value you passed does
259 : not meet the requirements stated in
260 : the documentation.
261 : */
262 : static_assert(
263 : grammar::is_charset<CharSet>::value,
264 : "Type requirements not met");
265 :
266 19 : auto const n = encoded_size(
267 : s, unreserved, opt);
268 19 : auto p = token.prepare(n);
269 19 : if(n > 0)
270 17 : encode_unsafe(
271 : p, n, s, unreserved, opt);
272 19 : return token.result();
273 : }
274 :
275 : } // urls
276 : } // boost
277 :
278 : #endif
|