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 : #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 : range&
225 : operator=(range&&) noexcept;
226 :
227 : /** Assignment
228 :
229 : The copy references the same
230 : underlying character buffer.
231 : Ownership is not transferred; the
232 : caller is responsible for ensuring
233 : that the lifetime of the buffer
234 : extends until it is no longer
235 : referenced.
236 :
237 : @par Exception Safety
238 : Throws nothing.
239 : */
240 : range&
241 : operator=(range const&) noexcept;
242 :
243 : /** Return an iterator to the beginning
244 : */
245 : iterator begin() const noexcept;
246 :
247 : /** Return an iterator to the end
248 : */
249 : iterator end() const noexcept;
250 :
251 : /** Return true if the range is empty
252 : */
253 : bool
254 11 : empty() const noexcept
255 : {
256 11 : return n_ == 0;
257 : }
258 :
259 : /** Return the number of elements in the range
260 : */
261 : std::size_t
262 34 : size() const noexcept
263 : {
264 34 : return n_;
265 : }
266 :
267 : /** Return the matching part of the string
268 : */
269 : core::string_view
270 19 : string() const noexcept
271 : {
272 19 : return s_;
273 : }
274 : };
275 :
276 : //------------------------------------------------
277 :
278 : namespace implementation_defined {
279 : template<
280 : class R0,
281 : class R1 = void>
282 : struct range_rule_t;
283 : }
284 :
285 : //------------------------------------------------
286 :
287 : namespace implementation_defined {
288 : template<class R>
289 : struct range_rule_t<R>
290 : {
291 : using value_type =
292 : range<typename R::value_type>;
293 :
294 : system::result<value_type>
295 : parse(
296 : char const*& it,
297 : char const* end) const;
298 :
299 : constexpr
300 18 : range_rule_t(
301 : R const& next,
302 : std::size_t N,
303 : std::size_t M) noexcept
304 18 : : next_(next)
305 18 : , N_(N)
306 18 : , M_(M)
307 : {
308 18 : }
309 :
310 : private:
311 : R const next_;
312 : std::size_t N_;
313 : std::size_t M_;
314 : };
315 : } // implementation_defined
316 :
317 : /** Match a repeating number of elements
318 :
319 : Elements are matched using the passed rule.
320 : <br>
321 : Normally when the rule returns an error,
322 : the range ends and the input is rewound to
323 : one past the last character that matched
324 : successfully. However, if the rule returns
325 : the special value @ref error::end_of_range, the
326 : input is not rewound. This allows for rules
327 : which consume input without producing
328 : elements in the range. For example, to
329 : relax the grammar for a comma-delimited
330 : list by allowing extra commas in between
331 : elements.
332 :
333 : @par Value Type
334 : @code
335 : using value_type = range< typename Rule::value_type >;
336 : @endcode
337 :
338 : @par Example
339 : Rules are used with the function @ref parse.
340 : @code
341 : // range = 1*( ";" token )
342 :
343 : system::result< range<core::string_view> > rv = parse( ";alpha;xray;charlie",
344 : range_rule(
345 : tuple_rule(
346 : squelch( delim_rule( ';' ) ),
347 : token_rule( alpha_chars ) ),
348 : 1 ) );
349 : @endcode
350 :
351 : @par BNF
352 : @code
353 : range = <N>*<M>next
354 : @endcode
355 :
356 : @par Specification
357 : @li <a href="https://datatracker.ietf.org/doc/html/rfc5234#section-3.6"
358 : >3.6. Variable Repetition (rfc5234)</a>
359 :
360 : @param next The rule to use for matching
361 : each element. The range extends until this
362 : rule returns an error.
363 :
364 : @param N The minimum number of elements for
365 : the range to be valid. If omitted, this
366 : defaults to zero.
367 :
368 : @param M The maximum number of elements for
369 : the range to be valid. If omitted, this
370 : defaults to unlimited.
371 :
372 : @see
373 : @ref alpha_chars,
374 : @ref delim_rule,
375 : @ref error::end_of_range,
376 : @ref parse,
377 : @ref range,
378 : @ref tuple_rule,
379 : @ref squelch.
380 : */
381 : template<BOOST_URL_CONSTRAINT(Rule) R>
382 : constexpr
383 : implementation_defined::range_rule_t<R>
384 18 : range_rule(
385 : R const& next,
386 : std::size_t N = 0,
387 : std::size_t M =
388 : std::size_t(-1)) noexcept
389 : {
390 : // If you get a compile error here it
391 : // means that your rule does not meet
392 : // the type requirements. Please check
393 : // the documentation.
394 : static_assert(
395 : is_rule<R>::value,
396 : "Rule requirements not met");
397 :
398 : return implementation_defined::range_rule_t<R>{
399 18 : next, N, M};
400 : }
401 :
402 : //------------------------------------------------
403 :
404 : namespace implementation_defined {
405 : template<class R0, class R1>
406 : struct range_rule_t
407 : {
408 : using value_type =
409 : range<typename R0::value_type>;
410 :
411 : system::result<value_type>
412 : parse(
413 : char const*& it,
414 : char const* end) const;
415 :
416 : constexpr
417 1 : range_rule_t(
418 : R0 const& first,
419 : R1 const& next,
420 : std::size_t N,
421 : std::size_t M) noexcept
422 1 : : first_(first)
423 1 : , next_(next)
424 1 : , N_(N)
425 1 : , M_(M)
426 : {
427 1 : }
428 :
429 : private:
430 : R0 const first_;
431 : R1 const next_;
432 : std::size_t N_;
433 : std::size_t M_;
434 : };
435 : } // implementation_defined
436 :
437 : /** Match a repeating number of elements
438 :
439 : Two rules are used for match. The rule
440 : `first` is used for matching the first
441 : element, while the `next` rule is used
442 : to match every subsequent element.
443 : <br>
444 : Normally when the rule returns an error,
445 : the range ends and the input is rewound to
446 : one past the last character that matched
447 : successfully. However, if the rule returns
448 : the special value @ref error::end_of_range, the
449 : input is not rewound. This allows for rules
450 : which consume input without producing
451 : elements in the range. For example, to
452 : relax the grammar for a comma-delimited
453 : list by allowing extra commas in between
454 : elements.
455 :
456 : @par Value Type
457 : @code
458 : using value_type = range< typename Rule::value_type >;
459 : @endcode
460 :
461 : @par Example
462 : Rules are used with the function @ref parse.
463 : @code
464 : // range = [ token ] *( "," token )
465 :
466 : system::result< range< core::string_view > > rv = parse( "whiskey,tango,foxtrot",
467 : range_rule(
468 : token_rule( alpha_chars ), // first
469 : tuple_rule( // next
470 : squelch( delim_rule(',') ),
471 : token_rule( alpha_chars ) ) ) );
472 : @endcode
473 :
474 : @par BNF
475 : @code
476 : range = <1>*<1>first
477 : / first <N-1>*<M-1>next
478 : @endcode
479 :
480 : @par Specification
481 : @li <a href="https://datatracker.ietf.org/doc/html/rfc5234#section-3.6"
482 : >3.6. Variable Repetition (rfc5234)</a>
483 :
484 : @param first The rule to use for matching
485 : the first element. If this rule returns
486 : an error, the range is empty.
487 :
488 : @param next The rule to use for matching
489 : each subsequent element. The range extends
490 : until this rule returns an error.
491 :
492 : @param N The minimum number of elements for
493 : the range to be valid. If omitted, this
494 : defaults to zero.
495 :
496 : @param M The maximum number of elements for
497 : the range to be valid. If omitted, this
498 : defaults to unlimited.
499 :
500 : @see
501 : @ref alpha_chars,
502 : @ref delim_rule,
503 : @ref error::end_of_range,
504 : @ref parse,
505 : @ref range,
506 : @ref tuple_rule,
507 : @ref squelch.
508 : */
509 : template<
510 : BOOST_URL_CONSTRAINT(Rule) R1,
511 : BOOST_URL_CONSTRAINT(Rule) R2>
512 : constexpr
513 : auto
514 1 : range_rule(
515 : R1 const& first,
516 : R2 const& next,
517 : std::size_t N = 0,
518 : std::size_t M =
519 : std::size_t(-1)) noexcept ->
520 : #if 1
521 : typename std::enable_if<
522 : ! std::is_integral<R2>::value,
523 : implementation_defined::range_rule_t<R1, R2>>::type
524 : #else
525 : range_rule_t<R1, R2>
526 : #endif
527 : {
528 : // If you get a compile error here it
529 : // means that your rule does not meet
530 : // the type requirements. Please check
531 : // the documentation.
532 : static_assert(
533 : is_rule<R1>::value,
534 : "Rule requirements not met");
535 : static_assert(
536 : is_rule<R2>::value,
537 : "Rule requirements not met");
538 :
539 : // If you get a compile error here it
540 : // means that your rules do not have
541 : // the exact same value_type. Please
542 : // check the documentation.
543 : static_assert(
544 : std::is_same<
545 : typename R1::value_type,
546 : typename R2::value_type>::value,
547 : "Rule requirements not met");
548 :
549 : return implementation_defined::range_rule_t<R1, R2>{
550 1 : first, next, N, M};
551 : }
552 :
553 : } // grammar
554 : } // urls
555 : } // boost
556 :
557 : #include <boost/url/grammar/impl/range_rule.hpp>
558 :
559 : #endif
|