1 // 2 // Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot 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/boostorg/beast 8 // 9 10 #ifndef BOOST_BEAST_EXAMPLE_FIELDS_ALLOC_HPP 11 #define BOOST_BEAST_EXAMPLE_FIELDS_ALLOC_HPP 12 13 #include <boost/throw_exception.hpp> 14 #include <cstdlib> 15 #include <memory> 16 #include <stdexcept> 17 18 namespace detail { 19 20 struct static_pool 21 { 22 std::size_t size_; 23 std::size_t refs_ = 1; 24 std::size_t count_ = 0; 25 char* p_; 26 27 char* enddetail::static_pool28 end() 29 { 30 return reinterpret_cast<char*>(this + 1) + size_; 31 } 32 33 explicit static_pooldetail::static_pool34 static_pool(std::size_t size) 35 : size_(size) 36 , p_(reinterpret_cast<char*>(this + 1)) 37 { 38 } 39 40 public: 41 static 42 static_pool& constructdetail::static_pool43 construct(std::size_t size) 44 { 45 auto p = new char[sizeof(static_pool) + size]; 46 return *(::new(p) static_pool{size}); 47 } 48 49 static_pool& sharedetail::static_pool50 share() 51 { 52 ++refs_; 53 return *this; 54 } 55 56 void destroydetail::static_pool57 destroy() 58 { 59 if(refs_--) 60 return; 61 this->~static_pool(); 62 delete[] reinterpret_cast<char*>(this); 63 } 64 65 void* allocdetail::static_pool66 alloc(std::size_t n) 67 { 68 auto last = p_ + n; 69 if(last >= end()) 70 BOOST_THROW_EXCEPTION(std::bad_alloc{}); 71 ++count_; 72 auto p = p_; 73 p_ = last; 74 return p; 75 } 76 77 void deallocdetail::static_pool78 dealloc() 79 { 80 if(--count_) 81 return; 82 p_ = reinterpret_cast<char*>(this + 1); 83 } 84 }; 85 86 } // detail 87 88 /** A non-thread-safe allocator optimized for @ref basic_fields. 89 90 This allocator obtains memory from a pre-allocated memory block 91 of a given size. It does nothing in deallocate until all 92 previously allocated blocks are deallocated, upon which it 93 resets the internal memory block for re-use. 94 95 To use this allocator declare an instance persistent to the 96 connection or session, and construct with the block size. 97 A good rule of thumb is 20% more than the maximum allowed 98 header size. For example if the application only allows up 99 to an 8,000 byte header, the block size could be 9,600. 100 101 Then, for every instance of `message` construct the header 102 with a copy of the previously declared allocator instance. 103 */ 104 template<class T> 105 struct fields_alloc 106 { 107 detail::static_pool* pool_; 108 109 public: 110 using value_type = T; 111 using is_always_equal = std::false_type; 112 using pointer = T*; 113 using reference = T&; 114 using const_pointer = T const*; 115 using const_reference = T const&; 116 using size_type = std::size_t; 117 using difference_type = std::ptrdiff_t; 118 119 template<class U> 120 struct rebind 121 { 122 using other = fields_alloc<U>; 123 }; 124 125 #if defined(_GLIBCXX_USE_CXX11_ABI) && (_GLIBCXX_USE_CXX11_ABI == 0) 126 // Workaround for g++ 127 // basic_string assumes that allocators are default-constructible 128 // See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56437 129 fields_alloc() = default; 130 #endif 131 132 explicit fields_allocfields_alloc133 fields_alloc(std::size_t size) 134 : pool_(&detail::static_pool::construct(size)) 135 { 136 } 137 fields_allocfields_alloc138 fields_alloc(fields_alloc const& other) 139 : pool_(&other.pool_->share()) 140 { 141 } 142 143 template<class U> fields_allocfields_alloc144 fields_alloc(fields_alloc<U> const& other) 145 : pool_(&other.pool_->share()) 146 { 147 } 148 ~fields_allocfields_alloc149 ~fields_alloc() 150 { 151 pool_->destroy(); 152 } 153 154 value_type* allocatefields_alloc155 allocate(size_type n) 156 { 157 return static_cast<value_type*>( 158 pool_->alloc(n * sizeof(T))); 159 } 160 161 void deallocatefields_alloc162 deallocate(value_type*, size_type) 163 { 164 pool_->dealloc(); 165 } 166 167 #if defined(BOOST_LIBSTDCXX_VERSION) && BOOST_LIBSTDCXX_VERSION < 60000 168 template<class U, class... Args> 169 void constructfields_alloc170 construct(U* ptr, Args&&... args) 171 { 172 ::new(static_cast<void*>(ptr)) U( 173 std::forward<Args>(args)...); 174 } 175 176 template<class U> 177 void destroyfields_alloc178 destroy(U* ptr) 179 { 180 ptr->~U(); 181 } 182 #endif 183 184 template<class U> 185 friend 186 bool operator ==(fields_alloc const & lhs,fields_alloc<U> const & rhs)187 operator==( 188 fields_alloc const& lhs, 189 fields_alloc<U> const& rhs) 190 { 191 return &lhs.pool_ == &rhs.pool_; 192 } 193 194 template<class U> 195 friend 196 bool operator !=(fields_alloc const & lhs,fields_alloc<U> const & rhs)197 operator!=( 198 fields_alloc const& lhs, 199 fields_alloc<U> const& rhs) 200 { 201 return ! (lhs == rhs); 202 } 203 }; 204 205 #endif 206