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