mruby/c VM Source Code master (2026/08/06)
Loading...
Searching...
No Matches
alloc.c
Go to the documentation of this file.
1
44
45/***** Feature test switches ************************************************/
46/***** System headers *******************************************************/
47//@cond
48#include "vm_config.h"
49#include <stdint.h>
50#include <string.h>
51#include <assert.h>
52//@endcond
53
54#if !defined(MRBC_ALLOC_LIBC)
55/***** Local headers ********************************************************/
56#include "alloc.h"
57#include "hal.h"
58#if defined(MRBC_DEBUG)
59#include "console.h"
60#endif
61
62/***** Constant values ******************************************************/
63/*
64 Layer 1st(f) and 2nd(s) model
65 last 4bit is ignored
66
67 FLI range SLI0 1 2 3 4 5 6 7 BlockSize
68 0 0000-007f unused 0010- 0020- 0030- 0040- 0050- 0060- 0070-007f 16
69 1 0080-00ff 0080- 0090- 00a0- 00b0- 00c0- 00d0- 00e0- 00f0-00ff 16
70 2 0100-01ff 0100- 0120- 0140- 0160- 0180- 01a0- 01c0- 01e0-01ff 32
71 3 0200-03ff 0200- 0240- 0280- 02c0- 0300- 0340- 0380- 03c0-03ff 64
72 4 0400-07ff 0400- 0480- 0500- 0580- 0600- 0680- 0700- 0780-07ff 128
73 5 0800-0fff 0800- 0900- 0a00- 0b00- 0c00- 0d00- 0e00- 0f00-0fff 256
74 6 1000-1fff 1000- 1200- 1400- 1600- 1800- 1a00- 1c00- 1e00-1fff 512
75 7 2000-3fff 2000- 2400- 2800- 2c00- 3000- 3400- 3800- 3c00-3fff 1024
76 8 4000-7fff 4000- 4800- 5000- 5800- 6000- 6800- 7000- 7800-7fff 2048
77 9 8000-ffff 8000- 9000- a000- b000- c000- d000- e000- f000-ffff 4096
78*/
79
80#ifndef MRBC_ALLOC_FLI_BIT_WIDTH // 0000 0000 0000 0000
81# define MRBC_ALLOC_FLI_BIT_WIDTH 9 // ~~~~~~~~~~~
82#endif
83#ifndef MRBC_ALLOC_SLI_BIT_WIDTH // 0000 0000 0000 0000
84# define MRBC_ALLOC_SLI_BIT_WIDTH 3 // ~~~
85#endif
86#ifndef MRBC_ALLOC_IGNORE_LSBS // 0000 0000 0000 0000
87# define MRBC_ALLOC_IGNORE_LSBS 4 // ~~~~
88#endif
89
90#define SIZE_FREE_BLOCKS \
91 ((MRBC_ALLOC_FLI_BIT_WIDTH + 1) * (1 << MRBC_ALLOC_SLI_BIT_WIDTH))
92 // maybe 80 (0x50)
93/*
94 Minimum memory block size parameter.
95 Choose large one from sizeof(FREE_BLOCK) or (1 << MRBC_ALLOC_IGNORE_LSBS)
96*/
97#if !defined(MRBC_MIN_MEMORY_BLOCK_SIZE)
98#define MRBC_MIN_MEMORY_BLOCK_SIZE sizeof(FREE_BLOCK)
99// #define MRBC_MIN_MEMORY_BLOCK_SIZE (1 << MRBC_ALLOC_IGNORE_LSBS)
100#endif
101
102
103/***** Macros ***************************************************************/
104#define FLI(x) ((x) >> MRBC_ALLOC_SLI_BIT_WIDTH)
105#define SLI(x) ((x) & ((1 << MRBC_ALLOC_SLI_BIT_WIDTH) - 1))
106
107/*
108 Pull Request #251 made NULL check unnecessary.
109 Should you need libc-compatible behavior for any reason,
110 you should enable this macro.
111*/
112#if 1
113# define RETURN_IF_NULL(ptr) (void)0
114#else
115# define RETURN_IF_NULL(ptr) if((ptr) == NULL) return (ptr)
116#endif
117
118
119/***** Typedefs *************************************************************/
120/*
121 define memory block header for 16 bit
122
123 (note)
124 Typical size of
125 USED_BLOCK is 2 bytes
126 FREE_BLOCK is 8 bytes
127 on 16bit machine.
128*/
129#if defined(MRBC_ALLOC_16BIT)
130#define MRBC_ALLOC_MEMSIZE_T uint16_t
131
132typedef struct USED_BLOCK {
133 MRBC_ALLOC_MEMSIZE_T size;
134#if defined(MRBC_ALLOC_VMID)
135 uint8_t vm_id;
136#endif
137} USED_BLOCK;
138
139typedef struct FREE_BLOCK {
140 MRBC_ALLOC_MEMSIZE_T size;
141#if defined(MRBC_ALLOC_VMID)
142 uint8_t vm_id;
143#endif
144
145 struct FREE_BLOCK *next_free;
146 struct FREE_BLOCK *prev_free;
147 struct FREE_BLOCK *top_adrs;
148} FREE_BLOCK;
149
150
151/*
152 define memory block header for 24/32 bit.
153
154 (note)
155 Typical size of
156 USED_BLOCK is 4 bytes
157 FREE_BLOCK is 16 bytes
158 on 32bit machine.
159*/
160#elif defined(MRBC_ALLOC_24BIT)
161#define MRBC_ALLOC_MEMSIZE_T uint32_t
162
163typedef struct USED_BLOCK {
164#if defined(MRBC_ALLOC_VMID)
165 MRBC_ALLOC_MEMSIZE_T size : 24;
166 uint8_t vm_id : 8;
167#else
168 MRBC_ALLOC_MEMSIZE_T size;
169#endif
170} USED_BLOCK;
171
172typedef struct FREE_BLOCK {
173#if defined(MRBC_ALLOC_VMID)
174 MRBC_ALLOC_MEMSIZE_T size : 24;
175 uint8_t vm_id : 8;
176#else
177 MRBC_ALLOC_MEMSIZE_T size;
178#endif
179
180 struct FREE_BLOCK *next_free;
181 struct FREE_BLOCK *prev_free;
182 struct FREE_BLOCK *top_adrs;
183} FREE_BLOCK;
184
185#else
186# error 'define MRBC_ALLOC_*' required.
187#endif
188
189/*
190 and operation macro
191*/
192#define BLOCK_SIZE(p) (((p)->size) & ~0x03)
193#define PHYS_NEXT(p) ((void *)((uint8_t *)(p) + BLOCK_SIZE(p)))
194#define SET_USED_BLOCK(p) ((p)->size |= 0x01)
195#define SET_FREE_BLOCK(p) ((p)->size &= ~0x01)
196#define IS_USED_BLOCK(p) ((p)->size & 0x01)
197#define IS_FREE_BLOCK(p) (!IS_USED_BLOCK(p))
198#define SET_PREV_USED(p) ((p)->size |= 0x02)
199#define SET_PREV_FREE(p) ((p)->size &= ~0x02)
200#define IS_PREV_USED(p) ((p)->size & 0x02)
201#define IS_PREV_FREE(p) (!IS_PREV_USED(p))
202
203#if defined(MRBC_ALLOC_VMID)
204#define SET_VM_ID(p,id) (((USED_BLOCK *)(p))->vm_id = (id))
205#define GET_VM_ID(p) (((USED_BLOCK *)(p))->vm_id)
206
207#else
208#define SET_VM_ID(p,id) ((void)0)
209#define GET_VM_ID(p) 0
210#endif
211
212
213/*
214 define memory pool header
215*/
216typedef struct MEMORY_POOL {
217 MRBC_ALLOC_MEMSIZE_T size;
218
219 // free memory bitmap
222 // +1=bit_width, +1=sentinel
223 uint8_t pad[3]; // for alignment compatibility on 16bit and 32bit machines
224
225 // free memory block index
226 FREE_BLOCK *free_blocks[SIZE_FREE_BLOCKS +1]; // +1=sentinel
228
229#define BPOOL_TOP(memory_pool) ((void *)((uint8_t *)(memory_pool) + sizeof(MEMORY_POOL)))
230#define BPOOL_END(memory_pool) ((void *)((uint8_t *)(memory_pool) + ((MEMORY_POOL *)(memory_pool))->size))
231#define BLOCK_ADRS(p) ((void *)((uint8_t *)(p) - sizeof(USED_BLOCK)))
232
233#define MSB_BIT1_FLI 0x8000
234#define MSB_BIT1_SLI 0x80
235#define NLZ_FLI(x) nlz16(x)
236#define NLZ_SLI(x) nlz8(x)
237
238
239/***** Function prototypes **************************************************/
240/***** Local variables ******************************************************/
241// memory pool
243
244#if defined(MRBC_USE_ALLOC_PROF)
245static int profiling = 0;
246static struct MRBC_ALLOC_PROF alloc_prof = {0, 0, 0};
247#endif
248
249/***** Global variables *****************************************************/
250/***** Signal catching functions ********************************************/
251/***** Local functions ******************************************************/
252//================================================================
258static inline int nlz16(uint16_t x)
259{
260 if( x == 0 ) return 16;
261
262 int n = 1;
263 if((x >> 8) == 0 ) { n += 8; x <<= 8; }
264 if((x >> 12) == 0 ) { n += 4; x <<= 4; }
265 if((x >> 14) == 0 ) { n += 2; x <<= 2; }
266 return n - (x >> 15);
267}
268
269
270//================================================================
276static inline int nlz8(uint8_t x)
277{
278 if( x == 0 ) return 8;
279
280 int n = 1;
281 if((x >> 4) == 0 ) { n += 4; x <<= 4; }
282 if((x >> 6) == 0 ) { n += 2; x <<= 2; }
283 return n - (x >> 7);
284}
285
286
287//================================================================
293static inline unsigned int calc_index(MRBC_ALLOC_MEMSIZE_T alloc_size)
294{
295 // check overflow
296 if( ((uint32_t)alloc_size >> (MRBC_ALLOC_FLI_BIT_WIDTH
298 + MRBC_ALLOC_IGNORE_LSBS)) != 0) {
299 return SIZE_FREE_BLOCKS - 1;
300 }
301
302 // calculate First Level Index.
303 unsigned int fli = 16 -
305
306 // calculate Second Level Index.
307 unsigned int shift = (fli == 0) ? MRBC_ALLOC_IGNORE_LSBS :
308 (MRBC_ALLOC_IGNORE_LSBS - 1 + fli);
309
310 unsigned int sli = (alloc_size >> shift) & ((1 << MRBC_ALLOC_SLI_BIT_WIDTH) - 1);
311 unsigned int index = (fli << MRBC_ALLOC_SLI_BIT_WIDTH) + sli;
312
313 assert(fli <= MRBC_ALLOC_FLI_BIT_WIDTH);
314 assert(sli <= (1 << MRBC_ALLOC_SLI_BIT_WIDTH) - 1);
315
316 return index;
317}
318
319
320//================================================================
326static void add_free_block(MEMORY_POOL *pool, FREE_BLOCK *target)
327{
328 SET_FREE_BLOCK(target);
329
330 FREE_BLOCK **top_adrs = (FREE_BLOCK **)((uint8_t*)target + BLOCK_SIZE(target) - sizeof(FREE_BLOCK *));
331 *top_adrs = target;
332
333 unsigned int index = calc_index(BLOCK_SIZE(target));
334 unsigned int fli = FLI(index);
335 unsigned int sli = SLI(index);
336 assert( index < SIZE_FREE_BLOCKS );
337
338 pool->free_fli_bitmap |= (MSB_BIT1_FLI >> fli);
339 pool->free_sli_bitmap[fli] |= (MSB_BIT1_SLI >> sli);
340
341 target->prev_free = NULL;
342 target->next_free = pool->free_blocks[index];
343 if( target->next_free != NULL ) {
344 target->next_free->prev_free = target;
345 }
346 pool->free_blocks[index] = target;
347}
348
349
350//================================================================
356static void remove_free_block(MEMORY_POOL *pool, FREE_BLOCK *target)
357{
358 // top of linked list?
359 if( target->prev_free == NULL ) {
360 unsigned int index = calc_index(BLOCK_SIZE(target));
361
362 pool->free_blocks[index] = target->next_free;
363 if( target->next_free == NULL ) {
364 unsigned int fli = FLI(index);
365 unsigned int sli = SLI(index);
366 pool->free_sli_bitmap[fli] &= ~(MSB_BIT1_SLI >> sli);
367 if( pool->free_sli_bitmap[fli] == 0 ) pool->free_fli_bitmap &= ~(MSB_BIT1_FLI >> fli);
368 }
369 }
370 else {
371 target->prev_free->next_free = target->next_free;
372 }
373
374 if( target->next_free != NULL ) {
375 target->next_free->prev_free = target->prev_free;
376 }
377}
378
379
380#if defined(MRBC_USE_ALLOC_PROF)
381//================================================================
384static void alloc_profile(void)
385{
386 if (!profiling) return;
387
388 MEMORY_POOL *pool = memory_pool;
389 USED_BLOCK *block = BPOOL_TOP(pool);
390 unsigned int used = 0;
391
392 while (block < (USED_BLOCK *)BPOOL_END(pool)) {
393 if (!IS_FREE_BLOCK(block)) {
394 used += BLOCK_SIZE(block);
395 }
396 block = PHYS_NEXT(block);
397 }
398
399 if (alloc_prof.max < used) alloc_prof.max = used;
400 if (used < alloc_prof.min) alloc_prof.min = used;
401}
402#else
403#define alloc_profile() ((void)0)
404#endif
405
406//================================================================
414static inline FREE_BLOCK* split_block(FREE_BLOCK *target, MRBC_ALLOC_MEMSIZE_T size)
415{
416 assert( BLOCK_SIZE(target) >= size );
417 if( (BLOCK_SIZE(target) - size) <= MRBC_MIN_MEMORY_BLOCK_SIZE ) return NULL;
418
419 // split block, free
420 FREE_BLOCK *split = (FREE_BLOCK *)((uint8_t *)target + size);
421
422 split->size = BLOCK_SIZE(target) - size;
423 target->size = size | (target->size & 0x03); // copy a size with flags.
424
425 return split;
426}
427
428
429//================================================================
436static inline void merge_block(FREE_BLOCK *target, FREE_BLOCK *next)
437{
438 assert(target < next);
439
440 // merge target and next
441 target->size += BLOCK_SIZE(next); // copy a size but save flags.
442}
443
444
445/***** Global functions *****************************************************/
446//================================================================
452void mrbc_init_alloc(void *ptr, unsigned int size)
453{
454 assert( MRBC_MIN_MEMORY_BLOCK_SIZE >= sizeof(FREE_BLOCK) );
456 /*
457 If you get this assertion, you can change minimum memory block size
458 parameter to `MRBC_MIN_MEMORY_BLOCK_SIZE (1 << MRBC_ALLOC_IGNORE_LSBS)`
459 and #define MRBC_ALLOC_16BIT.
460 */
461
462 assert( (sizeof(MEMORY_POOL) & 0x03) == 0 );
463#if defined(UINTPTR_MAX)
464 assert( ((uintptr_t)ptr & 0x03) == 0 );
465#else
466 assert( ((uint32_t)ptr & 0x03) == 0 );
467#endif
468 assert( size != 0 );
469 assert( size <= (MRBC_ALLOC_MEMSIZE_T)(~0) );
470
471 size &= ~(unsigned int)0x03; // align 4 byte.
472 memory_pool = ptr;
473 memset( memory_pool, 0, sizeof(MEMORY_POOL) );
474 memory_pool->size = size;
475
476 // initialize memory pool
477 // large free block + zero size used block (sentinel).
478 MRBC_ALLOC_MEMSIZE_T sentinel_size = sizeof(USED_BLOCK);
479 sentinel_size += (-sentinel_size & 0x03);
480 MRBC_ALLOC_MEMSIZE_T free_size = size - sizeof(MEMORY_POOL) - sentinel_size;
481 FREE_BLOCK *free_block = BPOOL_TOP(memory_pool);
482 USED_BLOCK *used_block = (USED_BLOCK *)((uint8_t *)free_block + free_size);
483
484 free_block->size = free_size | 0x02; // flag prev=1, used=0
485 used_block->size = sentinel_size | 0x01; // flag prev=0, used=1
486 SET_VM_ID( used_block, 0xff );
487
488 add_free_block( memory_pool, free_block );
489}
490
491
492//================================================================
496{
497#if defined(MRBC_DEBUG)
498 if( memory_pool ) {
499 memset( memory_pool, 0, memory_pool->size );
500 }
501#endif
502
503 memory_pool = 0;
504}
505
506
507//================================================================
514void * mrbc_raw_alloc(unsigned int size)
515{
516 MEMORY_POOL *pool = memory_pool;
517 MRBC_ALLOC_MEMSIZE_T alloc_size = size + sizeof(USED_BLOCK);
518
519 // align 4 byte
520 alloc_size += (-alloc_size & 3);
521
522 // check minimum alloc size.
523 if( alloc_size < MRBC_MIN_MEMORY_BLOCK_SIZE ) alloc_size = MRBC_MIN_MEMORY_BLOCK_SIZE;
524
525 FREE_BLOCK *target;
526 unsigned int fli, sli;
527 unsigned int index = calc_index(alloc_size);
528
529 // At first, check only the beginning of the same size block.
530 // because it immediately responds to the pattern in which
531 // same size memory are allocated and released continuously.
532 target = pool->free_blocks[index];
533 if( target && BLOCK_SIZE(target) >= alloc_size ) {
534 fli = FLI(index);
535 sli = SLI(index);
536 goto FOUND_TARGET_BLOCK;
537 }
538
539 // and then, check the next (larger) size block.
540 target = pool->free_blocks[++index];
541 fli = FLI(index);
542 sli = SLI(index);
543 if( target ) goto FOUND_TARGET_BLOCK;
544
545 // check in SLI bitmap table.
546 uint16_t masked = pool->free_sli_bitmap[fli] & ((MSB_BIT1_SLI >> sli) - 1);
547 if( masked != 0 ) {
548 sli = NLZ_SLI( masked );
549 goto FOUND_FLI_SLI;
550 }
551
552 // check in FLI bitmap table.
553 masked = pool->free_fli_bitmap & ((MSB_BIT1_FLI >> fli) - 1);
554 if( masked != 0 ) {
555 fli = NLZ_FLI( masked );
556 sli = NLZ_SLI( pool->free_sli_bitmap[fli] );
557 goto FOUND_FLI_SLI;
558 }
559
560 // Change strategy to First-fit.
561 target = pool->free_blocks[--index];
562 while( target ) {
563 if( BLOCK_SIZE(target) >= alloc_size ) {
564 remove_free_block( pool, target );
565 goto SPLIT_BLOCK;
566 }
567 target = target->next_free;
568 }
569
570 // else out of memory
571#if defined(MRBC_OUT_OF_MEMORY)
572 MRBC_OUT_OF_MEMORY();
573#else
574 static const char msg[] = "Fatal error: Out of memory.\n";
575 mrbc_hal_write(2, msg, sizeof(msg)-1);
576 mrbc_hal_abort(0);
577#endif
578 return NULL; // ENOMEM (unreachable if mrbc_hal_abort doesn't return)
579
580
581 FOUND_FLI_SLI:
582 index = (fli << MRBC_ALLOC_SLI_BIT_WIDTH) + sli;
583 assert( index < SIZE_FREE_BLOCKS );
584 target = pool->free_blocks[index];
585 assert( target != NULL );
586
587 FOUND_TARGET_BLOCK:
588 assert(BLOCK_SIZE(target) >= alloc_size);
589
590 // remove free_blocks index
591 pool->free_blocks[index] = target->next_free;
592 if( target->next_free == NULL ) {
593 pool->free_sli_bitmap[fli] &= ~(MSB_BIT1_SLI >> sli);
594 if( pool->free_sli_bitmap[fli] == 0 ) pool->free_fli_bitmap &= ~(MSB_BIT1_FLI >> fli);
595 }
596 else {
597 target->next_free->prev_free = NULL;
598 }
599
600 SPLIT_BLOCK: {
601 FREE_BLOCK *release = split_block(target, alloc_size);
602 if( release != NULL ) {
603 SET_PREV_USED(release);
604 add_free_block( pool, release );
605 } else {
606 FREE_BLOCK *next = PHYS_NEXT(target);
607 SET_PREV_USED(next);
608 }
609 }
610
611 SET_USED_BLOCK(target);
612 SET_VM_ID( target, 0 );
613
614#if defined(MRBC_DEBUG)
615 memset( (uint8_t *)target + sizeof(USED_BLOCK), 0xaa,
616 BLOCK_SIZE(target) - sizeof(USED_BLOCK) );
617#endif
619
620 return (uint8_t *)target + sizeof(USED_BLOCK);
621}
622
623
624//================================================================
631void * mrbc_raw_alloc_no_free(unsigned int size)
632{
633 MEMORY_POOL *pool = memory_pool;
634 MRBC_ALLOC_MEMSIZE_T alloc_size = size + (-size & 3); // align 4 byte
635
636 // find the tail block
637 FREE_BLOCK *tail = BPOOL_TOP(pool);
638 FREE_BLOCK *prev;
639 do {
640 prev = tail;
641 tail = PHYS_NEXT(tail);
642 } while( PHYS_NEXT(tail) < BPOOL_END(pool) );
643
644 // can resize it block?
645 if( IS_USED_BLOCK(prev) ) goto FALLBACK;
646 if( (BLOCK_SIZE(prev) - sizeof(USED_BLOCK)) < alloc_size ) goto FALLBACK;
647
648 remove_free_block( pool, prev );
649 MRBC_ALLOC_MEMSIZE_T free_size = BLOCK_SIZE(prev) - alloc_size;
650
651 if( free_size <= MRBC_MIN_MEMORY_BLOCK_SIZE ) {
652 // no split, use all
653 prev->size += BLOCK_SIZE(tail);
654 SET_USED_BLOCK( prev );
655 tail = prev;
656 }
657 else {
658 // split block
659 MRBC_ALLOC_MEMSIZE_T tail_size = tail->size + alloc_size; // w/ flags.
660 tail = (FREE_BLOCK*)((uint8_t *)tail - alloc_size);
661 tail->size = tail_size;
662 prev->size -= alloc_size; // w/ flags.
663 add_free_block( pool, prev );
664
665#if defined(MRBC_DEBUG)
666 memset( (uint8_t *)tail + sizeof(USED_BLOCK), 0xaa, alloc_size );
667#endif
668 }
669 SET_VM_ID( tail, 0xff );
670
671 return (uint8_t *)tail + sizeof(USED_BLOCK);
672
673 FALLBACK:
674 return mrbc_raw_alloc(alloc_size);
675}
676
677
678//================================================================
686void * mrbc_raw_calloc(unsigned int nmemb, unsigned int size)
687{
688 unsigned int total_size = nmemb * size;
689 void* ptr = mrbc_raw_alloc(total_size);
690 if (ptr != NULL) {
691 // Instead of using memset and memset_s (not available in C99),
692 // we use a volatile pointer to prevent unexpected optimization.
693 volatile unsigned char *vptr = (volatile unsigned char *)ptr;
694 while (total_size--) {
695 *vptr++ = 0;
696 }
697 }
698 return ptr;
699}
700
701
702//================================================================
707void mrbc_raw_free(void *ptr)
708{
709 MEMORY_POOL *pool = memory_pool;
710
711#if defined(MRBC_DEBUG)
712 {
713 if( ptr == NULL ) {
714 static const char msg[] = "mrbc_raw_free(): NULL pointer was given.\n";
715 mrbc_hal_write(2, msg, sizeof(msg)-1);
716 return;
717 }
718
719 FREE_BLOCK *target = BLOCK_ADRS(ptr);
720 if( target < (FREE_BLOCK *)BPOOL_TOP(pool) ||
721 target > (FREE_BLOCK *)BPOOL_END(pool) ) {
722 static const char msg[] = "mrbc_raw_free(): Outside memory pool address was specified.\n";
723 mrbc_hal_write(2, msg, sizeof(msg)-1);
724 return;
725 }
726
727 FREE_BLOCK *block = BPOOL_TOP(pool);
728 while(1) {
729 if( block == target ) break;
730 if( PHYS_NEXT(block) >= BPOOL_END(pool) ) break;
731 block = PHYS_NEXT(block);
732 }
733
734 if( block == target ) {
735 // found target block.
736 if( IS_FREE_BLOCK(block) ) { // is Free block?
737 static const char msg[] = "mrbc_raw_free(): double free detected.\n";
738 mrbc_hal_write(2, msg, sizeof(msg)-1);
739 return;
740 }
741
742 if( PHYS_NEXT(block) >= BPOOL_END(pool) ) { // Is this a sentinel?
743 static const char msg[] = "mrbc_raw_free(): no_free address was specified.\n";
744 mrbc_hal_write(2, msg, sizeof(msg)-1);
745 return;
746 }
747
748 } else {
749 // not found target block.
750 if( block < target ) {
751 static const char msg[] = "mrbc_raw_free(): no_free address was specified.\n";
752 mrbc_hal_write(2, msg, sizeof(msg)-1);
753 return;
754 }
755
756 static const char msg[] = "mrbc_raw_free(): Illegal address.\n";
757 mrbc_hal_write(2, msg, sizeof(msg)-1);
758 return;
759 }
760
761 SET_VM_ID( target, 0xff );
762 memset( ptr, 0xff, BLOCK_SIZE(target) - sizeof(USED_BLOCK) );
763 }
764#endif
765
766 if( ptr == NULL ) return;
767
768 // get target block
769 FREE_BLOCK *target = BLOCK_ADRS(ptr);
770
771 // check next block, merge?
772 FREE_BLOCK *next = PHYS_NEXT(target);
773
774 if( IS_FREE_BLOCK(next) ) {
775 remove_free_block( pool, next );
776 merge_block(target, next);
777 } else {
778 SET_PREV_FREE(next);
779 }
780
781 // check prev block, merge?
782 if( IS_PREV_FREE(target) ) {
783 FREE_BLOCK *prev = *((FREE_BLOCK **)((uint8_t*)target - sizeof(FREE_BLOCK *)));
784
785 assert( IS_FREE_BLOCK(prev) );
786 remove_free_block( pool, prev );
787 merge_block(prev, target);
788 target = prev;
789 }
790
791 // target, add to index
792 add_free_block( pool, target );
793
795}
796
797
798//================================================================
806void * mrbc_raw_realloc(void *ptr, unsigned int size)
807{
808 if( ptr == NULL ) {
809 return mrbc_raw_alloc(size);
810 }
811 if( size == 0 ) {
812 mrbc_raw_free(ptr); // C90(glibc) compatible.
813 return NULL;
814 }
815
816 MEMORY_POOL *pool = memory_pool;
817 volatile USED_BLOCK *target = BLOCK_ADRS(ptr);
818 MRBC_ALLOC_MEMSIZE_T alloc_size = size + sizeof(USED_BLOCK);
819 FREE_BLOCK *next;
820
821 // align 4 byte
822 alloc_size += (-alloc_size & 3);
823
824 // check minimum alloc size.
825 if( alloc_size < MRBC_MIN_MEMORY_BLOCK_SIZE ) alloc_size = MRBC_MIN_MEMORY_BLOCK_SIZE;
826
827 // expand? part1.
828 // next phys block is free and enough size?
829 if( alloc_size > BLOCK_SIZE(target) ) {
830 next = PHYS_NEXT(target);
831 if( IS_USED_BLOCK(next) ) goto ALLOC_AND_COPY;
832 if( (BLOCK_SIZE(target) + BLOCK_SIZE(next)) < alloc_size ) goto ALLOC_AND_COPY;
833
834 remove_free_block( pool, next );
835 merge_block((FREE_BLOCK *)target, next);
836 }
837 next = PHYS_NEXT(target);
838
839 // try shrink.
840 FREE_BLOCK *release = split_block((FREE_BLOCK *)target, alloc_size);
841 if( release != NULL ) {
842 SET_PREV_USED(release);
843 } else {
844 SET_PREV_USED(next);
846 return ptr;
847 }
848
849 // check next block, merge?
850 if( IS_FREE_BLOCK(next) ) {
851 remove_free_block( pool, next );
852 merge_block(release, next);
853 } else {
854 SET_PREV_FREE(next);
855 }
856 add_free_block( pool, release );
858 return ptr;
859
860
861 // expand part2.
862 // new alloc and copy
863 ALLOC_AND_COPY: {
864 void *new_ptr = mrbc_raw_alloc(size);
865 RETURN_IF_NULL( new_ptr ); // ENOMEM
866
867 memcpy(new_ptr, ptr, BLOCK_SIZE(target) - sizeof(USED_BLOCK));
868 mrbc_set_vm_id(new_ptr, target->vm_id);
869
870 mrbc_raw_free(ptr);
871
872 return new_ptr;
873 }
874}
875
876
877//================================================================
883unsigned int mrbc_alloc_usable_size(void *ptr)
884{
885 USED_BLOCK *target = BLOCK_ADRS(ptr);
886 return (unsigned int)(BLOCK_SIZE(target) - sizeof(USED_BLOCK));
887}
888
889
890#if defined(MRBC_ALLOC_VMID)
891//================================================================
899void * mrbc_alloc(const struct VM *vm, unsigned int size)
900{
901 void *ptr = mrbc_raw_alloc(size);
902 RETURN_IF_NULL( ptr ); // ENOMEM
903
904 if( vm ) mrbc_set_vm_id(ptr, vm->vm_id);
905
906 return ptr;
907}
908
909
910//================================================================
919void * mrbc_calloc(const struct VM *vm, unsigned int nmemb, unsigned int size)
920{
921 void *ptr = mrbc_raw_calloc(nmemb, size);
922 RETURN_IF_NULL( ptr ); // ENOMEM
923
924 if( vm ) mrbc_set_vm_id(ptr, vm->vm_id);
925
926 return ptr;
927}
928
929
930//================================================================
935void mrbc_free_all(const struct VM *vm)
936{
937 MEMORY_POOL *pool = memory_pool;
938 USED_BLOCK *target = BPOOL_TOP(pool);
939 USED_BLOCK *next;
940 int vm_id = vm->vm_id;
941
942 while( target < (USED_BLOCK *)BPOOL_END(pool) ) {
943 next = PHYS_NEXT(target);
944 if( IS_FREE_BLOCK(next) ) next = PHYS_NEXT(next);
945
946 if( IS_USED_BLOCK(target) && (target->vm_id == vm_id) ) {
947 mrbc_raw_free( (uint8_t *)target + sizeof(USED_BLOCK) );
948 }
949 target = next;
950 }
951}
952
953
954//================================================================
960void mrbc_set_vm_id(void *ptr, int vm_id)
961{
962 SET_VM_ID( BLOCK_ADRS(ptr), vm_id );
963}
964
965
966//================================================================
972int mrbc_get_vm_id(void *ptr)
973{
974 return GET_VM_ID( BLOCK_ADRS(ptr) );
975}
976#endif // defined(MRBC_ALLOC_VMID)
977
978
979//================================================================
985{
986 MEMORY_POOL *pool = memory_pool;
987 USED_BLOCK *block = BPOOL_TOP(pool);
988 int flag_used_free = IS_USED_BLOCK(block);
989
990 ret->total = pool->size;
991 ret->used = 0;
992 ret->free = 0;
993 ret->fragmentation = -1;
994
995 while( block < (USED_BLOCK *)BPOOL_END(pool) ) {
996 if( IS_FREE_BLOCK(block) ) {
997 ret->free += BLOCK_SIZE(block);
998 } else {
999 ret->used += BLOCK_SIZE(block);
1000 }
1001 if( flag_used_free != IS_USED_BLOCK(block) ) {
1002 ret->fragmentation++;
1003 flag_used_free = IS_USED_BLOCK(block);
1004 }
1005 block = PHYS_NEXT(block);
1006 }
1007}
1008
1009
1010#if defined(MRBC_USE_ALLOC_PROF)
1011//================================================================
1014void mrbc_alloc_start_profiling(void)
1015{
1016 if (profiling) return;
1017 profiling = 1;
1018 alloc_prof.max = 0;
1019 alloc_profile();
1020 alloc_prof.initial = alloc_prof.min = alloc_prof.max;
1021}
1022
1023//================================================================
1026void mrbc_alloc_stop_profiling(void)
1027{
1028 if (!profiling) return;
1029 profiling = 0;
1030}
1031
1032//================================================================
1037void mrbc_alloc_get_profiling(struct MRBC_ALLOC_PROF *prof)
1038{
1039 memcpy(prof, &alloc_prof, sizeof(struct MRBC_ALLOC_PROF));
1040}
1041#endif // defined(MRBC_USE_ALLOC_PROF)
1042
1043
1044#if defined(MRBC_DEBUG)
1045//================================================================
1051void mrbc_alloc_print_statistics( void )
1052{
1053 struct MRBC_ALLOC_STATISTICS stat;
1054 mrbc_alloc_statistics( &stat );
1055 mrbc_printf("== MEMORY STAT ==\n");
1056 mrbc_printf(" total:%d used:%d free:%d frag:%d\n",
1057 stat.total, stat.used, stat.free, stat.fragmentation );
1058}
1059
1060
1061//================================================================
1067void mrbc_alloc_print_pool_header( void *pool_header )
1068{
1069 MEMORY_POOL *pool = pool_header ? pool_header : memory_pool;
1070
1071 mrbc_printf("== MEMORY POOL HEADER DUMP ==\n");
1072 mrbc_printf(" Address:%p - %p - %p ", pool,
1073 BPOOL_TOP(pool), BPOOL_END(pool));
1074 mrbc_printf(" Size Total:%d User:%d\n",
1075 pool->size, pool->size - sizeof(MEMORY_POOL));
1076 mrbc_printf(" sizeof MEMORY_POOL:%d(%04x), USED_BLOCK:%d(%02x), FREE_BLOCK:%d(%02x)\n",
1077 sizeof(MEMORY_POOL), sizeof(MEMORY_POOL),
1078 sizeof(USED_BLOCK), sizeof(USED_BLOCK),
1079 sizeof(FREE_BLOCK), sizeof(FREE_BLOCK) );
1080
1081 mrbc_printf(" FLI/SLI bitmap and free_blocks table.\n");
1082 mrbc_printf(" FLI :S[0123 4567] -- free_blocks ");
1083 for( int i = 0; i < 64; i++ ) { mrbc_printf("-"); }
1084 mrbc_printf("\n");
1085 for( int i = 0; i < sizeof(pool->free_sli_bitmap); i++ ) {
1086 mrbc_printf(" [%2d] %d : ", i, !!((pool->free_fli_bitmap << i) & MSB_BIT1_FLI));
1087 for( int j = 0; j < 8; j++ ) {
1088 mrbc_printf("%d", !!((pool->free_sli_bitmap[i] << j) & MSB_BIT1_SLI));
1089 if( (j % 4) == 3 ) mrbc_printf(" ");
1090 }
1091
1092 for( int j = 0; j < 8; j++ ) {
1093 int idx = i * 8 + j;
1094 if( idx >= sizeof(pool->free_blocks) / sizeof(FREE_BLOCK *) ) break;
1095 mrbc_printf(" %p", pool->free_blocks[idx] );
1096 }
1097 mrbc_printf( "\n" );
1098 }
1099}
1100
1101void mrbc_alloc_print_memory_block( void *pool_header )
1102{
1103 const int DUMP_BYTES = 32;
1104 MEMORY_POOL *pool = pool_header ? pool_header : memory_pool;
1105
1106 mrbc_printf("== MEMORY BLOCK DUMP ==\n");
1107 FREE_BLOCK *block = BPOOL_TOP(pool);
1108
1109 while( block < (FREE_BLOCK *)BPOOL_END(pool) ) {
1110 mrbc_printf("%p", block );
1111#if defined(MRBC_ALLOC_VMID)
1112 mrbc_printf(" id:%02x", block->vm_id );
1113#endif
1114 mrbc_printf(" size:%5d($%04x) use:%d prv:%d ",
1115 block->size & ~0x03, block->size & ~0x03,
1116 !!(block->size & 0x01), !!(block->size & 0x02) );
1117
1118 if( IS_USED_BLOCK(block) ) {
1119 /* Used block */
1120 int n = DUMP_BYTES;
1121 if( n > (BLOCK_SIZE(block) - sizeof(USED_BLOCK)) ) {
1122 n = BLOCK_SIZE(block) - sizeof(USED_BLOCK);
1123 }
1124 uint8_t *p = (uint8_t *)block + sizeof(USED_BLOCK);
1125 int i;
1126 for( i = 0; i < n; i++) mrbc_printf(" %02x", *p++ );
1127 for( ; i < DUMP_BYTES; i++ ) mrbc_printf(" ");
1128
1129 mrbc_printf(" ");
1130 p = (uint8_t *)block + sizeof(USED_BLOCK);
1131 for( i = 0; i < n; i++) {
1132 int ch = *p++;
1133 mrbc_printf("%c", (' ' <= ch && ch < 0x7f)? ch : '.');
1134 }
1135
1136 } else {
1137 /* Free block */
1138 unsigned int index = calc_index(BLOCK_SIZE(block));
1139 mrbc_printf(" fli:%d sli:%d pf:%p nf:%p",
1140 FLI(index), SLI(index), block->prev_free, block->next_free);
1141 }
1142
1143 mrbc_printf("\n");
1144 block = PHYS_NEXT(block);
1145 }
1146}
1147
1148void mrbc_alloc_print_memory_pool( void )
1149{
1150 mrbc_alloc_print_pool_header(0);
1151 mrbc_alloc_print_memory_block(0);
1152}
1153
1154#endif // defined(MRBC_DEBUG)
1155#endif // !defined(MRBC_ALLOC_LIBC)
#define NLZ_SLI(x)
Definition alloc.c:236
static unsigned int calc_index(MRBC_ALLOC_MEMSIZE_T alloc_size)
Definition alloc.c:293
void * mrbc_raw_alloc(unsigned int size)
Definition alloc.c:514
static FREE_BLOCK * split_block(FREE_BLOCK *target, MRBC_ALLOC_MEMSIZE_T size)
Definition alloc.c:414
#define BLOCK_ADRS(p)
Definition alloc.c:231
#define SIZE_FREE_BLOCKS
Definition alloc.c:90
#define RETURN_IF_NULL(ptr)
Definition alloc.c:113
#define FLI(x)
Definition alloc.c:104
void mrbc_alloc_statistics(struct MRBC_ALLOC_STATISTICS *ret)
Definition alloc.c:984
#define SET_PREV_FREE(p)
Definition alloc.c:199
#define MRBC_ALLOC_FLI_BIT_WIDTH
Definition alloc.c:81
#define SET_VM_ID(p, id)
Definition alloc.c:208
#define BPOOL_TOP(memory_pool)
Definition alloc.c:229
#define IS_FREE_BLOCK(p)
Definition alloc.c:197
static int nlz8(uint8_t x)
Definition alloc.c:276
#define GET_VM_ID(p)
Definition alloc.c:209
static void merge_block(FREE_BLOCK *target, FREE_BLOCK *next)
Definition alloc.c:436
#define BLOCK_SIZE(p)
Definition alloc.c:192
void * mrbc_raw_alloc_no_free(unsigned int size)
Definition alloc.c:631
#define IS_USED_BLOCK(p)
Definition alloc.c:196
static void remove_free_block(MEMORY_POOL *pool, FREE_BLOCK *target)
Definition alloc.c:356
#define SET_FREE_BLOCK(p)
Definition alloc.c:195
#define MRBC_MIN_MEMORY_BLOCK_SIZE
Definition alloc.c:98
#define MRBC_ALLOC_IGNORE_LSBS
Definition alloc.c:87
#define PHYS_NEXT(p)
Definition alloc.c:193
#define alloc_profile()
Definition alloc.c:403
void * mrbc_raw_realloc(void *ptr, unsigned int size)
Definition alloc.c:806
#define BPOOL_END(memory_pool)
Definition alloc.c:230
void mrbc_init_alloc(void *ptr, unsigned int size)
Definition alloc.c:452
static int nlz16(uint16_t x)
Definition alloc.c:258
#define SET_PREV_USED(p)
Definition alloc.c:198
#define SET_USED_BLOCK(p)
Definition alloc.c:194
unsigned int mrbc_alloc_usable_size(void *ptr)
Definition alloc.c:883
#define MSB_BIT1_SLI
Definition alloc.c:234
#define MRBC_ALLOC_SLI_BIT_WIDTH
Definition alloc.c:84
static void add_free_block(MEMORY_POOL *pool, FREE_BLOCK *target)
Definition alloc.c:326
#define IS_PREV_FREE(p)
Definition alloc.c:201
void * mrbc_raw_calloc(unsigned int nmemb, unsigned int size)
Definition alloc.c:686
static MEMORY_POOL * memory_pool
Definition alloc.c:242
#define MSB_BIT1_FLI
Definition alloc.c:233
void mrbc_raw_free(void *ptr)
Definition alloc.c:707
void mrbc_cleanup_alloc(void)
Definition alloc.c:495
#define SLI(x)
Definition alloc.c:105
#define NLZ_FLI(x)
Definition alloc.c:235
mruby/c memory management.
static mrbc_int_t shift(mrbc_int_t x, mrbc_int_t y)
Definition c_numeric.c:180
void mrbc_printf(const char *fstr,...)
Definition console.c:205
console output module. (not yet input)
uint8_t pad[3]
Definition alloc.c:223
FREE_BLOCK * free_blocks[SIZE_FREE_BLOCKS+1]
Definition alloc.c:226
MRBC_ALLOC_MEMSIZE_T size
Definition alloc.c:217
uint8_t free_sli_bitmap[MRBC_ALLOC_FLI_BIT_WIDTH+1+1]
Definition alloc.c:221
uint16_t free_fli_bitmap
Definition alloc.c:220
for memory allocation profiling functions. if you use this, define MRBC_USE_ALLOC_PROF pre-processor ...
Definition alloc.h:50
unsigned long max
Definition alloc.h:52
unsigned long min
Definition alloc.h:53
unsigned long initial
Definition alloc.h:51
Return value structure for mrbc_alloc_statistics function.
Definition alloc.h:39
unsigned int total
returns total memory.
Definition alloc.h:40
unsigned int used
returns used memory.
Definition alloc.h:41
unsigned int fragmentation
returns memory fragmentation count.
Definition alloc.h:43
unsigned int free
returns free memory.
Definition alloc.h:42
Virtual Machine.
Definition vm.h:150
uint8_t vm_id
vm_id : 1..MAX_VM_COUNT
Definition vm.h:154
Global configuration of mruby/c VM's.