GCC Code Coverage Report


Directory: libs/url/
File: boost/url/decode_view.hpp
Date: 2024-08-20 16:05:55
Exec Total Coverage
Lines: 29 29 100.0%
Functions: 46 46 100.0%
Branches: 0 0 -%

Line Branch Exec Source
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 5571 decode_compare(decode_view s0, S const& s1) noexcept
574 {
575 5571 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 3319 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 3319 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 747 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 747 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 16 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 16 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 16 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 16 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 16 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 16 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 16 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 16 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
753