diff options
Diffstat (limited to 'py/gc.c')
-rw-r--r-- | py/gc.c | 142 |
1 files changed, 82 insertions, 60 deletions
@@ -1,3 +1,4 @@ +#include <assert.h> #include <stdio.h> #include <string.h> #include <stdbool.h> @@ -434,8 +435,14 @@ void *gc_realloc(void *ptr, machine_uint_t n_bytes) { if (n_bytes <= n_existing) { return ptr; } else { - // TODO check if we can grow inplace - void *ptr2 = gc_alloc(n_bytes); + // TODO false is incorrect! Should get value from current block! + void *ptr2 = gc_alloc(n_bytes, +#if MICROPY_ENABLE_FINALISER + FTB_GET(BLOCK_FROM_PTR((machine_uint_t)ptr)) +#else + false +#endif + ); if (ptr2 == NULL) { return ptr2; } @@ -444,86 +451,101 @@ void *gc_realloc(void *ptr, machine_uint_t n_bytes) { return ptr2; } } -#endif + +#else // Alternative gc_realloc impl void *gc_realloc(void *ptr_in, machine_uint_t n_bytes) { if (gc_lock_depth > 0) { return NULL; } - void *ptr_out = NULL; - machine_uint_t block = 0; - machine_uint_t ptr = (machine_uint_t)ptr_in; - + // check for pure allocation if (ptr_in == NULL) { return gc_alloc(n_bytes, false); } - if (VERIFY_PTR(ptr) // verify pointer - && (block = BLOCK_FROM_PTR(ptr)) // get first block - && ATB_GET_KIND(block) == AT_HEAD) { // make sure it's a HEAD block - - byte block_type; - machine_uint_t n_free = 0; - machine_uint_t n_blocks = 1; // counting HEAD block - machine_uint_t max_block = gc_alloc_table_byte_len * BLOCKS_PER_ATB; - - // get the number of consecutive tail blocks and - // the number of free blocks after last tail block - // stop if we reach (or are at) end of heap - while ((block + n_blocks + n_free) < max_block - // stop as soon as we find enough blocks for n_bytes - && (n_bytes > ((n_blocks+n_free) * BYTES_PER_BLOCK)) - // stop if block is HEAD - && (block_type = ATB_GET_KIND(block + n_blocks + n_free)) != AT_HEAD) { - switch (block_type) { - case AT_FREE: n_free++; break; - case AT_TAIL: n_blocks++; break; - default: break; - } + machine_uint_t ptr = (machine_uint_t)ptr_in; + + // sanity check the ptr + if (!VERIFY_PTR(ptr)) { + return NULL; + } + + // get first block + machine_uint_t block = BLOCK_FROM_PTR(ptr); + + // sanity check the ptr is pointing to the head of a block + if (ATB_GET_KIND(block) != AT_HEAD) { + return NULL; + } + + // compute number of new blocks that are requested + machine_uint_t new_blocks = (n_bytes + BYTES_PER_BLOCK) / BYTES_PER_BLOCK; + + // get the number of consecutive tail blocks and + // the number of free blocks after last tail block + // stop if we reach (or are at) end of heap + machine_uint_t n_free = 0; + machine_uint_t n_blocks = 1; // counting HEAD block + machine_uint_t max_block = gc_alloc_table_byte_len * BLOCKS_PER_ATB; + while (block + n_blocks + n_free < max_block) { + if (n_blocks + n_free >= new_blocks) { + // stop as soon as we find enough blocks for n_bytes + break; } - // number of allocated bytes - machine_uint_t n_existing = n_blocks * BYTES_PER_BLOCK; - - // check if realloc'ing to a smaller size - if (n_bytes <= n_existing) { - ptr_out = ptr_in; - // free unneeded tail blocks - for (machine_uint_t bl = block + n_blocks; ATB_GET_KIND(bl) == AT_TAIL; bl++) { - ATB_ANY_TO_FREE(bl); - } + byte block_type = ATB_GET_KIND(block + n_blocks + n_free); + switch (block_type) { + case AT_FREE: n_free++; continue; + case AT_TAIL: n_blocks++; continue; + case AT_MARK: assert(0); + } + break; + } - // check if we can expand in place - } else if (n_bytes <= (n_existing + (n_free * BYTES_PER_BLOCK))) { - // number of blocks needed to expand +1 if there's a remainder - machine_uint_t n_diff = ( n_bytes - n_existing)/BYTES_PER_BLOCK+ - ((n_bytes - n_existing)%BYTES_PER_BLOCK!=0); + // return original ptr if it already has the requested number of blocks + if (new_blocks == n_blocks) { + return ptr_in; + } - DEBUG_printf("gc_realloc: expanding " UINT_FMT " blocks (" UINT_FMT " bytes) to " UINT_FMT " blocks (" UINT_FMT " bytes)\n", - n_existing/BYTES_PER_BLOCK, n_existing, n_existing/BYTES_PER_BLOCK+n_diff, n_existing + n_diff*BYTES_PER_BLOCK); + // check if we can shrink the allocated area + if (new_blocks < n_blocks) { + // free unneeded tail blocks + for (machine_uint_t bl = block + new_blocks; ATB_GET_KIND(bl) == AT_TAIL; bl++) { + ATB_ANY_TO_FREE(bl); + } + return ptr_in; + } - // mark rest of blocks as used tail - for (machine_uint_t bl = block + n_blocks; bl < (block + n_blocks + n_diff); bl++) { - ATB_FREE_TO_TAIL(bl); - } - ptr_out = ptr_in; + // check if we can expand in place + if (new_blocks <= n_blocks + n_free) { + // mark few more blocks as used tail + for (machine_uint_t bl = block + n_blocks; bl < block + new_blocks; bl++) { + assert(ATB_GET_KIND(bl) == AT_FREE); + ATB_FREE_TO_TAIL(bl); + } + return ptr_in; + } - // try to find a new contiguous chain - } else if ((ptr_out = gc_alloc(n_bytes, + // can't resize inplace; try to find a new contiguous chain + void *ptr_out = gc_alloc(n_bytes, #if MICROPY_ENABLE_FINALISER - FTB_GET(block) + FTB_GET(block) #else - false + false #endif - )) != NULL) { - DEBUG_printf("gc_realloc: allocating new block\n"); - memcpy(ptr_out, ptr_in, n_existing); - gc_free(ptr_in); - } + ); + + // check that the alloc succeeded + if (ptr_out == NULL) { + return NULL; } + DEBUG_printf("gc_realloc: allocating new block\n"); + memcpy(ptr_out, ptr_in, n_blocks * BYTES_PER_BLOCK); + gc_free(ptr_in); return ptr_out; } +#endif // Alternative gc_realloc impl void gc_dump_info() { gc_info_t info; |