decode_view.hpp 16 KB

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