1  
//
1  
//
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
2  
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3  
// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
3  
// Copyright (c) 2022 Alan de Freitas (alandefreitas@gmail.com)
4  
//
4  
//
5  
// Distributed under the Boost Software License, Version 1.0. (See accompanying
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)
6  
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  
//
7  
//
8  
// Official repository: https://github.com/boostorg/url
8  
// Official repository: https://github.com/boostorg/url
9  
//
9  
//
10  

10  

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

13  

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

17  

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

21  

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

34  

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

49  

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

70  

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

79  

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

93  

94  
    update();
94  
    update();
95  
}
95  
}
96  

96  

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

124  

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

168  

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

219  

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

223  

224  
#endif
224  
#endif