3737// impossible to support zero-length allocations).
3838#define FREE 0
3939
40+ // The lowest two bits of a valid length are always zero, so we can use them to mark an allocation
41+ // as freed by the client but not yet reclaimed into the FREE middle.
42+ #define HOLE 1
43+
4044static supervisor_allocation allocations [CIRCUITPY_SUPERVISOR_ALLOC_COUNT ];
4145// We use uint32_t* to ensure word (4 byte) alignment.
4246uint32_t * low_address ;
@@ -67,27 +71,29 @@ void free_memory(supervisor_allocation* allocation) {
6771 high_address += allocation -> length / 4 ;
6872 allocation -> length = FREE ;
6973 for (index ++ ; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT ; index ++ ) {
70- if (allocations [index ].ptr != NULL ) {
74+ if (!( allocations [index ].length & HOLE ) ) {
7175 break ;
7276 }
77+ // Division automatically shifts out the HOLE bit.
7378 high_address += allocations [index ].length / 4 ;
7479 allocations [index ].length = FREE ;
7580 }
7681 } else if (allocation -> ptr + allocation -> length / 4 == low_address ) {
7782 low_address = allocation -> ptr ;
7883 allocation -> length = FREE ;
7984 for (index -- ; index >= 0 ; index -- ) {
80- if (allocations [index ].ptr != NULL ) {
85+ if (!( allocations [index ].length & HOLE ) ) {
8186 break ;
8287 }
8388 low_address -= allocations [index ].length / 4 ;
8489 allocations [index ].length = FREE ;
8590 }
8691 } else {
8792 // Freed memory isn't in the middle so skip updating bounds. The memory will be added to the
88- // middle when the memory to the inside is freed.
93+ // middle when the memory to the inside is freed. We still need its length, but setting
94+ // only the lowest bit is nondestructive.
95+ allocation -> length |= HOLE ;
8996 }
90- allocation -> ptr = NULL ;
9197}
9298
9399supervisor_allocation * allocation_from_ptr (void * ptr ) {
@@ -107,7 +113,7 @@ supervisor_allocation* allocate_remaining_memory(void) {
107113}
108114
109115supervisor_allocation * allocate_memory (uint32_t length , bool high ) {
110- if (length == 0 || ( high_address - low_address ) * 4 < ( int32_t ) length || length % 4 != 0 ) {
116+ if (length == 0 || length % 4 != 0 ) {
111117 return NULL ;
112118 }
113119 uint8_t index = 0 ;
@@ -116,15 +122,21 @@ supervisor_allocation* allocate_memory(uint32_t length, bool high) {
116122 index = CIRCUITPY_SUPERVISOR_ALLOC_COUNT - 1 ;
117123 direction = -1 ;
118124 }
125+ supervisor_allocation * alloc ;
119126 for (; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT ; index += direction ) {
120- if (allocations [index ].length == FREE ) {
127+ alloc = & allocations [index ];
128+ if (alloc -> length == FREE ) {
121129 break ;
122130 }
131+ // If a hole matches in length exactly, we can reuse it.
132+ if (alloc -> length == (length | HOLE )) {
133+ alloc -> length = length ;
134+ return alloc ;
135+ }
123136 }
124- if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT ) {
137+ if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT || ( high_address - low_address ) * 4 < ( int32_t ) length ) {
125138 return NULL ;
126139 }
127- supervisor_allocation * alloc = & allocations [index ];
128140 if (high ) {
129141 high_address -= length / 4 ;
130142 alloc -> ptr = high_address ;
0 commit comments