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_tlsf.h"
12 #include "lv_gc.h"
13 #include "lv_assert.h"
14 #include "lv_log.h"
15
16 #if LV_MEM_CUSTOM != 0
17 #include LV_MEM_CUSTOM_INCLUDE
18 #endif
19
20 #ifdef LV_MEM_POOL_INCLUDE
21 #include LV_MEM_POOL_INCLUDE
22 #endif
23
24 /*********************
25 * DEFINES
26 *********************/
27 /*memset the allocated memories to 0xaa and freed memories to 0xbb (just for testing purposes)*/
28 #ifndef LV_MEM_ADD_JUNK
29 #define LV_MEM_ADD_JUNK 0
30 #endif
31
32 #ifdef LV_ARCH_64
33 #define MEM_UNIT uint64_t
34 #define ALIGN_MASK 0x7
35 #else
36 #define MEM_UNIT uint32_t
37 #define ALIGN_MASK 0x3
38 #endif
39
40 #define ZERO_MEM_SENTINEL 0xa1b2c3d4
41
42 /**********************
43 * TYPEDEFS
44 **********************/
45
46 /**********************
47 * STATIC PROTOTYPES
48 **********************/
49 #if LV_MEM_CUSTOM == 0
50 static void lv_mem_walker(void * ptr, size_t size, int used, void * user);
51 #endif
52
53 /**********************
54 * STATIC VARIABLES
55 **********************/
56 #if LV_MEM_CUSTOM == 0
57 static lv_tlsf_t tlsf;
58 #endif
59
60 static uint32_t zero_mem = ZERO_MEM_SENTINEL; /*Give the address of this variable if 0 byte should be allocated*/
61
62 /**********************
63 * MACROS
64 **********************/
65 #if LV_LOG_TRACE_MEM
66 #define MEM_TRACE(...) LV_LOG_TRACE(__VA_ARGS__)
67 #else
68 #define MEM_TRACE(...)
69 #endif
70
71 #define COPY32 *d32 = *s32; d32++; s32++;
72 #define COPY8 *d8 = *s8; d8++; s8++;
73 #define SET32(x) *d32 = x; d32++;
74 #define SET8(x) *d8 = x; d8++;
75 #define REPEAT8(expr) expr expr expr expr expr expr expr expr
76
77 /**********************
78 * GLOBAL FUNCTIONS
79 **********************/
80
81 /**
82 * Initialize 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 #ifdef LV_MEM_POOL_ALLOC
90 tlsf = lv_tlsf_create_with_pool((void *)LV_MEM_POOL_ALLOC(LV_MEM_SIZE), LV_MEM_SIZE);
91 #else
92 /*Allocate a large array to store the dynamically allocated data*/
93 static LV_ATTRIBUTE_LARGE_RAM_ARRAY MEM_UNIT work_mem_int[LV_MEM_SIZE / sizeof(MEM_UNIT)];
94 tlsf = lv_tlsf_create_with_pool((void *)work_mem_int, LV_MEM_SIZE);
95 #endif
96 #else
97 tlsf = lv_tlsf_create_with_pool((void *)LV_MEM_ADR, LV_MEM_SIZE);
98 #endif
99 #endif
100
101 #if LV_MEM_ADD_JUNK
102 LV_LOG_WARN("LV_MEM_ADD_JUNK is enabled which makes LVGL much slower");
103 #endif
104 }
105
106 /**
107 * Clean up the memory buffer which frees all the allocated memories.
108 * @note It work only if `LV_MEM_CUSTOM == 0`
109 */
lv_mem_deinit(void)110 void lv_mem_deinit(void)
111 {
112 #if LV_MEM_CUSTOM == 0
113 lv_tlsf_destroy(tlsf);
114 lv_mem_init();
115 #endif
116 }
117
118 /**
119 * Allocate a memory dynamically
120 * @param size size of the memory to allocate in bytes
121 * @return pointer to the allocated memory
122 */
lv_mem_alloc(size_t size)123 void * lv_mem_alloc(size_t size)
124 {
125 MEM_TRACE("allocating %lu bytes", (unsigned long)size);
126 if(size == 0) {
127 MEM_TRACE("using zero_mem");
128 return &zero_mem;
129 }
130
131 #if LV_MEM_CUSTOM == 0
132 void * alloc = lv_tlsf_malloc(tlsf, size);
133 #else
134 void * alloc = LV_MEM_CUSTOM_ALLOC(size);
135 #endif
136
137 if(alloc == NULL) {
138 LV_LOG_ERROR("couldn't allocate memory (%lu bytes)", (unsigned long)size);
139 lv_mem_monitor_t mon;
140 lv_mem_monitor(&mon);
141 LV_LOG_ERROR("used: %6d (%3d %%), frag: %3d %%, biggest free: %6d",
142 (int)(mon.total_size - mon.free_size), mon.used_pct, mon.frag_pct,
143 (int)mon.free_biggest_size);
144 }
145 #if LV_MEM_ADD_JUNK
146 else {
147 lv_memset(alloc, 0xaa, size);
148 }
149 #endif
150
151 MEM_TRACE("allocated at %p", alloc);
152 return alloc;
153 }
154
155 /**
156 * Free an allocated data
157 * @param data pointer to an allocated memory
158 */
lv_mem_free(void * data)159 void lv_mem_free(void * data)
160 {
161 MEM_TRACE("freeing %p", data);
162 if(data == &zero_mem) return;
163 if(data == NULL) return;
164
165 #if LV_MEM_CUSTOM == 0
166 # if LV_MEM_ADD_JUNK
167 lv_memset(data, 0xbb, lv_tlsf_block_size(data));
168 # endif
169 lv_tlsf_free(tlsf, data);
170 #else
171 LV_MEM_CUSTOM_FREE(data);
172 #endif
173 }
174
175 /**
176 * Reallocate a memory with a new size. The old content will be kept.
177 * @param data pointer to an allocated memory.
178 * Its content will be copied to the new memory block and freed
179 * @param new_size the desired new size in byte
180 * @return pointer to the new memory
181 */
lv_mem_realloc(void * data_p,size_t new_size)182 void * lv_mem_realloc(void * data_p, size_t new_size)
183 {
184 MEM_TRACE("reallocating %p with %lu size", data_p, (unsigned long)new_size);
185 if(new_size == 0) {
186 MEM_TRACE("using zero_mem");
187 lv_mem_free(data_p);
188 return &zero_mem;
189 }
190
191 if(data_p == &zero_mem) return lv_mem_alloc(new_size);
192
193 #if LV_MEM_CUSTOM == 0
194 void * new_p = lv_tlsf_realloc(tlsf, data_p, new_size);
195 #else
196 void * new_p = LV_MEM_CUSTOM_REALLOC(data_p, new_size);
197 #endif
198 if(new_p == NULL) {
199 LV_LOG_ERROR("couldn't allocate memory");
200 return NULL;
201 }
202
203 MEM_TRACE("allocated at %p", new_p);
204 return new_p;
205 }
206
lv_mem_test(void)207 lv_res_t lv_mem_test(void)
208 {
209 if(zero_mem != ZERO_MEM_SENTINEL) {
210 LV_LOG_WARN("zero_mem is written");
211 return LV_RES_INV;
212 }
213
214 #if LV_MEM_CUSTOM == 0
215 if(lv_tlsf_check(tlsf)) {
216 LV_LOG_WARN("failed");
217 return LV_RES_INV;
218 }
219
220 if(lv_tlsf_check_pool(lv_tlsf_get_pool(tlsf))) {
221 LV_LOG_WARN("pool failed");
222 return LV_RES_INV;
223 }
224 #endif
225 MEM_TRACE("passed");
226 return LV_RES_OK;
227 }
228
229 /**
230 * Give information about the work memory of dynamic allocation
231 * @param mon_p pointer to a lv_mem_monitor_t variable,
232 * the result of the analysis will be stored here
233 */
lv_mem_monitor(lv_mem_monitor_t * mon_p)234 void lv_mem_monitor(lv_mem_monitor_t * mon_p)
235 {
236 /*Init the data*/
237 lv_memset(mon_p, 0, sizeof(lv_mem_monitor_t));
238 #if LV_MEM_CUSTOM == 0
239 MEM_TRACE("begin");
240
241 lv_tlsf_walk_pool(lv_tlsf_get_pool(tlsf), lv_mem_walker, mon_p);
242
243 mon_p->total_size = LV_MEM_SIZE;
244 mon_p->used_pct = 100 - (100U * mon_p->free_size) / mon_p->total_size;
245 if(mon_p->free_size > 0) {
246 mon_p->frag_pct = mon_p->free_biggest_size * 100U / mon_p->free_size;
247 mon_p->frag_pct = 100 - mon_p->frag_pct;
248 }
249 else {
250 mon_p->frag_pct = 0; /*no fragmentation if all the RAM is used*/
251 }
252
253 MEM_TRACE("finished");
254 #endif
255 }
256
257
258 /**
259 * Get a temporal buffer with the given size.
260 * @param size the required size
261 */
lv_mem_buf_get(uint32_t size)262 void * lv_mem_buf_get(uint32_t size)
263 {
264 if(size == 0) return NULL;
265
266 MEM_TRACE("begin, getting %d bytes", size);
267
268 /*Try to find a free buffer with suitable size*/
269 int8_t i_guess = -1;
270 for(uint8_t i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
271 if(LV_GC_ROOT(lv_mem_buf[i]).used == 0 && LV_GC_ROOT(lv_mem_buf[i]).size >= size) {
272 if(LV_GC_ROOT(lv_mem_buf[i]).size == size) {
273 LV_GC_ROOT(lv_mem_buf[i]).used = 1;
274 return LV_GC_ROOT(lv_mem_buf[i]).p;
275 }
276 else if(i_guess < 0) {
277 i_guess = i;
278 }
279 /*If size of `i` is closer to `size` prefer it*/
280 else if(LV_GC_ROOT(lv_mem_buf[i]).size < LV_GC_ROOT(lv_mem_buf[i_guess]).size) {
281 i_guess = i;
282 }
283 }
284 }
285
286 if(i_guess >= 0) {
287 LV_GC_ROOT(lv_mem_buf[i_guess]).used = 1;
288 MEM_TRACE("returning already allocated buffer (buffer id: %d, address: %p)", i_guess,
289 LV_GC_ROOT(lv_mem_buf[i_guess]).p);
290 return LV_GC_ROOT(lv_mem_buf[i_guess]).p;
291 }
292
293 /*Reallocate a free buffer*/
294 for(uint8_t i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
295 if(LV_GC_ROOT(lv_mem_buf[i]).used == 0) {
296 /*if this fails you probably need to increase your LV_MEM_SIZE/heap size*/
297 void * buf = lv_mem_realloc(LV_GC_ROOT(lv_mem_buf[i]).p, size);
298 LV_ASSERT_MSG(buf != NULL, "Out of memory, can't allocate a new buffer (increase your LV_MEM_SIZE/heap size)");
299 if(buf == NULL) return NULL;
300
301 LV_GC_ROOT(lv_mem_buf[i]).used = 1;
302 LV_GC_ROOT(lv_mem_buf[i]).size = size;
303 LV_GC_ROOT(lv_mem_buf[i]).p = buf;
304 MEM_TRACE("allocated (buffer id: %d, address: %p)", i, LV_GC_ROOT(lv_mem_buf[i]).p);
305 return LV_GC_ROOT(lv_mem_buf[i]).p;
306 }
307 }
308
309 LV_LOG_ERROR("no more buffers. (increase LV_MEM_BUF_MAX_NUM)");
310 LV_ASSERT_MSG(false, "No more buffers. Increase LV_MEM_BUF_MAX_NUM.");
311 return NULL;
312 }
313
314 /**
315 * Release a memory buffer
316 * @param p buffer to release
317 */
lv_mem_buf_release(void * p)318 void lv_mem_buf_release(void * p)
319 {
320 MEM_TRACE("begin (address: %p)", p);
321
322 for(uint8_t i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
323 if(LV_GC_ROOT(lv_mem_buf[i]).p == p) {
324 LV_GC_ROOT(lv_mem_buf[i]).used = 0;
325 return;
326 }
327 }
328
329 LV_LOG_ERROR("p is not a known buffer");
330 }
331
332 /**
333 * Free all memory buffers
334 */
lv_mem_buf_free_all(void)335 void lv_mem_buf_free_all(void)
336 {
337 for(uint8_t i = 0; i < LV_MEM_BUF_MAX_NUM; i++) {
338 if(LV_GC_ROOT(lv_mem_buf[i]).p) {
339 lv_mem_free(LV_GC_ROOT(lv_mem_buf[i]).p);
340 LV_GC_ROOT(lv_mem_buf[i]).p = NULL;
341 LV_GC_ROOT(lv_mem_buf[i]).used = 0;
342 LV_GC_ROOT(lv_mem_buf[i]).size = 0;
343 }
344 }
345 }
346
347 #if LV_MEMCPY_MEMSET_STD == 0
348 /**
349 * Same as `memcpy` but optimized for 4 byte operation.
350 * @param dst pointer to the destination buffer
351 * @param src pointer to the source buffer
352 * @param len number of byte to copy
353 */
lv_memcpy(void * dst,const void * src,size_t len)354 LV_ATTRIBUTE_FAST_MEM void * lv_memcpy(void * dst, const void * src, size_t len)
355 {
356 uint8_t * d8 = dst;
357 const uint8_t * s8 = src;
358
359 lv_uintptr_t d_align = (lv_uintptr_t)d8 & ALIGN_MASK;
360 lv_uintptr_t s_align = (lv_uintptr_t)s8 & ALIGN_MASK;
361
362 /*Byte copy for unaligned memories*/
363 if(s_align != d_align) {
364 while(len > 32) {
365 REPEAT8(COPY8);
366 REPEAT8(COPY8);
367 REPEAT8(COPY8);
368 REPEAT8(COPY8);
369 len -= 32;
370 }
371 while(len) {
372 COPY8
373 len--;
374 }
375 return dst;
376 }
377
378 /*Make the memories aligned*/
379 if(d_align) {
380 d_align = ALIGN_MASK + 1 - d_align;
381 while(d_align && len) {
382 COPY8;
383 d_align--;
384 len--;
385 }
386 }
387
388 uint32_t * d32 = (uint32_t *)d8;
389 const uint32_t * s32 = (uint32_t *)s8;
390 while(len > 32) {
391 REPEAT8(COPY32)
392 len -= 32;
393 }
394
395 while(len > 4) {
396 COPY32;
397 len -= 4;
398 }
399
400 d8 = (uint8_t *)d32;
401 s8 = (const uint8_t *)s32;
402 while(len) {
403 COPY8
404 len--;
405 }
406
407 return dst;
408 }
409
410 /**
411 * Same as `memset` but optimized for 4 byte operation.
412 * @param dst pointer to the destination buffer
413 * @param v value to set [0..255]
414 * @param len number of byte to set
415 */
lv_memset(void * dst,uint8_t v,size_t len)416 LV_ATTRIBUTE_FAST_MEM void lv_memset(void * dst, uint8_t v, size_t len)
417 {
418
419 uint8_t * d8 = (uint8_t *)dst;
420
421 uintptr_t d_align = (lv_uintptr_t) d8 & ALIGN_MASK;
422
423 /*Make the address aligned*/
424 if(d_align) {
425 d_align = ALIGN_MASK + 1 - d_align;
426 while(d_align && len) {
427 SET8(v);
428 len--;
429 d_align--;
430 }
431 }
432
433 uint32_t v32 = (uint32_t)v + ((uint32_t)v << 8) + ((uint32_t)v << 16) + ((uint32_t)v << 24);
434
435 uint32_t * d32 = (uint32_t *)d8;
436
437 while(len > 32) {
438 REPEAT8(SET32(v32));
439 len -= 32;
440 }
441
442 while(len > 4) {
443 SET32(v32);
444 len -= 4;
445 }
446
447 d8 = (uint8_t *)d32;
448 while(len) {
449 SET8(v);
450 len--;
451 }
452 }
453
454 /**
455 * Same as `memset(dst, 0x00, len)` but optimized for 4 byte operation.
456 * @param dst pointer to the destination buffer
457 * @param len number of byte to set
458 */
lv_memset_00(void * dst,size_t len)459 LV_ATTRIBUTE_FAST_MEM void lv_memset_00(void * dst, size_t len)
460 {
461 uint8_t * d8 = (uint8_t *)dst;
462 uintptr_t d_align = (lv_uintptr_t) d8 & ALIGN_MASK;
463
464 /*Make the address aligned*/
465 if(d_align) {
466 d_align = ALIGN_MASK + 1 - d_align;
467 while(d_align && len) {
468 SET8(0);
469 len--;
470 d_align--;
471 }
472 }
473
474 uint32_t * d32 = (uint32_t *)d8;
475 while(len > 32) {
476 REPEAT8(SET32(0));
477 len -= 32;
478 }
479
480 while(len > 4) {
481 SET32(0);
482 len -= 4;
483 }
484
485 d8 = (uint8_t *)d32;
486 while(len) {
487 SET8(0);
488 len--;
489 }
490 }
491
492 /**
493 * Same as `memset(dst, 0xFF, len)` but optimized for 4 byte operation.
494 * @param dst pointer to the destination buffer
495 * @param len number of byte to set
496 */
lv_memset_ff(void * dst,size_t len)497 LV_ATTRIBUTE_FAST_MEM void lv_memset_ff(void * dst, size_t len)
498 {
499 uint8_t * d8 = (uint8_t *)dst;
500 uintptr_t d_align = (lv_uintptr_t) d8 & ALIGN_MASK;
501
502 /*Make the address aligned*/
503 if(d_align) {
504 d_align = ALIGN_MASK + 1 - d_align;
505 while(d_align && len) {
506 SET8(0xFF);
507 len--;
508 d_align--;
509 }
510 }
511
512 uint32_t * d32 = (uint32_t *)d8;
513 while(len > 32) {
514 REPEAT8(SET32(0xFFFFFFFF));
515 len -= 32;
516 }
517
518 while(len > 4) {
519 SET32(0xFFFFFFFF);
520 len -= 4;
521 }
522
523 d8 = (uint8_t *)d32;
524 while(len) {
525 SET8(0xFF);
526 len--;
527 }
528 }
529
530 #endif /*LV_MEMCPY_MEMSET_STD*/
531
532 /**********************
533 * STATIC FUNCTIONS
534 **********************/
535
536 #if LV_MEM_CUSTOM == 0
lv_mem_walker(void * ptr,size_t size,int used,void * user)537 static void lv_mem_walker(void * ptr, size_t size, int used, void * user)
538 {
539 LV_UNUSED(ptr);
540
541 lv_mem_monitor_t * mon_p = user;
542 if(used) {
543 mon_p->used_cnt++;
544 }
545 else {
546 mon_p->free_cnt++;
547 mon_p->free_size += size;
548 if(size > mon_p->free_biggest_size)
549 mon_p->free_biggest_size = size;
550 }
551 }
552 #endif
553