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 855 : 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 855 : std::size_t n = 0;
44 855 : auto it = s.data();
45 855 : auto const last = it + s.size();
46 :
47 877 : if(! opt.space_as_plus ||
48 22 : unreserved(' '))
49 : {
50 4748 : while(it != last)
51 : {
52 3915 : if(unreserved(*it))
53 3764 : n += 1;
54 : else
55 151 : n += 3;
56 3915 : ++it;
57 : }
58 : }
59 : else
60 : {
61 66 : while(it != last)
62 : {
63 44 : auto c = *it;
64 44 : if(unreserved(c))
65 26 : ++n;
66 18 : else if(c == ' ')
67 9 : ++n;
68 : else
69 9 : n += 3;
70 44 : ++it;
71 : }
72 : }
73 855 : return n;
74 : }
75 :
76 : //------------------------------------------------
77 :
78 : template<class CharSet>
79 : std::size_t
80 543 : 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 543 : BOOST_ASSERT(! unreserved('%'));
98 :
99 543 : char const* const hex =
100 543 : detail::hexdigs[opt.lower_case];
101 653 : auto const encode = [hex](
102 : char*& dest,
103 : unsigned char c) noexcept
104 : {
105 110 : *dest++ = '%';
106 110 : *dest++ = hex[c>>4];
107 110 : *dest++ = hex[c&0xf];
108 : };
109 :
110 543 : auto it = s.data();
111 543 : auto const end = dest + size;
112 543 : auto const last = it + s.size();
113 543 : auto const dest0 = dest;
114 543 : auto const end3 = end - 3;
115 :
116 543 : if(! opt.space_as_plus)
117 : {
118 3286 : while(it != last)
119 : {
120 2787 : if(unreserved(*it))
121 : {
122 2668 : if(dest == end)
123 3 : return dest - dest0;
124 2665 : *dest++ = *it++;
125 2665 : continue;
126 : }
127 119 : if(dest > end3)
128 15 : return dest - dest0;
129 104 : encode(dest, *it++);
130 : }
131 499 : 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 175 : 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 175 : BOOST_ASSERT(! unreserved('%'));
183 :
184 175 : auto it = s.data();
185 175 : auto const last = it + s.size();
186 175 : auto const end = dest + size;
187 : ignore_unused(end);
188 :
189 175 : char const* const hex =
190 175 : detail::hexdigs[opt.lower_case];
191 359 : auto const encode = [end, hex](
192 : char*& dest,
193 : unsigned char c) noexcept
194 : {
195 46 : ignore_unused(end);
196 46 : *dest++ = '%';
197 46 : BOOST_ASSERT(dest != end);
198 46 : *dest++ = hex[c>>4];
199 46 : BOOST_ASSERT(dest != end);
200 46 : *dest++ = hex[c&0xf];
201 : };
202 :
203 175 : auto const dest0 = dest;
204 175 : if(! opt.space_as_plus)
205 : {
206 605 : while(it != last)
207 : {
208 439 : BOOST_ASSERT(dest != end);
209 439 : if(unreserved(*it))
210 396 : *dest++ = *it++;
211 : else
212 43 : 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 9 : BOOST_ASSERT(! unreserved(' '));
223 :
224 37 : while(it != last)
225 : {
226 28 : BOOST_ASSERT(dest != end);
227 28 : if(unreserved(*it))
228 : {
229 20 : *dest++ = *it++;
230 : }
231 8 : else if(*it == ' ')
232 : {
233 5 : *dest++ = '+';
234 5 : ++it;
235 : }
236 : else
237 : {
238 3 : encode(dest, *it++);
239 : }
240 : }
241 : }
242 175 : return dest - dest0;
243 : }
244 :
245 : //------------------------------------------------
246 :
247 : template<
248 : class StringToken,
249 : class CharSet>
250 : BOOST_URL_STRTOK_RETURN
251 24 : 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 24 : auto const n = encoded_size(
267 : s, unreserved, opt);
268 24 : auto p = token.prepare(n);
269 24 : if(n > 0)
270 22 : encode_unsafe(
271 : p, n, s, unreserved, opt);
272 24 : return token.result();
273 : }
274 :
275 : } // urls
276 : } // boost
277 :
278 : #endif
|