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