-
Notifications
You must be signed in to change notification settings - Fork 7.1k
/
Copy pathheap.h
282 lines (239 loc) · 7.07 KB
/
heap.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*
* Copyright (c) 2019 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_LIB_OS_HEAP_H_
#define ZEPHYR_INCLUDE_LIB_OS_HEAP_H_
/*
* Internal heap APIs
*/
/* These validation checks are non-trivially expensive, so enable
* only when debugging the heap code. They shouldn't be routine
* assertions.
*/
#ifdef CONFIG_SYS_HEAP_VALIDATE
#define CHECK(x) __ASSERT(x, "")
#else
#define CHECK(x) /**/
#endif
/* Chunks are identified by their offset in 8 byte units from the
* first address in the buffer (a zero-valued chunkid_t is used as a
* null; that chunk would always point into the metadata at the start
* of the heap and cannot be allocated). They are prefixed by a
* variable size header that depends on the size of the heap. Heaps
* with fewer than 2^15 units (256kb) of storage use shorts to store
* the fields, otherwise the units are 32 bit integers for a 16Gb heap
* space (larger spaces really aren't in scope for this code, but
* could be handled similarly I suppose). Because of that design
* there's a certain amount of boilerplate API needed to expose the
* field accessors since we can't use natural syntax.
*
* The fields are:
* LEFT_SIZE: The size of the left (next lower chunk in memory)
* neighbor chunk.
* SIZE_AND_USED: the total size (including header) of the chunk in
* 8-byte units. The bottom bit stores a "used" flag.
* FREE_PREV: Chunk ID of the previous node in a free list.
* FREE_NEXT: Chunk ID of the next node in a free list.
*
* The free lists are circular lists, one for each power-of-two size
* category. The free list pointers exist only for free chunks,
* obviously. This memory is part of the user's buffer when
* allocated.
*
* The field order is so that allocated buffers are immediately bounded
* by SIZE_AND_USED of the current chunk at the bottom, and LEFT_SIZE of
* the following chunk at the top. This ordering allows for quick buffer
* overflow detection by testing left_chunk(c + chunk_size(c)) == c.
*/
enum chunk_fields { LEFT_SIZE, SIZE_AND_USED, FREE_PREV, FREE_NEXT };
#define CHUNK_UNIT 8U
typedef struct { char bytes[CHUNK_UNIT]; } chunk_unit_t;
/* big_heap needs uint32_t, small_heap needs uint16_t */
typedef uint32_t chunkid_t;
typedef uint32_t chunksz_t;
struct z_heap_bucket {
chunkid_t next;
};
struct z_heap {
chunkid_t chunk0_hdr[2];
chunkid_t end_chunk;
uint32_t avail_buckets;
#ifdef CONFIG_SYS_HEAP_RUNTIME_STATS
size_t free_bytes;
size_t allocated_bytes;
size_t max_allocated_bytes;
#endif
struct z_heap_bucket buckets[0];
};
static inline bool big_heap_chunks(chunksz_t chunks)
{
if (IS_ENABLED(CONFIG_SYS_HEAP_SMALL_ONLY)) {
return false;
}
if (IS_ENABLED(CONFIG_SYS_HEAP_BIG_ONLY) || sizeof(void *) > 4U) {
return true;
}
return chunks > 0x7fffU;
}
static inline bool big_heap_bytes(size_t bytes)
{
return big_heap_chunks(bytes / CHUNK_UNIT);
}
static inline bool big_heap(struct z_heap *h)
{
return big_heap_chunks(h->end_chunk);
}
static inline chunk_unit_t *chunk_buf(struct z_heap *h)
{
/* the struct z_heap matches with the first chunk */
return (chunk_unit_t *)h;
}
static inline chunkid_t chunk_field(struct z_heap *h, chunkid_t c,
enum chunk_fields f)
{
chunk_unit_t *buf = chunk_buf(h);
void *cmem = &buf[c];
if (big_heap(h)) {
return ((uint32_t *)cmem)[f];
} else {
return ((uint16_t *)cmem)[f];
}
}
static inline void chunk_set(struct z_heap *h, chunkid_t c,
enum chunk_fields f, chunkid_t val)
{
CHECK(c <= h->end_chunk);
chunk_unit_t *buf = chunk_buf(h);
void *cmem = &buf[c];
if (big_heap(h)) {
CHECK(val == (uint32_t)val);
((uint32_t *)cmem)[f] = val;
} else {
CHECK(val == (uint16_t)val);
((uint16_t *)cmem)[f] = val;
}
}
static inline bool chunk_used(struct z_heap *h, chunkid_t c)
{
return chunk_field(h, c, SIZE_AND_USED) & 1U;
}
static inline chunksz_t chunk_size(struct z_heap *h, chunkid_t c)
{
return chunk_field(h, c, SIZE_AND_USED) >> 1;
}
static inline void set_chunk_used(struct z_heap *h, chunkid_t c, bool used)
{
chunk_unit_t *buf = chunk_buf(h);
void *cmem = &buf[c];
if (big_heap(h)) {
if (used) {
((uint32_t *)cmem)[SIZE_AND_USED] |= 1U;
} else {
((uint32_t *)cmem)[SIZE_AND_USED] &= ~1U;
}
} else {
if (used) {
((uint16_t *)cmem)[SIZE_AND_USED] |= 1U;
} else {
((uint16_t *)cmem)[SIZE_AND_USED] &= ~1U;
}
}
}
/*
* Note: no need to preserve the used bit here as the chunk is never in use
* when its size is modified, and potential set_chunk_used() is always
* invoked after set_chunk_size().
*/
static inline void set_chunk_size(struct z_heap *h, chunkid_t c, chunksz_t size)
{
chunk_set(h, c, SIZE_AND_USED, size << 1);
}
static inline chunkid_t prev_free_chunk(struct z_heap *h, chunkid_t c)
{
return chunk_field(h, c, FREE_PREV);
}
static inline chunkid_t next_free_chunk(struct z_heap *h, chunkid_t c)
{
return chunk_field(h, c, FREE_NEXT);
}
static inline void set_prev_free_chunk(struct z_heap *h, chunkid_t c,
chunkid_t prev)
{
chunk_set(h, c, FREE_PREV, prev);
}
static inline void set_next_free_chunk(struct z_heap *h, chunkid_t c,
chunkid_t next)
{
chunk_set(h, c, FREE_NEXT, next);
}
static inline chunkid_t left_chunk(struct z_heap *h, chunkid_t c)
{
return c - chunk_field(h, c, LEFT_SIZE);
}
static inline chunkid_t right_chunk(struct z_heap *h, chunkid_t c)
{
return c + chunk_size(h, c);
}
static inline void set_left_chunk_size(struct z_heap *h, chunkid_t c,
chunksz_t size)
{
chunk_set(h, c, LEFT_SIZE, size);
}
static inline bool solo_free_header(struct z_heap *h, chunkid_t c)
{
return big_heap(h) && (chunk_size(h, c) == 1U);
}
static inline size_t chunk_header_bytes(struct z_heap *h)
{
return big_heap(h) ? 8 : 4;
}
static inline size_t heap_footer_bytes(size_t size)
{
return big_heap_bytes(size) ? 8 : 4;
}
static inline chunksz_t chunksz(size_t bytes)
{
return (bytes + CHUNK_UNIT - 1U) / CHUNK_UNIT;
}
static inline chunksz_t bytes_to_chunksz(struct z_heap *h, size_t bytes)
{
return chunksz(chunk_header_bytes(h) + bytes);
}
static inline chunksz_t min_chunk_size(struct z_heap *h)
{
return bytes_to_chunksz(h, 1);
}
static inline size_t chunksz_to_bytes(struct z_heap *h, chunksz_t chunksz_in)
{
return chunksz_in * CHUNK_UNIT - chunk_header_bytes(h);
}
static inline int bucket_idx(struct z_heap *h, chunksz_t sz)
{
unsigned int usable_sz = sz - min_chunk_size(h) + 1;
return 31 - __builtin_clz(usable_sz);
}
static inline bool size_too_big(struct z_heap *h, size_t bytes)
{
/*
* Quick check to bail out early if size is too big.
* Also guards against potential arithmetic overflows elsewhere.
*/
return (bytes / CHUNK_UNIT) >= h->end_chunk;
}
static inline void get_alloc_info(struct z_heap *h, size_t *alloc_bytes,
size_t *free_bytes)
{
chunkid_t c;
*alloc_bytes = 0;
*free_bytes = 0;
for (c = right_chunk(h, 0); c < h->end_chunk; c = right_chunk(h, c)) {
if (chunk_used(h, c)) {
*alloc_bytes += chunksz_to_bytes(h, chunk_size(h, c));
} else if (!solo_free_header(h, c)) {
*free_bytes += chunksz_to_bytes(h, chunk_size(h, c));
}
}
}
#endif /* ZEPHYR_INCLUDE_LIB_OS_HEAP_H_ */