1 #ifdef COMPILATION// -*-indent-tabs-mode:t;c-basic-offset:4;tab-width:4;-*-
2 $CXX $0 -o $0x&&$0x&&rm $0x;exit
3 #endif
4 // © Alfredo A. Correa 2019-2020
5
6 #ifndef BOOST_MULTI_MEMORY_MONOTONIC_HPP
7 #define BOOST_MULTI_MEMORY_MONOTONIC_HPP
8
9 #include "../memory/block.hpp"
10 #include "../memory/allocator.hpp"
11
12 #include<cstddef> // max_align_t
13 #include<stdexcept>
14
15 namespace boost{
16 namespace multi{
17 namespace memory{
18
19 template<class Ptr> // TODO test with actual fancy ptr
align_up(Ptr p,std::size_t align=alignof (std::max_align_t))20 Ptr align_up(Ptr p, std::size_t align = alignof(std::max_align_t)){
21 using multi::to_address;
22 auto p_(to_address(p));
23 static_assert( sizeof(*p_)==1 , "!"); // crash
24 auto q_ = reinterpret_cast<decltype(p_)>(
25 (reinterpret_cast<std::uintptr_t>(p_) + (align-1))
26 & ~(align-1)
27 );
28 return p+std::distance(p_,q_);
29 }
30
31 template<class T>
align_up(T * p,std::size_t align=alignof (std::max_align_t))32 T* align_up(T* p, std::size_t align = alignof(std::max_align_t)){
33 return
34 reinterpret_cast<T*>(
35 (reinterpret_cast<std::uintptr_t>(p) + (align-1))
36 & ~(align-1)
37 )
38 ;
39 }
40
41 template<typename Ptr = byte*, std::size_t Align = alignof(std::max_align_t)>
42 class null_t{
43 public:
44 using pointer = Ptr;
45 using size_type = typename std::pointer_traits<pointer>::difference_type;
46 private:
47 using void_pointer = typename std::pointer_traits<pointer>::template rebind<void>;
48 public:
49 template<std::size_t AA = Align>
allocate(size_type required_bytes,size_type=AA)50 void_pointer allocate(size_type required_bytes, size_type = AA){
51 if(required_bytes > 0) throw std::bad_alloc{};
52 return nullptr;
53 }
deallocate(void_pointer p,size_type)54 void deallocate(void_pointer p, size_type /*discarded_bytes*/){
55 if(p != nullptr) throw std::bad_alloc{};
56 }
57 };
58
59 template<typename Ptr = byte*, std::size_t Align = alignof(std::max_align_t)>
60 class monotonic : protected block<Ptr>{
61 protected:
62 using void_pointer = typename std::pointer_traits<typename monotonic::pointer>::template rebind<void>;
63 public:
64 using block<Ptr>::start_;
65 using block<Ptr>::size_;
66 using block<Ptr>::block;
67 typename monotonic::pointer position_ = start_;
reset()68 void reset(){position_ = this->start_;}
69 template<std::size_t AA = Align>
allocate(typename monotonic::size_type required_bytes,typename monotonic::size_type align=AA)70 typename monotonic::void_pointer allocate(
71 typename monotonic::size_type required_bytes,
72 typename monotonic::size_type align = AA//alignof(std::max_align_t)
73 ){
74 auto ret = align_up(this->position_, align);
75 auto new_position_ = ret + required_bytes;
76 using std::distance;
77 if(not this->contains(new_position_-1))
78 throw overflow(required_bytes, this->size_ - distance(start_, position_));
79 this->position_ = new_position_;
80 return ret;
81 }
owns(typename monotonic::void_pointer p) const82 bool owns(typename monotonic::void_pointer p) const{
83 return this->contains(static_cast<typename monotonic::pointer>(p));
84 }
deallocate(typename monotonic::void_pointer p,typename monotonic::size_type)85 void deallocate(
86 typename monotonic::void_pointer p,
87 typename monotonic::size_type /*discarded_bytes*/
88 ){
89 if(not owns(p)) throw std::bad_alloc{};
90 }
91 struct overflow : public std::bad_alloc{
92 using size_type = typename monotonic::size_type;
93 size_type required;
94 size_type available;
95 // constexpr auto to_string = [](auto a){return std::to_string(a);};
96 std::string msg;
overflowboost::multi::memory::monotonic::overflow97 overflow(size_type required, size_type available)
98 : required{required}, available{available},
99 msg{"required "+std::to_string(required)+" while only "+std::to_string(available)+" bytes available"}
100 {}
whatboost::multi::memory::monotonic::overflow101 virtual const char* what() const noexcept{return msg.c_str();}// + std::to_string(required)).c_str();}
102 };
103 };
104
105 template<class T = void>
106 using monotonic_allocator = multi::memory::allocator<T, monotonic<char*>>;
107
108 }}}
109
110 #if not __INCLUDE_LEVEL__ // _TEST_BOOST_MULTI_MEMORY_MONOTONIC
111
112 #include "../../multi/array.hpp"
113
114 #include<iostream>
115 #include<vector>
116 #include<cmath>
117
118 namespace multi = boost::multi;
119 using std::cout;
120
main()121 int main(){
122 {
123 multi::memory::null_t<char*> mr;
124 try{
125 mr.allocate(1*sizeof(double), alignof(double));
126 }catch(...){}
127 }
128 {
129 alignas(double) char buffer[256*sizeof(double)];
130 multi::memory::monotonic<char*> m(buffer);
131 auto p1 = m.allocate(1*sizeof(double), alignof(double));
132 auto p2 = m.allocate(255*sizeof(double), alignof(double));
133 m.deallocate(p2, 255*sizeof(double));
134 m.deallocate(p1, 1*sizeof(double));
135 try{
136 m.deallocate((char*)p1 + 10000, 1*sizeof(double));
137 }catch(...){}
138 }
139 {
140 alignas(double) char buffer[300*sizeof(double)];
141 multi::memory::monotonic<char*> m(&buffer[0], 300*sizeof(double));
142 multi::memory::monotonic_allocator<double> alloc(&m);
143 multi::array<double, 2, multi::memory::monotonic_allocator<double>> A({10, 10}, &m);
144 multi::array<double, 2, multi::memory::monotonic_allocator<double>> B({10, 10}, &m);
145 multi::array<double, 2, multi::memory::monotonic_allocator<double>> C({10, 10}, &m);
146 }
147 // m.allocate(156*sizeof(double), alignof(double));
148
149
150
151 // m.allocate(256*sizeof(double));
152 // typename monotonic::pointer
153
154 #if 0
155 std::cout << sizeof(std::max_align_t) << " " << alignof(std::max_align_t) << std::endl;
156 // return 0;
157 {
158 multi::monotonic_buffer<> buf(250*sizeof(double));
159 {
160 multi::array<double, 2, multi::monotonic_allocator<> > A({10, 10}, &buf);
161 multi::array<double, 2, multi::monotonic_allocator<> > B({10, 10}, &buf);
162 multi::array<double, 2, multi::monotonic_allocator<> > C({10, 10}, &buf);
163 }
164 assert( buf.hits() == 2 );
165 assert( buf.misses() == 1 );
166 cout
167 <<"size: "<< buf.size()
168 <<"\nsaved: "<< buf.hits()
169 <<"\nmisses "<< buf.misses()
170 <<"\nallocated(bytes) "<< buf.allocated_bytes()
171 <<"\nreleased(bytes) "<< buf.deallocated_bytes()
172 << std::endl
173 ;
174 }
175 cout<<"----------"<<std::endl;
176 {
177 std::size_t guess = 0;
178 multi::monotonic_buffer<std::allocator<char>> buf(guess*sizeof(double));
179 for(int i = 0; i != 3; ++i){
180 cout<<"pass "<< i << std::endl;
181 {
182 multi::array<double, 2, multi::monotonic_allocator<> > A({10, 10}, &buf);
183 multi::array<double, 2, multi::monotonic_allocator<> > B({10, 10}, &buf);
184 multi::array<double, 2, multi::monotonic_allocator<> > C({10, 10}, &buf);
185 }
186 cout
187 <<" size: "<< buf.size()
188 <<"\n save: "<< buf.hits()
189 <<"\n misses "<< buf.misses()
190 <<"\n allocated(bytes) "<< buf.allocated_bytes()
191 <<"\n released(bytes) "<< buf.deallocated_bytes()
192 << std::endl
193 ;
194 // guess = std::max(guess, buf.allocated_bytes());
195 buf.reset(buf.allocated_bytes());
196 }
197 }
198 cout<<"----------monotonic"<<std::endl;
199 {
200 std::size_t guess_bytes = 120;
201 for(int i = 0; i != 3; ++i){
202 cout<<"pass "<< i << std::endl;
203 multi::monotonic_buffer<std::allocator<char>> buf(guess_bytes);
204 {
205 multi::array<double, 2, multi::monotonic_allocator<> > A({10, 10}, &buf);
206 for(int i = 0; i != 3; ++i){
207 multi::array<double, 2, multi::monotonic_allocator<> > B({10, 10}, &buf);
208 std::vector<int, multi::monotonic_allocator<int>> v(3, &buf);
209 v.push_back(33); v.push_back(33);
210 }
211 multi::array<double, 2, multi::monotonic_allocator<> > C({10, 10}, &buf);
212 }
213 cout
214 <<" size: "<< buf.size()
215 <<"\n hits: "<< buf.hits()
216 <<"\n misses "<< buf.misses()
217 <<"\n allocated(bytes) "<< buf.allocated_bytes()
218 <<"\n released(bytes) "<< buf.deallocated_bytes()
219 << std::endl
220 ;
221 guess_bytes = std::max(guess_bytes, buf.allocated_bytes());
222 }
223 }
224 #endif
225 }
226 #endif
227 #endif
228
229