emnapi 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/CMakeLists.txt +160 -0
  2. package/LICENSE +21 -0
  3. package/README.md +875 -0
  4. package/cmake/wasm32.cmake +32 -0
  5. package/dist/library_napi.js +4784 -0
  6. package/include/common.h +26 -0
  7. package/include/emnapi.h +81 -0
  8. package/include/js_native_api.h +560 -0
  9. package/include/js_native_api_types.h +159 -0
  10. package/include/napi-inl.deprecated.h +186 -0
  11. package/include/napi-inl.h +6299 -0
  12. package/include/napi.h +3127 -0
  13. package/include/node_api.h +226 -0
  14. package/include/node_api_types.h +56 -0
  15. package/include/uv/threadpool.h +41 -0
  16. package/include/uv/unix.h +21 -0
  17. package/include/uv.h +134 -0
  18. package/index.d.ts +4 -0
  19. package/index.js +22 -0
  20. package/lib/wasm32/libdlmalloc.a +0 -0
  21. package/lib/wasm32/libemmalloc.a +0 -0
  22. package/lib/wasm32/libemnapi.a +0 -0
  23. package/lib/wasm32-emscripten/libemnapi-mt.a +0 -0
  24. package/lib/wasm32-emscripten/libemnapi.a +0 -0
  25. package/lib/wasm32-emscripten.txt +5 -0
  26. package/lib/wasm32-wasi/libemnapi.a +0 -0
  27. package/lib/wasm32-wasi.txt +4 -0
  28. package/lib/wasm32.txt +4 -0
  29. package/package.json +43 -0
  30. package/src/emnapi.c +1344 -0
  31. package/src/malloc/dlmalloc/dlmalloc.c +92 -0
  32. package/src/malloc/dlmalloc/malloc.c +6395 -0
  33. package/src/malloc/emmalloc/emmalloc.c +1551 -0
  34. package/src/malloc/memcpy.c +136 -0
  35. package/src/malloc/memset.c +98 -0
  36. package/src/malloc/sbrk.c +29 -0
  37. package/src/uv/queue.h +108 -0
  38. package/src/uv/threadpool.c +408 -0
  39. package/src/uv/unix/async.c +206 -0
  40. package/src/uv/unix/core.c +35 -0
  41. package/src/uv/unix/loop.c +36 -0
  42. package/src/uv/unix/thread.c +118 -0
  43. package/src/uv/uv-common.c +51 -0
  44. package/src/uv/uv-common.h +68 -0
