Line data Source code
1 : // 2 : // Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com) 3 : // 4 : // Distributed under the Boost Software License, Version 1.0. (See accompanying 5 : // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 : // 7 : // Official repository: https://github.com/CPPAlliance/http_proto 8 : // 9 : 10 : #ifndef BOOST_HTTP_PROTO_DETAIL_WORKSPACE_HPP 11 : #define BOOST_HTTP_PROTO_DETAIL_WORKSPACE_HPP 12 : 13 : #include <boost/http_proto/detail/except.hpp> 14 : #include <boost/assert.hpp> 15 : #include <cstdlib> 16 : #include <new> 17 : #include <utility> 18 : #include <stddef.h> // ::max_align_t 19 : 20 : namespace boost { 21 : namespace http_proto { 22 : namespace detail { 23 : 24 : class workspace 25 : { 26 0 : struct any 27 : { 28 : any* next = nullptr; 29 : 30 : virtual ~any() = 0; 31 : }; 32 : 33 : template<class T> 34 : struct alignas(alignof(::max_align_t)) 35 : any_t : any 36 : { 37 : T t_; 38 : 39 : any_t() = delete; 40 : any_t(any_t&&) = default; 41 : 42 : explicit 43 : any_t( 44 : T&& t) 45 : : t_(std::move(t)) 46 : { 47 : } 48 : 49 : explicit 50 : any_t( 51 : T const& t) 52 : : t_(t) 53 : { 54 : } 55 : }; 56 : 57 : template<class T> 58 : struct alignas(alignof(::max_align_t)) 59 : any_n : any 60 : { 61 : std::size_t n_ = 0; 62 : 63 : any_n() = default; 64 : 65 : ~any_n() 66 : { 67 : for(std::size_t i = n_; 68 : i-- > 0;) 69 : data()[i].~T(); 70 : } 71 : 72 : any_n( 73 : std::size_t n, 74 : T const& t) 75 : : any_n() 76 : { 77 : while(n_ < n) 78 : { 79 : new(&data()[n_]) T(t); 80 : ++n_; 81 : } 82 : } 83 : 84 : T* 85 : data() noexcept 86 : { 87 : return 88 : reinterpret_cast<T*>( 89 : this + 1); 90 : } 91 : }; 92 : 93 : unsigned char* begin_ = nullptr; 94 : unsigned char* end_ = nullptr; 95 : unsigned char* head_ = nullptr; 96 : 97 : public: 98 : workspace() = default; 99 : 100 0 : ~workspace() 101 0 : { 102 0 : clear(); 103 0 : delete[] begin_; 104 0 : } 105 : 106 : explicit 107 0 : workspace( 108 : std::size_t n) 109 0 : : begin_(new unsigned char[n]) 110 0 : , end_(begin_ + n) 111 0 : , head_(end_) 112 : { 113 0 : } 114 : 115 : void* 116 0 : data() noexcept 117 : { 118 0 : return begin_; 119 : } 120 : 121 : std::size_t 122 0 : size() const noexcept 123 : { 124 0 : return head_ - begin_; 125 : } 126 : 127 : void 128 0 : clear() 129 : { 130 0 : auto const end = 131 : reinterpret_cast< 132 : any const*>(end_); 133 0 : auto p = 134 : reinterpret_cast< 135 : any const*>(head_); 136 0 : while(p != end) 137 : { 138 0 : auto next = p->next; 139 0 : p->~any(); 140 0 : p = next; 141 : } 142 0 : head_ = end_; 143 0 : } 144 : 145 : template<class T> 146 : T& 147 : push(T&& t) 148 : { 149 : using U = any_t<T>; 150 : auto p = ::new(bump_down( 151 : sizeof(U), alignof(U))) U( 152 : std::forward<T>(t)); 153 : p->next = reinterpret_cast< 154 : any*>(head_); 155 : head_ = reinterpret_cast< 156 : unsigned char*>(p); 157 : return p->t_; 158 : } 159 : 160 : template<class T> 161 : T* 162 : push_array( 163 : std::size_t n, 164 : T const& t) 165 : { 166 : using U = any_n<T>; 167 : auto p = ::new(bump_down( 168 : sizeof(U) + n * sizeof(T), 169 : alignof(::max_align_t))) U(n, t); 170 : p->next = reinterpret_cast< 171 : any*>(head_); 172 : head_ = reinterpret_cast< 173 : unsigned char*>(p); 174 : return p->data(); 175 : } 176 : 177 : private: 178 : // https://fitzgeraldnick.com/2019/11/01/always-bump-downwards.html 179 : void* 180 : bump_down( 181 : std::size_t size, 182 : std::size_t align) 183 : { 184 : BOOST_ASSERT(align > 0); 185 : BOOST_ASSERT( 186 : (align & (align - 1)) == 0); 187 : 188 : auto ip0 = reinterpret_cast< 189 : std::uintptr_t>(begin_); 190 : auto ip = reinterpret_cast< 191 : std::uintptr_t>(head_); 192 : if(size > ip - ip0) 193 : detail::throw_bad_alloc(); 194 : ip -= size; 195 : ip &= ~(align - 1); 196 : if(ip < ip0) 197 : detail::throw_bad_alloc(); 198 : return reinterpret_cast<void*>(ip); 199 : } 200 : }; 201 : 202 : } // detail 203 : } // http_proto 204 : } // boost 205 : 206 : #endif