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