33
33
34
34
#define CIRCUITPY_SUPERVISOR_ALLOC_COUNT (12)
35
35
36
+ // Using a zero length to mark an unused allocation makes the code a bit shorter (but makes it
37
+ // impossible to support zero-length allocations).
38
+ #define FREE 0
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
+
36
44
static supervisor_allocation allocations [CIRCUITPY_SUPERVISOR_ALLOC_COUNT ];
37
45
// We use uint32_t* to ensure word (4 byte) alignment.
38
46
uint32_t * low_address ;
@@ -61,25 +69,31 @@ void free_memory(supervisor_allocation* allocation) {
61
69
}
62
70
if (allocation -> ptr == high_address ) {
63
71
high_address += allocation -> length / 4 ;
72
+ allocation -> length = FREE ;
64
73
for (index ++ ; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT ; index ++ ) {
65
- if (allocations [index ].ptr != NULL ) {
74
+ if (!( allocations [index ].length & HOLE ) ) {
66
75
break ;
67
76
}
77
+ // Division automatically shifts out the HOLE bit.
68
78
high_address += allocations [index ].length / 4 ;
79
+ allocations [index ].length = FREE ;
69
80
}
70
81
} else if (allocation -> ptr + allocation -> length / 4 == low_address ) {
71
82
low_address = allocation -> ptr ;
83
+ allocation -> length = FREE ;
72
84
for (index -- ; index >= 0 ; index -- ) {
73
- if (allocations [index ].ptr != NULL ) {
85
+ if (!( allocations [index ].length & HOLE ) ) {
74
86
break ;
75
87
}
76
88
low_address -= allocations [index ].length / 4 ;
89
+ allocations [index ].length = FREE ;
77
90
}
78
91
} else {
79
92
// Freed memory isn't in the middle so skip updating bounds. The memory will be added to the
80
- // 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 ;
81
96
}
82
- allocation -> ptr = NULL ;
83
97
}
84
98
85
99
supervisor_allocation * allocation_from_ptr (void * ptr ) {
@@ -99,7 +113,7 @@ supervisor_allocation* allocate_remaining_memory(void) {
99
113
}
100
114
101
115
supervisor_allocation * allocate_memory (uint32_t length , bool high ) {
102
- if (( high_address - low_address ) * 4 < ( int32_t ) length || length % 4 != 0 ) {
116
+ if (length == 0 || length % 4 != 0 ) {
103
117
return NULL ;
104
118
}
105
119
uint8_t index = 0 ;
@@ -108,15 +122,21 @@ supervisor_allocation* allocate_memory(uint32_t length, bool high) {
108
122
index = CIRCUITPY_SUPERVISOR_ALLOC_COUNT - 1 ;
109
123
direction = -1 ;
110
124
}
125
+ supervisor_allocation * alloc ;
111
126
for (; index < CIRCUITPY_SUPERVISOR_ALLOC_COUNT ; index += direction ) {
112
- if (allocations [index ].ptr == NULL ) {
127
+ alloc = & allocations [index ];
128
+ if (alloc -> length == FREE && (high_address - low_address ) * 4 >= (int32_t ) length ) {
113
129
break ;
114
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
+ }
115
136
}
116
137
if (index >= CIRCUITPY_SUPERVISOR_ALLOC_COUNT ) {
117
138
return NULL ;
118
139
}
119
- supervisor_allocation * alloc = & allocations [index ];
120
140
if (high ) {
121
141
high_address -= length / 4 ;
122
142
alloc -> ptr = high_address ;
0 commit comments