Line data Source code
1 : //
2 : // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.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_DECODE_VIEW_HPP
11 : #define BOOST_URL_DECODE_VIEW_HPP
12 :
13 : #include <boost/url/detail/config.hpp>
14 : #include <boost/core/detail/string_view.hpp>
15 : #include <boost/url/encoding_opts.hpp>
16 : #include <boost/url/pct_string_view.hpp>
17 : #include <type_traits>
18 : #include <iterator>
19 : #include <iosfwd>
20 :
21 : namespace boost {
22 : namespace urls {
23 :
24 : //------------------------------------------------
25 :
26 : #ifndef BOOST_URL_DOCS
27 : class decode_view;
28 :
29 : namespace detail {
30 :
31 : // unchecked
32 : template<class... Args>
33 : decode_view
34 : make_decode_view(
35 : Args&&... args) noexcept;
36 :
37 : } // detail
38 : #endif
39 :
40 : //------------------------------------------------
41 :
42 : /** A reference to a valid, percent-encoded string
43 :
44 : These views reference strings in parts of URLs
45 : or other components that are percent-encoded.
46 : The special characters (those not in the
47 : allowed character set) are stored as three
48 : character escapes that consist of a percent
49 : sign ('%%') followed by a two-digit hexadecimal
50 : number of the corresponding unescaped character
51 : code, which may be part of a UTF-8 code point
52 : depending on the context.
53 :
54 : The view refers to the original character
55 : buffer and only decodes escaped sequences when
56 : needed. In particular these operations perform
57 : percent-decoding automatically without the
58 : need to allocate memory:
59 :
60 : @li Iteration of the string
61 : @li Accessing the encoded character buffer
62 : @li Comparison to encoded or plain strings
63 :
64 : These objects can only be constructed from
65 : strings that have a valid percent-encoding,
66 : otherwise construction fails. The caller is
67 : responsible for ensuring that the lifetime
68 : of the character buffer from which the view
69 : is constructed extends unmodified until the
70 : view is no longer accessed.
71 :
72 : @par Operators
73 : The following operators are supported between
74 : @ref decode_view and any object that is convertible
75 : to `core::string_view`
76 :
77 : @code
78 : bool operator==( decode_view, decode_view ) noexcept;
79 : bool operator!=( decode_view, decode_view ) noexcept;
80 : bool operator<=( decode_view, decode_view ) noexcept;
81 : bool operator< ( decode_view, decode_view ) noexcept;
82 : bool operator> ( decode_view, decode_view ) noexcept;
83 : bool operator>=( decode_view, decode_view ) noexcept;
84 : @endcode
85 :
86 : */
87 : class decode_view
88 : {
89 : char const* p_ = nullptr;
90 : std::size_t n_ = 0;
91 : std::size_t dn_ = 0;
92 : bool space_as_plus_ = true;
93 :
94 : #ifndef BOOST_URL_DOCS
95 : template<class... Args>
96 : friend
97 : decode_view
98 : detail::make_decode_view(
99 : Args&&... args) noexcept;
100 : #endif
101 :
102 : // unchecked
103 : BOOST_URL_DECL
104 : explicit
105 : decode_view(
106 : core::string_view s,
107 : std::size_t n,
108 : encoding_opts opt) noexcept;
109 :
110 : public:
111 : /** The value type
112 : */
113 : using value_type = char;
114 :
115 : /** The reference type
116 : */
117 : using reference = char;
118 :
119 : /// @copydoc reference
120 : using const_reference = char;
121 :
122 : /** The unsigned integer type
123 : */
124 : using size_type = std::size_t;
125 :
126 : /** The signed integer type
127 : */
128 : using difference_type = std::ptrdiff_t;
129 :
130 : /** An iterator of constant, decoded characters.
131 :
132 : This iterator is used to access the encoded
133 : string as a bidirectional range of characters
134 : with percent-decoding applied. Escape sequences
135 : are not decoded until the iterator is
136 : dereferenced.
137 : */
138 : #ifdef BOOST_URL_DOCS
139 : using iterator = __see_below__
140 : #else
141 :
142 : /** An iterator of constant, decoded characters.
143 :
144 : This iterator is used to access the encoded
145 : string as a bidirectional range of characters
146 : with percent-decoding applied. Escape sequences
147 : are not decoded until the iterator is
148 : dereferenced.
149 : */
150 : class iterator;
151 : #endif
152 :
153 : /// @copydoc iterator
154 : using const_iterator = iterator;
155 :
156 : //--------------------------------------------
157 : //
158 : // Special Members
159 : //
160 : //--------------------------------------------
161 :
162 : /** Constructor
163 :
164 : Default-constructed views represent
165 : empty strings.
166 :
167 : @par Example
168 : @code
169 : decode_view ds;
170 : @endcode
171 :
172 : @par Postconditions
173 : @code
174 : this->empty() == true
175 : @endcode
176 :
177 : @par Complexity
178 : Constant.
179 :
180 : @par Exception Safety
181 : Throws nothing.
182 : */
183 : decode_view() noexcept = default;
184 :
185 : /** Constructor
186 :
187 : This constructs a view from the character
188 : buffer `s`, which must remain valid and
189 : unmodified until the view is no longer
190 : accessed.
191 :
192 : @par Example
193 : @code
194 : decode_view ds( "Program%20Files" );
195 : @endcode
196 :
197 : @par Postconditions
198 : @code
199 : this->encoded() == s
200 : @endcode
201 :
202 : @par Complexity
203 : Linear in `s.size()`.
204 :
205 : @par Exception Safety
206 : Exceptions thrown on invalid input.
207 :
208 : @throw system_error
209 : The string contains an invalid percent encoding.
210 :
211 : @param s A percent-encoded string that has
212 : already been validated.
213 :
214 : @param opt The options for decoding. If
215 : this parameter is omitted, the default
216 : options are used.
217 : */
218 : explicit
219 3773 : decode_view(
220 : pct_string_view s,
221 : encoding_opts opt = {}) noexcept
222 3773 : : decode_view(
223 : detail::to_sv(s),
224 : s.decoded_size(),
225 3773 : opt)
226 : {
227 3773 : }
228 :
229 : //--------------------------------------------
230 : //
231 : // Observers
232 : //
233 : //--------------------------------------------
234 :
235 : /** Return true if the string is empty
236 :
237 : @par Example
238 : @code
239 : assert( decode_view( "" ).empty() );
240 : @endcode
241 :
242 : @par Complexity
243 : Constant.
244 :
245 : @par Exception Safety
246 : Throws nothing.
247 : */
248 : bool
249 15 : empty() const noexcept
250 : {
251 15 : return n_ == 0;
252 : }
253 :
254 : /** Return the number of decoded characters
255 :
256 : @par Example
257 : @code
258 : assert( decode_view( "Program%20Files" ).size() == 13 );
259 : @endcode
260 :
261 : @par Effects
262 : @code
263 : return std::distance( this->begin(), this->end() );
264 : @endcode
265 :
266 : @par Complexity
267 : Constant.
268 :
269 : @par Exception Safety
270 : Throws nothing.
271 : */
272 : size_type
273 3948 : size() const noexcept
274 : {
275 3948 : return dn_;
276 : }
277 :
278 : /** Return an iterator to the beginning
279 :
280 : @par Example
281 : @code
282 : auto it = this->begin();
283 : @endcode
284 :
285 : @par Complexity
286 : Constant.
287 :
288 : @par Exception Safety
289 : Throws nothing.
290 : */
291 : iterator
292 : begin() const noexcept;
293 :
294 : /** Return an iterator to the end
295 :
296 : @par Example
297 : @code
298 : auto it = this->end();
299 : @endcode
300 :
301 : @par Complexity
302 : Constant.
303 :
304 : @par Exception Safety
305 : Throws nothing.
306 : */
307 : iterator
308 : end() const noexcept;
309 :
310 : /** Return the first character
311 :
312 : @par Example
313 : @code
314 : assert( decode_view( "Program%20Files" ).front() == 'P' );
315 : @endcode
316 :
317 : @par Preconditions
318 : @code
319 : not this->empty()
320 : @endcode
321 :
322 : @par Complexity
323 : Constant.
324 :
325 : @par Exception Safety
326 : Throws nothing.
327 : */
328 : reference
329 : front() const noexcept;
330 :
331 : /** Return the last character
332 :
333 : @par Example
334 : @code
335 : assert( decode_view( "Program%20Files" ).back() == 's' );
336 : @endcode
337 :
338 : @par Preconditions
339 : @code
340 : not this->empty()
341 : @endcode
342 :
343 : @par Complexity
344 : Constant.
345 :
346 : @par Exception Safety
347 : Throws nothing.
348 : */
349 : reference
350 : back() const noexcept;
351 :
352 : /** Checks if the string begins with the given prefix
353 :
354 : @par Example
355 : @code
356 : assert( decode_view( "Program%20Files" ).starts_with("Program") );
357 : @endcode
358 :
359 : @par Complexity
360 : Linear.
361 :
362 : @par Exception Safety
363 : Throws nothing.
364 : */
365 : BOOST_URL_DECL
366 : bool
367 : starts_with( core::string_view s ) const noexcept;
368 :
369 : /** Checks if the string ends with the given prefix
370 :
371 : @par Example
372 : @code
373 : assert( decode_view( "Program%20Files" ).ends_with("Files") );
374 : @endcode
375 :
376 : @par Complexity
377 : Linear.
378 :
379 : @par Exception Safety
380 : Throws nothing.
381 : */
382 : BOOST_URL_DECL
383 : bool
384 : ends_with( core::string_view s ) const noexcept;
385 :
386 : /** Checks if the string begins with the given prefix
387 :
388 : @par Example
389 : @code
390 : assert( decode_view( "Program%20Files" ).starts_with('P') );
391 : @endcode
392 :
393 : @par Complexity
394 : Constant.
395 :
396 : @par Exception Safety
397 : Throws nothing.
398 : */
399 : BOOST_URL_DECL
400 : bool
401 : starts_with( char ch ) const noexcept;
402 :
403 : /** Checks if the string ends with the given prefix
404 :
405 : @par Example
406 : @code
407 : assert( decode_view( "Program%20Files" ).ends_with('s') );
408 : @endcode
409 :
410 : @par Complexity
411 : Constant.
412 :
413 : @par Exception Safety
414 : Throws nothing.
415 : */
416 : BOOST_URL_DECL
417 : bool
418 : ends_with( char ch ) const noexcept;
419 :
420 : /** Finds the first occurrence of character in this view
421 :
422 : @par Complexity
423 : Linear.
424 :
425 : @par Exception Safety
426 : Throws nothing.
427 : */
428 : BOOST_URL_DECL
429 : const_iterator
430 : find( char ch ) const noexcept;
431 :
432 : /** Finds the first occurrence of character in this view
433 :
434 : @par Complexity
435 : Linear.
436 :
437 : @par Exception Safety
438 : Throws nothing.
439 : */
440 : BOOST_URL_DECL
441 : const_iterator
442 : rfind( char ch ) const noexcept;
443 :
444 : /** Remove the first characters
445 :
446 : @par Example
447 : @code
448 : decode_view d( "Program%20Files" );
449 : d.remove_prefix( 8 );
450 : assert( d == "Files" );
451 : @endcode
452 :
453 : @par Preconditions
454 : @code
455 : not this->empty()
456 : @endcode
457 :
458 : @par Complexity
459 : Linear.
460 : */
461 : BOOST_URL_DECL
462 : void
463 : remove_prefix( size_type n );
464 :
465 : /** Remove the last characters
466 :
467 : @par Example
468 : @code
469 : decode_view d( "Program%20Files" );
470 : d.remove_prefix( 6 );
471 : assert( d == "Program" );
472 : @endcode
473 :
474 : @par Preconditions
475 : @code
476 : not this->empty()
477 : @endcode
478 :
479 : @par Complexity
480 : Linear.
481 : */
482 : BOOST_URL_DECL
483 : void
484 : remove_suffix( size_type n );
485 :
486 : /** Return the decoding options
487 : */
488 : encoding_opts
489 : options() const noexcept
490 : {
491 : encoding_opts opt;
492 : opt.space_as_plus = space_as_plus_;
493 : return opt;
494 : }
495 :
496 : //--------------------------------------------
497 : //
498 : // Comparison
499 : //
500 : //--------------------------------------------
501 :
502 : /** Return the result of comparing to another string
503 :
504 : The length of the sequences to compare is the smaller of
505 : `size()` and `other.size()`.
506 :
507 : The function compares the two strings as if by calling
508 : `char_traits<char>::compare(to_string().data(), v.data(), rlen)`.
509 : This means the comparison is performed with
510 : percent-decoding applied to the current string.
511 :
512 : @param other string to compare
513 :
514 : @return Negative value if this string is less than the other
515 : character sequence, zero if the both character sequences are
516 : equal, positive value if this string is greater than the other
517 : character sequence
518 : */
519 : BOOST_URL_DECL
520 : int
521 : compare(core::string_view other) const noexcept;
522 :
523 : /** Return the result of comparing to another string
524 :
525 : The length of the sequences to compare is the smaller of
526 : `size()` and `other.size()`.
527 :
528 : The function compares the two strings as if by calling
529 : `char_traits<char>::compare(to_string().data(), v.to_string().data(), rlen)`.
530 : This means the comparison is performed with
531 : percent-decoding applied to the current string.
532 :
533 : @param other string to compare
534 :
535 : @return Negative value if this string is less than the other
536 : character sequence, zero if the both character sequences are
537 : equal, positive value if this string is greater than the other
538 : character sequence
539 : */
540 : BOOST_URL_DECL
541 : int
542 : compare(decode_view other) const noexcept;
543 :
544 : //--------------------------------------------
545 :
546 : // relational operators
547 : #ifndef BOOST_URL_DOCS
548 : private:
549 : template<class S0, class S1>
550 : using is_match = std::integral_constant<bool,
551 : // both decode_view or convertible to core::string_view
552 : (
553 : std::is_same<typename std::decay<S0>::type, decode_view>::value ||
554 : std::is_convertible<S0, core::string_view>::value) &&
555 : (
556 : std::is_same<typename std::decay<S1>::type, decode_view>::value ||
557 : std::is_convertible<S1, core::string_view>::value) &&
558 : // not both are convertible to string view
559 : (
560 : !std::is_convertible<S0, core::string_view>::value ||
561 : !std::is_convertible<S1, core::string_view>::value)>;
562 :
563 : static
564 : int
565 316 : decode_compare(decode_view s0, decode_view s1) noexcept
566 : {
567 316 : return s0.compare(s1);
568 : }
569 :
570 : template <class S>
571 : static
572 : int
573 2926 : decode_compare(decode_view s0, S const& s1) noexcept
574 : {
575 2926 : return s0.compare(s1);
576 : }
577 :
578 : template <class S>
579 : static
580 : int
581 : decode_compare(S const& s0, decode_view s1) noexcept
582 : {
583 : return -s1.compare(s0);
584 : }
585 : public:
586 :
587 : /// Compare two decode views for equality
588 : /**
589 : * This function is only enabled if both types are
590 : * decode_view or convertible to `core::string_view`,
591 : * but not both are convertible to `core::string_view`
592 : */
593 : template<class S0, class S1>
594 2471 : BOOST_CXX14_CONSTEXPR friend auto operator==(
595 : S0 const& s0, S1 const& s1) noexcept ->
596 : typename std::enable_if<
597 : is_match<S0, S1>::value, bool>::type
598 : {
599 2471 : return decode_compare(s0, s1) == 0;
600 : }
601 :
602 : /// Compare two decode views for inequality
603 : /**
604 : * This function is only enabled if both types are
605 : * decode_view or convertible to `core::string_view`,
606 : * but not both are convertible to `core::string_view`
607 : */
608 : template<class S0, class S1>
609 739 : BOOST_CXX14_CONSTEXPR friend auto operator!=(
610 : S0 const& s0, S1 const& s1) noexcept ->
611 : typename std::enable_if<
612 : is_match<S0, S1>::value, bool>::type
613 : {
614 739 : return decode_compare(s0, s1) != 0;
615 : }
616 :
617 : /// Compare two decode views for less than
618 : /**
619 : * This function is only enabled if both types are
620 : * decode_view or convertible to `core::string_view`,
621 : * but not both are convertible to `core::string_view`
622 : */
623 : template<class S0, class S1>
624 8 : BOOST_CXX14_CONSTEXPR friend auto operator<(
625 : S0 const& s0, S1 const& s1) noexcept ->
626 : typename std::enable_if<
627 : is_match<S0, S1>::value, bool>::type
628 : {
629 8 : return decode_compare(s0, s1) < 0;
630 : }
631 :
632 : /// Compare two decode views for less than or equal
633 : /**
634 : * This function is only enabled if both types are
635 : * decode_view or convertible to `core::string_view`,
636 : * but not both are convertible to `core::string_view`
637 : */
638 : template<class S0, class S1>
639 8 : BOOST_CXX14_CONSTEXPR friend auto operator<=(
640 : S0 const& s0, S1 const& s1) noexcept ->
641 : typename std::enable_if<
642 : is_match<S0, S1>::value, bool>::type
643 : {
644 8 : return decode_compare(s0, s1) <= 0;
645 : }
646 :
647 : /// Compare two decode views for greater than
648 : /**
649 : * This function is only enabled if both types are
650 : * decode_view or convertible to `core::string_view`,
651 : * but not both are convertible to `core::string_view`
652 : */
653 : template<class S0, class S1>
654 8 : BOOST_CXX14_CONSTEXPR friend auto operator>(
655 : S0 const& s0, S1 const& s1) noexcept ->
656 : typename std::enable_if<
657 : is_match<S0, S1>::value, bool>::type
658 : {
659 8 : return decode_compare(s0, s1) > 0;
660 : }
661 :
662 : /// Compare two decode views for greater than or equal
663 : /**
664 : * This function is only enabled if both types are
665 : * decode_view or convertible to `core::string_view`,
666 : * but not both are convertible to `core::string_view`
667 : */
668 : template<class S0, class S1>
669 8 : BOOST_CXX14_CONSTEXPR friend auto operator>=(
670 : S0 const& s0, S1 const& s1) noexcept ->
671 : typename std::enable_if<
672 : is_match<S0, S1>::value, bool>::type
673 : {
674 8 : return decode_compare(s0, s1) >= 0;
675 : }
676 : #endif
677 :
678 : /** Format the string with percent-decoding applied to the output stream
679 :
680 : This hidden friend function serializes the decoded view
681 : to the output stream.
682 :
683 : @return A reference to the output stream, for chaining
684 :
685 : @param os The output stream to write to
686 :
687 : @param s The decoded view to write
688 : */
689 : friend
690 : std::ostream&
691 2 : operator<<(
692 : std::ostream& os,
693 : decode_view const& s)
694 : {
695 : // hidden friend
696 2 : s.write(os);
697 2 : return os;
698 : }
699 :
700 : private:
701 : BOOST_URL_DECL
702 : void
703 : write(std::ostream& os) const;
704 : };
705 :
706 : /** Format the string with percent-decoding applied to the output stream
707 :
708 : This function serializes the decoded view
709 : to the output stream.
710 :
711 : @return A reference to the output stream, for chaining
712 :
713 : @param os The output stream to write to
714 :
715 : @param s The decoded view to write
716 : */
717 : inline
718 : std::ostream&
719 : operator<<(
720 : std::ostream& os,
721 : decode_view const& s);
722 :
723 : //------------------------------------------------
724 :
725 : inline
726 : decode_view
727 3697 : pct_string_view::operator*() const noexcept
728 : {
729 3697 : return decode_view(*this);
730 : }
731 :
732 : #ifndef BOOST_URL_DOCS
733 : namespace detail {
734 : template<class... Args>
735 : decode_view
736 : make_decode_view(
737 : Args&&... args) noexcept
738 : {
739 : return decode_view(
740 : std::forward<Args>(args)...);
741 : }
742 : } // detail
743 : #endif
744 :
745 : //------------------------------------------------
746 :
747 : } // urls
748 : } // boost
749 :
750 : #include <boost/url/impl/decode_view.hpp>
751 :
752 : #endif
|