37
37
// impossible to support zero-length allocations).
38
38
#define FREE 0
39
39
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
+
40
44
static supervisor_allocation allocations [CIRCUITPY_SUPERVISOR_ALLOC_COUNT ];
41
45
// We use uint32_t* to ensure word (4 byte) alignment.
42
46
uint32_t * low_address ;
@@ -67,27 +71,29 @@ void free_memory(supervisor_allocation* allocation) {
67
71
high_address += allocation -> length / 4 ;
68
72
allocation -> length = FREE ;
69
73
for (index ++ ; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT ; index ++ ) {
70
- if (allocations [index ].ptr != NULL ) {
74
+ if (!( allocations [index ].length & HOLE ) ) {
71
75
break ;
72
76
}
77
+ // Division automatically shifts out the HOLE bit.
73
78
high_address += allocations [index ].length / 4 ;
74
79
allocations [index ].length = FREE ;
75
80
}
76
81
} else if (allocation -> ptr + allocation -> length / 4 == low_address ) {
77
82
low_address = allocation -> ptr ;
78
83
allocation -> length = FREE ;
79
84
for (index -- ; index >= 0 ; index -- ) {
80
- if (allocations [index ].ptr != NULL ) {
85
+ if (!( allocations [index ].length & HOLE ) ) {
81
86
break ;
82
87
}
83
88
low_address -= allocations [index ].length / 4 ;
84
89
allocations [index ].length = FREE ;
85
90
}
86
91
} else {
87
92
// 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 ;
89
96
}
90
- allocation -> ptr = NULL ;
91
97
}
92
98
93
99
supervisor_allocation * allocation_from_ptr (void * ptr ) {
@@ -107,7 +113,7 @@ supervisor_allocation* allocate_remaining_memory(void) {
107
113
}
108
114
109
115
supervisor_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 ) {
111
117
return NULL ;
112
118
}
113
119
uint8_t index = 0 ;
@@ -116,15 +122,21 @@ supervisor_allocation* allocate_memory(uint32_t length, bool high) {
116
122
index = CIRCUITPY_SUPERVISOR_ALLOC_COUNT - 1 ;
117
123
direction = -1 ;
118
124
}
125
+ supervisor_allocation * alloc ;
119
126
for (; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT ; index += direction ) {
120
- if (allocations [index ].length == FREE ) {
127
+ alloc = & allocations [index ];
128
+ if (alloc -> length == FREE ) {
121
129
break ;
122
130
}
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
+ }
123
136
}
124
- if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT ) {
137
+ if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT || ( high_address - low_address ) * 4 < ( int32_t ) length ) {
125
138
return NULL ;
126
139
}
127
- supervisor_allocation * alloc = & allocations [index ];
128
140
if (high ) {
129
141
high_address -= length / 4 ;
130
142
alloc -> ptr = high_address ;
0 commit comments