summaryrefslogtreecommitdiffstatshomepage
path: root/py/gc.c
diff options
context:
space:
mode:
Diffstat (limited to 'py/gc.c')
-rw-r--r--py/gc.c142
1 files changed, 82 insertions, 60 deletions
diff --git a/py/gc.c b/py/gc.c
index d39307464b..2930e90110 100644
--- a/py/gc.c
+++ b/py/gc.c
@@ -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;