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/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 855 : encoded_size(
32 : core::string_view s,
33 : CS const& unreserved,
34 : encoding_opts opt) noexcept
35 : {
36 : /* If you get a compile error here, it
37 : means that the value you passed does
38 : not meet the requirements stated in
39 : the documentation.
40 : */
41 : BOOST_STATIC_ASSERT(
42 : grammar::is_charset<CS>::value);
43 :
44 855 : std::size_t n = 0;
45 855 : auto it = s.data();
46 855 : auto const last = it + s.size();
47 :
48 877 : if(! opt.space_as_plus ||
49 22 : unreserved(' '))
50 : {
51 4748 : while(it != last)
52 : {
53 3915 : if(unreserved(*it))
54 3764 : n += 1;
55 : else
56 151 : n += 3;
57 3915 : ++it;
58 : }
59 : }
60 : else
61 : {
62 66 : while(it != last)
63 : {
64 44 : auto c = *it;
65 44 : if(unreserved(c))
66 26 : ++n;
67 18 : else if(c == ' ')
68 9 : ++n;
69 : else
70 9 : n += 3;
71 44 : ++it;
72 : }
73 : }
74 855 : return n;
75 : }
76 :
77 : //------------------------------------------------
78 :
79 : template<BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
80 : std::size_t
81 543 : encode(
82 : char* dest,
83 : std::size_t size,
84 : core::string_view s,
85 : CS const& unreserved,
86 : encoding_opts opt)
87 : {
88 : /* If you get a compile error here, it
89 : means that the value you passed does
90 : not meet the requirements stated in
91 : the documentation.
92 : */
93 : BOOST_STATIC_ASSERT(
94 : grammar::is_charset<CS>::value);
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<BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
173 : std::size_t
174 175 : encode_unsafe(
175 : char* dest,
176 : std::size_t size,
177 : core::string_view s,
178 : CS const& unreserved,
179 : encoding_opts opt)
180 : {
181 : BOOST_STATIC_ASSERT(
182 : grammar::is_charset<CS>::value);
183 :
184 : // '%' must be reserved
185 175 : BOOST_ASSERT(! unreserved('%'));
186 :
187 175 : auto it = s.data();
188 175 : auto const last = it + s.size();
189 175 : auto const end = dest + size;
190 : ignore_unused(end);
191 :
192 175 : char const* const hex =
193 175 : detail::hexdigs[opt.lower_case];
194 267 : auto const encode = [end, hex](
195 : char*& dest,
196 : unsigned char c) noexcept
197 : {
198 46 : ignore_unused(end);
199 46 : *dest++ = '%';
200 46 : BOOST_ASSERT(dest != end);
201 46 : *dest++ = hex[c>>4];
202 46 : BOOST_ASSERT(dest != end);
203 46 : *dest++ = hex[c&0xf];
204 : };
205 :
206 175 : auto const dest0 = dest;
207 175 : if(! opt.space_as_plus)
208 : {
209 605 : while(it != last)
210 : {
211 439 : BOOST_ASSERT(dest != end);
212 439 : if(unreserved(*it))
213 396 : *dest++ = *it++;
214 : else
215 43 : encode(dest, *it++);
216 : }
217 : }
218 : else
219 : {
220 : // VFALCO space is usually reserved,
221 : // and we depend on this for an
222 : // optimization. if this assert
223 : // goes off we can split the loop
224 : // below into two versions.
225 9 : BOOST_ASSERT(! unreserved(' '));
226 :
227 37 : while(it != last)
228 : {
229 28 : BOOST_ASSERT(dest != end);
230 28 : if(unreserved(*it))
231 : {
232 20 : *dest++ = *it++;
233 : }
234 8 : else if(*it == ' ')
235 : {
236 5 : *dest++ = '+';
237 5 : ++it;
238 : }
239 : else
240 : {
241 3 : encode(dest, *it++);
242 : }
243 : }
244 : }
245 175 : return dest - dest0;
246 : }
247 :
248 : //------------------------------------------------
249 :
250 : template<
251 : BOOST_URL_CONSTRAINT(string_token::StringToken) StringToken,
252 : BOOST_URL_CONSTRAINT(grammar::CharSet) CS>
253 : BOOST_URL_STRTOK_RETURN
254 24 : encode(
255 : core::string_view s,
256 : CS const& unreserved,
257 : encoding_opts opt,
258 : StringToken&& token) noexcept
259 : {
260 : BOOST_STATIC_ASSERT(
261 : grammar::is_charset<CS>::value);
262 :
263 24 : auto const n = encoded_size(
264 : s, unreserved, opt);
265 24 : auto p = token.prepare(n);
266 24 : if(n > 0)
267 22 : encode_unsafe(
268 : p, n, s, unreserved, opt);
269 24 : return token.result();
270 : }
271 :
272 : } // urls
273 : } // boost
274 :
275 : #endif
|