lv_mem.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
  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. * INCLUDES
  8. *********************/
  9. #include "../../lv_conf.h"
  10. #include "lv_mem.h"
  11. #include "lv_math.h"
  12. #include <string.h>
  13. #if LV_MEM_CUSTOM != 0
  14. #include LV_MEM_CUSTOM_INCLUDE
  15. #endif
  16. /*********************
  17. * DEFINES
  18. *********************/
  19. #define LV_MEM_ADD_JUNK 0 /*Add memory junk on alloc (0xaa) and free(0xbb) (just for testing purposes)*/
  20. /**********************
  21. * TYPEDEFS
  22. **********************/
  23. /*The size of this union must be 4 bytes (uint32_t)*/
  24. typedef union
  25. {
  26. struct
  27. {
  28. uint32_t used:1; //1: if the entry is used
  29. uint32_t d_size:31; //Size off the data (1 means 4 bytes)
  30. };
  31. uint32_t header; //The header (used + d_size)
  32. }lv_mem_header_t;
  33. typedef struct
  34. {
  35. lv_mem_header_t header;
  36. uint8_t first_data; /*First data byte in the allocated data (Just for easily create a pointer)*/
  37. }lv_mem_ent_t;
  38. /**********************
  39. * STATIC PROTOTYPES
  40. **********************/
  41. #if LV_MEM_CUSTOM == 0
  42. static lv_mem_ent_t * ent_get_next(lv_mem_ent_t * act_e);
  43. static void * ent_alloc(lv_mem_ent_t * e, uint32_t size);
  44. static void ent_trunc(lv_mem_ent_t * e, uint32_t size);
  45. #endif
  46. /**********************
  47. * STATIC VARIABLES
  48. **********************/
  49. #if LV_MEM_CUSTOM == 0
  50. static LV_MEM_ATTR uint8_t work_mem[LV_MEM_SIZE]; /*Work memory for allocations*/
  51. #endif
  52. static uint32_t zero_mem; /*Give the address of this variable if 0 byte should be allocated*/
  53. /**********************
  54. * MACROS
  55. **********************/
  56. /**********************
  57. * GLOBAL FUNCTIONS
  58. **********************/
  59. /**
  60. * Initiaiize the dyn_mem module (work memory and other variables)
  61. */
  62. void lv_mem_init(void)
  63. {
  64. #if LV_MEM_CUSTOM == 0
  65. lv_mem_ent_t * full = (lv_mem_ent_t *)&work_mem;
  66. full->header.used = 0;
  67. /*The total mem size id reduced by the first header and the close patterns */
  68. full->header.d_size = LV_MEM_SIZE - sizeof(lv_mem_header_t);
  69. #endif
  70. }
  71. /**
  72. * Allocate a memory dynamically
  73. * @param size size of the memory to allocate in bytes
  74. * @return pointer to the allocated memory
  75. */
  76. void * lv_mem_alloc(uint32_t size)
  77. {
  78. if(size == 0) {
  79. return &zero_mem;
  80. }
  81. /*Round the size up to 4*/
  82. if(size & 0x3 ) {
  83. size = size & (~0x3);
  84. size += 4;
  85. }
  86. void * alloc = NULL;
  87. #if LV_MEM_CUSTOM == 0 /*Use the allocation from dyn_mem*/
  88. lv_mem_ent_t * e = NULL;
  89. //Search for a appropriate entry
  90. do {
  91. //Get the next entry
  92. e = ent_get_next(e);
  93. //If there is next entry then try to allocate there
  94. if(e != NULL) {
  95. alloc = ent_alloc(e, size);
  96. }
  97. //End if there is not next entry OR the alloc. is successful
  98. }while(e != NULL && alloc == NULL);
  99. #if LV_MEM_ADD_JUNK
  100. if(alloc != NULL) memset(alloc, 0xaa, size);
  101. #endif
  102. #else /*Use custom, user defined malloc function*/
  103. /*Allocate a header too to store the size*/
  104. alloc = LV_MEM_CUSTOM_ALLOC(size + sizeof(lv_mem_header_t));
  105. if(alloc != NULL) {
  106. ((lv_mem_ent_t*) alloc)->header.d_size = size;
  107. ((lv_mem_ent_t*) alloc)->header.used = 1;
  108. alloc = &((lv_mem_ent_t*) alloc)->first_data;
  109. }
  110. #endif
  111. return alloc;
  112. }
  113. /**
  114. * Free an allocated data
  115. * @param data pointer to an allocated memory
  116. */
  117. void lv_mem_free(const void * data)
  118. {
  119. if(data == &zero_mem) return;
  120. if(data == NULL) return;
  121. #if LV_MEM_ADD_JUNK
  122. memset((void*)data, 0xbb, lv_mem_get_size(data));
  123. #endif
  124. /*e points to the header*/
  125. lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data - sizeof(lv_mem_header_t));
  126. e->header.used = 0;
  127. #if LV_MEM_CUSTOM == 0
  128. #if LV_MEM_AUTO_DEFRAG
  129. /* Make a simple defrag.
  130. * Join the following free entries after this*/
  131. lv_mem_ent_t * e_next;
  132. e_next = ent_get_next(e);
  133. while(e_next != NULL) {
  134. if(e_next->header.used == 0) {
  135. e->header.d_size += e_next->header.d_size + sizeof(e->header);
  136. } else {
  137. break;
  138. }
  139. e_next = ent_get_next(e_next);
  140. }
  141. #endif
  142. #else /*Use custom, user defined free function*/
  143. LV_MEM_CUSTOM_FREE(e);
  144. #endif
  145. }
  146. /**
  147. * Reallocate a memory with a new size. The old content will be kept.
  148. * @param data pointer to an allocated memory.
  149. * Its content will be copied to the new memory block and freed
  150. * @param new_size the desired new size in byte
  151. * @return pointer to the new memory
  152. */
  153. void * lv_mem_realloc(void * data_p, uint32_t new_size)
  154. {
  155. /*data_p could be previously freed pointer (in this case it is invalid)*/
  156. if(data_p != NULL) {
  157. lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data_p - sizeof(lv_mem_header_t));
  158. if(e->header.used == 0) {
  159. data_p = NULL;
  160. }
  161. }
  162. uint32_t old_size = lv_mem_get_size(data_p);
  163. if(old_size == new_size) return data_p; /*Also avoid reallocating the same memory*/
  164. #if LV_MEM_CUSTOM == 0
  165. /* Only truncate the memory is possible
  166. * If the 'old_size' was extended by a header size in 'ent_trunc' it avoids reallocating this same memory */
  167. if(new_size < old_size) {
  168. lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data_p - sizeof(lv_mem_header_t));
  169. ent_trunc(e, new_size);
  170. return &e->first_data;
  171. }
  172. #endif
  173. void * new_p;
  174. new_p = lv_mem_alloc(new_size);
  175. if(new_p != NULL && data_p != NULL) {
  176. /*Copy the old data to the new. Use the smaller size*/
  177. if(old_size != 0) {
  178. memcpy(new_p, data_p, LV_MATH_MIN(new_size, old_size));
  179. lv_mem_free(data_p);
  180. }
  181. }
  182. return new_p;
  183. }
  184. /**
  185. * Join the adjacent free memory blocks
  186. */
  187. void lv_mem_defrag(void)
  188. {
  189. #if LV_MEM_CUSTOM == 0
  190. lv_mem_ent_t * e_free;
  191. lv_mem_ent_t * e_next;
  192. e_free = ent_get_next(NULL);
  193. while(1) {
  194. /*Search the next free entry*/
  195. while(e_free != NULL) {
  196. if(e_free->header.used != 0) {
  197. e_free = ent_get_next(e_free);
  198. } else {
  199. break;
  200. }
  201. }
  202. if(e_free == NULL) return;
  203. /*Joint the following free entries to the free*/
  204. e_next = ent_get_next(e_free);
  205. while(e_next != NULL) {
  206. if(e_next->header.used == 0) {
  207. e_free->header.d_size += e_next->header.d_size + sizeof(e_next->header);
  208. } else {
  209. break;
  210. }
  211. e_next = ent_get_next(e_next);
  212. }
  213. if(e_next == NULL) return;
  214. /*Continue from the lastly checked entry*/
  215. e_free = e_next;
  216. }
  217. #endif
  218. }
  219. /**
  220. * Give information about the work memory of dynamic allocation
  221. * @param mon_p pointer to a dm_mon_p variable,
  222. * the result of the analysis will be stored here
  223. */
  224. void lv_mem_monitor(lv_mem_monitor_t * mon_p)
  225. {
  226. /*Init the data*/
  227. memset(mon_p, 0, sizeof(lv_mem_monitor_t));
  228. #if LV_MEM_CUSTOM == 0
  229. lv_mem_ent_t * e;
  230. e = NULL;
  231. e = ent_get_next(e);
  232. while(e != NULL) {
  233. if(e->header.used == 0) {
  234. mon_p->free_cnt++;
  235. mon_p->free_size += e->header.d_size;
  236. if(e->header.d_size > mon_p->free_biggest_size) {
  237. mon_p->free_biggest_size = e->header.d_size;
  238. }
  239. } else {
  240. mon_p->used_cnt++;
  241. }
  242. e = ent_get_next(e);
  243. }
  244. mon_p->total_size = LV_MEM_SIZE;
  245. mon_p->used_pct = 100 - (100U * mon_p->free_size) / mon_p->total_size;
  246. mon_p->frag_pct = (uint32_t)mon_p->free_biggest_size * 100U / mon_p->free_size;
  247. mon_p->frag_pct = 100 - mon_p->frag_pct;
  248. #endif
  249. }
  250. /**
  251. * Give the size of an allocated memory
  252. * @param data pointer to an allocated memory
  253. * @return the size of data memory in bytes
  254. */
  255. uint32_t lv_mem_get_size(const void * data)
  256. {
  257. if(data == NULL) return 0;
  258. if(data == &zero_mem) return 0;
  259. lv_mem_ent_t * e = (lv_mem_ent_t *)((uint8_t *) data - sizeof(lv_mem_header_t));
  260. return e->header.d_size;
  261. }
  262. /**********************
  263. * STATIC FUNCTIONS
  264. **********************/
  265. #if LV_MEM_CUSTOM == 0
  266. /**
  267. * Give the next entry after 'act_e'
  268. * @param act_e pointer to an entry
  269. * @return pointer to an entry after 'act_e'
  270. */
  271. static lv_mem_ent_t * ent_get_next(lv_mem_ent_t * act_e)
  272. {
  273. lv_mem_ent_t * next_e = NULL;
  274. if(act_e == NULL) { /*NULL means: get the first entry*/
  275. next_e = (lv_mem_ent_t * ) work_mem;
  276. }
  277. else /*Get the next entry */
  278. {
  279. uint8_t * data = &act_e->first_data;
  280. next_e = (lv_mem_ent_t * )&data[act_e->header.d_size];
  281. if(&next_e->first_data >= &work_mem[LV_MEM_SIZE]) next_e = NULL;
  282. }
  283. return next_e;
  284. }
  285. /**
  286. * Try to do the real allocation with a given size
  287. * @param e try to allocate to this entry
  288. * @param size size of the new memory in bytes
  289. * @return pointer to the allocated memory or NULL if not enough memory in the entry
  290. */
  291. static void * ent_alloc(lv_mem_ent_t * e, uint32_t size)
  292. {
  293. void * alloc = NULL;
  294. /*If the memory is free and big enough then use it */
  295. if(e->header.used == 0 && e->header.d_size >= size) {
  296. /*Truncate the entry to the desired size */
  297. ent_trunc(e, size),
  298. e->header.used = 1;
  299. /*Save the allocated data*/
  300. alloc = &e->first_data;
  301. }
  302. return alloc;
  303. }
  304. /**
  305. * Truncate the data of entry to the given size
  306. * @param e Pointer to an entry
  307. * @param size new size in bytes
  308. */
  309. static void ent_trunc(lv_mem_ent_t * e, uint32_t size)
  310. {
  311. /*Round the size up to 4*/
  312. if(size & 0x3 ) {
  313. size = size & (~0x3);
  314. size += 4;
  315. }
  316. /*Don't let empty space only for a header without data*/
  317. if(e->header.d_size == size + sizeof(lv_mem_header_t)) {
  318. size = e->header.d_size;
  319. }
  320. /* Create the new entry after the current if there is space for it */
  321. if(e->header.d_size != size) {
  322. uint8_t * e_data = &e->first_data;
  323. lv_mem_ent_t * after_new_e = (lv_mem_ent_t *)&e_data[size];
  324. after_new_e->header.used = 0;
  325. after_new_e->header.d_size = e->header.d_size - size - sizeof(lv_mem_header_t);
  326. }
  327. /* Set the new size for the original entry */
  328. e->header.d_size = size;
  329. }
  330. #endif