params_encoded_base.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. //
  2. // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
  3. // Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
  4. //
  5. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  6. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. // Official repository: https://github.com/boostorg/url
  9. //
  10. #ifndef BOOST_URL_PARAMS_ENCODED_BASE_HPP
  11. #define BOOST_URL_PARAMS_ENCODED_BASE_HPP
  12. #include <boost/url/detail/config.hpp>
  13. #include <boost/url/ignore_case.hpp>
  14. #include <boost/url/param.hpp>
  15. #include <boost/url/detail/params_iter_impl.hpp>
  16. #include <boost/url/detail/url_impl.hpp>
  17. #include <iosfwd>
  18. namespace boost {
  19. namespace urls {
  20. /** Common functionality for containers
  21. This base class is used by the library
  22. to provide common member functions for
  23. containers. This cannot be instantiated
  24. directly; Instead, use one of the
  25. containers or functions:
  26. @par Containers
  27. @li @ref params_ref
  28. @li @ref params_view
  29. @li @ref params_encoded_ref
  30. @li @ref params_encoded_view
  31. */
  32. class BOOST_URL_DECL params_encoded_base
  33. {
  34. friend class url_view_base;
  35. friend class params_encoded_ref;
  36. friend class params_encoded_view;
  37. detail::query_ref ref_;
  38. params_encoded_base() = default;
  39. params_encoded_base(
  40. params_encoded_base const&) = default;
  41. params_encoded_base& operator=(
  42. params_encoded_base const&) = default;
  43. params_encoded_base(
  44. detail::query_ref const& ref) noexcept;
  45. public:
  46. /** A Bidirectional iterator to a query parameter
  47. Objects of this type allow iteration
  48. through the parameters in the query.
  49. Strings returned by iterators may
  50. contain percent escapes.
  51. The values returned are read-only;
  52. changes to parameters must be made
  53. through the container instead, if the
  54. container supports modification.
  55. <br>
  56. The strings produced when iterators
  57. are dereferenced refer to the underlying
  58. character buffer.
  59. Ownership is not transferred; the caller
  60. is responsible for ensuring that the
  61. lifetime of the buffer extends until
  62. it is no longer referenced by any
  63. container or iterator.
  64. */
  65. class iterator;
  66. /// @copydoc iterator
  67. using const_iterator = iterator;
  68. /** The value type
  69. Values of this type represent parameters
  70. whose strings retain unique ownership by
  71. making a copy.
  72. @par Example
  73. @code
  74. params_encoded_view::value_type qp( *url_view( "?first=John&last=Doe" ).params().find( "first" ) );
  75. @endcode
  76. @see
  77. @ref param.
  78. */
  79. using value_type = param;
  80. /** The reference type
  81. This is the type of value returned when
  82. iterators of the view are dereferenced.
  83. @see
  84. @ref param_view.
  85. */
  86. using reference = param_pct_view;
  87. /// @copydoc reference
  88. using const_reference = param_pct_view;
  89. /** An unsigned integer type to represent sizes.
  90. */
  91. using size_type = std::size_t;
  92. /** A signed integer type used to represent differences.
  93. */
  94. using difference_type = std::ptrdiff_t;
  95. //--------------------------------------------
  96. //
  97. // Observers
  98. //
  99. //--------------------------------------------
  100. /** Return the maximum number of characters possible
  101. This represents the largest number of
  102. characters that are possible in a path,
  103. not including any null terminator.
  104. @par Exception Safety
  105. Throws nothing.
  106. @return The maximum number of characters possible.
  107. */
  108. static
  109. constexpr
  110. std::size_t
  111. max_size() noexcept
  112. {
  113. return BOOST_URL_MAX_SIZE;
  114. }
  115. /** Return the query corresponding to these params
  116. This function returns the query string
  117. referenced by the container.
  118. The returned string may contain
  119. percent escapes.
  120. @par Example
  121. @code
  122. assert( url_view( "?first=John&last=Doe" ).encoded_params().buffer() == "first=John&last=Doe" );
  123. @endcode
  124. @par Complexity
  125. Constant.
  126. @par Exception Safety
  127. Throws nothing.
  128. @par BNF
  129. @code
  130. query-params = query-param *( "&" query-param )
  131. query-param = key [ "=" value ]
  132. key = *qpchar
  133. value = *( qpchar / "=" )
  134. @endcode
  135. @par Specification
  136. @li <a href="https://en.wikipedia.org/wiki/Query_string"
  137. >Query string (Wikipedia)</a>
  138. @return The query string.
  139. */
  140. pct_string_view
  141. buffer() const noexcept;
  142. /** Return true if there are no params
  143. @par Example
  144. @code
  145. assert( ! url_view( "?key=value" ).encoded_params().empty() );
  146. @endcode
  147. @par Complexity
  148. Constant.
  149. @par Exception Safety
  150. Throws nothing.
  151. @return `true` if there are no params.
  152. */
  153. bool
  154. empty() const noexcept;
  155. /** Return the number of params
  156. @par Example
  157. @code
  158. assert( url_view( "?key=value").encoded_params().size() == 1 );
  159. @endcode
  160. @par Complexity
  161. Constant.
  162. @par Exception Safety
  163. Throws nothing.
  164. @return The number of params.
  165. */
  166. std::size_t
  167. size() const noexcept;
  168. /** Return an iterator to the beginning
  169. @par Complexity
  170. Linear in the size of the first param.
  171. @par Exception Safety
  172. Throws nothing.
  173. @return An iterator to the beginning.
  174. */
  175. iterator
  176. begin() const noexcept;
  177. /** Return an iterator to the end
  178. @par Complexity
  179. Constant.
  180. @par Exception Safety
  181. Throws nothing.
  182. @return An iterator to the end.
  183. */
  184. iterator
  185. end() const noexcept;
  186. //--------------------------------------------
  187. /** Return true if a matching key exists
  188. This function examines the parameters
  189. in the container to find a match for
  190. the specified key,
  191. which may contain percent escapes.
  192. The comparison is performed as if all
  193. escaped characters were decoded first.
  194. @par Example
  195. @code
  196. assert( url_view( "?first=John&last=Doe" ).encoded_params().contains( "first" ) );
  197. @endcode
  198. @par Complexity
  199. Linear in `this->buffer().size()`.
  200. @par Exception Safety
  201. Exceptions thrown on invalid input.
  202. @throw system_error
  203. `key` contains an invalid percent-encoding.
  204. @param key The key to match.
  205. By default, a case-sensitive
  206. comparison is used.
  207. @param ic An optional parameter. If
  208. the value @ref ignore_case is passed
  209. here, the comparison is
  210. case-insensitive.
  211. @return `true` if a matching key exists.
  212. */
  213. bool
  214. contains(
  215. pct_string_view key,
  216. ignore_case_param ic = {}) const noexcept;
  217. /** Return the number of matching keys
  218. This function examines the parameters
  219. in the container to find the number of
  220. matches for the specified key,
  221. which may contain percent escapes.
  222. The comparison is performed as if all
  223. escaped characters were decoded first.
  224. @par Example
  225. @code
  226. assert( url_view( "?first=John&last=Doe" ).encoded_params().count( "first" ) == 1 );
  227. @endcode
  228. @par Complexity
  229. Linear in `this->buffer().size()`.
  230. @par Exception Safety
  231. Exceptions thrown on invalid input.
  232. @throw system_error
  233. `key` contains an invalid percent-encoding.
  234. @param key The key to match.
  235. By default, a case-sensitive
  236. comparison is used.
  237. @param ic An optional parameter. If
  238. the value @ref ignore_case is passed
  239. here, the comparison is
  240. case-insensitive.
  241. @return The number of matching keys.
  242. */
  243. std::size_t
  244. count(
  245. pct_string_view key,
  246. ignore_case_param ic = {}) const noexcept;
  247. /** Find a matching key
  248. This function examines the parameters
  249. in the container to find a match for
  250. the specified key,
  251. which may contain percent escapes.
  252. The comparison is performed as if all
  253. escaped characters were decoded first.
  254. <br>
  255. The search starts from the first param
  256. and proceeds forward until either the
  257. key is found or the end of the range is
  258. reached, in which case `end()` is
  259. returned.
  260. @par Example
  261. @code
  262. assert( url_view( "?first=John&last=Doe" ).encoded_params().find( "First", ignore_case )->value == "John" );
  263. @endcode
  264. @par Effects
  265. @code
  266. return this->find( this->begin(), key, ic );
  267. @endcode
  268. @par Complexity
  269. Linear in `this->buffer().size()`.
  270. @par Exception Safety
  271. Exceptions thrown on invalid input.
  272. @throw system_error
  273. `key` contains an invalid percent-encoding.
  274. @return an iterator to the param
  275. @param key The key to match.
  276. By default, a case-sensitive
  277. comparison is used.
  278. @param ic An optional parameter. If
  279. the value @ref ignore_case is passed
  280. here, the comparison is
  281. case-insensitive.
  282. */
  283. iterator
  284. find(
  285. pct_string_view key,
  286. ignore_case_param ic = {}) const noexcept;
  287. /** Find a matching key
  288. This function examines the parameters
  289. in the container to find a match for
  290. the specified key, which may contain
  291. percent escapes.
  292. The comparison is performed as if all
  293. escaped characters were decoded first.
  294. <br>
  295. The search starts at `from`
  296. and proceeds forward until either the
  297. key is found or the end of the range is
  298. reached, in which case `end()` is
  299. returned.
  300. @par Example
  301. @code
  302. url_view u( "?First=John&Last=Doe" );
  303. assert( u.encoded_params().find( "first" ) != u.encoded_params().find( "first", ignore_case ) );
  304. @endcode
  305. @par Complexity
  306. Linear in `this->buffer().size()`.
  307. @par Exception Safety
  308. Exceptions thrown on invalid input.
  309. @throw system_error
  310. `key` contains an invalid percent-encoding.
  311. @return an iterator to the param
  312. @param from The position to begin the
  313. search from. This can be `end()`.
  314. @param key The key to match.
  315. By default, a case-sensitive
  316. comparison is used.
  317. @param ic An optional parameter. If
  318. the value @ref ignore_case is passed
  319. here, the comparison is
  320. case-insensitive.
  321. */
  322. iterator
  323. find(
  324. iterator from,
  325. pct_string_view key,
  326. ignore_case_param ic = {}) const noexcept;
  327. /** Find a matching key
  328. This function examines the parameters
  329. in the container to find a match for
  330. the specified key, which may contain
  331. percent escapes.
  332. The comparison is performed as if all
  333. escaped characters were decoded first.
  334. <br>
  335. The search starts from the last param
  336. and proceeds backwards until either the
  337. key is found or the beginning of the
  338. range is reached, in which case `end()`
  339. is returned.
  340. @par Example
  341. @code
  342. assert( url_view( "?first=John&last=Doe" ).encoded_params().find_last( "last" )->value == "Doe" );
  343. @endcode
  344. @par Complexity
  345. Linear in `this->buffer().size()`.
  346. @par Exception Safety
  347. Exceptions thrown on invalid input.
  348. @throw system_error
  349. `key` contains an invalid percent-encoding.
  350. @return an iterator to the param
  351. @param key The key to match.
  352. By default, a case-sensitive
  353. comparison is used.
  354. @param ic An optional parameter. If
  355. the value @ref ignore_case is passed
  356. here, the comparison is
  357. case-insensitive.
  358. */
  359. iterator
  360. find_last(
  361. pct_string_view key,
  362. ignore_case_param ic = {}) const noexcept;
  363. /** Find a matching key
  364. This function examines the parameters
  365. in the container to find a match for
  366. the specified key, which may contain
  367. percent escapes.
  368. The comparison is performed as if all
  369. escaped characters were decoded first.
  370. <br>
  371. The search starts prior to `before`
  372. and proceeds backwards until either the
  373. key is found or the beginning of the
  374. range is reached, in which case `end()`
  375. is returned.
  376. @par Example
  377. @code
  378. url_view u( "?First=John&Last=Doe" );
  379. assert( u.encoded_params().find_last( "last" ) != u.encoded_params().find_last( "last", ignore_case ) );
  380. @endcode
  381. @par Complexity
  382. Linear in `this->buffer().size()`.
  383. @return an iterator to the param
  384. @param before One past the position
  385. to begin the search from. This can
  386. be `end()`.
  387. @param key The key to match.
  388. By default, a case-sensitive
  389. comparison is used.
  390. @param ic An optional parameter. If
  391. the value @ref ignore_case is passed
  392. here, the comparison is
  393. case-insensitive.
  394. */
  395. iterator
  396. find_last(
  397. iterator before,
  398. pct_string_view key,
  399. ignore_case_param ic = {}) const noexcept;
  400. private:
  401. detail::params_iter_impl
  402. find_impl(
  403. detail::params_iter_impl,
  404. pct_string_view,
  405. ignore_case_param) const noexcept;
  406. detail::params_iter_impl
  407. find_last_impl(
  408. detail::params_iter_impl,
  409. pct_string_view,
  410. ignore_case_param) const noexcept;
  411. };
  412. //------------------------------------------------
  413. /** Format to an output stream
  414. Any percent-escapes are emitted as-is;
  415. no decoding is performed.
  416. @par Complexity
  417. Linear in `ps.buffer().size()`.
  418. @par Effects
  419. @code
  420. return os << ps.buffer();
  421. @endcode
  422. @param os The output stream to write to
  423. @param qp The params to write
  424. @return A reference to the output stream, for chaining
  425. */
  426. BOOST_URL_DECL
  427. std::ostream&
  428. operator<<(
  429. std::ostream& os,
  430. params_encoded_base const& qp);
  431. } // urls
  432. } // boost
  433. #include <boost/url/impl/params_encoded_base.hpp>
  434. #endif