Line data Source code
1 : //
2 : // Copyright (c) 2021 Vinnie Falco (vinnie dot falco at gmail dot 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_GRAMMAR_STRING_TOKEN_HPP
11 : #define BOOST_URL_GRAMMAR_STRING_TOKEN_HPP
12 :
13 : #include <boost/url/detail/config.hpp>
14 : #include <boost/core/detail/string_view.hpp>
15 : #include <boost/url/detail/except.hpp>
16 : #include <memory>
17 : #include <string>
18 :
19 : namespace boost {
20 : namespace urls {
21 : namespace string_token {
22 :
23 : /** Base class for string tokens, and algorithm parameters
24 :
25 : This abstract interface provides a means
26 : for an algorithm to generically obtain a
27 : modifiable, contiguous character buffer
28 : of prescribed size.
29 :
30 : A @ref StringToken should be derived
31 : from this class. As the author of an
32 : algorithm using a @ref StringToken,
33 : simply declare an rvalue reference
34 : as a parameter type.
35 :
36 : Instances of this type are intended only
37 : to be used once and then destroyed.
38 :
39 : @par Example
40 : The declared function accepts any
41 : temporary instance of `arg` to be
42 : used for writing:
43 : @code
44 : void algorithm( string_token::arg&& dest );
45 : @endcode
46 :
47 : To implement the interface for your type
48 : or use-case, derive from the class and
49 : implement the prepare function.
50 : */
51 : struct arg
52 : {
53 : /** Return a modifiable character buffer
54 :
55 : This function attempts to obtain a
56 : character buffer with space for at
57 : least `n` characters. Upon success,
58 : a pointer to the beginning of the
59 : buffer is returned. Ownership is not
60 : transferred; the caller should not
61 : attempt to free the storage. The
62 : buffer shall remain valid until
63 : `this` is destroyed.
64 :
65 : @note
66 : This function may only be called once.
67 : After invoking the function, the only
68 : valid operation is destruction.
69 : */
70 : virtual char* prepare(std::size_t n) = 0;
71 :
72 : /// Virtual destructor
73 3400 : virtual ~arg() = default;
74 :
75 : /// Default constructor
76 3400 : arg() = default;
77 :
78 : /// Default move constructor
79 : arg(arg&&) = default;
80 :
81 : /// Deleted copy constructor
82 : arg(arg const&) = delete;
83 :
84 : /// Deleted move assignment
85 : arg& operator=(arg&&) = delete;
86 :
87 : /// Deleted copy assignment
88 : arg& operator=(arg const&) = delete;
89 : };
90 :
91 : //------------------------------------------------
92 :
93 : namespace implementation_defined {
94 : template<class T, class = void>
95 : struct is_token : std::false_type {};
96 :
97 : template<class T>
98 : struct is_token<T, void_t<
99 : decltype(std::declval<T&>().prepare(
100 : std::declval<std::size_t>())),
101 : decltype(std::declval<T&>().result())
102 : > > : std::integral_constant<bool,
103 : std::is_convertible<decltype(
104 : std::declval<T&>().result()),
105 : typename T::result_type>::value &&
106 : std::is_same<decltype(
107 : std::declval<T&>().prepare(0)),
108 : char*>::value &&
109 : std::is_base_of<arg, T>::value &&
110 : std::is_convertible<T const volatile*,
111 : arg const volatile*>::value
112 : >
113 : {
114 : };
115 : } // implementation_defined
116 :
117 : /** Trait to determine if a type is a string token
118 :
119 : This trait returns `true` if `T` is a valid
120 : @ref StringToken type, and `false` otherwise.
121 :
122 : @par Example
123 : @code
124 : static_assert( string_token::is_token<T>::value );
125 : @endcode
126 : */
127 : template<class T>
128 : using is_token = implementation_defined::is_token<T>;
129 :
130 : #ifdef BOOST_URL_HAS_CONCEPTS
131 : /** Concept for a string token
132 :
133 : This concept is satisfied if `T` is a
134 : valid string token type.
135 :
136 : A string token is an rvalue passed to a function template
137 : which customizes the return type of the function and also
138 : controls how a modifiable character buffer is obtained and presented.
139 :
140 : The string token's lifetime extends only for the duration of the
141 : function call in which it appears as a parameter.
142 :
143 : A string token cannot be copied, moved, or assigned, and must be
144 : destroyed when the function returns or throws.
145 :
146 : @par Semantics
147 :
148 : `T::result_type` determines the return type of functions
149 : that accept a string token.
150 :
151 : The `prepare()` function overrides the virtual function
152 : in the base class @ref arg. It must return a pointer to
153 : a character buffer of at least size `n`, otherwise
154 : throw an exception. This function is called only
155 : once or not at all.
156 :
157 : The `result()` function is invoked by the algorithm
158 : to receive the result from the string token.
159 : It is only invoked if `prepare()` returned
160 : successfully and the string token was not destroyed.
161 : It is only called after `prepare()` returns
162 : successfully, and the string token is destroyed
163 : when the algorithm completes or if an exception
164 : is thrown.
165 :
166 : String tokens cannot be reused.
167 :
168 : @par Exemplars
169 : String token prototype:
170 :
171 : @code
172 : struct StringToken : string_token::arg
173 : {
174 : using result_type = std::string;
175 :
176 : char* prepare( std::size_t n ) override;
177 :
178 : result_type result();
179 : };
180 : @endcode
181 :
182 : Algorithm prototype:
183 :
184 : @code
185 : namespace detail {
186 :
187 : // Algorithm implementation may be placed
188 : // out of line, and written as an ordinary
189 : // function (no template required).
190 : void algorithm_impl( string_token::arg& token )
191 : {
192 : std::size_t n = 0;
193 :
194 : // calculate space needed in n
195 : // ...
196 :
197 : // acquire a destination buffer
198 : char* dest = token.prepare( n );
199 :
200 : // write the characters to the buffer
201 : }
202 : } // detail
203 :
204 : // public interface is a function template,
205 : // defaulting to return std::string.
206 : template< class StringToken = string_token::return_string >
207 : auto
208 : algorithm( StringToken&& token = {} ) ->
209 : typename StringToken::result_type
210 : {
211 : // invoke the algorithm with the token
212 : algorithm_impl( token );
213 :
214 : // return the result from the token
215 : return token.result();
216 : }
217 : @endcode
218 :
219 : @par Models
220 : The following classes and functions implement and
221 : generate string tokens.
222 :
223 : @li @ref return_string
224 : @li @ref assign_to
225 : @li @ref preserve_size
226 :
227 : */
228 : template <class T>
229 : concept StringToken =
230 : std::derived_from<T, string_token::arg> &&
231 : requires (T t, std::size_t n)
232 : {
233 : typename T::result_type;
234 : { t.prepare(n) } -> std::same_as<char*>;
235 : { t.result() } -> std::convertible_to<typename T::result_type>;
236 : };
237 : #endif
238 :
239 : //------------------------------------------------
240 :
241 : namespace implementation_defined {
242 : struct return_string
243 : : arg
244 : {
245 : using result_type = std::string;
246 :
247 : char*
248 3056 : prepare(std::size_t n) override
249 : {
250 3056 : s_.resize(n);
251 3056 : return &s_[0];
252 : }
253 :
254 : result_type
255 3056 : result() noexcept
256 : {
257 3056 : return std::move(s_);
258 : }
259 :
260 : private:
261 : result_type s_;
262 : };
263 : } // implementation_defined
264 :
265 : /** A string token for returning a plain string
266 :
267 : This @ref StringToken is used to customize
268 : a function to return a plain string.
269 :
270 : This is default token type used by
271 : the methods of @ref url_view_base
272 : that return decoded strings.
273 : */
274 : using return_string = implementation_defined::return_string;
275 :
276 : //------------------------------------------------
277 :
278 : namespace implementation_defined {
279 : template<class Alloc>
280 : struct append_to_t
281 : : arg
282 : {
283 : using string_type = std::basic_string<
284 : char, std::char_traits<char>,
285 : Alloc>;
286 :
287 : using result_type = string_type&;
288 :
289 : explicit
290 3 : append_to_t(
291 : string_type& s) noexcept
292 3 : : s_(s)
293 : {
294 3 : }
295 :
296 : char*
297 3 : prepare(std::size_t n) override
298 : {
299 3 : std::size_t n0 = s_.size();
300 3 : if(n > s_.max_size() - n0)
301 0 : urls::detail::throw_length_error();
302 3 : s_.resize(n0 + n);
303 3 : return &s_[n0];
304 : }
305 :
306 : result_type
307 3 : result() noexcept
308 : {
309 3 : return s_;
310 : }
311 :
312 : private:
313 : string_type& s_;
314 : };
315 : } // implementation_defined
316 :
317 : /** Create a string token for appending to a plain string
318 :
319 : This function creates a @ref StringToken
320 : which appends to an existing plain string.
321 :
322 : Functions using this token will append
323 : the result to the existing string and
324 : return a reference to it.
325 : */
326 : template<
327 : class Alloc =
328 : std::allocator<char>>
329 : implementation_defined::append_to_t<Alloc>
330 3 : append_to(
331 : std::basic_string<
332 : char,
333 : std::char_traits<char>,
334 : Alloc>& s)
335 : {
336 3 : return implementation_defined::append_to_t<Alloc>(s);
337 : }
338 :
339 : //------------------------------------------------
340 :
341 : namespace implementation_defined {
342 : template<class Alloc>
343 : struct assign_to_t
344 : : arg
345 : {
346 : using string_type = std::basic_string<
347 : char, std::char_traits<char>,
348 : Alloc>;
349 :
350 : using result_type = string_type&;
351 :
352 : explicit
353 337 : assign_to_t(
354 : string_type& s) noexcept
355 337 : : s_(s)
356 : {
357 337 : }
358 :
359 : char*
360 337 : prepare(std::size_t n) override
361 : {
362 337 : s_.resize(n);
363 337 : return &s_[0];
364 : }
365 :
366 : result_type
367 337 : result() noexcept
368 : {
369 337 : return s_;
370 : }
371 :
372 : private:
373 : string_type& s_;
374 : };
375 : } // implementation_defined
376 :
377 : /** Create a string token for assigning to a plain string
378 :
379 : This function creates a @ref StringToken
380 : which assigns to an existing plain string.
381 :
382 : Functions using this token will assign
383 : the result to the existing string and
384 : return a reference to it.
385 : */
386 : template<
387 : class Alloc =
388 : std::allocator<char>>
389 : implementation_defined::assign_to_t<Alloc>
390 337 : assign_to(
391 : std::basic_string<
392 : char,
393 : std::char_traits<char>,
394 : Alloc>& s)
395 : {
396 337 : return implementation_defined::assign_to_t<Alloc>(s);
397 : }
398 :
399 : //------------------------------------------------
400 :
401 : namespace implementation_defined {
402 : template<class Alloc>
403 : struct preserve_size_t
404 : : arg
405 : {
406 : using result_type = core::string_view;
407 :
408 : using string_type = std::basic_string<
409 : char, std::char_traits<char>,
410 : Alloc>;
411 :
412 : explicit
413 4 : preserve_size_t(
414 : string_type& s) noexcept
415 4 : : s_(s)
416 : {
417 4 : }
418 :
419 : char*
420 4 : prepare(std::size_t n) override
421 : {
422 4 : n_ = n;
423 : // preserve size() to
424 : // avoid value-init
425 4 : if(s_.size() < n)
426 2 : s_.resize(n);
427 4 : return &s_[0];
428 : }
429 :
430 : result_type
431 4 : result() noexcept
432 : {
433 4 : return core::string_view(
434 8 : s_.data(), n_);
435 : }
436 :
437 : private:
438 : string_type& s_;
439 : std::size_t n_ = 0;
440 : };
441 : } // implementation_defined
442 :
443 : /** Create a string token for a durable core::string_view
444 :
445 : This function creates a @ref StringToken
446 : which assigns to an existing plain string.
447 :
448 : Functions using this token will assign
449 : the result to the existing string and
450 : return a `core::string_view` to it.
451 : */
452 : template<
453 : class Alloc =
454 : std::allocator<char>>
455 : implementation_defined::preserve_size_t<Alloc>
456 4 : preserve_size(
457 : std::basic_string<
458 : char,
459 : std::char_traits<char>,
460 : Alloc>& s)
461 : {
462 4 : return implementation_defined::preserve_size_t<Alloc>(s);
463 : }
464 : } // string_token
465 :
466 : namespace grammar {
467 : namespace string_token = ::boost::urls::string_token;
468 : } // grammar
469 :
470 : } // urls
471 : } // boost
472 :
473 : #endif
|