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) 2025 Mohammad Nejati
3  
// Copyright (c) 2025 Mohammad Nejati
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/cppalliance/http
8  
// Official repository: https://github.com/cppalliance/http
9  
//
9  
//
10  

10  

11  
#include <boost/http/detail/workspace.hpp>
11  
#include <boost/http/detail/workspace.hpp>
12  
#include <boost/http/detail/except.hpp>
12  
#include <boost/http/detail/except.hpp>
13  
#include <boost/assert.hpp>
13  
#include <boost/assert.hpp>
14  
#include <boost/core/exchange.hpp>
14  
#include <boost/core/exchange.hpp>
15  
#include <utility>
15  
#include <utility>
16  

16  

17  
namespace boost {
17  
namespace boost {
18  
namespace http {
18  
namespace http {
19  
namespace detail {
19  
namespace detail {
20  

20  

21  
workspace::
21  
workspace::
22  
any::
22  
any::
23  
~any() = default;
23  
~any() = default;
24  

24  

25  
workspace::
25  
workspace::
26  
~workspace()
26  
~workspace()
27  
{
27  
{
28  
    clear();
28  
    clear();
29  
    delete[] begin_;
29  
    delete[] begin_;
30  
}
30  
}
31  

31  

32  
workspace::
32  
workspace::
33  
workspace(
33  
workspace(
34  
    std::size_t n)
34  
    std::size_t n)
35  
    : begin_(new unsigned char[n])
35  
    : begin_(new unsigned char[n])
36  
    , front_(begin_)
36  
    , front_(begin_)
37  
    , head_(begin_ + n)
37  
    , head_(begin_ + n)
38  
    , back_(head_)
38  
    , back_(head_)
39  
    , end_(head_)
39  
    , end_(head_)
40  
{
40  
{
41  
}
41  
}
42  

42  

43  
workspace::
43  
workspace::
44  
workspace(
44  
workspace(
45  
    workspace&& other) noexcept
45  
    workspace&& other) noexcept
46  
    : begin_(boost::exchange(other.begin_, nullptr))
46  
    : begin_(boost::exchange(other.begin_, nullptr))
47  
    , front_(boost::exchange(other.front_, nullptr))
47  
    , front_(boost::exchange(other.front_, nullptr))
48  
    , head_(boost::exchange(other.head_, nullptr))
48  
    , head_(boost::exchange(other.head_, nullptr))
49  
    , back_(boost::exchange(other.back_, nullptr))
49  
    , back_(boost::exchange(other.back_, nullptr))
50  
    , end_(boost::exchange(other.end_, nullptr))
50  
    , end_(boost::exchange(other.end_, nullptr))
51  
{
51  
{
52  
}
52  
}
53  

53  

54  
workspace&
54  
workspace&
55  
workspace::
55  
workspace::
56  
operator=(
56  
operator=(
57  
    workspace&& other) noexcept
57  
    workspace&& other) noexcept
58  
{
58  
{
59  
    if(this != &other)
59  
    if(this != &other)
60  
    {
60  
    {
61  
        delete[] begin_;
61  
        delete[] begin_;
62  

62  

63  
        begin_ = boost::exchange(other.begin_, nullptr);
63  
        begin_ = boost::exchange(other.begin_, nullptr);
64  
        front_ = boost::exchange(other.front_, nullptr);
64  
        front_ = boost::exchange(other.front_, nullptr);
65  
        head_  = boost::exchange(other.head_, nullptr);
65  
        head_  = boost::exchange(other.head_, nullptr);
66  
        back_  = boost::exchange(other.back_, nullptr);
66  
        back_  = boost::exchange(other.back_, nullptr);
67  
        end_   = boost::exchange(other.end_, nullptr);
67  
        end_   = boost::exchange(other.end_, nullptr);
68  
    }
68  
    }
69  
    return *this;
69  
    return *this;
70  
}
70  
}
71  

71  

72  
void
72  
void
73  
workspace::
73  
workspace::
74  
allocate(
74  
allocate(
75  
    std::size_t n)
75  
    std::size_t n)
76  
{
76  
{
77  
    // Cannot be empty
77  
    // Cannot be empty
78  
    if(n == 0)
78  
    if(n == 0)
79  
        detail::throw_invalid_argument();
79  
        detail::throw_invalid_argument();
80  

80  

81  
    // Already allocated
81  
    // Already allocated
82  
    if(begin_ != nullptr)
82  
    if(begin_ != nullptr)
83  
        detail::throw_logic_error();
83  
        detail::throw_logic_error();
84  

84  

85  
    begin_ = new unsigned char[n];
85  
    begin_ = new unsigned char[n];
86  
    front_ = begin_;
86  
    front_ = begin_;
87  
    head_ = begin_ + n;
87  
    head_ = begin_ + n;
88  
    back_ = head_;
88  
    back_ = head_;
89  
    end_ = head_;
89  
    end_ = head_;
90  
}
90  
}
91  

91  

92  
void
92  
void
93  
workspace::
93  
workspace::
94  
clear() noexcept
94  
clear() noexcept
95  
{
95  
{
96  
    if(! begin_)
96  
    if(! begin_)
97  
        return;
97  
        return;
98  

98  

99  
    auto const end =
99  
    auto const end =
100  
        reinterpret_cast<
100  
        reinterpret_cast<
101  
            any const*>(back_);
101  
            any const*>(back_);
102  
    auto p =
102  
    auto p =
103  
        reinterpret_cast<
103  
        reinterpret_cast<
104  
            any const*>(head_);
104  
            any const*>(head_);
105  
    while(p != end)
105  
    while(p != end)
106  
    {
106  
    {
107  
        auto next = p->next;
107  
        auto next = p->next;
108  
        p->~any();
108  
        p->~any();
109  
        p = next;
109  
        p = next;
110  
    }
110  
    }
111  
    front_ = begin_;
111  
    front_ = begin_;
112  
    head_ = end_;
112  
    head_ = end_;
113  
    back_ = end_;
113  
    back_ = end_;
114  
}
114  
}
115  

115  

116  
unsigned char*
116  
unsigned char*
117  
workspace::
117  
workspace::
118  
reserve_front(
118  
reserve_front(
119  
    std::size_t n)
119  
    std::size_t n)
120  
{
120  
{
121  
    // Requested size exceeds available space.
121  
    // Requested size exceeds available space.
122  
    // Note you can never reserve the last byte.
122  
    // Note you can never reserve the last byte.
123  
    if(n >= size())
123  
    if(n >= size())
124  
        detail::throw_length_error();
124  
        detail::throw_length_error();
125  

125  

126  
    auto const p = front_;
126  
    auto const p = front_;
127  
    front_ += n ;
127  
    front_ += n ;
128  
    return p;
128  
    return p;
129  
}
129  
}
130  

130  

131  
unsigned char*
131  
unsigned char*
132  
workspace::
132  
workspace::
133  
try_reserve_front(
133  
try_reserve_front(
134  
    std::size_t n) noexcept
134  
    std::size_t n) noexcept
135  
{
135  
{
136  
    // Requested size exceeds available space.
136  
    // Requested size exceeds available space.
137  
    // Note you can never reserve the last byte.
137  
    // Note you can never reserve the last byte.
138  
    if(n >= size())
138  
    if(n >= size())
139  
        return nullptr;
139  
        return nullptr;
140  

140  

141  
    auto const p = front_;
141  
    auto const p = front_;
142  
    front_ += n ;
142  
    front_ += n ;
143  
    return p;
143  
    return p;
144  
}
144  
}
145  

145  

146  
unsigned char*
146  
unsigned char*
147  
workspace::
147  
workspace::
148  
reserve_back(
148  
reserve_back(
149  
    std::size_t n)
149  
    std::size_t n)
150  
{
150  
{
151  
    // // can't reserve after acquire
151  
    // // can't reserve after acquire
152  
    // if(head_ != end_)
152  
    // if(head_ != end_)
153  
    //     detail::throw_logic_error();
153  
    //     detail::throw_logic_error();
154  

154  

155  
    // can't reserve twice
155  
    // can't reserve twice
156  
    if(back_ != end_)
156  
    if(back_ != end_)
157  
        detail::throw_logic_error();
157  
        detail::throw_logic_error();
158  

158  

159  
    // over capacity
159  
    // over capacity
160  
    std::size_t const lim =
160  
    std::size_t const lim =
161  
        head_ - front_;
161  
        head_ - front_;
162  
    if(n >= lim)
162  
    if(n >= lim)
163  
        detail::throw_length_error();
163  
        detail::throw_length_error();
164  

164  

165  
    head_ -= n;
165  
    head_ -= n;
166  
    back_ = head_;
166  
    back_ = head_;
167  
    return back_;
167  
    return back_;
168  
}
168  
}
169  

169  

170  
// https://fitzgeraldnick.com/2019/11/01/always-bump-downwards.html
170  
// https://fitzgeraldnick.com/2019/11/01/always-bump-downwards.html
171  
unsigned char*
171  
unsigned char*
172  
workspace::
172  
workspace::
173  
bump_down(
173  
bump_down(
174  
    std::size_t size,
174  
    std::size_t size,
175  
    std::size_t align)
175  
    std::size_t align)
176  
{
176  
{
177  
    BOOST_ASSERT(align > 0);
177  
    BOOST_ASSERT(align > 0);
178  
    BOOST_ASSERT(
178  
    BOOST_ASSERT(
179  
        (align & (align - 1)) == 0);
179  
        (align & (align - 1)) == 0);
180  

180  

181  
    auto ip0 = reinterpret_cast<
181  
    auto ip0 = reinterpret_cast<
182  
        std::uintptr_t>(front_);
182  
        std::uintptr_t>(front_);
183  
    auto ip = reinterpret_cast<
183  
    auto ip = reinterpret_cast<
184  
        std::uintptr_t>(head_);
184  
        std::uintptr_t>(head_);
185  

185  

186  
    // If you get an exception here, it
186  
    // If you get an exception here, it
187  
    // means that a buffer was too small
187  
    // means that a buffer was too small
188  
    // for your workload. Increase the
188  
    // for your workload. Increase the
189  
    // buffer size.
189  
    // buffer size.
190  
    if(size > ip - ip0)
190  
    if(size > ip - ip0)
191  
        detail::throw_length_error();
191  
        detail::throw_length_error();
192  

192  

193  
    ip -= size;
193  
    ip -= size;
194  
    ip &= ~(align - 1);
194  
    ip &= ~(align - 1);
195  

195  

196  
    // If you get an exception here, it
196  
    // If you get an exception here, it
197  
    // means that a buffer was too small
197  
    // means that a buffer was too small
198  
    // for your workload. Increase the
198  
    // for your workload. Increase the
199  
    // buffer size.
199  
    // buffer size.
200  
    if(ip < ip0)
200  
    if(ip < ip0)
201  
        detail::throw_length_error();
201  
        detail::throw_length_error();
202  

202  

203  
    return reinterpret_cast<
203  
    return reinterpret_cast<
204  
        unsigned char*>(ip);
204  
        unsigned char*>(ip);
205  
}
205  
}
206  

206  

207  
} // detail
207  
} // detail
208  
} // http
208  
} // http
209  
} // boost
209  
} // boost