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 : class iterator;
142 : #endif
143 :
144 : /// @copydoc iterator
145 : using const_iterator = iterator;
146 :
147 : //--------------------------------------------
148 : //
149 : // Special Members
150 : //
151 : //--------------------------------------------
152 :
153 : /** Constructor
154 :
155 : Default-constructed views represent
156 : empty strings.
157 :
158 : @par Example
159 : @code
160 : decode_view ds;
161 : @endcode
162 :
163 : @par Postconditions
164 : @code
165 : this->empty() == true
166 : @endcode
167 :
168 : @par Complexity
169 : Constant.
170 :
171 : @par Exception Safety
172 : Throws nothing.
173 : */
174 : decode_view() noexcept = default;
175 :
176 : /** Constructor
177 :
178 : This constructs a view from the character
179 : buffer `s`, which must remain valid and
180 : unmodified until the view is no longer
181 : accessed.
182 :
183 : @par Example
184 : @code
185 : decode_view ds( "Program%20Files" );
186 : @endcode
187 :
188 : @par Postconditions
189 : @code
190 : this->encoded() == s
191 : @endcode
192 :
193 : @par Complexity
194 : Linear in `s.size()`.
195 :
196 : @par Exception Safety
197 : Exceptions thrown on invalid input.
198 :
199 : @throw system_error
200 : The string contains an invalid percent encoding.
201 :
202 : @param s A percent-encoded string that has
203 : already been validated.
204 :
205 : @param opt The options for decoding. If
206 : this parameter is omitted, the default
207 : options are used.
208 : */
209 : explicit
210 3773 : decode_view(
211 : pct_string_view s,
212 : encoding_opts opt = {}) noexcept
213 3773 : : decode_view(
214 : detail::to_sv(s),
215 : s.decoded_size(),
216 3773 : opt)
217 : {
218 3773 : }
219 :
220 : //--------------------------------------------
221 : //
222 : // Observers
223 : //
224 : //--------------------------------------------
225 :
226 : /** Return true if the string is empty
227 :
228 : @par Example
229 : @code
230 : assert( decode_view( "" ).empty() );
231 : @endcode
232 :
233 : @par Complexity
234 : Constant.
235 :
236 : @par Exception Safety
237 : Throws nothing.
238 : */
239 : bool
240 15 : empty() const noexcept
241 : {
242 15 : return n_ == 0;
243 : }
244 :
245 : /** Return the number of decoded characters
246 :
247 : @par Example
248 : @code
249 : assert( decode_view( "Program%20Files" ).size() == 13 );
250 : @endcode
251 :
252 : @par Effects
253 : @code
254 : return std::distance( this->begin(), this->end() );
255 : @endcode
256 :
257 : @par Complexity
258 : Constant.
259 :
260 : @par Exception Safety
261 : Throws nothing.
262 : */
263 : size_type
264 3948 : size() const noexcept
265 : {
266 3948 : return dn_;
267 : }
268 :
269 : /** Return an iterator to the beginning
270 :
271 : @par Example
272 : @code
273 : auto it = this->begin();
274 : @endcode
275 :
276 : @par Complexity
277 : Constant.
278 :
279 : @par Exception Safety
280 : Throws nothing.
281 : */
282 : iterator
283 : begin() const noexcept;
284 :
285 : /** Return an iterator to the end
286 :
287 : @par Example
288 : @code
289 : auto it = this->end();
290 : @endcode
291 :
292 : @par Complexity
293 : Constant.
294 :
295 : @par Exception Safety
296 : Throws nothing.
297 : */
298 : iterator
299 : end() const noexcept;
300 :
301 : /** Return the first character
302 :
303 : @par Example
304 : @code
305 : assert( decode_view( "Program%20Files" ).front() == 'P' );
306 : @endcode
307 :
308 : @par Preconditions
309 : @code
310 : not this->empty()
311 : @endcode
312 :
313 : @par Complexity
314 : Constant.
315 :
316 : @par Exception Safety
317 : Throws nothing.
318 : */
319 : reference
320 : front() const noexcept;
321 :
322 : /** Return the last character
323 :
324 : @par Example
325 : @code
326 : assert( decode_view( "Program%20Files" ).back() == 's' );
327 : @endcode
328 :
329 : @par Preconditions
330 : @code
331 : not this->empty()
332 : @endcode
333 :
334 : @par Complexity
335 : Constant.
336 :
337 : @par Exception Safety
338 : Throws nothing.
339 : */
340 : reference
341 : back() const noexcept;
342 :
343 : /** Checks if the string begins with the given prefix
344 :
345 : @par Example
346 : @code
347 : assert( decode_view( "Program%20Files" ).starts_with("Program") );
348 : @endcode
349 :
350 : @par Complexity
351 : Linear.
352 :
353 : @par Exception Safety
354 : Throws nothing.
355 : */
356 : BOOST_URL_DECL
357 : bool
358 : starts_with( core::string_view s ) const noexcept;
359 :
360 : /** Checks if the string ends with the given prefix
361 :
362 : @par Example
363 : @code
364 : assert( decode_view( "Program%20Files" ).ends_with("Files") );
365 : @endcode
366 :
367 : @par Complexity
368 : Linear.
369 :
370 : @par Exception Safety
371 : Throws nothing.
372 : */
373 : BOOST_URL_DECL
374 : bool
375 : ends_with( core::string_view s ) const noexcept;
376 :
377 : /** Checks if the string begins with the given prefix
378 :
379 : @par Example
380 : @code
381 : assert( decode_view( "Program%20Files" ).starts_with('P') );
382 : @endcode
383 :
384 : @par Complexity
385 : Constant.
386 :
387 : @par Exception Safety
388 : Throws nothing.
389 : */
390 : BOOST_URL_DECL
391 : bool
392 : starts_with( char ch ) const noexcept;
393 :
394 : /** Checks if the string ends with the given prefix
395 :
396 : @par Example
397 : @code
398 : assert( decode_view( "Program%20Files" ).ends_with('s') );
399 : @endcode
400 :
401 : @par Complexity
402 : Constant.
403 :
404 : @par Exception Safety
405 : Throws nothing.
406 : */
407 : BOOST_URL_DECL
408 : bool
409 : ends_with( char ch ) const noexcept;
410 :
411 : /** Finds the first occurrence of character in this view
412 :
413 : @par Complexity
414 : Linear.
415 :
416 : @par Exception Safety
417 : Throws nothing.
418 : */
419 : BOOST_URL_DECL
420 : const_iterator
421 : find( char ch ) const noexcept;
422 :
423 : /** Finds the first occurrence of character in this view
424 :
425 : @par Complexity
426 : Linear.
427 :
428 : @par Exception Safety
429 : Throws nothing.
430 : */
431 : BOOST_URL_DECL
432 : const_iterator
433 : rfind( char ch ) const noexcept;
434 :
435 : /** Remove the first characters
436 :
437 : @par Example
438 : @code
439 : decode_view d( "Program%20Files" );
440 : d.remove_prefix( 8 );
441 : assert( d == "Files" );
442 : @endcode
443 :
444 : @par Preconditions
445 : @code
446 : not this->empty()
447 : @endcode
448 :
449 : @par Complexity
450 : Linear.
451 : */
452 : BOOST_URL_DECL
453 : void
454 : remove_prefix( size_type n );
455 :
456 : /** Remove the last characters
457 :
458 : @par Example
459 : @code
460 : decode_view d( "Program%20Files" );
461 : d.remove_prefix( 6 );
462 : assert( d == "Program" );
463 : @endcode
464 :
465 : @par Preconditions
466 : @code
467 : not this->empty()
468 : @endcode
469 :
470 : @par Complexity
471 : Linear.
472 : */
473 : BOOST_URL_DECL
474 : void
475 : remove_suffix( size_type n );
476 :
477 : /** Return the decoding options
478 : */
479 : encoding_opts
480 : options() const noexcept
481 : {
482 : encoding_opts opt;
483 : opt.space_as_plus = space_as_plus_;
484 : return opt;
485 : }
486 :
487 : //--------------------------------------------
488 : //
489 : // Comparison
490 : //
491 : //--------------------------------------------
492 :
493 : /** Return the result of comparing to another string
494 :
495 : The length of the sequences to compare is the smaller of
496 : `size()` and `other.size()`.
497 :
498 : The function compares the two strings as if by calling
499 : `char_traits<char>::compare(to_string().data(), v.data(), rlen)`.
500 : This means the comparison is performed with
501 : percent-decoding applied to the current string.
502 :
503 : @param other string to compare
504 :
505 : @return Negative value if this string is less than the other
506 : character sequence, zero if the both character sequences are
507 : equal, positive value if this string is greater than the other
508 : character sequence
509 : */
510 : BOOST_URL_DECL
511 : int
512 : compare(core::string_view other) const noexcept;
513 :
514 : /** Return the result of comparing to another string
515 :
516 : The length of the sequences to compare is the smaller of
517 : `size()` and `other.size()`.
518 :
519 : The function compares the two strings as if by calling
520 : `char_traits<char>::compare(to_string().data(), v.to_string().data(), rlen)`.
521 : This means the comparison is performed with
522 : percent-decoding applied to the current string.
523 :
524 : @param other string to compare
525 :
526 : @return Negative value if this string is less than the other
527 : character sequence, zero if the both character sequences are
528 : equal, positive value if this string is greater than the other
529 : character sequence
530 : */
531 : BOOST_URL_DECL
532 : int
533 : compare(decode_view other) const noexcept;
534 :
535 : //--------------------------------------------
536 :
537 : // relational operators
538 : #ifndef BOOST_URL_DOCS
539 : private:
540 : template<class S0, class S1>
541 : using is_match = std::integral_constant<bool,
542 : // both decode_view or convertible to core::string_view
543 : (
544 : std::is_same<typename std::decay<S0>::type, decode_view>::value ||
545 : std::is_convertible<S0, core::string_view>::value) &&
546 : (
547 : std::is_same<typename std::decay<S1>::type, decode_view>::value ||
548 : std::is_convertible<S1, core::string_view>::value) &&
549 : // not both are convertible to string view
550 : (
551 : !std::is_convertible<S0, core::string_view>::value ||
552 : !std::is_convertible<S1, core::string_view>::value)>;
553 :
554 : static
555 : int
556 316 : decode_compare(decode_view s0, decode_view s1) noexcept
557 : {
558 316 : return s0.compare(s1);
559 : }
560 :
561 : template <class S>
562 : static
563 : int
564 2926 : decode_compare(decode_view s0, S const& s1) noexcept
565 : {
566 2926 : return s0.compare(s1);
567 : }
568 :
569 : template <class S>
570 : static
571 : int
572 : decode_compare(S const& s0, decode_view s1) noexcept
573 : {
574 : return -s1.compare(s0);
575 : }
576 : public:
577 :
578 : template<class S0, class S1>
579 2471 : BOOST_CXX14_CONSTEXPR friend auto operator==(
580 : S0 const& s0, S1 const& s1) noexcept ->
581 : typename std::enable_if<
582 : is_match<S0, S1>::value, bool>::type
583 : {
584 2471 : return decode_compare(s0, s1) == 0;
585 : }
586 :
587 : template<class S0, class S1>
588 739 : BOOST_CXX14_CONSTEXPR friend auto operator!=(
589 : S0 const& s0, S1 const& s1) noexcept ->
590 : typename std::enable_if<
591 : is_match<S0, S1>::value, bool>::type
592 : {
593 739 : return decode_compare(s0, s1) != 0;
594 : }
595 :
596 : template<class S0, class S1>
597 8 : BOOST_CXX14_CONSTEXPR friend auto operator<(
598 : S0 const& s0, S1 const& s1) noexcept ->
599 : typename std::enable_if<
600 : is_match<S0, S1>::value, bool>::type
601 : {
602 8 : return decode_compare(s0, s1) < 0;
603 : }
604 :
605 : template<class S0, class S1>
606 8 : BOOST_CXX14_CONSTEXPR friend auto operator<=(
607 : S0 const& s0, S1 const& s1) noexcept ->
608 : typename std::enable_if<
609 : is_match<S0, S1>::value, bool>::type
610 : {
611 8 : return decode_compare(s0, s1) <= 0;
612 : }
613 :
614 : template<class S0, class S1>
615 8 : BOOST_CXX14_CONSTEXPR friend auto operator>(
616 : S0 const& s0, S1 const& s1) noexcept ->
617 : typename std::enable_if<
618 : is_match<S0, S1>::value, bool>::type
619 : {
620 8 : return decode_compare(s0, s1) > 0;
621 : }
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 : #endif
632 :
633 : // hidden friend
634 : friend
635 : std::ostream&
636 2 : operator<<(
637 : std::ostream& os,
638 : decode_view const& s)
639 : {
640 : // hidden friend
641 2 : s.write(os);
642 2 : return os;
643 : }
644 :
645 : private:
646 : BOOST_URL_DECL
647 : void
648 : write(std::ostream& os) const;
649 : };
650 :
651 : /** Format the string with percent-decoding applied to the output stream
652 :
653 : This function serializes the decoded view
654 : to the output stream.
655 :
656 : @return A reference to the output stream, for chaining
657 :
658 : @param os The output stream to write to
659 :
660 : @param s The decoded view to write
661 : */
662 : inline
663 : std::ostream&
664 : operator<<(
665 : std::ostream& os,
666 : decode_view const& s);
667 :
668 : //------------------------------------------------
669 :
670 : inline
671 : decode_view
672 3697 : pct_string_view::operator*() const noexcept
673 : {
674 3697 : return decode_view(*this);
675 : }
676 :
677 : #ifndef BOOST_URL_DOCS
678 : namespace detail {
679 : template<class... Args>
680 : decode_view
681 : make_decode_view(
682 : Args&&... args) noexcept
683 : {
684 : return decode_view(
685 : std::forward<Args>(args)...);
686 : }
687 : } // detail
688 : #endif
689 :
690 : //------------------------------------------------
691 :
692 : } // urls
693 : } // boost
694 :
695 : #include <boost/url/impl/decode_view.hpp>
696 :
697 : #endif
|