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