1 // -*- C++ -*-
2 /** @file */
3 #pragma once
4 
5 
6 #include <cstdio>
7 #include <cstddef>
8 #include <vector>
9 #include "common.hpp"
10 
11 namespace arm_cmsis_dsp {
12 
13 /** \addtogroup MEMORY Memory allocator
14  *  \ingroup DSPPP
15  *  @{
16  */
17 
18 /*
19 
20 Buffer allocator
21 
22 Can be used to build memory allocators foe vector
23 and matrix.
24 
25 For instance, it is usedin the Memory pool allocator
26 
27 */
28 
29 /** \defgroup MEMBUF Memory buffer allocator
30  *  \ingroup MEMORY
31  *  Allocators for allocating memory buffers
32  */
33 
34 /** \defgroup MEMVEC Vector / matrix buffer allocator
35  *  \ingroup MEMORY
36  *  Allocators for allocating vector / matrix buffers
37  */
38 
39 /** \defgroup MEMTOOL Miscellaneous utilities for memory
40  *  \ingroup MEMORY
41  *  Miscellaneous utilities for implementing memory allocators
42  */
43 
44 /**
45  * @ingroup MEMBUF
46  * @brief      Malloc memory allocator
47  *
48  */
49 struct default_user_allocator_malloc_free
50 {
51   /**
52    * @brief      Allocate a buffer
53    *
54    * @param[in]  bytes  The bytes
55    *
56    * @return     A pointer to the allocated buffer
57    */
mallocarm_cmsis_dsp::default_user_allocator_malloc_free58   static char * malloc(const std::size_t bytes)
59   {
60     #if !defined(MEMORY_ALLOCATION_DEBUG)
61     return reinterpret_cast<char *>(std::malloc(bytes));
62     #else
63     char *ret=reinterpret_cast<char *>(std::malloc(bytes));
64     if (ret==nullptr)
65     {
66        std::cout << "out of memory for " << bytes << " bytes\r\n";
67     }
68     return(ret);
69     #endif
70   }
71 
72   /**
73    * @brief      Free a buffer
74    *
75    * @param      block  The buffer to free
76    */
freearm_cmsis_dsp::default_user_allocator_malloc_free77   static void free(char * const block)
78   {
79     #if defined(MEMORY_ALLOCATION_DEBUG)
80     if (block==nullptr)
81     {
82        std::cout << "free null ptr \r\n";
83     }
84     #endif
85     std::free(block);
86   }
87 };
88 
89 /**
90  * @ingroup MEMBUF
91  * @brief      Aligned memory allocation
92  *
93  * @param[in]  alignment  The alignment of the buffer
94  * @param[in]  size       The size of the buffer
95  *
96  * @return     A pointer to the new buffer
97  */
aligned_malloc(std::size_t alignment,std::size_t size)98 inline void* aligned_malloc(std::size_t alignment, std::size_t size)
99 {
100    void *ptr=std::malloc(size+alignment+sizeof(void*));
101    void *aligned =
102    reinterpret_cast<char*>(
103     (reinterpret_cast<std::size_t>(ptr)+sizeof(void*)+alignment) & ~(alignment-1)
104    );
105 
106    *(static_cast<void**>(aligned) - 1) = ptr;
107    return(aligned);
108 }
109 
110 /**
111  * @ingroup MEMBUF
112  * @brief      Free an aligned buffer
113  *
114  * @param      ptr   The pointer
115  */
116 inline void
aligned_free(void * ptr)117 aligned_free(void* ptr)
118 {
119     if (ptr) {
120         std::free(*(static_cast<void**>(ptr) - 1));
121     }
122 };
123 
124 /**
125  * @ingroup MEMBUF
126  * @brief      Memory allocation for aligned buffers
127  *
128  */
129 struct user_allocator_aligned_malloc
130 {
131   typedef std::size_t size_type;
132   typedef std::ptrdiff_t difference_type;
133 
134   /**
135    * @brief      Allocate a new buffer
136    *
137    * @param[in]  bytes  The bytes
138    *
139    * @return     Pointer to the new buffer
140    */
mallocarm_cmsis_dsp::user_allocator_aligned_malloc141   static char * malloc(const size_type bytes)
142   {
143     #if !defined(MEMORY_ALLOCATION_DEBUG)
144     return reinterpret_cast<char *>(aligned_malloc(MEMORY_POOL_ALIGNMENT, bytes));
145     #else
146     char *ret = reinterpret_cast<char *>(aligned_malloc(MEMORY_POOL_ALIGNMENT, bytes));
147     if (ret==nullptr)
148     {
149         std::cout << "out of memory for " << bytes << " bytes\r\n";
150     }
151     return(ret);
152     #endif
153   }
154 
155   /**
156    * @brief      Free a buffer
157    *
158    * @param      block  Pointer to the buffer
159    */
freearm_cmsis_dsp::user_allocator_aligned_malloc160   static void free(char * const block)
161   {
162     #if defined(MEMORY_ALLOCATION_DEBUG)
163     if (block==nullptr)
164     {
165        std::cout << "free null ptr \r\n";
166     }
167     #endif
168     aligned_free(block);
169   }
170 };
171 
172 /*
173 
174 Memory allocator for vector and matrix.
175 
176 */
177 
178 // Default allocator
179 // Other allocator must be provided by user of the library
180 
181 /**
182  * @ingroup MEMVEC
183  * @brief      Default memory allocator for vectors and matrixes
184  *
185  * @tparam     L     Size known at build time in bytes
186  */
187 template<int L>
188 struct malloc_allocator {
189     /**
190      * @brief      Allocate a buffer with size known at runtime
191      *
192      * @param[in]  sz    The size
193      *
194      * @return     Pointer to the buffer
195      */
allocatearm_cmsis_dsp::malloc_allocator196     static  char* allocate  ( vector_length_t sz) noexcept{
197         char *res;
198         res=reinterpret_cast<char*>(std::malloc(sz));
199         #if defined(MEMORY_ALLOCATION_DEBUG)
200         if (res==nullptr)
201         {
202            std::cout << "out of memory for " << sz << " bytes\r\n";
203         }
204         #endif
205         return(res);
206     }
207 
208     /**
209      * @brief      Allocate a buffer with size known at build time
210      *
211      * @return     Pointer to the buffer
212      */
allocatearm_cmsis_dsp::malloc_allocator213     static  char* allocate  ( ) noexcept{
214         char *res;
215         res=reinterpret_cast<char*>(std::malloc(L));
216         #if defined(MEMORY_ALLOCATION_DEBUG)
217         if (res==nullptr)
218         {
219            std::cout << "out of memory for " << L << " bytes\r\n";
220         }
221         #endif
222         return(res);
223     }
224 
225     /**
226      * @brief      Destroys the given pointer.
227      *
228      * @param      ptr   The pointer
229      */
destroyarm_cmsis_dsp::malloc_allocator230     static void destroy  ( char* ptr ) noexcept {
231         #if defined(MEMORY_ALLOCATION_DEBUG)
232         if (ptr==nullptr)
233         {
234            std::cout << "free null ptr \r\n";
235         }
236         #endif
237         std::free(ptr);
238     }
239 
240 };
241 
242 
243 /*
244 
245 Memory pool
246 
247 Memory pool  is using a buffer
248 allocator (aligned or normal malloc)
249 
250 A memory pool can be used to by a memory allocator for
251 vectors and matrixes.
252 
253 
254 */
255 
256 struct ListElem;
257 
258 /**
259  * @ingroup MEMTOOL
260  * @brief      Simple list of elements
261  *
262  */
263 struct ListElem {
264     ListElem *next;
265 };
266 
267 /**
268  * @ingroup MEMTOOL
269  * @brief      This class describes a memory pool that can be used to build
270  *             a memory allocator for vectors and matrixes
271  *
272  * @tparam     BUF_SIZE       Size of a buffer known at build time
273  * @tparam     UserAllocator  Memory allocator to allocate the memory buffer
274  */
275 template<int BUF_SIZE,typename UserAllocator = default_user_allocator_malloc_free>
276 class MemoryPool {
277 public:
278     /**
279      * @brief      Create a new memory pool
280      *
281      * @param[in]  nbBufs  The number of buffers to pre-allocate
282      */
MemoryPool(const uint16_t nbBufs)283     explicit MemoryPool(const uint16_t nbBufs)
284     {
285         buffer_list.reserve(nbBufs);
286         buffer_list.assign(nbBufs,nullptr);
287         for(auto p=buffer_list.begin();p != buffer_list.end(); ++p)
288         {
289             *p = UserAllocator::malloc(BUF_SIZE < sizeof(ListElem) ? sizeof(ListElem) : BUF_SIZE);
290         }
291         reset();
292     };
293 
294     /**
295      * @brief      Destroys the object.
296      */
~MemoryPool()297     ~MemoryPool()
298     {
299         for(auto p=buffer_list.begin();p != buffer_list.end(); ++p)
300         {
301             UserAllocator::free(*p);
302         }
303     }
304 
305     MemoryPool(const MemoryPool& other) = delete;
306 
307     MemoryPool(MemoryPool&& other) = delete;
308 
309 
310     MemoryPool& operator=(const MemoryPool& other) = delete;
311 
312     MemoryPool& operator=(MemoryPool&& other) = delete;
313 
314     /**
315      * @brief      Gets the new free buffer.
316      *
317      * @return     The new buffer.
318      */
get_new_buffer()319     char* get_new_buffer() noexcept
320     {
321          /* No error handling.
322             The sizing of the pool must have been done, for
323             instance, with a statistic allocator.
324             Allocation is thus assumed to succeed */
325          char* res = reinterpret_cast<char*>(free);
326          free = free->next;
327          #if defined(MEMORY_ALLOCATION_DEBUG)
328          if (res == nullptr)
329          {
330            std::cout << "memory pool alloc error " << BUF_SIZE << " bytes\r\n";
331          }
332          #endif
333          return(res);
334     }
335 
336     /**
337      * @brief      Release the buffer so that it can be reused
338      *
339      * @param      buf   The buffer
340      */
recycle_buffer(char * buf)341     void recycle_buffer(char* buf)  noexcept
342     {
343         ListElem *l = reinterpret_cast<ListElem*>(buf);
344         #if defined(MEMORY_ALLOCATION_DEBUG)
345         if (l == nullptr)
346         {
347            std::cout << "memory pool free error " << BUF_SIZE << " bytes\r\n";
348         }
349         #endif
350         l->next = free;
351         free = l;
352     }
353 
354     /**
355      * @brief      Release all the buffers so that they can be reused
356      */
reset()357     void reset()  noexcept
358     {
359         const int nbBufs = buffer_list.size();
360         for(int i=0;i<nbBufs-1;i++)
361         {
362             ListElem *l=reinterpret_cast<ListElem*>(buffer_list[i]);
363             l->next = reinterpret_cast<ListElem*>(buffer_list[i+1]);
364         }
365         ListElem *l=reinterpret_cast<ListElem*>(buffer_list[nbBufs-1]);
366         l->next = nullptr;
367         free = reinterpret_cast<ListElem*>(buffer_list[0]);
368     }
369 
370 
371 
372 protected:
373     ListElem *free;
374     std::vector<char*> buffer_list;
375 };
376 
377 
378 /*! @} */
379 
380 }