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  

11  

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

18  

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

22  

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

48  

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

68  

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

77  

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

91  

92  
    update();
92  
    update();
93  
}
93  
}
94  

94  

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

121  

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

164  

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

214  

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