GCC Code Coverage Report


Directory: libs/url/
File: include/boost/url/grammar/range_rule.hpp
Date: 2025-11-10 19:06:22
Exec Total Coverage
Lines: 25 25 100.0%
Functions: 16 16 100.0%
Branches: 0 0 -%

Line Branch Exec Source
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
697