1 /*
2  * Copyright (C) 2020 Robin Gareus <robin@gareus.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #ifndef PBD_STACK_ALLOCATOR_H
20 #define PBD_STACK_ALLOCATOR_H
21 
22 #include <boost/type_traits/aligned_storage.hpp>
23 #include <limits>
24 
25 #include "pbd/libpbd_visibility.h"
26 
27 #if 0
28 # include <cstdio>
29 # define DEBUG_STACK_ALLOC(...) printf (__VA_ARGS__)
30 #else
31 # define DEBUG_STACK_ALLOC(...)
32 #endif
33 
34 namespace PBD {
35 
36 template <class T, std::size_t stack_capacity>
37 class /*LIBPBD_API*/ StackAllocator
38 {
39 public:
40 #if 0 /* may be needed for compatibility */
41 	typedef typename std::allocator<T>::value_type value_type;
42 	typedef typename std::allocator<T>::size_type size_type;
43 	typedef typename std::allocator<T>::difference_type difference_type;
44 	typedef typename std::allocator<T>::pointer pointer;
45 	typedef typename std::allocator<T>::const_pointer const_pointer;
46 	typedef typename std::allocator<T>::reference reference;
47 	typedef typename std::allocator<T>::const_reference const_reference;
48 #else
49 	typedef T                 value_type;
50 	typedef std::size_t       size_type;
51 	typedef std::ptrdiff_t    difference_type;
52 	typedef value_type*       pointer;
53 	typedef const value_type* const_pointer;
54 	typedef value_type&       reference;
55 	typedef const value_type& const_reference;
56 #endif
57 
58 	template <class U>
59 	struct rebind {
60 		typedef StackAllocator<U, stack_capacity> other;
61 	};
62 
StackAllocator()63 	StackAllocator ()
64 		: _ptr ((pointer)&_buf)
65 	{ }
66 
StackAllocator(const StackAllocator &)67 	StackAllocator (const StackAllocator&)
68 		: _ptr ((pointer)&_buf)
69 	{ }
70 
71 	template <typename U, size_t other_capacity>
StackAllocator(const StackAllocator<U,other_capacity> &)72 	StackAllocator (const StackAllocator<U, other_capacity>&)
73 		: _ptr ((pointer)&_buf)
74 	{ }
75 
76 	/* inspired by http://howardhinnant.github.io/stack_alloc.h */
77 	pointer allocate (size_type n, void* hint = 0)
78 	{
79 		if ((pointer)&_buf + stack_capacity >= _ptr + n) {
80 			DEBUG_STACK_ALLOC ("Allocate %ld item(s) of size %zu on the stack\n", n, sizeof (T));
81 			pointer rv = _ptr;
82 			_ptr += n;
83 			return rv;
84 		} else {
85 			DEBUG_STACK_ALLOC ("Allocate using new (%ld * %zu)\n", n, sizeof (T));
86 			return static_cast<pointer> (::operator new (n * sizeof (T)));
87 		}
88 	}
89 
deallocate(pointer p,size_type n)90 	void deallocate (pointer p, size_type n)
91 	{
92 		if (pointer_in_buffer (p)) {
93 			if (p + n == _ptr) {
94 				DEBUG_STACK_ALLOC ("Deallocate: pop item from the top of the stack\n");
95 				_ptr = p;
96 			} else {
97 				DEBUG_STACK_ALLOC ("Deallocate: ignored. Item is not at the top of the stack \n");
98 			}
99 		} else {
100 			::operator delete (p);
101 		}
102 	}
103 
max_size()104 	size_type max_size () const throw ()
105 	{
106 		return std::numeric_limits<size_type>::max () / sizeof (T);
107 	}
108 
109 	bool operator== (StackAllocator const& a) const
110 	{
111 		return &_buf == &a._buf;
112 	}
113 
114 	bool operator!= (StackAllocator const& a) const
115 	{
116 		return &_buf != &a._buf;
117 	}
118 
119 	template <class U>
destroy(U * const p)120 	void destroy (U* const p)
121 	{
122 		p->~U ();
123 	}
124 
125 	template <class U>
construct(U * const p)126 	void construct (U* const p)
127 	{
128 		new (p) U ();
129 	}
130 
131 #if __cplusplus > 201103L || defined __clang__
132 	template <class U, class A>
construct(U * const p,A * const a)133 	void construct (U* const p, A* const a)
134 	{
135 		new (p) U (a);
136 	}
137 #else
138 	template <class U, class A>
construct(U * const p,A const & a)139 	void construct (U* const p, A const& a)
140 	{
141 		new (p) U (a);
142 	}
143 #endif
144 
145 private:
146 	StackAllocator& operator= (const StackAllocator&);
147 
pointer_in_buffer(pointer const p)148 	bool pointer_in_buffer (pointer const p)
149 	{
150 		return ((pointer const)&_buf <= p && p < (pointer const)&_buf + stack_capacity);
151 	}
152 
153 	typedef typename boost::aligned_storage<sizeof (T) * stack_capacity, 16>::type align_t;
154 
155 	align_t _buf;
156 	pointer _ptr;
157 };
158 
159 } // namespace PBD
160 
161 #endif
162