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/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 : range&
225 : operator=(range&&) noexcept;
226 :
227 : /** Assignment
228 :
229 : The copy references the same
230 : underlying character buffer.
231 : Ownership is not transferred; the
232 : caller is responsible for ensuring
233 : that the lifetime of the buffer
234 : extends until it is no longer
235 : referenced.
236 :
237 : @par Exception Safety
238 : Throws nothing.
239 : */
240 : range&
241 : operator=(range const&) noexcept;
242 :
243 : /** Return an iterator to the beginning
244 : */
245 : iterator begin() const noexcept;
246 :
247 : /** Return an iterator to the end
248 : */
249 : iterator end() const noexcept;
250 :
251 : /** Return true if the range is empty
252 : */
253 : bool
254 11 : empty() const noexcept
255 : {
256 11 : return n_ == 0;
257 : }
258 :
259 : /** Return the number of elements in the range
260 : */
261 : std::size_t
262 34 : size() const noexcept
263 : {
264 34 : return n_;
265 : }
266 :
267 : /** Return the matching part of the string
268 : */
269 : core::string_view
270 19 : string() const noexcept
271 : {
272 19 : return s_;
273 : }
274 : };
275 :
276 : //------------------------------------------------
277 :
278 : #ifndef BOOST_URL_DOCS
279 : namespace implementation_defined {
280 : template<
281 : class R0,
282 : class R1 = void>
283 : struct range_rule_t;
284 : }
285 : #endif
286 :
287 : //------------------------------------------------
288 :
289 : /** Match a repeating number of elements
290 :
291 : Elements are matched using the passed rule.
292 : <br>
293 : Normally when the rule returns an error,
294 : the range ends and the input is rewound to
295 : one past the last character that matched
296 : successfully. However, if the rule returns
297 : the special value @ref error::end_of_range, the
298 : input is not rewound. This allows for rules
299 : which consume input without producing
300 : elements in the range. For example, to
301 : relax the grammar for a comma-delimited
302 : list by allowing extra commas in between
303 : elements.
304 :
305 : @par Value Type
306 : @code
307 : using value_type = range< typename Rule::value_type >;
308 : @endcode
309 :
310 : @par Example
311 : Rules are used with the function @ref parse.
312 : @code
313 : // range = 1*( ";" token )
314 :
315 : system::result< range<core::string_view> > rv = parse( ";alpha;xray;charlie",
316 : range_rule(
317 : tuple_rule(
318 : squelch( delim_rule( ';' ) ),
319 : token_rule( alpha_chars ) ),
320 : 1 ) );
321 : @endcode
322 :
323 : @par BNF
324 : @code
325 : range = <N>*<M>next
326 : @endcode
327 :
328 : @par Specification
329 : @li <a href="https://datatracker.ietf.org/doc/html/rfc5234#section-3.6"
330 : >3.6. Variable Repetition (rfc5234)</a>
331 :
332 : @param next The rule to use for matching
333 : each element. The range extends until this
334 : rule returns an error.
335 :
336 : @param N The minimum number of elements for
337 : the range to be valid. If omitted, this
338 : defaults to zero.
339 :
340 : @param M The maximum number of elements for
341 : the range to be valid. If omitted, this
342 : defaults to unlimited.
343 :
344 : @see
345 : @ref alpha_chars,
346 : @ref delim_rule,
347 : @ref error::end_of_range,
348 : @ref parse,
349 : @ref range,
350 : @ref tuple_rule,
351 : @ref squelch.
352 : */
353 : #ifdef BOOST_URL_DOCS
354 : template<class Rule>
355 : constexpr
356 : __implementation_defined__
357 : range_rule(
358 : Rule next,
359 : std::size_t N = 0,
360 : std::size_t M =
361 : std::size_t(-1)) noexcept;
362 : #else
363 : namespace implementation_defined {
364 : template<class R>
365 : struct range_rule_t<R>
366 : {
367 : using value_type =
368 : range<typename R::value_type>;
369 :
370 : system::result<value_type>
371 : parse(
372 : char const*& it,
373 : char const* end) const;
374 :
375 : constexpr
376 18 : range_rule_t(
377 : R const& next,
378 : std::size_t N,
379 : std::size_t M) noexcept
380 18 : : next_(next)
381 18 : , N_(N)
382 18 : , M_(M)
383 : {
384 18 : }
385 :
386 : private:
387 : R const next_;
388 : std::size_t N_;
389 : std::size_t M_;
390 : };
391 : } // implementation_defined
392 :
393 : /** Match a repeating number of elements
394 :
395 : Elements are matched using the passed rule.
396 : <br>
397 : Normally when the rule returns an error,
398 : the range ends and the input is rewound to
399 : one past the last character that matched
400 : successfully. However, if the rule returns
401 : the special value @ref error::end_of_range, the
402 : input is not rewound. This allows for rules
403 : which consume input without producing
404 : elements in the range. For example, to
405 : relax the grammar for a comma-delimited
406 : list by allowing extra commas in between
407 : elements.
408 :
409 : @par Value Type
410 : @code
411 : using value_type = range< typename Rule::value_type >;
412 : @endcode
413 :
414 : @par Example
415 : Rules are used with the function @ref parse.
416 : @code
417 : // range = 1*( ";" token )
418 :
419 : system::result< range<core::string_view> > rv = parse( ";alpha;xray;charlie",
420 : range_rule(
421 : tuple_rule(
422 : squelch( delim_rule( ';' ) ),
423 : token_rule( alpha_chars ) ),
424 : 1 ) );
425 : @endcode
426 :
427 : @par BNF
428 : @code
429 : range = <N>*<M>next
430 : @endcode
431 :
432 : @par Specification
433 : @li <a href="https://datatracker.ietf.org/doc/html/rfc5234#section-3.6"
434 : >3.6. Variable Repetition (rfc5234)</a>
435 :
436 : @param next The rule to use for matching
437 : each element. The range extends until this
438 : rule returns an error.
439 :
440 : @param N The minimum number of elements for
441 : the range to be valid. If omitted, this
442 : defaults to zero.
443 :
444 : @param M The maximum number of elements for
445 : the range to be valid. If omitted, this
446 : defaults to unlimited.
447 :
448 : @see
449 : @ref alpha_chars,
450 : @ref delim_rule,
451 : @ref error::end_of_range,
452 : @ref parse,
453 : @ref range,
454 : @ref tuple_rule,
455 : @ref squelch.
456 : */
457 : template<class Rule>
458 : constexpr
459 : implementation_defined::range_rule_t<Rule>
460 18 : range_rule(
461 : Rule const& next,
462 : std::size_t N = 0,
463 : std::size_t M =
464 : std::size_t(-1)) noexcept
465 : {
466 : // If you get a compile error here it
467 : // means that your rule does not meet
468 : // the type requirements. Please check
469 : // the documentation.
470 : static_assert(
471 : is_rule<Rule>::value,
472 : "Rule requirements not met");
473 :
474 : return implementation_defined::range_rule_t<Rule>{
475 18 : next, N, M};
476 : }
477 : #endif
478 :
479 : //------------------------------------------------
480 :
481 : /** Match a repeating number of elements
482 :
483 : Two rules are used for match. The rule
484 : `first` is used for matching the first
485 : element, while the `next` rule is used
486 : to match every subsequent element.
487 : <br>
488 : Normally when the rule returns an error,
489 : the range ends and the input is rewound to
490 : one past the last character that matched
491 : successfully. However, if the rule returns
492 : the special value @ref error::end_of_range, the
493 : input is not rewound. This allows for rules
494 : which consume input without producing
495 : elements in the range. For example, to
496 : relax the grammar for a comma-delimited
497 : list by allowing extra commas in between
498 : elements.
499 :
500 : @par Value Type
501 : @code
502 : using value_type = range< typename Rule::value_type >;
503 : @endcode
504 :
505 : @par Example
506 : Rules are used with the function @ref parse.
507 : @code
508 : // range = [ token ] *( "," token )
509 :
510 : system::result< range< core::string_view > > rv = parse( "whiskey,tango,foxtrot",
511 : range_rule(
512 : token_rule( alpha_chars ), // first
513 : tuple_rule( // next
514 : squelch( delim_rule(',') ),
515 : token_rule( alpha_chars ) ) ) );
516 : @endcode
517 :
518 : @par BNF
519 : @code
520 : range = <1>*<1>first
521 : / first <N-1>*<M-1>next
522 : @endcode
523 :
524 : @par Specification
525 : @li <a href="https://datatracker.ietf.org/doc/html/rfc5234#section-3.6"
526 : >3.6. Variable Repetition (rfc5234)</a>
527 :
528 : @param first The rule to use for matching
529 : the first element. If this rule returns
530 : an error, the range is empty.
531 :
532 : @param next The rule to use for matching
533 : each subsequent element. The range extends
534 : until this rule returns an error.
535 :
536 : @param N The minimum number of elements for
537 : the range to be valid. If omitted, this
538 : defaults to zero.
539 :
540 : @param M The maximum number of elements for
541 : the range to be valid. If omitted, this
542 : defaults to unlimited.
543 :
544 : @see
545 : @ref alpha_chars,
546 : @ref delim_rule,
547 : @ref error::end_of_range,
548 : @ref parse,
549 : @ref range,
550 : @ref tuple_rule,
551 : @ref squelch.
552 : */
553 : #ifdef BOOST_URL_DOCS
554 : template<
555 : class Rule1, class Rule2>
556 : constexpr
557 : __implementation_defined__
558 : range_rule(
559 : Rule1 first,
560 : Rule2 next,
561 : std::size_t N = 0,
562 : std::size_t M =
563 : std::size_t(-1)) noexcept;
564 : #else
565 : namespace implementation_defined {
566 : template<class R0, class R1>
567 : struct range_rule_t
568 : {
569 : using value_type =
570 : range<typename R0::value_type>;
571 :
572 : system::result<value_type>
573 : parse(
574 : char const*& it,
575 : char const* end) const;
576 :
577 : constexpr
578 1 : range_rule_t(
579 : R0 const& first,
580 : R1 const& next,
581 : std::size_t N,
582 : std::size_t M) noexcept
583 1 : : first_(first)
584 1 : , next_(next)
585 1 : , N_(N)
586 1 : , M_(M)
587 : {
588 1 : }
589 :
590 : private:
591 : R0 const first_;
592 : R1 const next_;
593 : std::size_t N_;
594 : std::size_t M_;
595 : };
596 : } // implementation_defined
597 :
598 : /** Match a repeating number of elements
599 :
600 : Two rules are used for match. The rule
601 : `first` is used for matching the first
602 : element, while the `next` rule is used
603 : to match every subsequent element.
604 : <br>
605 : Normally when the rule returns an error,
606 : the range ends and the input is rewound to
607 : one past the last character that matched
608 : successfully. However, if the rule returns
609 : the special value @ref error::end_of_range, the
610 : input is not rewound. This allows for rules
611 : which consume input without producing
612 : elements in the range. For example, to
613 : relax the grammar for a comma-delimited
614 : list by allowing extra commas in between
615 : elements.
616 :
617 : @par Value Type
618 : @code
619 : using value_type = range< typename Rule::value_type >;
620 : @endcode
621 :
622 : @par Example
623 : Rules are used with the function @ref parse.
624 : @code
625 : // range = [ token ] *( "," token )
626 :
627 : system::result< range< core::string_view > > rv = parse( "whiskey,tango,foxtrot",
628 : range_rule(
629 : token_rule( alpha_chars ), // first
630 : tuple_rule( // next
631 : squelch( delim_rule(',') ),
632 : token_rule( alpha_chars ) ) ) );
633 : @endcode
634 :
635 : @par BNF
636 : @code
637 : range = <1>*<1>first
638 : / first <N-1>*<M-1>next
639 : @endcode
640 :
641 : @par Specification
642 : @li <a href="https://datatracker.ietf.org/doc/html/rfc5234#section-3.6"
643 : >3.6. Variable Repetition (rfc5234)</a>
644 :
645 : @param first The rule to use for matching
646 : the first element. If this rule returns
647 : an error, the range is empty.
648 :
649 : @param next The rule to use for matching
650 : each subsequent element. The range extends
651 : until this rule returns an error.
652 :
653 : @param N The minimum number of elements for
654 : the range to be valid. If omitted, this
655 : defaults to zero.
656 :
657 : @param M The maximum number of elements for
658 : the range to be valid. If omitted, this
659 : defaults to unlimited.
660 :
661 : @see
662 : @ref alpha_chars,
663 : @ref delim_rule,
664 : @ref error::end_of_range,
665 : @ref parse,
666 : @ref range,
667 : @ref tuple_rule,
668 : @ref squelch.
669 : */
670 : template<
671 : class Rule1, class Rule2>
672 : constexpr
673 : auto
674 1 : range_rule(
675 : Rule1 const& first,
676 : Rule2 const& next,
677 : std::size_t N = 0,
678 : std::size_t M =
679 : std::size_t(-1)) noexcept ->
680 : #if 1
681 : typename std::enable_if<
682 : ! std::is_integral<Rule2>::value,
683 : implementation_defined::range_rule_t<Rule1, Rule2>>::type
684 : #else
685 : range_rule_t<Rule1, Rule2>
686 : #endif
687 : {
688 : // If you get a compile error here it
689 : // means that your rule does not meet
690 : // the type requirements. Please check
691 : // the documentation.
692 : static_assert(
693 : is_rule<Rule1>::value,
694 : "Rule requirements not met");
695 : static_assert(
696 : is_rule<Rule2>::value,
697 : "Rule requirements not met");
698 :
699 : // If you get a compile error here it
700 : // means that your rules do not have
701 : // the exact same value_type. Please
702 : // check the documentation.
703 : static_assert(
704 : std::is_same<
705 : typename Rule1::value_type,
706 : typename Rule2::value_type>::value,
707 : "Rule requirements not met");
708 :
709 : return implementation_defined::range_rule_t<Rule1, Rule2>{
710 1 : first, next, N, M};
711 : }
712 : #endif
713 :
714 : } // grammar
715 : } // urls
716 : } // boost
717 :
718 : #include <boost/url/grammar/impl/range_rule.hpp>
719 :
720 : #endif
|