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/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 2820 : void* addr() noexcept
148 : {
149 2820 : 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 42 : size() const noexcept
394 : {
395 42 : 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 19 : string() const noexcept
404 : {
405 19 : 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 : system::result<value_type>
428 : parse(
429 : char const*& it,
430 : char const* end) const;
431 :
432 : constexpr
433 18 : range_rule_t(
434 : R const& next,
435 : std::size_t N,
436 : std::size_t M) noexcept
437 18 : : next_(next)
438 18 : , N_(N)
439 18 : , M_(M)
440 : {
441 18 : }
442 :
443 : private:
444 : R const next_;
445 : std::size_t N_;
446 : std::size_t M_;
447 : };
448 : } // implementation_defined
449 :
450 : /** Match a repeating number of elements
451 :
452 : Elements are matched using the passed rule.
453 : <br>
454 : Normally when the rule returns an error,
455 : the range ends and the input is rewound to
456 : one past the last character that matched
457 : successfully. However, if the rule returns
458 : the special value @ref error::end_of_range, the
459 : input is not rewound. This allows for rules
460 : which consume input without producing
461 : elements in the range. For example, to
462 : relax the grammar for a comma-delimited
463 : list by allowing extra commas in between
464 : elements.
465 :
466 : @par Value Type
467 : @code
468 : using value_type = range< typename Rule::value_type >;
469 : @endcode
470 :
471 : @par Example
472 : Rules are used with the function @ref parse.
473 : @code
474 : // range = 1*( ";" token )
475 :
476 : system::result< range<core::string_view> > rv = parse( ";alpha;xray;charlie",
477 : range_rule(
478 : tuple_rule(
479 : squelch( delim_rule( ';' ) ),
480 : token_rule( alpha_chars ) ),
481 : 1 ) );
482 : @endcode
483 :
484 : @par BNF
485 : @code
486 : range = <N>*<M>next
487 : @endcode
488 :
489 : @par Specification
490 : @li <a href="https://datatracker.ietf.org/doc/html/rfc5234#section-3.6"
491 : >3.6. Variable Repetition (rfc5234)</a>
492 :
493 : @param next The rule to use for matching
494 : each element. The range extends until this
495 : rule returns an error.
496 :
497 : @param N The minimum number of elements for
498 : the range to be valid. If omitted, this
499 : defaults to zero.
500 :
501 : @param M The maximum number of elements for
502 : the range to be valid. If omitted, this
503 : defaults to unlimited.
504 :
505 : @return A rule that matches the range.
506 :
507 : @see
508 : @ref alpha_chars,
509 : @ref delim_rule,
510 : @ref error::end_of_range,
511 : @ref parse,
512 : @ref range,
513 : @ref tuple_rule,
514 : @ref squelch.
515 : */
516 : template<BOOST_URL_CONSTRAINT(Rule) R>
517 : constexpr
518 : implementation_defined::range_rule_t<R>
519 18 : range_rule(
520 : R const& next,
521 : std::size_t N = 0,
522 : std::size_t M =
523 : std::size_t(-1)) noexcept
524 : {
525 : // If you get a compile error here it
526 : // means that your rule does not meet
527 : // the type requirements. Please check
528 : // the documentation.
529 : static_assert(
530 : is_rule<R>::value,
531 : "Rule requirements not met");
532 :
533 : return implementation_defined::range_rule_t<R>{
534 18 : next, N, M};
535 : }
536 :
537 : //------------------------------------------------
538 :
539 : namespace implementation_defined {
540 : template<class R0, class R1>
541 : struct range_rule_t
542 : {
543 : using value_type =
544 : range<typename R0::value_type>;
545 :
546 : system::result<value_type>
547 : parse(
548 : char const*& it,
549 : char const* end) const;
550 :
551 : constexpr
552 1 : range_rule_t(
553 : R0 const& first,
554 : R1 const& next,
555 : std::size_t N,
556 : std::size_t M) noexcept
557 1 : : first_(first)
558 1 : , next_(next)
559 1 : , N_(N)
560 1 : , M_(M)
561 : {
562 1 : }
563 :
564 : private:
565 : R0 const first_;
566 : R1 const next_;
567 : std::size_t N_;
568 : std::size_t M_;
569 : };
570 : } // implementation_defined
571 :
572 : /** Match a repeating number of elements
573 :
574 : Two rules are used for match. The rule
575 : `first` is used for matching the first
576 : element, while the `next` rule is used
577 : to match every subsequent element.
578 : <br>
579 : Normally when the rule returns an error,
580 : the range ends and the input is rewound to
581 : one past the last character that matched
582 : successfully. However, if the rule returns
583 : the special value @ref error::end_of_range, the
584 : input is not rewound. This allows for rules
585 : which consume input without producing
586 : elements in the range. For example, to
587 : relax the grammar for a comma-delimited
588 : list by allowing extra commas in between
589 : elements.
590 :
591 : @par Value Type
592 : @code
593 : using value_type = range< typename Rule::value_type >;
594 : @endcode
595 :
596 : @par Example
597 : Rules are used with the function @ref parse.
598 : @code
599 : // range = [ token ] *( "," token )
600 :
601 : system::result< range< core::string_view > > rv = parse( "whiskey,tango,foxtrot",
602 : range_rule(
603 : token_rule( alpha_chars ), // first
604 : tuple_rule( // next
605 : squelch( delim_rule(',') ),
606 : token_rule( alpha_chars ) ) ) );
607 : @endcode
608 :
609 : @par BNF
610 : @code
611 : range = <1>*<1>first
612 : / first <N-1>*<M-1>next
613 : @endcode
614 :
615 : @par Specification
616 : @li <a href="https://datatracker.ietf.org/doc/html/rfc5234#section-3.6"
617 : >3.6. Variable Repetition (rfc5234)</a>
618 :
619 : @param first The rule to use for matching
620 : the first element. If this rule returns
621 : an error, the range is empty.
622 :
623 : @param next The rule to use for matching
624 : each subsequent element. The range extends
625 : until this rule returns an error.
626 :
627 : @param N The minimum number of elements for
628 : the range to be valid. If omitted, this
629 : defaults to zero.
630 :
631 : @param M The maximum number of elements for
632 : the range to be valid. If omitted, this
633 : defaults to unlimited.
634 :
635 : @return A rule that matches the range.
636 :
637 : @see
638 : @ref alpha_chars,
639 : @ref delim_rule,
640 : @ref error::end_of_range,
641 : @ref parse,
642 : @ref range,
643 : @ref tuple_rule,
644 : @ref squelch.
645 : */
646 : template<
647 : BOOST_URL_CONSTRAINT(Rule) R1,
648 : BOOST_URL_CONSTRAINT(Rule) R2>
649 : constexpr
650 : auto
651 1 : range_rule(
652 : R1 const& first,
653 : R2 const& next,
654 : std::size_t N = 0,
655 : std::size_t M =
656 : std::size_t(-1)) noexcept ->
657 : #if 1
658 : typename std::enable_if<
659 : ! std::is_integral<R2>::value,
660 : implementation_defined::range_rule_t<R1, R2>>::type
661 : #else
662 : range_rule_t<R1, R2>
663 : #endif
664 : {
665 : // If you get a compile error here it
666 : // means that your rule does not meet
667 : // the type requirements. Please check
668 : // the documentation.
669 : static_assert(
670 : is_rule<R1>::value,
671 : "Rule requirements not met");
672 : static_assert(
673 : is_rule<R2>::value,
674 : "Rule requirements not met");
675 :
676 : // If you get a compile error here it
677 : // means that your rules do not have
678 : // the exact same value_type. Please
679 : // check the documentation.
680 : static_assert(
681 : std::is_same<
682 : typename R1::value_type,
683 : typename R2::value_type>::value,
684 : "Rule requirements not met");
685 :
686 : return implementation_defined::range_rule_t<R1, R2>{
687 1 : first, next, N, M};
688 : }
689 :
690 : } // grammar
691 : } // urls
692 : } // boost
693 :
694 : #include <boost/url/grammar/impl/range_rule.hpp>
695 :
696 : #endif
|