@@ -0,0 +1,1551 @@
1
+ /*
2
+ * Copyright 2018 The Emscripten Authors. All rights reserved.
3
+ * Emscripten is available under two separate licenses, the MIT license and the
4
+ * University of Illinois/NCSA Open Source License. Both these licenses can be
5
+ * found in the LICENSE file.
6
+ *
7
+ * Simple minimalistic but efficient sbrk()-based malloc/free that works in
8
+ * singlethreaded and multithreaded builds.
9
+ *
10
+ * Assumptions:
11
+ *
12
+ * - sbrk() is used to claim new memory (sbrk handles geometric/linear
13
+ * - overallocation growth)
14
+ * - sbrk() can be used by other code outside emmalloc.
15
+ * - sbrk() is very fast in most cases (internal wasm call).
16
+ * - sbrk() returns pointers with an alignment of alignof(max_align_t)
17
+ *
18
+ * Invariants:
19
+ *
20
+ * - Per-allocation header overhead is 8 bytes, smallest allocated payload
21
+ * amount is 8 bytes, and a multiple of 4 bytes.
22
+ * - Acquired memory blocks are subdivided into disjoint regions that lie
23
+ * next to each other.
24
+ * - A region is either in used or free.
25
+ * Used regions may be adjacent, and a used and unused region
26
+ * may be adjacent, but not two unused ones - they would be
27
+ * merged.
28
+ * - Memory allocation takes constant time, unless the alloc needs to sbrk()
29
+ * or memory is very close to being exhausted.
30
+ *
31
+ * Debugging:
32
+ *
33
+ * - If not NDEBUG, runtime assert()s are in use.
34
+ * - If EMMALLOC_MEMVALIDATE is defined, a large amount of extra checks are done.
35
+ * - If EMMALLOC_VERBOSE is defined, a lot of operations are logged
36
+ * out, in addition to EMMALLOC_MEMVALIDATE.
37
+ * - Debugging and logging directly uses console.log via uses EM_ASM, not
38
+ * printf etc., to minimize any risk of debugging or logging depending on
39
+ * malloc.
40
+ */
41
+
42
+ #include <stdalign.h>
43
+ #include <stdbool.h>
44
+ #include <stddef.h>
45
+ #include <stdint.h>
46
+ // #include <unistd.h>
47
+ // #include <memory.h>
48
+ // #include <assert.h>
49
+ // #include <malloc.h>
50
+ #include <limits.h>
51
+ // #include <stdlib.h>
52
+
53
+ void* sbrk(ptrdiff_t);
54
+ void *memset(void *dst, int c, size_t n);
55
+ void *memcpy(void *dst, const void* src, size_t n);
56
+ #undef assert
57
+
58
+ #ifdef NDEBUG
59
+ #define assert(x) (void)0
60
+ #else
61
+ #define assert(x) ((void)((x) || (__assert_fail(#x, __FILE__, __LINE__, __func__),0)))
62
+ #endif
63
+ #if __STDC_VERSION__ >= 201112L && !defined(__cplusplus)
64
+ #define static_assert _Static_assert
65
+ #endif
66
+
67
+ #ifndef NDEBUG
68
+ _Noreturn void __assert_fail(const char *expr, const char *file, int line, const char *func)
69
+ {
70
+ __builtin_trap();
71
+ }
72
+ #endif
73
+
74
+ #ifdef __EMSCRIPTEN_TRACING__
75
+ #include <emscripten/trace.h>
76
+ #endif
77
+
78
+ // Defind by the linker to have the address of the start of the heap.
79
+ extern unsigned char __heap_base;
80
+
81
+ // Behavior of right shifting a signed integer is compiler implementation defined.
82
+ static_assert((((int32_t)0x80000000U) >> 31) == -1, "This malloc implementation requires that right-shifting a signed integer produces a sign-extending (arithmetic) shift!");
83
+
84
+ // Configuration: specifies the minimum alignment that malloc()ed memory outputs. Allocation requests with smaller alignment
85
+ // than this will yield an allocation with this much alignment.
86
+ #define MALLOC_ALIGNMENT alignof(max_align_t)
87
+ static_assert(alignof(max_align_t) == 16, "max_align_t must be correct");
88
+
89
+ #define EMMALLOC_EXPORT __attribute__((weak))
90
+
91
+ #define MIN(x, y) ((x) < (y) ? (x) : (y))
92
+ #define MAX(x, y) ((x) > (y) ? (x) : (y))
93
+
94
+ #define NUM_FREE_BUCKETS 64
95
+ #define BUCKET_BITMASK_T uint64_t
96
+
97
+ // Dynamic memory is subdivided into regions, in the format
98
+
99
+ // <size:uint32_t> ..... <size:uint32_t> | <size:uint32_t> ..... <size:uint32_t> | <size:uint32_t> ..... <size:uint32_t> | .....
100
+
101
+ // That is, at the bottom and top end of each memory region, the size of that region is stored. That allows traversing the
102
+ // memory regions backwards and forwards. Because each allocation must be at least a multiple of 4 bytes, the lowest two bits of
103
+ // each size field is unused. Free regions are distinguished by used regions by having the FREE_REGION_FLAG bit present
104
+ // in the size field. I.e. for free regions, the size field is odd, and for used regions, the size field reads even.
105
+ #define FREE_REGION_FLAG 0x1u
106
+
107
+ // Attempts to malloc() more than this many bytes would cause an overflow when calculating the size of a region,
108
+ // therefore allocations larger than this are short-circuited immediately on entry.
109
+ #define MAX_ALLOC_SIZE 0xFFFFFFC7u
110
+
111
+ // A free region has the following structure:
112
+ // <size:size_t> <prevptr> <nextptr> ... <size:size_t>
113
+
114
+ typedef struct Region
115
+ {
116
+ size_t size;
117
+ // Use a circular doubly linked list to represent free region data.
118
+ struct Region *prev, *next;
119
+ // ... N bytes of free data
120
+ size_t _at_the_end_of_this_struct_size; // do not dereference, this is present for convenient struct sizeof() computation only
121
+ } Region;
122
+
123
+ // Each memory block starts with a RootRegion at the beginning.
124
+ // The RootRegion specifies the size of the region block, and forms a linked
125
+ // list of all RootRegions in the program, starting with `listOfAllRegions`
126
+ // below.
127
+ typedef struct RootRegion
128
+ {
129
+ uint32_t size;
130
+ struct RootRegion *next;
131
+ uint8_t* endPtr;
132
+ } RootRegion;
133
+
134
+ #ifdef __EMSCRIPTEN_SHARED_MEMORY__
135
+ // In multithreaded builds, use a simple global spinlock strategy to acquire/release access to the memory allocator.
136
+ static volatile uint8_t multithreadingLock = 0;
137
+ #define MALLOC_ACQUIRE() while(__sync_lock_test_and_set(&multithreadingLock, 1)) { while(multithreadingLock) { /*nop*/ } }
138
+ #define MALLOC_RELEASE() __sync_lock_release(&multithreadingLock)
139
+ // Test code to ensure we have tight malloc acquire/release guards in place.
140
+ #define ASSERT_MALLOC_IS_ACQUIRED() assert(multithreadingLock == 1)
141
+ #else
142
+ // In singlethreaded builds, no need for locking.
143
+ #define MALLOC_ACQUIRE() ((void)0)
144
+ #define MALLOC_RELEASE() ((void)0)
145
+ #define ASSERT_MALLOC_IS_ACQUIRED() ((void)0)
146
+ #endif
147
+
148
+ #define IS_POWER_OF_2(val) (((val) & ((val)-1)) == 0)
149
+ #define ALIGN_UP(ptr, alignment) ((uint8_t*)((((uintptr_t)(ptr)) + ((alignment)-1)) & ~((alignment)-1)))
150
+ #define HAS_ALIGNMENT(ptr, alignment) ((((uintptr_t)(ptr)) & ((alignment)-1)) == 0)
151
+
152
+ static_assert(IS_POWER_OF_2(MALLOC_ALIGNMENT), "MALLOC_ALIGNMENT must be a power of two value!");
153
+ static_assert(MALLOC_ALIGNMENT >= 4, "Smallest possible MALLOC_ALIGNMENT if 4!");
154
+
155
+ // A region that contains as payload a single forward linked list of pointers to
156
+ // root regions of each disjoint region blocks.
157
+ static RootRegion *listOfAllRegions = NULL;
158
+
159
+ // For each of the buckets, maintain a linked list head node. The head node for each
160
+ // free region is a sentinel node that does not actually represent any free space, but
161
+ // the sentinel is used to avoid awkward testing against (if node == freeRegionHeadNode)
162
+ // when adding and removing elements from the linked list, i.e. we are guaranteed that
163
+ // the sentinel node is always fixed and there, and the actual free region list elements
164
+ // start at freeRegionBuckets[i].next each.
165
+ static Region freeRegionBuckets[NUM_FREE_BUCKETS] = {
166
+ { .prev = &freeRegionBuckets[0], .next = &freeRegionBuckets[0] },
167
+ { .prev = &freeRegionBuckets[1], .next = &freeRegionBuckets[1] },
168
+ { .prev = &freeRegionBuckets[2], .next = &freeRegionBuckets[2] },
169
+ { .prev = &freeRegionBuckets[3], .next = &freeRegionBuckets[3] },
170
+ { .prev = &freeRegionBuckets[4], .next = &freeRegionBuckets[4] },
171
+ { .prev = &freeRegionBuckets[5], .next = &freeRegionBuckets[5] },
172
+ { .prev = &freeRegionBuckets[6], .next = &freeRegionBuckets[6] },
173
+ { .prev = &freeRegionBuckets[7], .next = &freeRegionBuckets[7] },
174
+ { .prev = &freeRegionBuckets[8], .next = &freeRegionBuckets[8] },
175
+ { .prev = &freeRegionBuckets[9], .next = &freeRegionBuckets[9] },
176
+ { .prev = &freeRegionBuckets[10], .next = &freeRegionBuckets[10] },
177
+ { .prev = &freeRegionBuckets[11], .next = &freeRegionBuckets[11] },
178
+ { .prev = &freeRegionBuckets[12], .next = &freeRegionBuckets[12] },
179
+ { .prev = &freeRegionBuckets[13], .next = &freeRegionBuckets[13] },
180
+ { .prev = &freeRegionBuckets[14], .next = &freeRegionBuckets[14] },
181
+ { .prev = &freeRegionBuckets[15], .next = &freeRegionBuckets[15] },
182
+ { .prev = &freeRegionBuckets[16], .next = &freeRegionBuckets[16] },
183
+ { .prev = &freeRegionBuckets[17], .next = &freeRegionBuckets[17] },
184
+ { .prev = &freeRegionBuckets[18], .next = &freeRegionBuckets[18] },
185
+ { .prev = &freeRegionBuckets[19], .next = &freeRegionBuckets[19] },
186
+ { .prev = &freeRegionBuckets[20], .next = &freeRegionBuckets[20] },
187
+ { .prev = &freeRegionBuckets[21], .next = &freeRegionBuckets[21] },
188
+ { .prev = &freeRegionBuckets[22], .next = &freeRegionBuckets[22] },
189
+ { .prev = &freeRegionBuckets[23], .next = &freeRegionBuckets[23] },
190
+ { .prev = &freeRegionBuckets[24], .next = &freeRegionBuckets[24] },
191
+ { .prev = &freeRegionBuckets[25], .next = &freeRegionBuckets[25] },
192
+ { .prev = &freeRegionBuckets[26], .next = &freeRegionBuckets[26] },
193
+ { .prev = &freeRegionBuckets[27], .next = &freeRegionBuckets[27] },
194
+ { .prev = &freeRegionBuckets[28], .next = &freeRegionBuckets[28] },
195
+ { .prev = &freeRegionBuckets[29], .next = &freeRegionBuckets[29] },
196
+ { .prev = &freeRegionBuckets[30], .next = &freeRegionBuckets[30] },
197
+ { .prev = &freeRegionBuckets[31], .next = &freeRegionBuckets[31] },
198
+ { .prev = &freeRegionBuckets[32], .next = &freeRegionBuckets[32] },
199
+ { .prev = &freeRegionBuckets[33], .next = &freeRegionBuckets[33] },
200
+ { .prev = &freeRegionBuckets[34], .next = &freeRegionBuckets[34] },
201
+ { .prev = &freeRegionBuckets[35], .next = &freeRegionBuckets[35] },
202
+ { .prev = &freeRegionBuckets[36], .next = &freeRegionBuckets[36] },
203
+ { .prev = &freeRegionBuckets[37], .next = &freeRegionBuckets[37] },
204
+ { .prev = &freeRegionBuckets[38], .next = &freeRegionBuckets[38] },
205
+ { .prev = &freeRegionBuckets[39], .next = &freeRegionBuckets[39] },
206
+ { .prev = &freeRegionBuckets[40], .next = &freeRegionBuckets[40] },
207
+ { .prev = &freeRegionBuckets[41], .next = &freeRegionBuckets[41] },
208
+ { .prev = &freeRegionBuckets[42], .next = &freeRegionBuckets[42] },
209
+ { .prev = &freeRegionBuckets[43], .next = &freeRegionBuckets[43] },
210
+ { .prev = &freeRegionBuckets[44], .next = &freeRegionBuckets[44] },
211
+ { .prev = &freeRegionBuckets[45], .next = &freeRegionBuckets[45] },
212
+ { .prev = &freeRegionBuckets[46], .next = &freeRegionBuckets[46] },
213
+ { .prev = &freeRegionBuckets[47], .next = &freeRegionBuckets[47] },
214
+ { .prev = &freeRegionBuckets[48], .next = &freeRegionBuckets[48] },
215
+ { .prev = &freeRegionBuckets[49], .next = &freeRegionBuckets[49] },
216
+ { .prev = &freeRegionBuckets[50], .next = &freeRegionBuckets[50] },
217
+ { .prev = &freeRegionBuckets[51], .next = &freeRegionBuckets[51] },
218
+ { .prev = &freeRegionBuckets[52], .next = &freeRegionBuckets[52] },
219
+ { .prev = &freeRegionBuckets[53], .next = &freeRegionBuckets[53] },
220
+ { .prev = &freeRegionBuckets[54], .next = &freeRegionBuckets[54] },
221
+ { .prev = &freeRegionBuckets[55], .next = &freeRegionBuckets[55] },
222
+ { .prev = &freeRegionBuckets[56], .next = &freeRegionBuckets[56] },
223
+ { .prev = &freeRegionBuckets[57], .next = &freeRegionBuckets[57] },
224
+ { .prev = &freeRegionBuckets[58], .next = &freeRegionBuckets[58] },
225
+ { .prev = &freeRegionBuckets[59], .next = &freeRegionBuckets[59] },
226
+ { .prev = &freeRegionBuckets[60], .next = &freeRegionBuckets[60] },
227
+ { .prev = &freeRegionBuckets[61], .next = &freeRegionBuckets[61] },
228
+ { .prev = &freeRegionBuckets[62], .next = &freeRegionBuckets[62] },
229
+ { .prev = &freeRegionBuckets[63], .next = &freeRegionBuckets[63] },
230
+ };
231
+
232
+ // A bitmask that tracks the population status for each of the 64 distinct memory regions:
233
+ // a zero at bit position i means that the free list bucket i is empty. This bitmask is
234
+ // used to avoid redundant scanning of the 64 different free region buckets: instead by
235
+ // looking at the bitmask we can find in constant time an index to a free region bucket
236
+ // that contains free memory of desired size.
237
+ static BUCKET_BITMASK_T freeRegionBucketsUsed = 0;
238
+
239
+ // Amount of bytes taken up by allocation header data
240
+ #define REGION_HEADER_SIZE (2*sizeof(size_t))
241
+
242
+ // Smallest allocation size that is possible is 2*pointer size, since payload of each region must at least contain space
243
+ // to store the free region linked list prev and next pointers. An allocation size smaller than this will be rounded up
244
+ // to this size.
245
+ #define SMALLEST_ALLOCATION_SIZE (2*sizeof(void*))
246
+
247
+ /* Subdivide regions of free space into distinct circular doubly linked lists, where each linked list
248
+ represents a range of free space blocks. The following function compute_free_list_bucket() converts
249
+ an allocation size to the bucket index that should be looked at. The buckets are grouped as follows:
250
+
251
+ Bucket 0: [8, 15], range size=8
252
+ Bucket 1: [16, 23], range size=8
253
+ Bucket 2: [24, 31], range size=8
254
+ Bucket 3: [32, 39], range size=8
255
+ Bucket 4: [40, 47], range size=8
256
+ Bucket 5: [48, 55], range size=8
257
+ Bucket 6: [56, 63], range size=8
258
+ Bucket 7: [64, 71], range size=8
259
+ Bucket 8: [72, 79], range size=8
260
+ Bucket 9: [80, 87], range size=8
261
+ Bucket 10: [88, 95], range size=8
262
+ Bucket 11: [96, 103], range size=8
263
+ Bucket 12: [104, 111], range size=8
264
+ Bucket 13: [112, 119], range size=8
265
+ Bucket 14: [120, 159], range size=40
266
+ Bucket 15: [160, 191], range size=32
267
+ Bucket 16: [192, 223], range size=32
268
+ Bucket 17: [224, 255], range size=32
269
+ Bucket 18: [256, 319], range size=64
270
+ Bucket 19: [320, 383], range size=64
271
+ Bucket 20: [384, 447], range size=64
272
+ Bucket 21: [448, 511], range size=64
273
+ Bucket 22: [512, 639], range size=128
274
+ Bucket 23: [640, 767], range size=128
275
+ Bucket 24: [768, 895], range size=128
276
+ Bucket 25: [896, 1023], range size=128
277
+ Bucket 26: [1024, 1279], range size=256
278
+ Bucket 27: [1280, 1535], range size=256
279
+ Bucket 28: [1536, 1791], range size=256
280
+ Bucket 29: [1792, 2047], range size=256
281
+ Bucket 30: [2048, 2559], range size=512
282
+ Bucket 31: [2560, 3071], range size=512
283
+ Bucket 32: [3072, 3583], range size=512
284
+ Bucket 33: [3584, 6143], range size=2560
285
+ Bucket 34: [6144, 8191], range size=2048
286
+ Bucket 35: [8192, 12287], range size=4096
287
+ Bucket 36: [12288, 16383], range size=4096
288
+ Bucket 37: [16384, 24575], range size=8192
289
+ Bucket 38: [24576, 32767], range size=8192
290
+ Bucket 39: [32768, 49151], range size=16384
291
+ Bucket 40: [49152, 65535], range size=16384
292
+ Bucket 41: [65536, 98303], range size=32768
293
+ Bucket 42: [98304, 131071], range size=32768
294
+ Bucket 43: [131072, 196607], range size=65536
295
+ Bucket 44: [196608, 262143], range size=65536
296
+ Bucket 45: [262144, 393215], range size=131072
297
+ Bucket 46: [393216, 524287], range size=131072
298
+ Bucket 47: [524288, 786431], range size=262144
299
+ Bucket 48: [786432, 1048575], range size=262144
300
+ Bucket 49: [1048576, 1572863], range size=524288
301
+ Bucket 50: [1572864, 2097151], range size=524288
302
+ Bucket 51: [2097152, 3145727], range size=1048576
303
+ Bucket 52: [3145728, 4194303], range size=1048576
304
+ Bucket 53: [4194304, 6291455], range size=2097152
305
+ Bucket 54: [6291456, 8388607], range size=2097152
306
+ Bucket 55: [8388608, 12582911], range size=4194304
307
+ Bucket 56: [12582912, 16777215], range size=4194304
308
+ Bucket 57: [16777216, 25165823], range size=8388608
309
+ Bucket 58: [25165824, 33554431], range size=8388608
310
+ Bucket 59: [33554432, 50331647], range size=16777216
311
+ Bucket 60: [50331648, 67108863], range size=16777216
312
+ Bucket 61: [67108864, 100663295], range size=33554432
313
+ Bucket 62: [100663296, 134217727], range size=33554432
314
+ Bucket 63: 134217728 bytes and larger. */
315
+ static_assert(NUM_FREE_BUCKETS == 64, "Following function is tailored specifically for NUM_FREE_BUCKETS == 64 case");
316
+ static int compute_free_list_bucket(size_t allocSize)
317
+ {
318
+ if (allocSize < 128) return (allocSize >> 3) - 1;
319
+ int clz = __builtin_clz(allocSize);
320
+ int bucketIndex = (clz > 19) ? 110 - (clz<<2) + ((allocSize >> (29-clz)) ^ 4) : MIN(71 - (clz<<1) + ((allocSize >> (30-clz)) ^ 2), NUM_FREE_BUCKETS-1);
321
+ assert(bucketIndex >= 0);
322
+ assert(bucketIndex < NUM_FREE_BUCKETS);
323
+ return bucketIndex;
324
+ }
325
+
326
+ #define DECODE_CEILING_SIZE(size) ((size_t)((size) & ~FREE_REGION_FLAG))
327
+
328
+ static Region *prev_region(Region *region)
329
+ {
330
+ size_t prevRegionSize = ((size_t*)region)[-1];
331
+ prevRegionSize = DECODE_CEILING_SIZE(prevRegionSize);
332
+ return (Region*)((uint8_t*)region - prevRegionSize);
333
+ }
334
+
335
+ static Region *next_region(Region *region)
336
+ {
337
+ return (Region*)((uint8_t*)region + region->size);
338
+ }
339
+
340
+ static size_t region_ceiling_size(Region *region)
341
+ {
342
+ return ((size_t*)((uint8_t*)region + region->size))[-1];
343
+ }
344
+
345
+ static bool region_is_free(Region *r)
346
+ {
347
+ return region_ceiling_size(r) & FREE_REGION_FLAG;
348
+ }
349
+
350
+ static bool region_is_in_use(Region *r)
351
+ {
352
+ return r->size == region_ceiling_size(r);
353
+ }
354
+
355
+ static size_t size_of_region_from_ceiling(Region *r)
356
+ {
357
+ size_t size = region_ceiling_size(r);
358
+ return DECODE_CEILING_SIZE(size);
359
+ }
360
+
361
+ static bool debug_region_is_consistent(Region *r)
362
+ {
363
+ assert(r);
364
+ size_t sizeAtBottom = r->size;
365
+ size_t sizeAtCeiling = size_of_region_from_ceiling(r);
366
+ return sizeAtBottom == sizeAtCeiling;
367
+ }
368
+
369
+ static uint8_t *region_payload_start_ptr(Region *region)
370
+ {
371
+ return (uint8_t*)region + sizeof(size_t);
372
+ }
373
+
374
+ static uint8_t *region_payload_end_ptr(Region *region)
375
+ {
376
+ return (uint8_t*)region + region->size - sizeof(size_t);
377
+ }
378
+
379
+ static void create_used_region(void *ptr, size_t size)
380
+ {
381
+ assert(ptr);
382
+ assert(HAS_ALIGNMENT(ptr, sizeof(size_t)));
383
+ assert(HAS_ALIGNMENT(size, sizeof(size_t)));
384
+ assert(size >= sizeof(Region));
385
+ *(size_t*)ptr = size;
386
+ ((size_t*)ptr)[(size/sizeof(size_t))-1] = size;
387
+ }
388
+
389
+ static void create_free_region(void *ptr, size_t size)
390
+ {
391
+ assert(ptr);
392
+ assert(HAS_ALIGNMENT(ptr, sizeof(size_t)));
393
+ assert(HAS_ALIGNMENT(size, sizeof(size_t)));
394
+ assert(size >= sizeof(Region));
395
+ Region *freeRegion = (Region*)ptr;
396
+ freeRegion->size = size;
397
+ ((size_t*)ptr)[(size/sizeof(size_t))-1] = size | FREE_REGION_FLAG;
398
+ }
399
+
400
+ static void prepend_to_free_list(Region *region, Region *prependTo)
401
+ {
402
+ assert(region);
403
+ assert(prependTo);
404
+ // N.b. the region we are prepending to is always the sentinel node,
405
+ // which represents a dummy node that is technically not a free node, so
406
+ // region_is_free(prependTo) does not hold.
407
+ assert(region_is_free((Region*)region));
408
+ region->next = prependTo;
409
+ region->prev = prependTo->prev;
410
+ assert(region->prev);
411
+ prependTo->prev = region;
412
+ region->prev->next = region;
413
+ }
414
+
415
+ static void unlink_from_free_list(Region *region)
416
+ {
417
+ assert(region);
418
+ assert(region_is_free((Region*)region));
419
+ assert(region->prev);
420
+ assert(region->next);
421
+ region->prev->next = region->next;
422
+ region->next->prev = region->prev;
423
+ }
424
+
425
+ static void link_to_free_list(Region *freeRegion)
426
+ {
427
+ assert(freeRegion);
428
+ assert(freeRegion->size >= sizeof(Region));
429
+ int bucketIndex = compute_free_list_bucket(freeRegion->size-REGION_HEADER_SIZE);
430
+ Region *freeListHead = freeRegionBuckets + bucketIndex;
431
+ freeRegion->prev = freeListHead;
432
+ freeRegion->next = freeListHead->next;
433
+ assert(freeRegion->next);
434
+ freeListHead->next = freeRegion;
435
+ freeRegion->next->prev = freeRegion;
436
+ freeRegionBucketsUsed |= ((BUCKET_BITMASK_T)1) << bucketIndex;
437
+ }
438
+
439
+ #if 0
440
+ static void dump_memory_regions()
441
+ {
442
+ ASSERT_MALLOC_IS_ACQUIRED();
443
+ RootRegion *root = listOfAllRegions;
444
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('All memory regions:'));
445
+ while(root)
446
+ {
447
+ Region *r = (Region*)root;
448
+ assert(debug_region_is_consistent(r));
449
+ uint8_t *lastRegionEnd = root->endPtr;
450
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('Region block 0x'+($0>>>0).toString(16)+' - 0x'+($1>>>0).toString(16)+ ' ('+($2>>>0)+' bytes):'),
451
+ r, lastRegionEnd, lastRegionEnd-(uint8_t*)r);
452
+ while((uint8_t*)r < lastRegionEnd)
453
+ {
454
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('Region 0x'+($0>>>0).toString(16)+', size: '+($1>>>0)+' ('+($2?"used":"--FREE--")+')'),
455
+ r, r->size, region_ceiling_size(r) == r->size);
456
+
457
+ assert(debug_region_is_consistent(r));
458
+ size_t sizeFromCeiling = size_of_region_from_ceiling(r);
459
+ if (sizeFromCeiling != r->size)
460
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('Corrupt region! Size marker at the end of the region does not match: '+($0>>>0)), sizeFromCeiling);
461
+ if (r->size == 0)
462
+ break;
463
+ r = next_region(r);
464
+ }
465
+ root = root->next;
466
+ MAIN_THREAD_ASYNC_EM_ASM(console.log(""));
467
+ }
468
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('Free regions:'));
469
+ for(int i = 0; i < NUM_FREE_BUCKETS; ++i)
470
+ {
471
+ Region *prev = &freeRegionBuckets[i];
472
+ Region *fr = freeRegionBuckets[i].next;
473
+ while(fr != &freeRegionBuckets[i])
474
+ {
475
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('In bucket '+$0+', free region 0x'+($1>>>0).toString(16)+', size: ' + ($2>>>0) + ' (size at ceiling: '+($3>>>0)+'), prev: 0x' + ($4>>>0).toString(16) + ', next: 0x' + ($5>>>0).toString(16)),
476
+ i, fr, fr->size, size_of_region_from_ceiling(fr), fr->prev, fr->next);
477
+ assert(debug_region_is_consistent(fr));
478
+ assert(region_is_free(fr));
479
+ assert(fr->prev == prev);
480
+ prev = fr;
481
+ assert(fr->next != fr);
482
+ assert(fr->prev != fr);
483
+ fr = fr->next;
484
+ }
485
+ }
486
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('Free bucket index map: ' + ($0>>>0).toString(2) + ' ' + ($1>>>0).toString(2)), (uint32_t)(freeRegionBucketsUsed >> 32), (uint32_t)freeRegionBucketsUsed);
487
+ MAIN_THREAD_ASYNC_EM_ASM(console.log(""));
488
+ }
489
+
490
+ void emmalloc_dump_memory_regions()
491
+ {
492
+ MALLOC_ACQUIRE();
493
+ dump_memory_regions();
494
+ MALLOC_RELEASE();
495
+ }
496
+
497
+ static int validate_memory_regions()
498
+ {
499
+ ASSERT_MALLOC_IS_ACQUIRED();
500
+ RootRegion *root = listOfAllRegions;
501
+ while(root)
502
+ {
503
+ Region *r = (Region*)root;
504
+ if (!debug_region_is_consistent(r))
505
+ {
506
+ MAIN_THREAD_ASYNC_EM_ASM(console.error('Used region 0x'+($0>>>0).toString(16)+', size: '+($1>>>0)+' ('+($2?"used":"--FREE--")+') is corrupt (size markers in the beginning and at the end of the region do not match!)'),
507
+ r, r->size, region_ceiling_size(r) == r->size);
508
+ return 1;
509
+ }
510
+ uint8_t *lastRegionEnd = root->endPtr;
511
+ while((uint8_t*)r < lastRegionEnd)
512
+ {
513
+ if (!debug_region_is_consistent(r))
514
+ {
515
+ MAIN_THREAD_ASYNC_EM_ASM(console.error('Used region 0x'+($0>>>0).toString(16)+', size: '+($1>>>0)+' ('+($2?"used":"--FREE--")+') is corrupt (size markers in the beginning and at the end of the region do not match!)'),
516
+ r, r->size, region_ceiling_size(r) == r->size);
517
+ return 1;
518
+ }
519
+ if (r->size == 0)
520
+ break;
521
+ r = next_region(r);
522
+ }
523
+ root = root->next;
524
+ }
525
+ for(int i = 0; i < NUM_FREE_BUCKETS; ++i)
526
+ {
527
+ Region *prev = &freeRegionBuckets[i];
528
+ Region *fr = freeRegionBuckets[i].next;
529
+ while(fr != &freeRegionBuckets[i])
530
+ {
531
+ if (!debug_region_is_consistent(fr) || !region_is_free(fr) || fr->prev != prev || fr->next == fr || fr->prev == fr)
532
+ {
533
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('In bucket '+$0+', free region 0x'+($1>>>0).toString(16)+', size: ' + ($2>>>0) + ' (size at ceiling: '+($3>>>0)+'), prev: 0x' + ($4>>>0).toString(16) + ', next: 0x' + ($5>>>0).toString(16) + ' is corrupt!'),
534
+ i, fr, fr->size, size_of_region_from_ceiling(fr), fr->prev, fr->next);
535
+ return 1;
536
+ }
537
+ prev = fr;
538
+ fr = fr->next;
539
+ }
540
+ }
541
+ return 0;
542
+ }
543
+
544
+ int emmalloc_validate_memory_regions()
545
+ {
546
+ MALLOC_ACQUIRE();
547
+ int memoryError = validate_memory_regions();
548
+ MALLOC_RELEASE();
549
+ return memoryError;
550
+ }
551
+ #endif
552
+
553
+ static bool claim_more_memory(size_t numBytes)
554
+ {
555
+ #ifdef EMMALLOC_VERBOSE
556
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('claim_more_memory(numBytes='+($0>>>0)+ ')'), numBytes);
557
+ #endif
558
+
559
+ #ifdef EMMALLOC_MEMVALIDATE
560
+ validate_memory_regions();
561
+ #endif
562
+
563
+ uint8_t *startPtr;
564
+ uint8_t *endPtr;
565
+ do {
566
+ // If this is the first time we're called, see if we can use
567
+ // the initial heap memory set up by wasm-ld.
568
+ if (!listOfAllRegions) {
569
+ unsigned char *heap_end = sbrk(0);
570
+ if (numBytes <= (size_t)(heap_end - &__heap_base)) {
571
+ startPtr = &__heap_base;
572
+ endPtr = heap_end;
573
+ break;
574
+ }
575
+ }
576
+
577
+ // Round numBytes up to the nearest page size.
578
+ numBytes = (numBytes + (PAGESIZE-1)) & -PAGESIZE;
579
+
580
+ // Claim memory via sbrk
581
+ startPtr = (uint8_t*)sbrk(numBytes);
582
+ if ((intptr_t)startPtr == -1)
583
+ {
584
+ #ifdef EMMALLOC_VERBOSE
585
+ MAIN_THREAD_ASYNC_EM_ASM(console.error('claim_more_memory: sbrk failed!'));
586
+ #endif
587
+ return false;
588
+ }
589
+ #ifdef EMMALLOC_VERBOSE
590
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('claim_more_memory: claimed 0x' + ($0>>>0).toString(16) + ' - 0x' + ($1>>>0).toString(16) + ' (' + ($2>>>0) + ' bytes) via sbrk()'), startPtr, startPtr + numBytes, numBytes);
591
+ #endif
592
+ assert(HAS_ALIGNMENT(startPtr, alignof(size_t)));
593
+ endPtr = startPtr + numBytes;
594
+ } while (0);
595
+
596
+ // Create a sentinel region at the end of the new heap block
597
+ Region *endSentinelRegion = (Region*)(endPtr - sizeof(Region));
598
+ create_used_region(endSentinelRegion, sizeof(Region));
599
+
600
+ // If we are the sole user of sbrk(), it will feed us continuous/consecutive memory addresses - take advantage
601
+ // of that if so: instead of creating two disjoint memory regions blocks, expand the previous one to a larger size.
602
+ uint8_t *previousSbrkEndAddress = listOfAllRegions ? listOfAllRegions->endPtr : 0;
603
+ if (startPtr == previousSbrkEndAddress)
604
+ {
605
+ Region *prevEndSentinel = prev_region((Region*)startPtr);
606
+ assert(debug_region_is_consistent(prevEndSentinel));
607
+ assert(region_is_in_use(prevEndSentinel));
608
+ Region *prevRegion = prev_region(prevEndSentinel);
609
+ assert(debug_region_is_consistent(prevRegion));
610
+
611
+ listOfAllRegions->endPtr = endPtr;
612
+
613
+ // Two scenarios, either the last region of the previous block was in use, in which case we need to create
614
+ // a new free region in the newly allocated space; or it was free, in which case we can extend that region
615
+ // to cover a larger size.
616
+ if (region_is_free(prevRegion))
617
+ {
618
+ size_t newFreeRegionSize = (uint8_t*)endSentinelRegion - (uint8_t*)prevRegion;
619
+ unlink_from_free_list(prevRegion);
620
+ create_free_region(prevRegion, newFreeRegionSize);
621
+ link_to_free_list(prevRegion);
622
+ return true;
623
+ }
624
+ // else: last region of the previous block was in use. Since we are joining two consecutive sbrk() blocks,
625
+ // we can swallow the end sentinel of the previous block away.
626
+ startPtr -= sizeof(Region);
627
+ }
628
+ else
629
+ {
630
+ // Create a root region at the start of the heap block
631
+ create_used_region(startPtr, sizeof(Region));
632
+
633
+ // Dynamic heap start region:
634
+ RootRegion *newRegionBlock = (RootRegion*)startPtr;
635
+ newRegionBlock->next = listOfAllRegions; // Pointer to next region block head
636
+ newRegionBlock->endPtr = endPtr; // Pointer to the end address of this region block
637
+ listOfAllRegions = newRegionBlock;
638
+ startPtr += sizeof(Region);
639
+ }
640
+
641
+ // Create a new memory region for the new claimed free space.
642
+ create_free_region(startPtr, (uint8_t*)endSentinelRegion - startPtr);
643
+ link_to_free_list((Region*)startPtr);
644
+ return true;
645
+ }
646
+
647
+ #if 0
648
+ // Initialize emmalloc during static initialization.
649
+ // See system/lib/README.md for static constructor ordering.
650
+ __attribute__((constructor(47)))
651
+ static void initialize_emmalloc_heap()
652
+ {
653
+ // Initialize circular doubly linked lists representing free space
654
+ // Never useful to unroll this for loop, just takes up code size.
655
+ #pragma clang loop unroll(disable)
656
+ for(int i = 0; i < NUM_FREE_BUCKETS; ++i)
657
+ freeRegionBuckets[i].prev = freeRegionBuckets[i].next = &freeRegionBuckets[i];
658
+
659
+ #ifdef EMMALLOC_VERBOSE
660
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('initialize_emmalloc_heap()'));
661
+ #endif
662
+
663
+ // Start with a tiny dynamic region.
664
+ claim_more_memory(3*sizeof(Region));
665
+ }
666
+
667
+ void emmalloc_blank_slate_from_orbit()
668
+ {
669
+ MALLOC_ACQUIRE();
670
+ listOfAllRegions = NULL;
671
+ freeRegionBucketsUsed = 0;
672
+ initialize_emmalloc_heap();
673
+ MALLOC_RELEASE();
674
+ }
675
+ #endif
676
+
677
+ static void *attempt_allocate(Region *freeRegion, size_t alignment, size_t size)
678
+ {
679
+ ASSERT_MALLOC_IS_ACQUIRED();
680
+ assert(freeRegion);
681
+ // Look at the next potential free region to allocate into.
682
+ // First, we should check if the free region has enough of payload bytes contained
683
+ // in it to accommodate the new allocation. This check needs to take account the
684
+ // requested allocation alignment, so the payload memory area needs to be rounded
685
+ // upwards to the desired alignment.
686
+ uint8_t *payloadStartPtr = region_payload_start_ptr(freeRegion);
687
+ uint8_t *payloadStartPtrAligned = ALIGN_UP(payloadStartPtr, alignment);
688
+ uint8_t *payloadEndPtr = region_payload_end_ptr(freeRegion);
689
+
690
+ // Do we have enough free space, taking into account alignment?
691
+ if (payloadStartPtrAligned + size > payloadEndPtr)
692
+ return NULL;
693
+
694
+ // We have enough free space, so the memory allocation will be made into this region. Remove this free region
695
+ // from the list of free regions: whatever slop remains will be later added back to the free region pool.
696
+ unlink_from_free_list(freeRegion);
697
+
698
+ // Before we proceed further, fix up the boundary between this and the preceding region,
699
+ // so that the boundary between the two regions happens at a right spot for the payload to be aligned.
700
+ if (payloadStartPtr != payloadStartPtrAligned)
701
+ {
702
+ Region *prevRegion = prev_region((Region*)freeRegion);
703
+ // We never have two free regions adjacent to each other, so the region before this free
704
+ // region should be in use.
705
+ assert(region_is_in_use(prevRegion));
706
+ size_t regionBoundaryBumpAmount = payloadStartPtrAligned - payloadStartPtr;
707
+ size_t newThisRegionSize = freeRegion->size - regionBoundaryBumpAmount;
708
+ create_used_region(prevRegion, prevRegion->size + regionBoundaryBumpAmount);
709
+ freeRegion = (Region *)((uint8_t*)freeRegion + regionBoundaryBumpAmount);
710
+ freeRegion->size = newThisRegionSize;
711
+ }
712
+ // Next, we need to decide whether this region is so large that it should be split into two regions,
713
+ // one representing the newly used memory area, and at the high end a remaining leftover free area.
714
+ // This splitting to two is done always if there is enough space for the high end to fit a region.
715
+ // Carve 'size' bytes of payload off this region. So,
716
+ // [sz prev next sz]
717
+ // becomes
718
+ // [sz payload sz] [sz prev next sz]
719
+ if (sizeof(Region) + REGION_HEADER_SIZE + size <= freeRegion->size)
720
+ {
721
+ // There is enough space to keep a free region at the end of the carved out block
722
+ // -> construct the new block
723
+ Region *newFreeRegion = (Region *)((uint8_t*)freeRegion + REGION_HEADER_SIZE + size);
724
+ create_free_region(newFreeRegion, freeRegion->size - size - REGION_HEADER_SIZE);
725
+ link_to_free_list(newFreeRegion);
726
+
727
+ // Recreate the resized Region under its new size.
728
+ create_used_region(freeRegion, size + REGION_HEADER_SIZE);
729
+ }
730
+ else
731
+ {
732
+ // There is not enough space to split the free memory region into used+free parts, so consume the whole
733
+ // region as used memory, not leaving a free memory region behind.
734
+ // Initialize the free region as used by resetting the ceiling size to the same value as the size at bottom.
735
+ ((size_t*)((uint8_t*)freeRegion + freeRegion->size))[-1] = freeRegion->size;
736
+ }
737
+
738
+ #ifdef __EMSCRIPTEN_TRACING__
739
+ emscripten_trace_record_allocation(freeRegion, freeRegion->size);
740
+ #endif
741
+
742
+ #ifdef EMMALLOC_VERBOSE
743
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('attempt_allocate - succeeded allocating memory, region ptr=0x' + ($0>>>0).toString(16) + ', align=' + $1 + ', payload size=' + ($2>>>0) + ' bytes)'), freeRegion, alignment, size);
744
+ #endif
745
+
746
+ return (uint8_t*)freeRegion + sizeof(size_t);
747
+ }
748
+
749
+ static size_t validate_alloc_alignment(size_t alignment)
750
+ {
751
+ // Cannot perform allocations that are less than 4 byte aligned, because the Region
752
+ // control structures need to be aligned. Also round up to minimum outputted alignment.
753
+ alignment = MAX(alignment, MALLOC_ALIGNMENT);
754
+ // Arbitrary upper limit on alignment - very likely a programming bug if alignment is higher than this.
755
+ assert(alignment <= 1024*1024);
756
+ return alignment;
757
+ }
758
+
759
+ static size_t validate_alloc_size(size_t size)
760
+ {
761
+ assert(size + REGION_HEADER_SIZE > size);
762
+
763
+ // Allocation sizes must be a multiple of pointer sizes, and at least 2*sizeof(pointer).
764
+ size_t validatedSize = size > SMALLEST_ALLOCATION_SIZE ? (size_t)ALIGN_UP(size, sizeof(Region*)) : SMALLEST_ALLOCATION_SIZE;
765
+ assert(validatedSize >= size); // 32-bit wraparound should not occur, too large sizes should be stopped before
766
+
767
+ return validatedSize;
768
+ }
769
+
770
+ static void *allocate_memory(size_t alignment, size_t size)
771
+ {
772
+ ASSERT_MALLOC_IS_ACQUIRED();
773
+
774
+ #ifdef EMMALLOC_VERBOSE
775
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('allocate_memory(align=' + $0 + ', size=' + ($1>>>0) + ' bytes)'), alignment, size);
776
+ #endif
777
+
778
+ #ifdef EMMALLOC_MEMVALIDATE
779
+ validate_memory_regions();
780
+ #endif
781
+
782
+ if (!IS_POWER_OF_2(alignment))
783
+ {
784
+ #ifdef EMMALLOC_VERBOSE
785
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('Allocation failed: alignment not power of 2!'));
786
+ #endif
787
+ return 0;
788
+ }
789
+
790
+ if (size > MAX_ALLOC_SIZE)
791
+ {
792
+ #ifdef EMMALLOC_VERBOSE
793
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('Allocation failed: attempted allocation size is too large: ' + ($0 >>> 0) + 'bytes! (negative integer wraparound?)'), size);
794
+ #endif
795
+ return 0;
796
+ }
797
+
798
+ alignment = validate_alloc_alignment(alignment);
799
+ size = validate_alloc_size(size);
800
+
801
+ // Attempt to allocate memory starting from smallest bucket that can contain the required amount of memory.
802
+ // Under normal alignment conditions this should always be the first or second bucket we look at, but if
803
+ // performing an allocation with complex alignment, we may need to look at multiple buckets.
804
+ int bucketIndex = compute_free_list_bucket(size);
805
+ BUCKET_BITMASK_T bucketMask = freeRegionBucketsUsed >> bucketIndex;
806
+
807
+ // Loop through each bucket that has free regions in it, based on bits set in freeRegionBucketsUsed bitmap.
808
+ while(bucketMask)
809
+ {
810
+ BUCKET_BITMASK_T indexAdd = __builtin_ctzll(bucketMask);
811
+ bucketIndex += indexAdd;
812
+ bucketMask >>= indexAdd;
813
+ assert(bucketIndex >= 0);
814
+ assert(bucketIndex <= NUM_FREE_BUCKETS-1);
815
+ assert(freeRegionBucketsUsed & (((BUCKET_BITMASK_T)1) << bucketIndex));
816
+
817
+ Region *freeRegion = freeRegionBuckets[bucketIndex].next;
818
+ assert(freeRegion);
819
+ if (freeRegion != &freeRegionBuckets[bucketIndex])
820
+ {
821
+ void *ptr = attempt_allocate(freeRegion, alignment, size);
822
+ if (ptr)
823
+ return ptr;
824
+
825
+ // We were not able to allocate from the first region found in this bucket, so penalize
826
+ // the region by cycling it to the end of the doubly circular linked list. (constant time)
827
+ // This provides a randomized guarantee that when performing allocations of size k to a
828
+ // bucket of [k-something, k+something] range, we will not always attempt to satisfy the
829
+ // allocation from the same available region at the front of the list, but we try each
830
+ // region in turn.
831
+ unlink_from_free_list(freeRegion);
832
+ prepend_to_free_list(freeRegion, &freeRegionBuckets[bucketIndex]);
833
+ // But do not stick around to attempt to look at other regions in this bucket - move
834
+ // to search the next populated bucket index if this did not fit. This gives a practical
835
+ // "allocation in constant time" guarantee, since the next higher bucket will only have
836
+ // regions that are all of strictly larger size than the requested allocation. Only if
837
+ // there is a difficult alignment requirement we may fail to perform the allocation from
838
+ // a region in the next bucket, and if so, we keep trying higher buckets until one of them
839
+ // works.
840
+ ++bucketIndex;
841
+ bucketMask >>= 1;
842
+ }
843
+ else
844
+ {
845
+ // This bucket was not populated after all with any regions,
846
+ // but we just had a stale bit set to mark a populated bucket.
847
+ // Reset the bit to update latest status so that we do not
848
+ // redundantly look at this bucket again.
849
+ freeRegionBucketsUsed &= ~(((BUCKET_BITMASK_T)1) << bucketIndex);
850
+ bucketMask ^= 1;
851
+ }
852
+ // Instead of recomputing bucketMask from scratch at the end of each loop, it is updated as we go,
853
+ // to avoid undefined behavior with (x >> 32)/(x >> 64) when bucketIndex reaches 32/64, (the shift would come out as a no-op instead of 0).
854
+ assert((bucketIndex == NUM_FREE_BUCKETS && bucketMask == 0) || (bucketMask == freeRegionBucketsUsed >> bucketIndex));
855
+ }
856
+
857
+ // None of the buckets were able to accommodate an allocation. If this happens we are almost out of memory.
858
+ // The largest bucket might contain some suitable regions, but we only looked at one region in that bucket, so
859
+ // as a last resort, loop through more free regions in the bucket that represents the largest allocations available.
860
+ // But only if the bucket representing largest allocations available is not any of the first thirty buckets,
861
+ // these represent allocatable areas less than <1024 bytes - which could be a lot of scrap.
862
+ // In such case, prefer to sbrk() in more memory right away.
863
+ int largestBucketIndex = NUM_FREE_BUCKETS - 1 - __builtin_clzll(freeRegionBucketsUsed);
864
+ // freeRegion will be null if there is absolutely no memory left. (all buckets are 100% used)
865
+ Region *freeRegion = freeRegionBucketsUsed ? freeRegionBuckets[largestBucketIndex].next : 0;
866
+ // The 30 first free region buckets cover memory blocks < 2048 bytes, so skip looking at those here (too small)
867
+ if (freeRegionBucketsUsed >> 30)
868
+ {
869
+ // Look only at a constant number of regions in this bucket max, to avoid bad worst case behavior.
870
+ // If this many regions cannot find free space, we give up and prefer to sbrk() more instead.
871
+ const int maxRegionsToTryBeforeGivingUp = 99;
872
+ int numTriesLeft = maxRegionsToTryBeforeGivingUp;
873
+ while(freeRegion != &freeRegionBuckets[largestBucketIndex] && numTriesLeft-- > 0)
874
+ {
875
+ void *ptr = attempt_allocate(freeRegion, alignment, size);
876
+ if (ptr)
877
+ return ptr;
878
+ freeRegion = freeRegion->next;
879
+ }
880
+ }
881
+
882
+ // We were unable to find a free memory region. Must sbrk() in more memory!
883
+ size_t numBytesToClaim = size+sizeof(Region)*3;
884
+ assert(numBytesToClaim > size); // 32-bit wraparound should not happen here, allocation size has been validated above!
885
+ bool success = claim_more_memory(numBytesToClaim);
886
+ if (success)
887
+ return allocate_memory(alignment, size); // Recurse back to itself to try again
888
+
889
+ // also sbrk() failed, we are really really constrained :( As a last resort, go back to looking at the
890
+ // bucket we already looked at above, continuing where the above search left off - perhaps there are
891
+ // regions we overlooked the first time that might be able to satisfy the allocation.
892
+ if (freeRegion)
893
+ {
894
+ while(freeRegion != &freeRegionBuckets[largestBucketIndex])
895
+ {
896
+ void *ptr = attempt_allocate(freeRegion, alignment, size);
897
+ if (ptr)
898
+ return ptr;
899
+ freeRegion = freeRegion->next;
900
+ }
901
+ }
902
+
903
+ #ifdef EMMALLOC_VERBOSE
904
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('Could not find a free memory block!'));
905
+ #endif
906
+
907
+ return 0;
908
+ }
909
+
910
+ static
911
+ void *emmalloc_memalign(size_t alignment, size_t size)
912
+ {
913
+ MALLOC_ACQUIRE();
914
+ void *ptr = allocate_memory(alignment, size);
915
+ MALLOC_RELEASE();
916
+ return ptr;
917
+ }
918
+
919
+ #if 0
920
+ void * EMMALLOC_EXPORT memalign(size_t alignment, size_t size)
921
+ {
922
+ return emmalloc_memalign(alignment, size);
923
+ }
924
+ #endif
925
+
926
+ void * EMMALLOC_EXPORT aligned_alloc(size_t alignment, size_t size)
927
+ {
928
+ if ((alignment % sizeof(void *) != 0) || (size % alignment) != 0)
929
+ return 0;
930
+ return emmalloc_memalign(alignment, size);
931
+ }
932
+
933
+ static
934
+ void *emmalloc_malloc(size_t size)
935
+ {
936
+ return emmalloc_memalign(MALLOC_ALIGNMENT, size);
937
+ }
938
+
939
+ void * EMMALLOC_EXPORT malloc(size_t size)
940
+ {
941
+ return emmalloc_malloc(size);
942
+ }
943
+
944
+ static
945
+ size_t emmalloc_usable_size(void *ptr)
946
+ {
947
+ if (!ptr)
948
+ return 0;
949
+
950
+ uint8_t *regionStartPtr = (uint8_t*)ptr - sizeof(size_t);
951
+ Region *region = (Region*)(regionStartPtr);
952
+ assert(HAS_ALIGNMENT(region, sizeof(size_t)));
953
+
954
+ MALLOC_ACQUIRE();
955
+
956
+ size_t size = region->size;
957
+ assert(size >= sizeof(Region));
958
+ assert(region_is_in_use(region));
959
+
960
+ MALLOC_RELEASE();
961
+
962
+ return size - REGION_HEADER_SIZE;
963
+ }
964
+
965
+ size_t EMMALLOC_EXPORT malloc_usable_size(void *ptr)
966
+ {
967
+ return emmalloc_usable_size(ptr);
968
+ }
969
+
970
+ static
971
+ void emmalloc_free(void *ptr)
972
+ {
973
+ #ifdef EMMALLOC_MEMVALIDATE
974
+ emmalloc_validate_memory_regions();
975
+ #endif
976
+
977
+ if (!ptr)
978
+ return;
979
+
980
+ #ifdef EMMALLOC_VERBOSE
981
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('free(ptr=0x'+($0>>>0).toString(16)+')'), ptr);
982
+ #endif
983
+
984
+ uint8_t *regionStartPtr = (uint8_t*)ptr - sizeof(size_t);
985
+ Region *region = (Region*)(regionStartPtr);
986
+ assert(HAS_ALIGNMENT(region, sizeof(size_t)));
987
+
988
+ MALLOC_ACQUIRE();
989
+
990
+ size_t size = region->size;
991
+ #ifdef EMMALLOC_VERBOSE
992
+ if (size < sizeof(Region) || !region_is_in_use(region))
993
+ {
994
+ if (debug_region_is_consistent(region))
995
+ // LLVM wasm backend bug: cannot use MAIN_THREAD_ASYNC_EM_ASM() here, that generates internal compiler error
996
+ // Reproducible by running e.g. other.test_alloc_3GB
997
+ EM_ASM(console.error('Double free at region ptr 0x' + ($0>>>0).toString(16) + ', region->size: 0x' + ($1>>>0).toString(16) + ', region->sizeAtCeiling: 0x' + ($2>>>0).toString(16) + ')'), region, size, region_ceiling_size(region));
998
+ else
999
+ MAIN_THREAD_ASYNC_EM_ASM(console.error('Corrupt region at region ptr 0x' + ($0>>>0).toString(16) + ' region->size: 0x' + ($1>>>0).toString(16) + ', region->sizeAtCeiling: 0x' + ($2>>>0).toString(16) + ')'), region, size, region_ceiling_size(region));
1000
+ }
1001
+ #endif
1002
+ assert(size >= sizeof(Region));
1003
+ assert(region_is_in_use(region));
1004
+
1005
+ #ifdef __EMSCRIPTEN_TRACING__
1006
+ emscripten_trace_record_free(region);
1007
+ #endif
1008
+
1009
+ // Check merging with left side
1010
+ size_t prevRegionSizeField = ((size_t*)region)[-1];
1011
+ size_t prevRegionSize = prevRegionSizeField & ~FREE_REGION_FLAG;
1012
+ if (prevRegionSizeField != prevRegionSize) // Previous region is free?
1013
+ {
1014
+ Region *prevRegion = (Region*)((uint8_t*)region - prevRegionSize);
1015
+ assert(debug_region_is_consistent(prevRegion));
1016
+ unlink_from_free_list(prevRegion);
1017
+ regionStartPtr = (uint8_t*)prevRegion;
1018
+ size += prevRegionSize;
1019
+ }
1020
+
1021
+ // Check merging with right side
1022
+ Region *nextRegion = next_region(region);
1023
+ assert(debug_region_is_consistent(nextRegion));
1024
+ size_t sizeAtEnd = *(size_t*)region_payload_end_ptr(nextRegion);
1025
+ if (nextRegion->size != sizeAtEnd)
1026
+ {
1027
+ unlink_from_free_list(nextRegion);
1028
+ size += nextRegion->size;
1029
+ }
1030
+
1031
+ create_free_region(regionStartPtr, size);
1032
+ link_to_free_list((Region*)regionStartPtr);
1033
+
1034
+ MALLOC_RELEASE();
1035
+
1036
+ #ifdef EMMALLOC_MEMVALIDATE
1037
+ emmalloc_validate_memory_regions();
1038
+ #endif
1039
+ }
1040
+
1041
+ void EMMALLOC_EXPORT free(void *ptr)
1042
+ {
1043
+ emmalloc_free(ptr);
1044
+ }
1045
+
1046
+ // Can be called to attempt to increase or decrease the size of the given region
1047
+ // to a new size (in-place). Returns 1 if resize succeeds, and 0 on failure.
1048
+ static int attempt_region_resize(Region *region, size_t size)
1049
+ {
1050
+ ASSERT_MALLOC_IS_ACQUIRED();
1051
+ assert(size > 0);
1052
+ assert(HAS_ALIGNMENT(size, sizeof(size_t)));
1053
+
1054
+ #ifdef EMMALLOC_VERBOSE
1055
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('attempt_region_resize(region=0x' + ($0>>>0).toString(16) + ', size=' + ($1>>>0) + ' bytes)'), region, size);
1056
+ #endif
1057
+
1058
+ // First attempt to resize this region, if the next region that follows this one
1059
+ // is a free region.
1060
+ Region *nextRegion = next_region(region);
1061
+ uint8_t *nextRegionEndPtr = (uint8_t*)nextRegion + nextRegion->size;
1062
+ size_t sizeAtCeiling = ((size_t*)nextRegionEndPtr)[-1];
1063
+ if (nextRegion->size != sizeAtCeiling) // Next region is free?
1064
+ {
1065
+ assert(region_is_free(nextRegion));
1066
+ uint8_t *newNextRegionStartPtr = (uint8_t*)region + size;
1067
+ assert(HAS_ALIGNMENT(newNextRegionStartPtr, sizeof(size_t)));
1068
+ // Next region does not shrink to too small size?
1069
+ if (newNextRegionStartPtr + sizeof(Region) <= nextRegionEndPtr)
1070
+ {
1071
+ unlink_from_free_list(nextRegion);
1072
+ create_free_region(newNextRegionStartPtr, nextRegionEndPtr - newNextRegionStartPtr);
1073
+ link_to_free_list((Region*)newNextRegionStartPtr);
1074
+ create_used_region(region, newNextRegionStartPtr - (uint8_t*)region);
1075
+ return 1;
1076
+ }
1077
+ // If we remove the next region altogether, allocation is satisfied?
1078
+ if (newNextRegionStartPtr <= nextRegionEndPtr)
1079
+ {
1080
+ unlink_from_free_list(nextRegion);
1081
+ create_used_region(region, region->size + nextRegion->size);
1082
+ return 1;
1083
+ }
1084
+ }
1085
+ else
1086
+ {
1087
+ // Next region is an used region - we cannot change its starting address. However if we are shrinking the
1088
+ // size of this region, we can create a new free region between this and the next used region.
1089
+ if (size + sizeof(Region) <= region->size)
1090
+ {
1091
+ size_t freeRegionSize = region->size - size;
1092
+ create_used_region(region, size);
1093
+ Region *freeRegion = (Region *)((uint8_t*)region + size);
1094
+ create_free_region(freeRegion, freeRegionSize);
1095
+ link_to_free_list(freeRegion);
1096
+ return 1;
1097
+ }
1098
+ else if (size <= region->size)
1099
+ {
1100
+ // Caller was asking to shrink the size, but due to not being able to fit a full Region in the shrunk
1101
+ // area, we cannot actually do anything. This occurs if the shrink amount is really small. In such case,
1102
+ // just call it success without doing any work.
1103
+ return 1;
1104
+ }
1105
+ }
1106
+ #ifdef EMMALLOC_VERBOSE
1107
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('attempt_region_resize failed.'));
1108
+ #endif
1109
+ return 0;
1110
+ }
1111
+
1112
+ static int acquire_and_attempt_region_resize(Region *region, size_t size)
1113
+ {
1114
+ MALLOC_ACQUIRE();
1115
+ int success = attempt_region_resize(region, size);
1116
+ MALLOC_RELEASE();
1117
+ return success;
1118
+ }
1119
+
1120
+ static
1121
+ void *emmalloc_aligned_realloc(void *ptr, size_t alignment, size_t size)
1122
+ {
1123
+ #ifdef EMMALLOC_VERBOSE
1124
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('aligned_realloc(ptr=0x' + ($0>>>0).toString(16) + ', alignment=' + $1 + ', size=' + ($2>>>0)), ptr, alignment, size);
1125
+ #endif
1126
+
1127
+ if (!ptr)
1128
+ return emmalloc_memalign(alignment, size);
1129
+
1130
+ if (size == 0)
1131
+ {
1132
+ free(ptr);
1133
+ return 0;
1134
+ }
1135
+
1136
+ if (size > MAX_ALLOC_SIZE)
1137
+ {
1138
+ #ifdef EMMALLOC_VERBOSE
1139
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('Allocation failed: attempted allocation size is too large: ' + ($0 >>> 0) + 'bytes! (negative integer wraparound?)'), size);
1140
+ #endif
1141
+ return 0;
1142
+ }
1143
+
1144
+ assert(IS_POWER_OF_2(alignment));
1145
+ // aligned_realloc() cannot be used to ask to change the alignment of a pointer.
1146
+ assert(HAS_ALIGNMENT(ptr, alignment));
1147
+ size = validate_alloc_size(size);
1148
+
1149
+ // Calculate the region start address of the original allocation
1150
+ Region *region = (Region*)((uint8_t*)ptr - sizeof(size_t));
1151
+
1152
+ // First attempt to resize the given region to avoid having to copy memory around
1153
+ if (acquire_and_attempt_region_resize(region, size + REGION_HEADER_SIZE))
1154
+ {
1155
+ #ifdef __EMSCRIPTEN_TRACING__
1156
+ emscripten_trace_record_reallocation(ptr, ptr, size);
1157
+ #endif
1158
+ return ptr;
1159
+ }
1160
+
1161
+ // If resize failed, we must allocate a new region, copy the data over, and then
1162
+ // free the old region.
1163
+ void *newptr = emmalloc_memalign(alignment, size);
1164
+ if (newptr)
1165
+ {
1166
+ memcpy(newptr, ptr, MIN(size, region->size - REGION_HEADER_SIZE));
1167
+ free(ptr);
1168
+ }
1169
+ // N.B. If there is not enough memory, the old memory block should not be freed and
1170
+ // null pointer is returned.
1171
+ return newptr;
1172
+ }
1173
+
1174
+ #if 0
1175
+ void * EMMALLOC_EXPORT aligned_realloc(void *ptr, size_t alignment, size_t size)
1176
+ {
1177
+ return emmalloc_aligned_realloc(ptr, alignment, size);
1178
+ }
1179
+ #endif
1180
+
1181
+ #if 0
1182
+ // realloc_try() is like realloc(), but only attempts to try to resize the existing memory
1183
+ // area. If resizing the existing memory area fails, then realloc_try() will return 0
1184
+ // (the original memory block is not freed or modified). If resizing succeeds, previous
1185
+ // memory contents will be valid up to min(old length, new length) bytes.
1186
+ void *emmalloc_realloc_try(void *ptr, size_t size)
1187
+ {
1188
+ if (!ptr)
1189
+ return 0;
1190
+
1191
+ if (size == 0)
1192
+ {
1193
+ free(ptr);
1194
+ return 0;
1195
+ }
1196
+
1197
+ if (size > MAX_ALLOC_SIZE)
1198
+ {
1199
+ #ifdef EMMALLOC_VERBOSE
1200
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('Allocation failed: attempted allocation size is too large: ' + ($0 >>> 0) + 'bytes! (negative integer wraparound?)'), size);
1201
+ #endif
1202
+ return 0;
1203
+ }
1204
+
1205
+ size = validate_alloc_size(size);
1206
+
1207
+ // Calculate the region start address of the original allocation
1208
+ Region *region = (Region*)((uint8_t*)ptr - sizeof(size_t));
1209
+
1210
+ // Attempt to resize the given region to avoid having to copy memory around
1211
+ int success = acquire_and_attempt_region_resize(region, size + REGION_HEADER_SIZE);
1212
+ #ifdef __EMSCRIPTEN_TRACING__
1213
+ if (success)
1214
+ emscripten_trace_record_reallocation(ptr, ptr, size);
1215
+ #endif
1216
+ return success ? ptr : 0;
1217
+ }
1218
+
1219
+ // emmalloc_aligned_realloc_uninitialized() is like aligned_realloc(), but old memory contents
1220
+ // will be undefined after reallocation. (old memory is not preserved in any case)
1221
+ void *emmalloc_aligned_realloc_uninitialized(void *ptr, size_t alignment, size_t size)
1222
+ {
1223
+ if (!ptr)
1224
+ return emmalloc_memalign(alignment, size);
1225
+
1226
+ if (size == 0)
1227
+ {
1228
+ free(ptr);
1229
+ return 0;
1230
+ }
1231
+
1232
+ if (size > MAX_ALLOC_SIZE)
1233
+ {
1234
+ #ifdef EMMALLOC_VERBOSE
1235
+ MAIN_THREAD_ASYNC_EM_ASM(console.log('Allocation failed: attempted allocation size is too large: ' + ($0 >>> 0) + 'bytes! (negative integer wraparound?)'), size);
1236
+ #endif
1237
+ return 0;
1238
+ }
1239
+
1240
+ size = validate_alloc_size(size);
1241
+
1242
+ // Calculate the region start address of the original allocation
1243
+ Region *region = (Region*)((uint8_t*)ptr - sizeof(size_t));
1244
+
1245
+ // First attempt to resize the given region to avoid having to copy memory around
1246
+ if (acquire_and_attempt_region_resize(region, size + REGION_HEADER_SIZE))
1247
+ {
1248
+ #ifdef __EMSCRIPTEN_TRACING__
1249
+ emscripten_trace_record_reallocation(ptr, ptr, size);
1250
+ #endif
1251
+ return ptr;
1252
+ }
1253
+
1254
+ // If resize failed, drop the old region and allocate a new region. Memory is not
1255
+ // copied over
1256
+ free(ptr);
1257
+ return emmalloc_memalign(alignment, size);
1258
+ }
1259
+ #endif
1260
+
1261
+ static
1262
+ void *emmalloc_realloc(void *ptr, size_t size)
1263
+ {
1264
+ return emmalloc_aligned_realloc(ptr, MALLOC_ALIGNMENT, size);
1265
+ }
1266
+
1267
+ void * EMMALLOC_EXPORT realloc(void *ptr, size_t size)
1268
+ {
1269
+ return emmalloc_realloc(ptr, size);
1270
+ }
1271
+
1272
+ #if 0
1273
+ // realloc_uninitialized() is like realloc(), but old memory contents
1274
+ // will be undefined after reallocation. (old memory is not preserved in any case)
1275
+ void *emmalloc_realloc_uninitialized(void *ptr, size_t size)
1276
+ {
1277
+ return emmalloc_aligned_realloc_uninitialized(ptr, MALLOC_ALIGNMENT, size);
1278
+ }
1279
+ #endif
1280
+
1281
+ static
1282
+ int emmalloc_posix_memalign(void **memptr, size_t alignment, size_t size)
1283
+ {
1284
+ assert(memptr);
1285
+ if (alignment % sizeof(void *) != 0)
1286
+ return 22/* EINVAL*/;
1287
+ *memptr = emmalloc_memalign(alignment, size);
1288
+ return *memptr ? 0 : 12/*ENOMEM*/;
1289
+ }
1290
+
1291
+ int EMMALLOC_EXPORT posix_memalign(void **memptr, size_t alignment, size_t size)
1292
+ {
1293
+ return emmalloc_posix_memalign(memptr, alignment, size);
1294
+ }
1295
+
1296
+ static
1297
+ void *emmalloc_calloc(size_t num, size_t size)
1298
+ {
1299
+ size_t bytes = num*size;
1300
+ void *ptr = emmalloc_memalign(MALLOC_ALIGNMENT, bytes);
1301
+ if (ptr)
1302
+ memset(ptr, 0, bytes);
1303
+ return ptr;
1304
+ }
1305
+
1306
+ void * EMMALLOC_EXPORT calloc(size_t num, size_t size)
1307
+ {
1308
+ return emmalloc_calloc(num, size);
1309
+ }
1310
+
1311
+ #if 0
1312
+ static int count_linked_list_size(Region *list)
1313
+ {
1314
+ int size = 1;
1315
+ for(Region *i = list->next; i != list; list = list->next)
1316
+ ++size;
1317
+ return size;
1318
+ }
1319
+
1320
+ static size_t count_linked_list_space(Region *list)
1321
+ {
1322
+ size_t space = 0;
1323
+ for(Region *i = list->next; i != list; list = list->next)
1324
+ space += region_payload_end_ptr(i) - region_payload_start_ptr(i);
1325
+ return space;
1326
+ }
1327
+
1328
+ struct mallinfo emmalloc_mallinfo()
1329
+ {
1330
+ MALLOC_ACQUIRE();
1331
+
1332
+ struct mallinfo info;
1333
+ // Non-mmapped space allocated (bytes): For emmalloc,
1334
+ // let's define this as the difference between heap size and dynamic top end.
1335
+ info.arena = emscripten_get_heap_size() - (size_t)sbrk(0);
1336
+ // Number of "ordinary" blocks. Let's define this as the number of highest
1337
+ // size blocks. (subtract one from each, since there is a sentinel node in each list)
1338
+ info.ordblks = count_linked_list_size(&freeRegionBuckets[NUM_FREE_BUCKETS-1])-1;
1339
+ // Number of free "fastbin" blocks. For emmalloc, define this as the number
1340
+ // of blocks that are not in the largest pristine block.
1341
+ info.smblks = 0;
1342
+ // The total number of bytes in free "fastbin" blocks.
1343
+ info.fsmblks = 0;
1344
+ for(int i = 0; i < NUM_FREE_BUCKETS-1; ++i)
1345
+ {
1346
+ info.smblks += count_linked_list_size(&freeRegionBuckets[i])-1;
1347
+ info.fsmblks += count_linked_list_space(&freeRegionBuckets[i]);
1348
+ }
1349
+
1350
+ info.hblks = 0; // Number of mmapped regions: always 0. (no mmap support)
1351
+ info.hblkhd = 0; // Amount of bytes in mmapped regions: always 0. (no mmap support)
1352
+
1353
+ // Walk through all the heap blocks to report the following data:
1354
+ // The "highwater mark" for allocated space—that is, the maximum amount of
1355
+ // space that was ever allocated. Emmalloc does not want to pay code to
1356
+ // track this, so this is only reported from current allocation data, and
1357
+ // may not be accurate.
1358
+ info.usmblks = 0;
1359
+ info.uordblks = 0; // The total number of bytes used by in-use allocations.
1360
+ info.fordblks = 0; // The total number of bytes in free blocks.
1361
+ // The total amount of releasable free space at the top of the heap.
1362
+ // This is the maximum number of bytes that could ideally be released by malloc_trim(3).
1363
+ Region *lastActualRegion = prev_region((Region*)(listOfAllRegions->endPtr - sizeof(Region)));
1364
+ info.keepcost = region_is_free(lastActualRegion) ? lastActualRegion->size : 0;
1365
+
1366
+ RootRegion *root = listOfAllRegions;
1367
+ while(root)
1368
+ {
1369
+ Region *r = (Region*)root;
1370
+ assert(debug_region_is_consistent(r));
1371
+ uint8_t *lastRegionEnd = root->endPtr;
1372
+ while((uint8_t*)r < lastRegionEnd)
1373
+ {
1374
+ assert(debug_region_is_consistent(r));
1375
+
1376
+ if (region_is_free(r))
1377
+ {
1378
+ // Count only the payload of the free block towards free memory.
1379
+ info.fordblks += region_payload_end_ptr(r) - region_payload_start_ptr(r);
1380
+ // But the header data of the free block goes towards used memory.
1381
+ info.uordblks += REGION_HEADER_SIZE;
1382
+ }
1383
+ else
1384
+ {
1385
+ info.uordblks += r->size;
1386
+ }
1387
+ // Update approximate watermark data
1388
+ info.usmblks = MAX(info.usmblks, (intptr_t)r + r->size);
1389
+
1390
+ if (r->size == 0)
1391
+ break;
1392
+ r = next_region(r);
1393
+ }
1394
+ root = root->next;
1395
+ }
1396
+
1397
+ MALLOC_RELEASE();
1398
+ return info;
1399
+ }
1400
+
1401
+ struct mallinfo EMMALLOC_EXPORT mallinfo()
1402
+ {
1403
+ return emmalloc_mallinfo();
1404
+ }
1405
+
1406
+ // Note! This function is not fully multithreadin safe: while this function is running, other threads should not be
1407
+ // allowed to call sbrk()!
1408
+ static int trim_dynamic_heap_reservation(size_t pad)
1409
+ {
1410
+ ASSERT_MALLOC_IS_ACQUIRED();
1411
+
1412
+ if (!listOfAllRegions)
1413
+ return 0; // emmalloc is not controlling any dynamic memory at all - cannot release memory.
1414
+ uint8_t *previousSbrkEndAddress = listOfAllRegions->endPtr;
1415
+ assert(sbrk(0) == previousSbrkEndAddress);
1416
+ size_t lastMemoryRegionSize = ((size_t*)previousSbrkEndAddress)[-1];
1417
+ assert(lastMemoryRegionSize == 16); // // The last memory region should be a sentinel node of exactly 16 bytes in size.
1418
+ Region *endSentinelRegion = (Region*)(previousSbrkEndAddress - sizeof(Region));
1419
+ Region *lastActualRegion = prev_region(endSentinelRegion);
1420
+
1421
+ // Round padding up to multiple of 4 bytes to keep sbrk() and memory region alignment intact.
1422
+ // Also have at least 8 bytes of payload so that we can form a full free region.
1423
+ size_t newRegionSize = (size_t)ALIGN_UP(pad, 4);
1424
+ if (pad > 0)
1425
+ newRegionSize += sizeof(Region) - (newRegionSize - pad);
1426
+
1427
+ if (!region_is_free(lastActualRegion) || lastActualRegion->size <= newRegionSize)
1428
+ return 0; // Last actual region is in use, or caller desired to leave more free memory intact than there is.
1429
+
1430
+ // This many bytes will be shrunk away.
1431
+ size_t shrinkAmount = lastActualRegion->size - newRegionSize;
1432
+ assert(HAS_ALIGNMENT(shrinkAmount, 4));
1433
+
1434
+ unlink_from_free_list(lastActualRegion);
1435
+ // If pad == 0, we should delete the last free region altogether. If pad > 0,
1436
+ // shrink the last free region to the desired size.
1437
+ if (newRegionSize > 0)
1438
+ {
1439
+ create_free_region(lastActualRegion, newRegionSize);
1440
+ link_to_free_list(lastActualRegion);
1441
+ }
1442
+
1443
+ // Recreate the sentinel region at the end of the last free region
1444
+ endSentinelRegion = (Region*)((uint8_t*)lastActualRegion + newRegionSize);
1445
+ create_used_region(endSentinelRegion, sizeof(Region));
1446
+
1447
+ // And update the size field of the whole region block.
1448
+ listOfAllRegions->endPtr = (uint8_t*)endSentinelRegion + sizeof(Region);
1449
+
1450
+ // Finally call sbrk() to shrink the memory area.
1451
+ void *oldSbrk = sbrk(-(intptr_t)shrinkAmount);
1452
+ assert((intptr_t)oldSbrk != -1); // Shrinking with sbrk() should never fail.
1453
+ assert(oldSbrk == previousSbrkEndAddress); // Another thread should not have raced to increase sbrk() on us!
1454
+
1455
+ // All successful, and we actually trimmed memory!
1456
+ return 1;
1457
+ }
1458
+
1459
+ int emmalloc_trim(size_t pad)
1460
+ {
1461
+ MALLOC_ACQUIRE();
1462
+ int success = trim_dynamic_heap_reservation(pad);
1463
+ MALLOC_RELEASE();
1464
+ return success;
1465
+ }
1466
+
1467
+ int EMMALLOC_EXPORT malloc_trim(size_t pad)
1468
+ {
1469
+ return emmalloc_trim(pad);
1470
+ }
1471
+
1472
+ size_t emmalloc_dynamic_heap_size()
1473
+ {
1474
+ size_t dynamicHeapSize = 0;
1475
+
1476
+ MALLOC_ACQUIRE();
1477
+ RootRegion *root = listOfAllRegions;
1478
+ while(root)
1479
+ {
1480
+ dynamicHeapSize += root->endPtr - (uint8_t*)root;
1481
+ root = root->next;
1482
+ }
1483
+ MALLOC_RELEASE();
1484
+ return dynamicHeapSize;
1485
+ }
1486
+
1487
+ size_t emmalloc_free_dynamic_memory()
1488
+ {
1489
+ size_t freeDynamicMemory = 0;
1490
+
1491
+ int bucketIndex = 0;
1492
+
1493
+ MALLOC_ACQUIRE();
1494
+ BUCKET_BITMASK_T bucketMask = freeRegionBucketsUsed;
1495
+
1496
+ // Loop through each bucket that has free regions in it, based on bits set in freeRegionBucketsUsed bitmap.
1497
+ while(bucketMask)
1498
+ {
1499
+ BUCKET_BITMASK_T indexAdd = __builtin_ctzll(bucketMask);
1500
+ bucketIndex += indexAdd;
1501
+ bucketMask >>= indexAdd;
1502
+ for(Region *freeRegion = freeRegionBuckets[bucketIndex].next;
1503
+ freeRegion != &freeRegionBuckets[bucketIndex];
1504
+ freeRegion = freeRegion->next)
1505
+ {
1506
+ freeDynamicMemory += freeRegion->size - REGION_HEADER_SIZE;
1507
+ }
1508
+ ++bucketIndex;
1509
+ bucketMask >>= 1;
1510
+ }
1511
+ MALLOC_RELEASE();
1512
+ return freeDynamicMemory;
1513
+ }
1514
+
1515
+ size_t emmalloc_compute_free_dynamic_memory_fragmentation_map(size_t freeMemorySizeMap[32])
1516
+ {
1517
+ memset((void*)freeMemorySizeMap, 0, sizeof(freeMemorySizeMap[0])*32);
1518
+
1519
+ size_t numFreeMemoryRegions = 0;
1520
+ int bucketIndex = 0;
1521
+ MALLOC_ACQUIRE();
1522
+ BUCKET_BITMASK_T bucketMask = freeRegionBucketsUsed;
1523
+
1524
+ // Loop through each bucket that has free regions in it, based on bits set in freeRegionBucketsUsed bitmap.
1525
+ while(bucketMask)
1526
+ {
1527
+ BUCKET_BITMASK_T indexAdd = __builtin_ctzll(bucketMask);
1528
+ bucketIndex += indexAdd;
1529
+ bucketMask >>= indexAdd;
1530
+ for(Region *freeRegion = freeRegionBuckets[bucketIndex].next;
1531
+ freeRegion != &freeRegionBuckets[bucketIndex];
1532
+ freeRegion = freeRegion->next)
1533
+ {
1534
+ ++numFreeMemoryRegions;
1535
+ size_t freeDynamicMemory = freeRegion->size - REGION_HEADER_SIZE;
1536
+ if (freeDynamicMemory > 0)
1537
+ ++freeMemorySizeMap[31-__builtin_clz(freeDynamicMemory)];
1538
+ else
1539
+ ++freeMemorySizeMap[0];
1540
+ }
1541
+ ++bucketIndex;
1542
+ bucketMask >>= 1;
1543
+ }
1544
+ MALLOC_RELEASE();
1545
+ return numFreeMemoryRegions;
1546
+ }
1547
+
1548
+ size_t emmalloc_unclaimed_heap_memory(void) {
1549
+ return emscripten_get_heap_max() - (size_t)sbrk(0);
1550
+ }
1551
+ #endif