Line data Source code
1 : //
2 : // Copyright (c) 2016-2019 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_RANGE_RULE_HPP
11 : #define BOOST_URL_GRAMMAR_RANGE_RULE_HPP
12 :
13 : #include <boost/url/detail/config.hpp>
14 : #include <boost/url/error.hpp>
15 : #include <boost/core/detail/string_view.hpp>
16 : #include <boost/url/grammar/parse.hpp>
17 : #include <boost/url/grammar/type_traits.hpp>
18 : #include <boost/core/detail/static_assert.hpp>
19 : #include <cstddef>
20 : #include <iterator>
21 : #include <type_traits>
22 : #include <stddef.h> // ::max_align_t
23 :
24 : namespace boost {
25 : namespace urls {
26 : namespace grammar {
27 : namespace implementation_defined {
28 : template<class R0, class R1>
29 : struct range_rule_t;
30 : } // implementation_defined
31 :
32 : /** A forward range of parsed elements
33 :
34 : Objects of this type are forward ranges
35 : returned when parsing using the
36 : @ref range_rule.
37 : Iteration is performed by re-parsing the
38 : underlying character buffer. Ownership
39 : of the buffer is not transferred; the
40 : caller is responsible for ensuring that
41 : the lifetime of the buffer extends until
42 : it is no longer referenced by the range.
43 :
44 : @note
45 :
46 : The implementation may use temporary,
47 : recycled storage for type-erasure. Objects
48 : of type `range` are intended to be used
49 : ephemerally. That is, for short durations
50 : such as within a function scope. If it is
51 : necessary to store the range for a long
52 : period of time or with static storage
53 : duration, it is necessary to copy the
54 : contents to an object of a different type.
55 :
56 : @tparam T The value type of the range
57 :
58 : @see
59 : @ref parse,
60 : @ref range_rule.
61 : */
62 : template<class T>
63 : class range
64 : {
65 : // buffer size for type-erased rule
66 : static constexpr
67 : std::size_t BufferSize = 128;
68 :
69 : struct small_buffer
70 : {
71 : alignas(alignof(::max_align_t))
72 : unsigned char buf[BufferSize];
73 :
74 707 : void const* addr() const noexcept
75 : {
76 707 : return buf;
77 : }
78 :
79 3317 : void* addr() noexcept
80 : {
81 3317 : return buf;
82 : }
83 : };
84 :
85 : small_buffer sb_;
86 : core::string_view s_;
87 : std::size_t n_ = 0;
88 :
89 : //--------------------------------------------
90 :
91 : struct any_rule;
92 :
93 : template<class R, bool>
94 : struct impl1;
95 :
96 : template<
97 : class R0, class R1, bool>
98 : struct impl2;
99 :
100 : template<
101 : class R0, class R1>
102 : friend struct implementation_defined::range_rule_t;
103 :
104 : any_rule&
105 3317 : get() noexcept
106 : {
107 : return *reinterpret_cast<
108 3317 : any_rule*>(sb_.addr());
109 : }
110 :
111 : any_rule const&
112 707 : get() const noexcept
113 : {
114 : return *reinterpret_cast<
115 : any_rule const*>(
116 707 : sb_.addr());
117 : }
118 :
119 : template<class R>
120 : range(
121 : core::string_view s,
122 : std::size_t n,
123 : R const& r);
124 :
125 : template<
126 : class R0, class R1>
127 : range(
128 : core::string_view s,
129 : std::size_t n,
130 : R0 const& first,
131 : R1 const& next);
132 :
133 : public:
134 : /** The type of each element of the range
135 : */
136 : using value_type = T;
137 :
138 : /** The type of each element of the range
139 : */
140 : using reference = T const&;
141 :
142 : /** The type of each element of the range
143 : */
144 : using const_reference = T const&;
145 :
146 : /** Provided for compatibility, unused
147 : */
148 : using pointer = void const*;
149 :
150 : /** The type used to represent unsigned integers
151 : */
152 : using size_type = std::size_t;
153 :
154 : /** The type used to represent signed integers
155 : */
156 : using difference_type = std::ptrdiff_t;
157 :
158 : /** A constant, forward iterator to elements of the range
159 : */
160 : class iterator;
161 :
162 : /** A constant, forward iterator to elements of the range
163 : */
164 : using const_iterator = iterator;
165 :
166 : /** Destructor
167 : */
168 : ~range();
169 :
170 : /** Constructor
171 :
172 : Default-constructed ranges have
173 : zero elements.
174 :
175 : @par Exception Safety
176 : Throws nothing.
177 : */
178 : range() noexcept;
179 :
180 : /** Constructor
181 :
182 : The new range references the
183 : same underlying character buffer.
184 : Ownership is not transferred; the
185 : caller is responsible for ensuring
186 : that the lifetime of the buffer
187 : extends until it is no longer
188 : referenced. The moved-from object
189 : becomes as if default-constructed.
190 :
191 : @par Exception Safety
192 : Throws nothing.
193 : */
194 : range(range&&) noexcept;
195 :
196 : /** Constructor
197 :
198 : The copy references the same
199 : underlying character buffer.
200 : Ownership is not transferred; the
201 : caller is responsible for ensuring
202 : that the lifetime of the buffer
203 : extends until it is no longer
204 : referenced.
205 :
206 : @par Exception Safety
207 : Throws nothing.
208 : */
209 : range(range const&) noexcept;
210 :
211 : /** Assignment
212 :
213 : After the move, this references the
214 : same underlying character buffer. Ownership
215 : is not transferred; the caller is responsible
216 : for ensuring that the lifetime of the buffer
217 : extends until it is no longer referenced.
218 : The moved-from object becomes as if
219 : default-constructed.
220 :
221 : @par Exception Safety
222 : Throws nothing.
223 :
224 : @return `*this`
225 : */
226 : range&
227 : operator=(range&&) noexcept;
228 :
229 : /** Assignment
230 :
231 : The copy references the same
232 : underlying character buffer.
233 : Ownership is not transferred; the
234 : caller is responsible for ensuring
235 : that the lifetime of the buffer
236 : extends until it is no longer
237 : referenced.
238 :
239 : @par Exception Safety
240 : Throws nothing.
241 :
242 : @return `*this`
243 : */
244 : range&
245 : operator=(range const&) noexcept;
246 :
247 : /** Return an iterator to the beginning
248 :
249 : @return An iterator to the first element
250 : */
251 : iterator begin() const noexcept;
252 :
253 : /** Return an iterator to the end
254 :
255 : @return An iterator to one past the last element
256 : */
257 : iterator end() const noexcept;
258 :
259 : /** Return true if the range is empty
260 :
261 : @return `true` if the range is empty
262 : */
263 : bool
264 11 : empty() const noexcept
265 : {
266 11 : return n_ == 0;
267 : }
268 :
269 : /** Return the number of elements in the range
270 :
271 : @return The number of elements
272 : */
273 : std::size_t
274 34 : size() const noexcept
275 : {
276 34 : return n_;
277 : }
278 :
279 : /** Return the matching part of the string
280 :
281 : @return A string view representing the range
282 : */
283 : core::string_view
284 19 : string() const noexcept
285 : {
286 19 : return s_;
287 : }
288 : };
289 :
290 : //------------------------------------------------
291 :
292 : namespace implementation_defined {
293 : template<
294 : class R0,
295 : class R1 = void>
296 : struct range_rule_t;
297 : }
298 :
299 : //------------------------------------------------
300 :
301 : namespace implementation_defined {
302 : template<class R>
303 : struct range_rule_t<R>
304 : {
305 : using value_type =
306 : range<typename R::value_type>;
307 :
308 : system::result<value_type>
309 : parse(
310 : char const*& it,
311 : char const* end) const;
312 :
313 : constexpr
314 18 : range_rule_t(
315 : R const& next,
316 : std::size_t N,
317 : std::size_t M) noexcept
318 18 : : next_(next)
319 18 : , N_(N)
320 18 : , M_(M)
321 : {
322 18 : }
323 :
324 : private:
325 : R const next_;
326 : std::size_t N_;
327 : std::size_t M_;
328 : };
329 : } // implementation_defined
330 :
331 : /** Match a repeating number of elements
332 :
333 : Elements are matched using the passed rule.
334 : <br>
335 : Normally when the rule returns an error,
336 : the range ends and the input is rewound to
337 : one past the last character that matched
338 : successfully. However, if the rule returns
339 : the special value @ref error::end_of_range, the
340 : input is not rewound. This allows for rules
341 : which consume input without producing
342 : elements in the range. For example, to
343 : relax the grammar for a comma-delimited
344 : list by allowing extra commas in between
345 : elements.
346 :
347 : @par Value Type
348 : @code
349 : using value_type = range< typename Rule::value_type >;
350 : @endcode
351 :
352 : @par Example
353 : Rules are used with the function @ref parse.
354 : @code
355 : // range = 1*( ";" token )
356 :
357 : system::result< range<core::string_view> > rv = parse( ";alpha;xray;charlie",
358 : range_rule(
359 : tuple_rule(
360 : squelch( delim_rule( ';' ) ),
361 : token_rule( alpha_chars ) ),
362 : 1 ) );
363 : @endcode
364 :
365 : @par BNF
366 : @code
367 : range = <N>*<M>next
368 : @endcode
369 :
370 : @par Specification
371 : @li <a href="https://datatracker.ietf.org/doc/html/rfc5234#section-3.6"
372 : >3.6. Variable Repetition (rfc5234)</a>
373 :
374 : @param next The rule to use for matching
375 : each element. The range extends until this
376 : rule returns an error.
377 :
378 : @param N The minimum number of elements for
379 : the range to be valid. If omitted, this
380 : defaults to zero.
381 :
382 : @param M The maximum number of elements for
383 : the range to be valid. If omitted, this
384 : defaults to unlimited.
385 :
386 : @return A rule that matches the range.
387 :
388 : @see
389 : @ref alpha_chars,
390 : @ref delim_rule,
391 : @ref error::end_of_range,
392 : @ref parse,
393 : @ref range,
394 : @ref tuple_rule,
395 : @ref squelch.
396 : */
397 : template<BOOST_URL_CONSTRAINT(Rule) R>
398 : constexpr
399 : implementation_defined::range_rule_t<R>
400 18 : range_rule(
401 : R const& next,
402 : std::size_t N = 0,
403 : std::size_t M =
404 : std::size_t(-1)) noexcept
405 : {
406 : // If you get a compile error here it
407 : // means that your rule does not meet
408 : // the type requirements. Please check
409 : // the documentation.
410 : static_assert(
411 : is_rule<R>::value,
412 : "Rule requirements not met");
413 :
414 : return implementation_defined::range_rule_t<R>{
415 18 : next, N, M};
416 : }
417 :
418 : //------------------------------------------------
419 :
420 : namespace implementation_defined {
421 : template<class R0, class R1>
422 : struct range_rule_t
423 : {
424 : using value_type =
425 : range<typename R0::value_type>;
426 :
427 : system::result<value_type>
428 : parse(
429 : char const*& it,
430 : char const* end) const;
431 :
432 : constexpr
433 1 : range_rule_t(
434 : R0 const& first,
435 : R1 const& next,
436 : std::size_t N,
437 : std::size_t M) noexcept
438 1 : : first_(first)
439 1 : , next_(next)
440 1 : , N_(N)
441 1 : , M_(M)
442 : {
443 1 : }
444 :
445 : private:
446 : R0 const first_;
447 : R1 const next_;
448 : std::size_t N_;
449 : std::size_t M_;
450 : };
451 : } // implementation_defined
452 :
453 : /** Match a repeating number of elements
454 :
455 : Two rules are used for match. The rule
456 : `first` is used for matching the first
457 : element, while the `next` rule is used
458 : to match every subsequent element.
459 : <br>
460 : Normally when the rule returns an error,
461 : the range ends and the input is rewound to
462 : one past the last character that matched
463 : successfully. However, if the rule returns
464 : the special value @ref error::end_of_range, the
465 : input is not rewound. This allows for rules
466 : which consume input without producing
467 : elements in the range. For example, to
468 : relax the grammar for a comma-delimited
469 : list by allowing extra commas in between
470 : elements.
471 :
472 : @par Value Type
473 : @code
474 : using value_type = range< typename Rule::value_type >;
475 : @endcode
476 :
477 : @par Example
478 : Rules are used with the function @ref parse.
479 : @code
480 : // range = [ token ] *( "," token )
481 :
482 : system::result< range< core::string_view > > rv = parse( "whiskey,tango,foxtrot",
483 : range_rule(
484 : token_rule( alpha_chars ), // first
485 : tuple_rule( // next
486 : squelch( delim_rule(',') ),
487 : token_rule( alpha_chars ) ) ) );
488 : @endcode
489 :
490 : @par BNF
491 : @code
492 : range = <1>*<1>first
493 : / first <N-1>*<M-1>next
494 : @endcode
495 :
496 : @par Specification
497 : @li <a href="https://datatracker.ietf.org/doc/html/rfc5234#section-3.6"
498 : >3.6. Variable Repetition (rfc5234)</a>
499 :
500 : @param first The rule to use for matching
501 : the first element. If this rule returns
502 : an error, the range is empty.
503 :
504 : @param next The rule to use for matching
505 : each subsequent element. The range extends
506 : until this rule returns an error.
507 :
508 : @param N The minimum number of elements for
509 : the range to be valid. If omitted, this
510 : defaults to zero.
511 :
512 : @param M The maximum number of elements for
513 : the range to be valid. If omitted, this
514 : defaults to unlimited.
515 :
516 : @return A rule that matches the range.
517 :
518 : @see
519 : @ref alpha_chars,
520 : @ref delim_rule,
521 : @ref error::end_of_range,
522 : @ref parse,
523 : @ref range,
524 : @ref tuple_rule,
525 : @ref squelch.
526 : */
527 : template<
528 : BOOST_URL_CONSTRAINT(Rule) R1,
529 : BOOST_URL_CONSTRAINT(Rule) R2>
530 : constexpr
531 : auto
532 1 : range_rule(
533 : R1 const& first,
534 : R2 const& next,
535 : std::size_t N = 0,
536 : std::size_t M =
537 : std::size_t(-1)) noexcept ->
538 : #if 1
539 : typename std::enable_if<
540 : ! std::is_integral<R2>::value,
541 : implementation_defined::range_rule_t<R1, R2>>::type
542 : #else
543 : range_rule_t<R1, R2>
544 : #endif
545 : {
546 : // If you get a compile error here it
547 : // means that your rule does not meet
548 : // the type requirements. Please check
549 : // the documentation.
550 : static_assert(
551 : is_rule<R1>::value,
552 : "Rule requirements not met");
553 : static_assert(
554 : is_rule<R2>::value,
555 : "Rule requirements not met");
556 :
557 : // If you get a compile error here it
558 : // means that your rules do not have
559 : // the exact same value_type. Please
560 : // check the documentation.
561 : static_assert(
562 : std::is_same<
563 : typename R1::value_type,
564 : typename R2::value_type>::value,
565 : "Rule requirements not met");
566 :
567 : return implementation_defined::range_rule_t<R1, R2>{
568 1 : first, next, N, M};
569 : }
570 :
571 : } // grammar
572 : } // urls
573 : } // boost
574 :
575 : #include <boost/url/grammar/impl/range_rule.hpp>
576 :
577 : #endif
|