segments_range.hpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. //
  2. // Copyright (c) 2025 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. #ifndef BOOST_URL_DETAIL_SEGMENTS_RANGE_HPP
  10. #define BOOST_URL_DETAIL_SEGMENTS_RANGE_HPP
  11. #include <boost/url/detail/config.hpp>
  12. #include <boost/url/detail/url_impl.hpp>
  13. #include <boost/url/segments_base.hpp>
  14. #include <boost/url/segments_encoded_base.hpp>
  15. #include <boost/core/detail/string_view.hpp>
  16. #include <boost/assert.hpp>
  17. namespace boost {
  18. namespace urls {
  19. namespace detail {
  20. struct segments_iter_access
  21. {
  22. static
  23. segments_iter_impl const&
  24. impl(segments_base::iterator const& it) noexcept
  25. {
  26. return it.it_;
  27. }
  28. static
  29. segments_iter_impl const&
  30. impl(segments_encoded_base::iterator const& it) noexcept
  31. {
  32. return it.it_;
  33. }
  34. };
  35. inline
  36. path_ref
  37. make_subref_from_impls(
  38. segments_iter_impl const& first,
  39. segments_iter_impl const& last) noexcept
  40. {
  41. BOOST_ASSERT(first.ref.alias_of(last.ref));
  42. path_ref const& ref = first.ref;
  43. std::size_t const i0 = first.index;
  44. std::size_t const i1 = last.index;
  45. BOOST_ASSERT(i0 <= i1);
  46. std::size_t const nseg = i1 - i0;
  47. bool const absolute = ref.buffer().starts_with('/');
  48. // Empty range
  49. if (nseg == 0)
  50. {
  51. std::size_t off0;
  52. if (i0 == 0)
  53. {
  54. // [begin, begin): don't include the leading '/'
  55. // for absolute, start right after the leading '/';
  56. if (absolute)
  57. {
  58. off0 = 1;
  59. }
  60. // for relative, start at the first segment character.
  61. else
  62. {
  63. off0 = first.pos;
  64. }
  65. }
  66. else
  67. {
  68. // [it, it) in the middle:
  69. // skip the separator before segment i0
  70. off0 = first.pos + 1;
  71. }
  72. core::string_view const sub(ref.data() + off0, 0);
  73. return {sub, 0, 0};
  74. }
  75. // General case: non-empty range
  76. // Start offset
  77. std::size_t off0;
  78. if (i0 == 0)
  79. {
  80. if (absolute)
  81. {
  82. // include leading '/'
  83. off0 = 0;
  84. }
  85. else
  86. {
  87. // relative: start at first segment
  88. off0 = first.pos;
  89. }
  90. }
  91. else
  92. {
  93. // include the separator preceding segment i0
  94. off0 = first.pos;
  95. }
  96. // End offset
  97. std::size_t off1;
  98. if(i1 == ref.nseg())
  99. {
  100. off1 = ref.size();
  101. }
  102. else
  103. {
  104. // stop before the slash preceding i1
  105. off1 = last.pos;
  106. }
  107. BOOST_ASSERT(off1 >= off0);
  108. core::string_view const sub(ref.data() + off0, off1 - off0);
  109. // decoded sizes reuse iterator bookkeeping instead of rescanning
  110. std::size_t start_dn = (i0 == 0) ? 0 : first.decoded_prefix_size();
  111. std::size_t const end_dn = last.decoded_prefix_size(); // already excludes segment at `last`
  112. BOOST_ASSERT(end_dn >= start_dn);
  113. std::size_t const dn_sum = end_dn - start_dn;
  114. return {sub, dn_sum, nseg};
  115. }
  116. template<class Iter>
  117. inline
  118. path_ref
  119. make_subref(Iter const& first, Iter const& last) noexcept
  120. {
  121. auto const& f = segments_iter_access::impl(first);
  122. auto const& l = segments_iter_access::impl(last);
  123. return make_subref_from_impls(f, l);
  124. }
  125. } // detail
  126. } // urls
  127. } // boost
  128. #endif