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