GCC Code Coverage Report


Directory: libs/url/
File: include/boost/url/grammar/range_rule.hpp
Date: 2025-11-12 22:47:45
Exec Total Coverage
Lines: 29 29 100.0%
Functions: 21 21 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/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
578