1 /**
2 * @file lv_mem.c
3 * General and portable implementation of malloc and free.
4 * The dynamic memory monitoring is also supported.
5 */
6
7 /*********************
8 * INCLUDES
9 *********************/
10 #include "lv_mem.h"
11 #include "lv_math.h"
12 #include <string.h>
13
14 #if LV_MEM_CUSTOM != 0
15 #include LV_MEM_CUSTOM_INCLUDE
16 #endif
17
18 /*********************
19 * DEFINES
20 *********************/
21 /*Add memory junk on alloc (0xaa) and free(0xbb) (just for testing purposes)*/
22 #define LV_MEM_ADD_JUNK 1
23
24 #ifdef LV_MEM_ENV64
25 #define MEM_UNIT uint64_t
26 #else
27 #define MEM_UNIT uint32_t
28 #endif
29
30 /**********************
31 * TYPEDEFS
32 **********************/
33
34 #if LV_ENABLE_GC == 0 /*gc custom allocations must not include header*/
35
36 /*The size of this union must be 4 bytes (uint32_t)*/
37 typedef union
38 {
39 struct
40 {
41 MEM_UNIT used : 1; /* 1: if the entry is used*/
42 MEM_UNIT d_size : 31; /* Size off the data (1 means 4 bytes)*/
43 } s;
44 MEM_UNIT header; /* The header (used + d_size)*/
45 } lv_mem_header_t;
46
47 typedef struct
48 {
49 lv_mem_header_t header;
50 uint8_t first_data; /*First data byte in the allocated data (Just for easily create a pointer)*/
51 } lv_mem_ent_t;
52
53 #endif /* LV_ENABLE_GC */
54
55 /**********************
56 * STATIC PROTOTYPES
57 **********************/
58 #if LV_MEM_CUSTOM == 0
59 static lv_mem_ent_t * ent_get_next(lv_mem_ent_t * act_e);
60 static void * ent_alloc(lv_mem_ent_t * e, uint32_t size);
61 static void ent_trunc(lv_mem_ent_t * e, uint32_t size);
62 #endif
63
64 /**********************
65 * STATIC VARIABLES
66 **********************/
67 #if LV_MEM_CUSTOM == 0
68 static uint8_t * work_mem;
69 #endif
70
71 static uint32_t zero_mem; /*Give the address of this variable if 0 byte should be allocated*/
72
73 /**********************
74 * MACROS
75 **********************/
76
77 /**********************
78 * GLOBAL FUNCTIONS
79 **********************/
80
81 /**
82 * Initiaiize the dyn_mem module (work memory and other variables)
83 */
lv_mem_init(void)84 void lv_mem_init(void)
85 {
86 #if LV_MEM_CUSTOM == 0
87
88 #if LV_MEM_ADR == 0
89 /*Allocate a large array to store the dynamically allocated data*/
90 static LV_MEM_ATTR MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)];
91 work_mem = (uint8_t *)work_mem_int;
92 #else
93 work_mem = (uint8_t *)LV_MEM_ADR;
94 #endif
95
96 lv_mem_ent_t * full = (lv_mem_ent_t *)work_mem;
97 full->header.s.used = 0;
98 /*The total mem size id reduced by the first header and the close patterns */
99 full->header.s.d_size = LV_MEM_SIZE - sizeof(lv_mem_header_t);
100 #endif
101 }
102
103 /**
104 * Allocate a memory dynamically
105 * @param size size of the memory to allocate in bytes
106 * @return pointer to the allocated memory
107 */
lv_mem_alloc(uint32_t size)108 void * lv_mem_alloc(uint32_t size)
109 {
110 if(size == 0) {
111 return &zero_mem;
112 }
113
114 #ifdef LV_MEM_ENV64
115 /*Round the size up to 8*/
116 if(size & 0x7) {
117 size = size & (~0x7);
118 size += 8;
119 }
120 #else
121 /*Round the size up to 4*/
122 if(size & 0x3) {
123 size = size & (~0x3);
124 size += 4;
125 }
126 #endif
127 void * alloc = NULL;
128
129 #if LV_MEM_CUSTOM == 0
130 /*Use the built-in allocators*/
131 lv_mem_ent_t * e = NULL;
132
133 /* Search for a appropriate entry*/
134 do {
135 /* Get the next entry*/
136 e = ent_get_next(e);
137
138 /*If there is next entry then try to allocate there*/
139 if(e != NULL) {
140 alloc = ent_alloc(e, size);
141 }
142 /* End if there is not next entry OR the alloc. is successful*/
143 } while(e != NULL && alloc == NULL);
144
145 #else
146 /*Use custom, user defined malloc function*/
147 #if LV_ENABLE_GC == 1 /*gc must not include header*/
148 alloc = LV_MEM_CUSTOM_ALLOC(size);
149 #else /* LV_ENABLE_GC */
150 /*Allocate a header too to store the size*/
151 alloc = LV_MEM_CUSTOM_ALLOC(size + sizeof(lv_mem_header_t));
152 if(alloc != NULL) {
153 ((lv_mem_ent_t *)alloc)->header.s.d_size = size;
154 ((lv_mem_ent_t *)alloc)->header.s.used = 1;
155
156 alloc = &((lv_mem_ent_t *)alloc)->first_data;
157 }
158 #endif /* LV_ENABLE_GC */
159 #endif /* LV_MEM_CUSTOM */
160
161 #if LV_MEM_ADD_JUNK
162 if(alloc != NULL) memset(alloc, 0xaa, size);
163 #endif
164
165 if(alloc == NULL) LV_LOG_WARN("Couldn't allocate memory");
166
167 return alloc;
168 }
169
170 /**
171 * Free an allocated data
172 * @param data pointer to an allocated memory
173 */
lv_mem_free(const void * data)174 void lv_mem_free(const void * data)
175 {
176 if(data == &zero_mem) return;
177 if(data == NULL) return;
178
179 #if LV_MEM_ADD_JUNK
180 memset((void *)data, 0xbb, lv_mem_get_size(data));
181 #endif
182
183 #if LV_ENABLE_GC == 0
184 /*e points to the header*/
185 lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *)data - sizeof(lv_mem_header_t));
186 e->header.s.used = 0;
187 #endif
188
189 #if LV_MEM_CUSTOM == 0
190 #if LV_MEM_AUTO_DEFRAG
191 /* Make a simple defrag.
192 * Join the following free entries after this*/
193 lv_mem_ent_t * e_next;
194 e_next = ent_get_next(e);
195 while(e_next != NULL) {
196 if(e_next->header.s.used == 0) {
197 e->header.s.d_size += e_next->header.s.d_size + sizeof(e->header);
198 } else {
199 break;
200 }
201 e_next = ent_get_next(e_next);
202 }
203 #endif
204 #else /*Use custom, user defined free function*/
205 #if LV_ENABLE_GC == 0
206 LV_MEM_CUSTOM_FREE(e);
207 #else
208 LV_MEM_CUSTOM_FREE((void *)data);
209 #endif /*LV_ENABLE_GC*/
210 #endif
211 }
212
213 /**
214 * Reallocate a memory with a new size. The old content will be kept.
215 * @param data pointer to an allocated memory.
216 * Its content will be copied to the new memory block and freed
217 * @param new_size the desired new size in byte
218 * @return pointer to the new memory
219 */
220
221 #if LV_ENABLE_GC == 0
222
lv_mem_realloc(void * data_p,uint32_t new_size)223 void * lv_mem_realloc(void * data_p, uint32_t new_size)
224 {
225 /*data_p could be previously freed pointer (in this case it is invalid)*/
226 if(data_p != NULL) {
227 lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *)data_p - sizeof(lv_mem_header_t));
228 if(e->header.s.used == 0) {
229 data_p = NULL;
230 }
231 }
232
233 uint32_t old_size = lv_mem_get_size(data_p);
234 if(old_size == new_size) return data_p; /*Also avoid reallocating the same memory*/
235
236 #if LV_MEM_CUSTOM == 0
237 /* Truncate the memory if the new size is smaller. */
238 if(new_size < old_size) {
239 lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *)data_p - sizeof(lv_mem_header_t));
240 ent_trunc(e, new_size);
241 return &e->first_data;
242 }
243 #endif
244
245 void * new_p;
246 new_p = lv_mem_alloc(new_size);
247
248 if(new_p != NULL && data_p != NULL) {
249 /*Copy the old data to the new. Use the smaller size*/
250 if(old_size != 0) {
251 memcpy(new_p, data_p, LV_MATH_MIN(new_size, old_size));
252 lv_mem_free(data_p);
253 }
254 }
255
256 if(new_p == NULL) LV_LOG_WARN("Couldn't allocate memory");
257
258 return new_p;
259 }
260
261 #else /* LV_ENABLE_GC */
262
lv_mem_realloc(void * data_p,uint32_t new_size)263 void * lv_mem_realloc(void * data_p, uint32_t new_size)
264 {
265 void * new_p = LV_MEM_CUSTOM_REALLOC(data_p, new_size);
266 if(new_p == NULL) LV_LOG_WARN("Couldn't allocate memory");
267 return new_p;
268 }
269
270 #endif /* lv_enable_gc */
271
272 /**
273 * Join the adjacent free memory blocks
274 */
lv_mem_defrag(void)275 void lv_mem_defrag(void)
276 {
277 #if LV_MEM_CUSTOM == 0
278 lv_mem_ent_t * e_free;
279 lv_mem_ent_t * e_next;
280 e_free = ent_get_next(NULL);
281
282 while(1) {
283 /*Search the next free entry*/
284 while(e_free != NULL) {
285 if(e_free->header.s.used != 0) {
286 e_free = ent_get_next(e_free);
287 } else {
288 break;
289 }
290 }
291
292 if(e_free == NULL) return;
293
294 /*Joint the following free entries to the free*/
295 e_next = ent_get_next(e_free);
296 while(e_next != NULL) {
297 if(e_next->header.s.used == 0) {
298 e_free->header.s.d_size += e_next->header.s.d_size + sizeof(e_next->header);
299 } else {
300 break;
301 }
302
303 e_next = ent_get_next(e_next);
304 }
305
306 if(e_next == NULL) return;
307
308 /*Continue from the lastly checked entry*/
309 e_free = e_next;
310 }
311 #endif
312 }
313
314 /**
315 * Give information about the work memory of dynamic allocation
316 * @param mon_p pointer to a dm_mon_p variable,
317 * the result of the analysis will be stored here
318 */
lv_mem_monitor(lv_mem_monitor_t * mon_p)319 void lv_mem_monitor(lv_mem_monitor_t * mon_p)
320 {
321 /*Init the data*/
322 memset(mon_p, 0, sizeof(lv_mem_monitor_t));
323 #if LV_MEM_CUSTOM == 0
324 lv_mem_ent_t * e;
325 e = NULL;
326
327 e = ent_get_next(e);
328
329 while(e != NULL) {
330 if(e->header.s.used == 0) {
331 mon_p->free_cnt++;
332 mon_p->free_size += e->header.s.d_size;
333 if(e->header.s.d_size > mon_p->free_biggest_size) {
334 mon_p->free_biggest_size = e->header.s.d_size;
335 }
336 } else {
337 mon_p->used_cnt++;
338 }
339
340 e = ent_get_next(e);
341 }
342 mon_p->total_size = LV_MEM_SIZE;
343 mon_p->used_pct = 100 - (100U * mon_p->free_size) / mon_p->total_size;
344 mon_p->frag_pct = (uint32_t)mon_p->free_biggest_size * 100U / mon_p->free_size;
345 mon_p->frag_pct = 100 - mon_p->frag_pct;
346 #endif
347 }
348
349 /**
350 * Give the size of an allocated memory
351 * @param data pointer to an allocated memory
352 * @return the size of data memory in bytes
353 */
354
355 #if LV_ENABLE_GC == 0
356
lv_mem_get_size(const void * data)357 uint32_t lv_mem_get_size(const void * data)
358 {
359 if(data == NULL) return 0;
360 if(data == &zero_mem) return 0;
361
362 lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *)data - sizeof(lv_mem_header_t));
363
364 return e->header.s.d_size;
365 }
366
367 #else /* LV_ENABLE_GC */
368
lv_mem_get_size(const void * data)369 uint32_t lv_mem_get_size(const void * data)
370 {
371 return LV_MEM_CUSTOM_GET_SIZE(data);
372 }
373
374 #endif /*LV_ENABLE_GC*/
375
376 /**********************
377 * STATIC FUNCTIONS
378 **********************/
379
380 #if LV_MEM_CUSTOM == 0
381 /**
382 * Give the next entry after 'act_e'
383 * @param act_e pointer to an entry
384 * @return pointer to an entry after 'act_e'
385 */
ent_get_next(lv_mem_ent_t * act_e)386 static lv_mem_ent_t * ent_get_next(lv_mem_ent_t * act_e)
387 {
388 lv_mem_ent_t * next_e = NULL;
389
390 if(act_e == NULL) { /*NULL means: get the first entry*/
391 next_e = (lv_mem_ent_t *)work_mem;
392 } else { /*Get the next entry */
393 uint8_t * data = &act_e->first_data;
394 next_e = (lv_mem_ent_t *)&data[act_e->header.s.d_size];
395
396 if(&next_e->first_data >= &work_mem[LV_MEM_SIZE]) next_e = NULL;
397 }
398
399 return next_e;
400 }
401
402 /**
403 * Try to do the real allocation with a given size
404 * @param e try to allocate to this entry
405 * @param size size of the new memory in bytes
406 * @return pointer to the allocated memory or NULL if not enough memory in the entry
407 */
ent_alloc(lv_mem_ent_t * e,uint32_t size)408 static void * ent_alloc(lv_mem_ent_t * e, uint32_t size)
409 {
410 void * alloc = NULL;
411
412 /*If the memory is free and big enough then use it */
413 if(e->header.s.used == 0 && e->header.s.d_size >= size) {
414 /*Truncate the entry to the desired size */
415 ent_trunc(e, size),
416
417 e->header.s.used = 1;
418
419 /*Save the allocated data*/
420 alloc = &e->first_data;
421 }
422
423 return alloc;
424 }
425
426 /**
427 * Truncate the data of entry to the given size
428 * @param e Pointer to an entry
429 * @param size new size in bytes
430 */
ent_trunc(lv_mem_ent_t * e,uint32_t size)431 static void ent_trunc(lv_mem_ent_t * e, uint32_t size)
432 {
433 #ifdef LV_MEM_ENV64
434 /*Round the size up to 8*/
435 if(size & 0x7) {
436 size = size & (~0x7);
437 size += 8;
438 }
439 #else
440 /*Round the size up to 4*/
441 if(size & 0x3) {
442 size = size & (~0x3);
443 size += 4;
444 }
445 #endif
446
447 /*Don't let empty space only for a header without data*/
448 if(e->header.s.d_size == size + sizeof(lv_mem_header_t)) {
449 size = e->header.s.d_size;
450 }
451
452 /* Create the new entry after the current if there is space for it */
453 if(e->header.s.d_size != size) {
454 uint8_t * e_data = &e->first_data;
455 lv_mem_ent_t * after_new_e = (lv_mem_ent_t *)&e_data[size];
456 after_new_e->header.s.used = 0;
457 after_new_e->header.s.d_size = e->header.s.d_size - size - sizeof(lv_mem_header_t);
458 }
459
460 /* Set the new size for the original entry */
461 e->header.s.d_size = size;
462 }
463
464 #endif
465