include/boost/url/grammar/range_rule.hpp

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