1 /* USER CODE BEGIN Header */
2 /**
3   ******************************************************************************
4   * @file    dm_alloc.c
5   * @author  GPM WBL Application Team
6   * @brief   Dynamic Memory Allocator
7   ******************************************************************************
8   * @attention
9   *
10   * Copyright (c) 2024 STMicroelectronics.
11   * All rights reserved.
12   *
13   * This software is licensed under terms that can be found in the LICENSE file
14   * in the root directory of this software component.
15   * If no LICENSE file comes with this software, it is provided AS-IS.
16   *
17   ******************************************************************************
18   */
19 /* USER CODE END Header */
20 
21 /******************************************************************************
22  * INCLUDE HEADER FILES
23  *****************************************************************************/
24 #include "dm_alloc.h"
25 #include "osal.h"
26 #include <string.h>
27 
28 /******************************************************************************
29  * LOCAL MACROS
30  *****************************************************************************/
31 #define ALIGN_UPTO_32BITS(VAL)          (((((unsigned int)(VAL)) - 1U) | (sizeof(uint32_t) - 1U)) + 1U)
32 #define DM_SLICE_THRESHOLD              (3 * sizeof(db_alloc_header_t))
33 #define DM_FREE                         0x00
34 #define DM_ALLOC                        0x01
35 /*#define DM_DEBUG                        (1) */
36 #if defined(DM_DEBUG)
37 #define DM_DEBUG_STAMP                  (0xBEAF)
38 #endif
39 
40 #define MIN(a,b)                        (((a) < (b))? (a) : (b))
41 
42 /******************************************************************************
43  * LOCAL TYPEDEFS (STRUCTURES, UNIONS, ENUMS)
44  *****************************************************************************/
45 typedef struct db_alloc_header_s {
46     uint16_t buffer_size;
47     uint16_t flags;
48     uint32_t buffer_a[];
49 } db_alloc_header_t;
50 
51 typedef struct db_free_header_s {
52     uint16_t buffer_size;
53     uint16_t flags;
54     struct db_free_header_s *next;
55     uint32_t buffer_a[];
56 } dm_free_header_t;
57 
58 typedef struct dm_ctx_s {
59 #if defined(DM_DEBUG)
60     uint16_t alloc_size;
61     uint16_t alloc_max_size;
62 #endif
63     dm_free_header_t *head;
64     uint32_t *alloc_space_p;
65     uint16_t buffer_size;
66 } dm_ctx_t;
67 
68 /******************************************************************************
69  * LOCAL FUNCTION PROTOTYPES
70  *****************************************************************************/
71 /******************************************************************************
72  * Local Variables
73  *****************************************************************************/
74 static dm_ctx_t dm_ctx;
75 
dm_init(uint16_t buffer_size,uint32_t * buffer_p)76 void dm_init(uint16_t buffer_size, uint32_t *buffer_p)
77 {
78     dm_ctx.alloc_space_p = (void *)buffer_p;
79     dm_ctx.head = (dm_free_header_t *)buffer_p;
80     dm_ctx.head->buffer_size = buffer_size;
81     dm_ctx.buffer_size = buffer_size;
82     dm_ctx.head->flags = DM_FREE;
83 #if defined(DM_DEBUG)
84     dm_ctx.alloc_size = 0U;
85     dm_ctx.alloc_max_size = 0U;
86 #endif
87     dm_ctx.head->next = NULL;
88 }
89 
db_extract_from_free_list(dm_free_header_t * entry_p)90 static void db_extract_from_free_list(dm_free_header_t *entry_p)
91 {
92     dm_free_header_t *e_p;
93 
94 #if defined(DM_DEBUG)
95     if (entry_p->flags != DM_FREE)
96     {
97         while (1)
98             ;
99     }
100 #endif
101     if (entry_p == dm_ctx.head)
102     {
103         dm_ctx.head = entry_p->next;
104     }
105     else
106     {
107         e_p = dm_ctx.head;
108         while (e_p != NULL)
109         {
110             if (e_p->next == entry_p)
111             {
112                 e_p->next = entry_p->next;
113                 break;
114             }
115             e_p = e_p->next;
116         }
117     }
118 }
119 
db_add_to_free_list(dm_free_header_t * free_entry_p)120 static void db_add_to_free_list(dm_free_header_t *free_entry_p)
121 {
122     dm_free_header_t *prev_p;
123 
124     if (free_entry_p != NULL)
125     {
126 #if defined(DM_DEBUG)
127         if (free_entry_p->flags != DM_FREE)
128         {
129             while (1)
130                 ;
131         }
132 #endif
133         prev_p = NULL;
134         if (dm_ctx.head == NULL)
135         {
136             /**
137              * The free list is empty. Assign the new entry to the list.
138              */
139             dm_ctx.head = free_entry_p;
140         }
141         else
142         {
143             if ((uintptr_t)free_entry_p < (uintptr_t)dm_ctx.head)
144             {
145                 /**
146                  * Insert the new element at the head of the list.
147                  */
148                 free_entry_p->next = dm_ctx.head;
149                 dm_ctx.head = free_entry_p;
150             }
151             else
152             {
153                 prev_p = dm_ctx.head;
154                 while (prev_p->next != NULL)
155                 {
156                     /**
157                      * The free list is ordered by index (address) then search
158                      * into the list to find the previous node.
159                      */
160                     if ((uintptr_t)prev_p->next > (uintptr_t)free_entry_p)
161                     {
162                         break;
163                     }
164                     prev_p = prev_p->next;
165                 }
166 
167                 /**
168                  * Insert the new element.
169                  */
170                 free_entry_p->next = prev_p->next;
171                 prev_p->next = free_entry_p;
172 
173                 /**
174                  * Try to make the new free entry coalesce with the previous.
175                  */
176                 if (((uintptr_t)prev_p + prev_p->buffer_size) ==
177                     (uintptr_t)free_entry_p)
178                 {
179                     prev_p->next = free_entry_p->next;
180                     prev_p->buffer_size += free_entry_p->buffer_size;
181                 }
182             }
183 
184             /**
185              * Try to make the new free entry coalesce with the next.
186              */
187             if (((uintptr_t)free_entry_p + free_entry_p->buffer_size) ==
188                 (uintptr_t)free_entry_p->next)
189             {
190                 dm_free_header_t *ne_p;
191 
192                 ne_p = free_entry_p->next;
193                 free_entry_p->next = ne_p->next;
194                 free_entry_p->buffer_size += ne_p->buffer_size;
195             }
196         }
197     }
198 }
199 
200 /* Release part of a free block and add it to the list of free blocks.
201  Returns the size of the slot not freed. */
dm_slice(dm_free_header_t * free_entry_p,uint16_t min_size)202 static uint16_t dm_slice(dm_free_header_t *free_entry_p, uint16_t min_size)
203 {
204     uint16_t slice_size;
205 
206     slice_size = free_entry_p->buffer_size - min_size;
207     if (slice_size > DM_SLICE_THRESHOLD)
208     {
209         dm_free_header_t *slice_p;
210 
211         slice_p = (dm_free_header_t *)&free_entry_p->buffer_a[(min_size -
212                                                                sizeof(dm_free_header_t)) >> 2];
213         slice_p->buffer_size = slice_size;
214         slice_p->flags = DM_FREE;
215         slice_p->next = NULL;
216         db_add_to_free_list(slice_p);
217         return min_size;
218     }
219 
220     return free_entry_p->buffer_size;
221 }
222 
dm_alloc(uint16_t size)223 void *dm_alloc(uint16_t size)
224 {
225     uint16_t alloc_size;
226     dm_free_header_t *entry_p, *best_entry_p;
227 
228     best_entry_p = NULL;
229     entry_p = dm_ctx.head;
230     alloc_size = (uint16_t)(ALIGN_UPTO_32BITS(size) + sizeof(db_alloc_header_t));
231 #if defined(DM_DEBUG)
232     if (entry_p != NULL)
233     {
234         if (entry_p->flags != DM_FREE)
235         {
236             while (1)
237                 ;
238         }
239     }
240 #endif
241     while (entry_p != NULL)
242     {
243         /**
244          * Best fit strategy: search for the entry that has the size closer to
245          * the requested value.
246          */
247         if (entry_p->buffer_size >= alloc_size)
248         {
249             if ((best_entry_p == NULL) ||
250                 ((best_entry_p != NULL) &&
251                  (best_entry_p->buffer_size > entry_p->buffer_size)))
252             {
253                 best_entry_p = entry_p;
254             }
255         }
256         entry_p = entry_p->next;
257     }
258 
259     if (best_entry_p != NULL)
260     {
261         db_alloc_header_t *alloc_entry_p;
262 
263         /**
264          * Detach entry by free list.
265          */
266         db_extract_from_free_list(best_entry_p);
267 
268         /**
269          * If the extracted entry has a size "much" greater then the
270          * requested one then slice it releasing the not requested space.
271          */
272         best_entry_p->buffer_size = dm_slice(best_entry_p, alloc_size);
273 
274         alloc_entry_p = (db_alloc_header_t *)best_entry_p;
275 #if defined(DM_DEBUG)
276         if (alloc_entry_p->flags != DM_FREE)
277         {
278             while (1)
279                 ;
280         }
281         dm_ctx.alloc_size += alloc_size + sizeof(uint32_t);
282         if (dm_ctx.alloc_size > dm_ctx.alloc_max_size)
283         {
284             dm_ctx.alloc_max_size = dm_ctx.alloc_size;
285         }
286 #endif
287         best_entry_p->flags = DM_ALLOC;
288 
289         return alloc_entry_p->buffer_a;
290     }
291 
292     return NULL;
293 }
294 
dm_free(void * buffer_p)295 void dm_free(void *buffer_p)
296 {
297     if (buffer_p == NULL)
298         return;
299     dm_free_header_t *free_entry_p;
300     uint32_t *buffer32_p = buffer_p;
301 
302     free_entry_p = (dm_free_header_t *)(--buffer32_p);
303     free_entry_p->flags = DM_FREE;
304     free_entry_p->next = NULL;
305 #if defined(DM_DEBUG)
306     dm_ctx.alloc_size -= free_entry_p->buffer_size;
307 #endif
308     db_add_to_free_list(free_entry_p);
309 }
310 
dm_realloc(void * buffer_p,uint16_t size)311 void *dm_realloc(void *buffer_p, uint16_t size)
312 {
313     uint16_t total_alloc_size; /* Total size that needs to be allocated for new buffer (including existing one).  */
314     uint16_t add_alloc_size; /* Additional space needed in case allocated memory needs to be increased. */
315     dm_free_header_t *entry_p;
316     uint32_t *next_addr, *new_buffer_p;
317     uint32_t *buffer32_p = buffer_p;
318 
319     db_alloc_header_t *allocated_entry_p = (db_alloc_header_t *)(buffer32_p - 1);
320 
321     total_alloc_size = ALIGN_UPTO_32BITS(size) + sizeof(db_alloc_header_t);
322 
323     /* Check if current buffer has already the requested size.
324        If this is the case, try to reduce it. */
325     if(allocated_entry_p->buffer_size >= total_alloc_size)
326     {
327         allocated_entry_p->buffer_size = dm_slice((dm_free_header_t*)allocated_entry_p, total_alloc_size);
328 
329         return buffer_p;
330     }
331 
332     add_alloc_size = total_alloc_size - allocated_entry_p->buffer_size;
333 
334     next_addr = &allocated_entry_p->buffer_a[(allocated_entry_p->buffer_size - sizeof(db_alloc_header_t)) >> 2];
335 
336     entry_p = (dm_free_header_t*)next_addr;
337 
338     /* Look into next block and check if it is free and has enough space to contain additional data. */
339     if(next_addr < dm_ctx.alloc_space_p + dm_ctx.buffer_size && entry_p->flags == DM_FREE)
340     {
341         if(entry_p->buffer_size >= add_alloc_size)
342         {
343             /* Next contiguous slot is big enough. */
344 
345             db_extract_from_free_list(entry_p);
346 
347             allocated_entry_p->buffer_size += dm_slice(entry_p, add_alloc_size);
348 
349 #if defined(DM_DEBUG)
350             dm_ctx.alloc_size += alloc_size + sizeof(uint32_t);
351             if (dm_ctx.alloc_size > dm_ctx.alloc_max_size)
352             {
353                 dm_ctx.alloc_max_size = dm_ctx.alloc_size;
354             }
355 #endif
356             return allocated_entry_p->buffer_a;
357         }
358     }
359 
360     /* No contiguous free memory slot found with enough space.
361        Need to allocate new slot. */
362 
363     new_buffer_p = dm_alloc(size);
364     if (new_buffer_p != NULL)
365     {
366         /* Copy old data */
367         uint16_t old_data_size = allocated_entry_p->buffer_size - sizeof(db_alloc_header_t);
368         Osal_MemCpy(new_buffer_p, buffer_p, MIN(size, old_data_size));
369         dm_free(buffer_p);
370     }
371 
372     return new_buffer_p;
373 }
374 
375