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 -

 
12 -
#include <boost/url/detail/config.hpp>
 
13 -
#include "path.hpp"
 
14 -
#include <boost/url/detail/decode.hpp>
 
15 -
#include <boost/url/detail/segments_iter_impl.hpp>
 
16 -
#include "boost/url/rfc/detail/path_rules.hpp"
 
17 -
#include <boost/assert.hpp>
 
18 -

 
19 -
namespace boost {
 
20 -
namespace urls {
 
21 -
namespace detail {
 
22 -

 
23 -
// begin
 
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 -
segments_iter_impl::
 
37 -
segments_iter_impl(
 
38 -
    detail::path_ref const& ref_,
 
39 -
    int) noexcept
 
40 -
    : ref(ref_)
 
41 -
    , pos(ref.size())
 
42 -
    , next(ref.size())
 
43 -
    , index(ref.nseg())
 
44 -
{
 
45 -
    // end() carries the total decoded length for O(1) range math
 
46 -
    decoded_prefix = ref.decoded_size();
 
47 -
}
 
48 -

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

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

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

 
92 -
    update();
 
93 -
}
 
94 -

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

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

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

 
215 -
} // detail
 
216 -
} // url
 
217 -
} // boost