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 +

 
11 +
#ifndef BOOST_URL_DETAIL_IMPL_SEGMENTS_ITER_IMPL_HPP
 
12 +
#define BOOST_URL_DETAIL_IMPL_SEGMENTS_ITER_IMPL_HPP
 
13 +

 
14 +
#include <boost/url/detail/decode.hpp>
 
15 +
#include <boost/url/detail/path.hpp>
 
16 +
#include <boost/assert.hpp>
 
17 +

 
18 +
namespace boost {
 
19 +
namespace urls {
 
20 +
namespace detail {
 
21 +

 
22 +
// begin
 
23 +
inline
 
24 +
segments_iter_impl::
 
25 +
segments_iter_impl(
 
26 +
    detail::path_ref const& ref_) noexcept
 
27 +
    : ref(ref_)
 
28 +
{
 
29 +
    pos = path_prefix(ref.buffer());
 
30 +
    // begin() starts after any malleable prefix but remembers decoded chars skipped
 
31 +
    decoded_prefix = pos;
 
32 +
    update();
 
33 +
}
 
34 +

 
35 +
// end
 
36 +
inline
 
37 +
segments_iter_impl::
 
38 +
segments_iter_impl(
 
39 +
    detail::path_ref const& ref_,
 
40 +
    int) noexcept
 
41 +
    : ref(ref_)
 
42 +
    , pos(ref.size())
 
43 +
    , next(ref.size())
 
44 +
    , index(ref.nseg())
 
45 +
{
 
46 +
    // end() carries the total decoded length for O(1) range math
 
47 +
    decoded_prefix = ref.decoded_size();
 
48 +
}
 
49 +

 
50 +
inline
 
51 +
segments_iter_impl::
 
52 +
segments_iter_impl(
 
53 +
    url_impl const& u_,
 
54 +
    std::size_t pos_,
 
55 +
    std::size_t index_) noexcept
 
56 +
    : ref(u_)
 
57 +
    , pos(pos_)
 
58 +
    , index(index_)
 
59 +
{
 
60 +
    auto const total = ref.nseg();
 
61 +
    if(index >= total)
 
62 +
    {
 
63 +
        pos = ref.size();
 
64 +
        next = ref.size();
 
65 +
        decoded_prefix = ref.decoded_size();
 
66 +
        // iterator equal to end: nothing to decode
 
67 +
        dn = 0;
 
68 +
        return;
 
69 +
    }
 
70 +

 
71 +
    if(index == 0)
 
72 +
    {
 
73 +
        pos = path_prefix(ref.buffer());
 
74 +
        // first segment inherits the prefix size (including leading '/')
 
75 +
        decoded_prefix = pos;
 
76 +
        update();
 
77 +
        return;
 
78 +
    }
 
79 +

 
80 +
    BOOST_ASSERT(pos <= ref.size());
 
81 +
    // compute decoded prefix by scanning once up to the encoded offset
 
82 +
    decoded_prefix = detail::decode_bytes_unsafe(
 
83 +
        core::string_view(ref.data(), pos));
 
84 +
    if(pos != ref.size())
 
85 +
    {
 
86 +
        BOOST_ASSERT(
 
87 +
            ref.data()[pos] == '/');
 
88 +
        ++pos; // skip '/'
 
89 +
        update();
 
90 +
        --pos;
 
91 +
        return;
 
92 +
    }
 
93 +

 
94 +
    update();
 
95 +
}
 
96 +

 
97 +
inline
 
98 +
void
 
99 +
segments_iter_impl::
 
100 +
update() noexcept
 
101 +
{
 
102 +
    auto const end = ref.end();
 
103 +
    char const* const p0 =
 
104 +
        ref.data() + pos;
 
105 +
    dn = 0;
 
106 +
    auto p = p0;
 
107 +
    while(p != end)
 
108 +
    {
 
109 +
        if(*p == '/')
 
110 +
            break;
 
111 +
        if(*p != '%')
 
112 +
        {
 
113 +
            ++p;
 
114 +
            continue;
 
115 +
        }
 
116 +
        p += 3;
 
117 +
        dn += 2;
 
118 +
    }
 
119 +
    next = p - ref.data();
 
120 +
    dn = p - p0 - dn;
 
121 +
    s_ = make_pct_string_view_unsafe(
 
122 +
        p0, p - p0, dn);
 
123 +
}
 
124 +

 
125 +
inline
 
126 +
void
 
127 +
segments_iter_impl::
 
128 +
increment() noexcept
 
129 +
{
 
130 +
    BOOST_ASSERT(
 
131 +
        index != ref.nseg());
 
132 +
    auto const old_index = index;
 
133 +
    auto const old_dn = dn;
 
134 +
    // add decoded length of previous segment
 
135 +
    decoded_prefix += old_dn;
 
136 +
    if(old_index > 0)
 
137 +
        // account for the '/' separator we just crossed
 
138 +
        ++decoded_prefix;
 
139 +
    ++index;
 
140 +
    pos = next;
 
141 +
    if(index == ref.nseg())
 
142 +
        return;
 
143 +
    // "/" segment
 
144 +
    auto const end = ref.end();
 
145 +
    auto p = ref.data() + pos;
 
146 +
    BOOST_ASSERT(p != end);
 
147 +
    BOOST_ASSERT(*p == '/');
 
148 +
    dn = 0;
 
149 +
    ++p; // skip '/'
 
150 +
    auto const p0 = p;
 
151 +
    while(p != end)
 
152 +
    {
 
153 +
        if(*p == '/')
 
154 +
            break;
 
155 +
        if(*p != '%')
 
156 +
        {
 
157 +
            ++p;
 
158 +
            continue;
 
159 +
        }
 
160 +
        p += 3;
 
161 +
        dn += 2;
 
162 +
    }
 
163 +
    next = p - ref.data();
 
164 +
    dn = p - p0 - dn;
 
165 +
    s_ = make_pct_string_view_unsafe(
 
166 +
        p0, p - p0, dn);
 
167 +
}
 
168 +

 
169 +
inline
 
170 +
void
 
171 +
segments_iter_impl::
 
172 +
decrement() noexcept
 
173 +
{
 
174 +
    BOOST_ASSERT(index != 0);
 
175 +
    auto const current_dn = dn;
 
176 +
    auto const current_index = index;
 
177 +
    // remove the decoded length of the segment we're leaving
 
178 +
    decoded_prefix -= current_dn;
 
179 +
    if(current_index > 0 && decoded_prefix > 0)
 
180 +
        // drop the '/' separator when stepping left of it
 
181 +
        --decoded_prefix;
 
182 +
    --index;
 
183 +
    if(index == 0)
 
184 +
    {
 
185 +
        next = pos;
 
186 +
        pos = path_prefix(ref.buffer());
 
187 +
        decoded_prefix = pos;
 
188 +
        s_ = core::string_view(
 
189 +
            ref.data() + pos,
 
190 +
            next - pos);
 
191 +
        BOOST_ASSERT(! s_.ends_with('/'));
 
192 +
        return;
 
193 +
    }
 
194 +
    auto const begin = ref.data() +
 
195 +
        path_prefix(ref.buffer());
 
196 +
    next = pos;
 
197 +
    auto p = ref.data() + next;
 
198 +
    auto const p1 = p;
 
199 +
    BOOST_ASSERT(p != begin);
 
200 +
    dn = 0;
 
201 +
    while(p != begin)
 
202 +
    {
 
203 +
        --p;
 
204 +
        if(*p == '/')
 
205 +
        {
 
206 +
            ++dn;
 
207 +
            break;
 
208 +
        }
 
209 +
        if(*p == '%')
 
210 +
            dn += 2;
 
211 +
    }
 
212 +
    dn = p1 - p - dn;
 
213 +
    pos = p - ref.data();
 
214 +
    // keep decoded_prefix consistent with new pos
 
215 +
    // (already adjusted above)
 
216 +
    s_ = make_pct_string_view_unsafe(
 
217 +
        p + 1, p1 - p - 1, dn);
 
218 +
}
 
219 +

 
220 +
} // detail
 
221 +
} // urls
 
222 +
} // boost
 
223 +

 
224 +
#endif