encode.hpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. //
  2. // Copyright (c) 2019 Vinnie Falco (vinnie.falco@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. #ifndef BOOST_URL_IMPL_ENCODE_HPP
  10. #define BOOST_URL_IMPL_ENCODE_HPP
  11. #include <boost/url/detail/encode.hpp>
  12. #include <boost/url/detail/except.hpp>
  13. #include <boost/url/encoding_opts.hpp>
  14. #include <boost/url/grammar/charset.hpp>
  15. #include <boost/url/grammar/hexdig_chars.hpp>
  16. #include <boost/url/grammar/type_traits.hpp>
  17. #include <boost/assert.hpp>
  18. #include <boost/static_assert.hpp>
  19. namespace boost {
  20. namespace urls {
  21. //------------------------------------------------
  22. template<class CharSet>
  23. std::size_t
  24. encoded_size(
  25. core::string_view s,
  26. CharSet const& unreserved,
  27. encoding_opts opt) noexcept
  28. {
  29. /* If you get a compile error here, it
  30. means that the value you passed does
  31. not meet the requirements stated in
  32. the documentation.
  33. */
  34. static_assert(
  35. grammar::is_charset<CharSet>::value,
  36. "Type requirements not met");
  37. std::size_t n = 0;
  38. auto it = s.data();
  39. auto const last = it + s.size();
  40. if(! opt.space_as_plus ||
  41. unreserved(' '))
  42. {
  43. while(it != last)
  44. {
  45. if(unreserved(*it))
  46. n += 1;
  47. else
  48. n += 3;
  49. ++it;
  50. }
  51. }
  52. else
  53. {
  54. while(it != last)
  55. {
  56. auto c = *it;
  57. if(unreserved(c))
  58. ++n;
  59. else if(c == ' ')
  60. ++n;
  61. else
  62. n += 3;
  63. ++it;
  64. }
  65. }
  66. return n;
  67. }
  68. //------------------------------------------------
  69. template<class CharSet>
  70. std::size_t
  71. encode(
  72. char* dest,
  73. std::size_t size,
  74. core::string_view s,
  75. CharSet const& unreserved,
  76. encoding_opts opt)
  77. {
  78. /* If you get a compile error here, it
  79. means that the value you passed does
  80. not meet the requirements stated in
  81. the documentation.
  82. */
  83. static_assert(
  84. grammar::is_charset<CharSet>::value,
  85. "Type requirements not met");
  86. // '%' must be reserved
  87. BOOST_ASSERT(! unreserved('%'));
  88. char const* const hex =
  89. detail::hexdigs[opt.lower_case];
  90. auto const encode = [hex](
  91. char*& dest,
  92. unsigned char c) noexcept
  93. {
  94. *dest++ = '%';
  95. *dest++ = hex[c>>4];
  96. *dest++ = hex[c&0xf];
  97. };
  98. auto it = s.data();
  99. auto const end = dest + size;
  100. auto const last = it + s.size();
  101. auto const dest0 = dest;
  102. auto const end3 = end - 3;
  103. if(! opt.space_as_plus)
  104. {
  105. while(it != last)
  106. {
  107. if(unreserved(*it))
  108. {
  109. if(dest == end)
  110. return dest - dest0;
  111. *dest++ = *it++;
  112. continue;
  113. }
  114. if(dest > end3)
  115. return dest - dest0;
  116. encode(dest, *it++);
  117. }
  118. return dest - dest0;
  119. }
  120. else if(! unreserved(' '))
  121. {
  122. // VFALCO space is usually reserved,
  123. // and we depend on this for an
  124. // optimization. if this assert
  125. // goes off we can split the loop
  126. // below into two versions.
  127. BOOST_ASSERT(! unreserved(' '));
  128. while(it != last)
  129. {
  130. if(unreserved(*it))
  131. {
  132. if(dest == end)
  133. return dest - dest0;
  134. *dest++ = *it++;
  135. continue;
  136. }
  137. if(*it == ' ')
  138. {
  139. if(dest == end)
  140. return dest - dest0;
  141. *dest++ = '+';
  142. ++it;
  143. continue;
  144. }
  145. if(dest > end3)
  146. return dest - dest0;
  147. encode(dest, *it++);
  148. }
  149. }
  150. return dest - dest0;
  151. }
  152. //------------------------------------------------
  153. // unsafe encode just
  154. // asserts on the output buffer
  155. //
  156. template<class CharSet>
  157. std::size_t
  158. encode_unsafe(
  159. char* dest,
  160. std::size_t size,
  161. core::string_view s,
  162. CharSet const& unreserved,
  163. encoding_opts opt)
  164. {
  165. // '%' must be reserved
  166. BOOST_ASSERT(! unreserved('%'));
  167. auto it = s.data();
  168. auto const last = it + s.size();
  169. auto const end = dest + size;
  170. ignore_unused(end);
  171. char const* const hex =
  172. detail::hexdigs[opt.lower_case];
  173. auto const encode = [end, hex](
  174. char*& dest,
  175. unsigned char c) noexcept
  176. {
  177. ignore_unused(end);
  178. *dest++ = '%';
  179. BOOST_ASSERT(dest != end);
  180. *dest++ = hex[c>>4];
  181. BOOST_ASSERT(dest != end);
  182. *dest++ = hex[c&0xf];
  183. };
  184. auto const dest0 = dest;
  185. if(! opt.space_as_plus)
  186. {
  187. while(it != last)
  188. {
  189. BOOST_ASSERT(dest != end);
  190. if(unreserved(*it))
  191. *dest++ = *it++;
  192. else
  193. encode(dest, *it++);
  194. }
  195. }
  196. else
  197. {
  198. // VFALCO space is usually reserved,
  199. // and we depend on this for an
  200. // optimization. if this assert
  201. // goes off we can split the loop
  202. // below into two versions.
  203. BOOST_ASSERT(! unreserved(' '));
  204. while(it != last)
  205. {
  206. BOOST_ASSERT(dest != end);
  207. if(unreserved(*it))
  208. {
  209. *dest++ = *it++;
  210. }
  211. else if(*it == ' ')
  212. {
  213. *dest++ = '+';
  214. ++it;
  215. }
  216. else
  217. {
  218. encode(dest, *it++);
  219. }
  220. }
  221. }
  222. return dest - dest0;
  223. }
  224. //------------------------------------------------
  225. template<
  226. class StringToken,
  227. class CharSet>
  228. BOOST_URL_STRTOK_RETURN
  229. encode(
  230. core::string_view s,
  231. CharSet const& unreserved,
  232. encoding_opts opt,
  233. StringToken&& token) noexcept
  234. {
  235. /* If you get a compile error here, it
  236. means that the value you passed does
  237. not meet the requirements stated in
  238. the documentation.
  239. */
  240. static_assert(
  241. grammar::is_charset<CharSet>::value,
  242. "Type requirements not met");
  243. auto const n = encoded_size(
  244. s, unreserved, opt);
  245. auto p = token.prepare(n);
  246. if(n > 0)
  247. encode_unsafe(
  248. p, n, s, unreserved, opt);
  249. return token.result();
  250. }
  251. } // urls
  252. } // boost
  253. #endif