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