toilscript 0.0.1

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 (117) hide show
  1. package/LICENSE +201 -0
  2. package/NOTICE +94 -0
  3. package/README.md +114 -0
  4. package/bin/asc.js +35 -0
  5. package/bin/asinit.js +468 -0
  6. package/dist/asc.d.ts +4 -0
  7. package/dist/toilscript.d.ts +4 -0
  8. package/dist/transform.cjs +1 -0
  9. package/dist/transform.d.ts +1 -0
  10. package/dist/transform.js +1 -0
  11. package/lib/binaryen.d.ts +2 -0
  12. package/lib/binaryen.js +2 -0
  13. package/package.json +114 -0
  14. package/std/README.md +6 -0
  15. package/std/assembly/array.ts +550 -0
  16. package/std/assembly/arraybuffer.ts +77 -0
  17. package/std/assembly/atomics.ts +127 -0
  18. package/std/assembly/bindings/asyncify.ts +16 -0
  19. package/std/assembly/bindings/dom.ts +291 -0
  20. package/std/assembly/bindings/node.ts +6 -0
  21. package/std/assembly/bitflags.ts +53 -0
  22. package/std/assembly/builtins.ts +2650 -0
  23. package/std/assembly/byteslice.ts +177 -0
  24. package/std/assembly/compat.ts +2 -0
  25. package/std/assembly/console.ts +42 -0
  26. package/std/assembly/crypto.ts +9 -0
  27. package/std/assembly/dataview.ts +181 -0
  28. package/std/assembly/date.ts +375 -0
  29. package/std/assembly/diagnostics.ts +11 -0
  30. package/std/assembly/encoding.ts +151 -0
  31. package/std/assembly/endian.ts +45 -0
  32. package/std/assembly/error.ts +44 -0
  33. package/std/assembly/fixedarray.ts +173 -0
  34. package/std/assembly/fixedmap.ts +326 -0
  35. package/std/assembly/fixedset.ts +275 -0
  36. package/std/assembly/function.ts +42 -0
  37. package/std/assembly/index.d.ts +2891 -0
  38. package/std/assembly/iterator.ts +35 -0
  39. package/std/assembly/map.ts +269 -0
  40. package/std/assembly/math.ts +3289 -0
  41. package/std/assembly/memory.ts +123 -0
  42. package/std/assembly/number.ts +388 -0
  43. package/std/assembly/object.ts +36 -0
  44. package/std/assembly/performance.ts +9 -0
  45. package/std/assembly/pointer.ts +80 -0
  46. package/std/assembly/polyfills.ts +27 -0
  47. package/std/assembly/process.ts +50 -0
  48. package/std/assembly/reference.ts +48 -0
  49. package/std/assembly/regexp.ts +12 -0
  50. package/std/assembly/rt/README.md +83 -0
  51. package/std/assembly/rt/common.ts +81 -0
  52. package/std/assembly/rt/index-incremental.ts +2 -0
  53. package/std/assembly/rt/index-memory.ts +1 -0
  54. package/std/assembly/rt/index-minimal.ts +2 -0
  55. package/std/assembly/rt/index-stub.ts +1 -0
  56. package/std/assembly/rt/index.d.ts +37 -0
  57. package/std/assembly/rt/itcms.ts +419 -0
  58. package/std/assembly/rt/memory-runtime.ts +94 -0
  59. package/std/assembly/rt/rtrace.ts +15 -0
  60. package/std/assembly/rt/stub.ts +133 -0
  61. package/std/assembly/rt/tcms.ts +254 -0
  62. package/std/assembly/rt/tlsf.ts +592 -0
  63. package/std/assembly/rt.ts +90 -0
  64. package/std/assembly/set.ts +225 -0
  65. package/std/assembly/shared/feature.ts +68 -0
  66. package/std/assembly/shared/runtime.ts +13 -0
  67. package/std/assembly/shared/target.ts +11 -0
  68. package/std/assembly/shared/tsconfig.json +11 -0
  69. package/std/assembly/shared/typeinfo.ts +72 -0
  70. package/std/assembly/staticarray.ts +423 -0
  71. package/std/assembly/string.ts +850 -0
  72. package/std/assembly/symbol.ts +114 -0
  73. package/std/assembly/table.ts +16 -0
  74. package/std/assembly/tsconfig.json +6 -0
  75. package/std/assembly/typedarray.ts +1954 -0
  76. package/std/assembly/uri.ts +17 -0
  77. package/std/assembly/util/bytes.ts +107 -0
  78. package/std/assembly/util/casemap.ts +497 -0
  79. package/std/assembly/util/error.ts +58 -0
  80. package/std/assembly/util/hash.ts +117 -0
  81. package/std/assembly/util/math.ts +1922 -0
  82. package/std/assembly/util/memory.ts +290 -0
  83. package/std/assembly/util/number.ts +873 -0
  84. package/std/assembly/util/sort.ts +313 -0
  85. package/std/assembly/util/string.ts +1202 -0
  86. package/std/assembly/util/uri.ts +275 -0
  87. package/std/assembly/vector.ts +4 -0
  88. package/std/assembly.json +16 -0
  89. package/std/portable/index.d.ts +461 -0
  90. package/std/portable/index.js +416 -0
  91. package/std/portable.json +11 -0
  92. package/std/types/assembly/index.d.ts +1 -0
  93. package/std/types/assembly/package.json +3 -0
  94. package/std/types/portable/index.d.ts +1 -0
  95. package/std/types/portable/package.json +3 -0
  96. package/tsconfig-base.json +13 -0
  97. package/util/README.md +23 -0
  98. package/util/browser/fs.js +1 -0
  99. package/util/browser/module.js +5 -0
  100. package/util/browser/path.js +520 -0
  101. package/util/browser/process.js +59 -0
  102. package/util/browser/url.js +23 -0
  103. package/util/cpu.d.ts +9 -0
  104. package/util/cpu.js +42 -0
  105. package/util/find.d.ts +6 -0
  106. package/util/find.js +20 -0
  107. package/util/node.d.ts +21 -0
  108. package/util/node.js +34 -0
  109. package/util/options.d.ts +70 -0
  110. package/util/options.js +262 -0
  111. package/util/terminal.d.ts +52 -0
  112. package/util/terminal.js +35 -0
  113. package/util/text.d.ts +26 -0
  114. package/util/text.js +114 -0
  115. package/util/tsconfig.json +9 -0
  116. package/util/web.d.ts +11 -0
  117. package/util/web.js +33 -0
@@ -0,0 +1,592 @@
1
+ import { AL_BITS, AL_SIZE, AL_MASK, DEBUG, BLOCK, BLOCK_OVERHEAD, BLOCK_MAXSIZE } from "./common";
2
+ import { oninit, onalloc, onresize, onmove, onfree } from "./rtrace";
3
+ import { E_ALLOCATION_TOO_LARGE } from "../util/error";
4
+
5
+ // === The TLSF (Two-Level Segregate Fit) memory allocator ===
6
+ // see: http://www.gii.upv.es/tlsf/
7
+
8
+ // - `ffs(x)` is equivalent to `ctz(x)` with x != 0
9
+ // - `fls(x)` is equivalent to `sizeof(x) * 8 - clz(x) - 1`
10
+
11
+ // ╒══════════════ Block size interpretation (32-bit) ═════════════╕
12
+ // 3 2 1
13
+ // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
14
+ // ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┴─┴─┴─╫─┴─┴─┴─┤
15
+ // │ | FL │ SB = SL + AL │ ◄─ usize
16
+ // └───────────────────────────────────────────────┴───────╨───────┘
17
+ // FL: first level, SL: second level, AL: alignment, SB: small block
18
+
19
+ // @ts-ignore: decorator
20
+ @inline const SL_BITS: u32 = 4;
21
+ // @ts-ignore: decorator
22
+ @inline const SL_SIZE: u32 = 1 << SL_BITS;
23
+
24
+ // @ts-ignore: decorator
25
+ @inline const SB_BITS: u32 = SL_BITS + AL_BITS;
26
+ // @ts-ignore: decorator
27
+ @inline const SB_SIZE: u32 = 1 << SB_BITS;
28
+
29
+ // @ts-ignore: decorator
30
+ @inline const FL_BITS: u32 = 31 - SB_BITS;
31
+
32
+ // [00]: < 256B (SB) [12]: < 1M
33
+ // [01]: < 512B [13]: < 2M
34
+ // [02]: < 1K [14]: < 4M
35
+ // [03]: < 2K [15]: < 8M
36
+ // [04]: < 4K [16]: < 16M
37
+ // [05]: < 8K [17]: < 32M
38
+ // [06]: < 16K [18]: < 64M
39
+ // [07]: < 32K [19]: < 128M
40
+ // [08]: < 64K [20]: < 256M
41
+ // [09]: < 128K [21]: < 512M
42
+ // [10]: < 256K [22]: <= 1G - OVERHEAD
43
+ // [11]: < 512K
44
+ // VMs limit to 2GB total (currently), making one 1G block max (or three 512M etc.) due to block overhead
45
+
46
+ // Tags stored in otherwise unused alignment bits
47
+
48
+ // @ts-ignore: decorator
49
+ @inline const FREE: usize = 1 << 0;
50
+ // @ts-ignore: decorator
51
+ @inline const LEFTFREE: usize = 1 << 1;
52
+ // @ts-ignore: decorator
53
+ @inline const TAGS_MASK: usize = FREE | LEFTFREE; // <= AL_MASK
54
+
55
+ // ╒════════════════════ Block layout (32-bit) ════════════════════╕
56
+ // 3 2 1
57
+ // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
58
+ // ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┼─┤ ┐
59
+ // │ size │L│F│ ◄─┐ info overhead
60
+ // ╞>ptr═══════════════════════════════════════════════════════╧═╧═╡ │ ┘
61
+ // │ if free: ◄ prev │ ◄─┤ usize
62
+ // ├───────────────────────────────────────────────────────────────┤ │
63
+ // │ if free: next ► │ ◄─┤
64
+ // ├───────────────────────────────────────────────────────────────┤ │
65
+ // │ ... │ │ >= 0
66
+ // ├───────────────────────────────────────────────────────────────┤ │
67
+ // │ if free: back ▲ │ ◄─┘
68
+ // └───────────────────────────────────────────────────────────────┘ >= MIN SIZE
69
+ // F: FREE, L: LEFTFREE
70
+ @unmanaged export class Block extends BLOCK {
71
+
72
+ /** Previous free block, if any. Only valid if free, otherwise part of payload. */
73
+ prev: Block | null;
74
+ /** Next free block, if any. Only valid if free, otherwise part of payload. */
75
+ next: Block | null;
76
+
77
+ // If the block is free, there is a 'back'reference at its end pointing at its start.
78
+ }
79
+
80
+ // Block constants. A block must have a minimum size of three pointers so it can hold `prev`,
81
+ // `next` and `back` if free.
82
+
83
+ // @ts-ignore: decorator
84
+ @inline const BLOCK_MINSIZE: usize = ((3 * sizeof<usize>() + BLOCK_OVERHEAD + AL_MASK) & ~AL_MASK) - BLOCK_OVERHEAD; // prev + next + back
85
+ // @ts-ignore: decorator
86
+ // @inline const BLOCK_MAXSIZE: usize = 1 << (FL_BITS + SB_BITS - 1); // exclusive, lives in common.ts
87
+
88
+ /** Gets the left block of a block. Only valid if the left block is free. */
89
+ // @ts-ignore: decorator
90
+ @inline function GETFREELEFT(block: Block): Block {
91
+ return load<Block>(changetype<usize>(block) - sizeof<usize>());
92
+ }
93
+
94
+ /** Gets the right block of a block by advancing to the right by its size. */
95
+ // @ts-ignore: decorator
96
+ @inline function GETRIGHT(block: Block): Block {
97
+ return changetype<Block>(changetype<usize>(block) + BLOCK_OVERHEAD + (block.mmInfo & ~TAGS_MASK));
98
+ }
99
+
100
+ // ╒═════════════════════ Root layout (32-bit) ════════════════════╕
101
+ // 3 2 1
102
+ // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 bits
103
+ // ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤ ┐
104
+ // │ 0 | flMap S│ ◄────┐
105
+ // ╞═══════════════════════════════════════════════════════════════╡ │
106
+ // │ slMap[0] S │ ◄─┐ │
107
+ // ├───────────────────────────────────────────────────────────────┤ │ │
108
+ // │ slMap[1] │ ◄─┤ │
109
+ // ├───────────────────────────────────────────────────────────────┤ u32 │
110
+ // │ slMap[22] │ ◄─┘ │
111
+ // ╞═══════════════════════════════════════════════════════════════╡ usize
112
+ // │ head[0] │ ◄────┤
113
+ // ├───────────────────────────────────────────────────────────────┤ │
114
+ // │ ... │ ◄────┤
115
+ // ├───────────────────────────────────────────────────────────────┤ │
116
+ // │ head[367] │ ◄────┤
117
+ // ╞═══════════════════════════════════════════════════════════════╡ │
118
+ // │ tail │ ◄────┘
119
+ // └───────────────────────────────────────────────────────────────┘ SIZE ┘
120
+ // S: Small blocks map
121
+ @unmanaged class Root {
122
+ /** First level bitmap. */
123
+ flMap: usize;
124
+ }
125
+
126
+ // Root constants. Where stuff is stored inside of the root structure.
127
+
128
+ // @ts-ignore: decorator
129
+ @inline const SL_START: usize = sizeof<usize>();
130
+ // @ts-ignore: decorator
131
+ @inline const SL_END: usize = SL_START + (FL_BITS << alignof<u32>());
132
+ // @ts-ignore: decorator
133
+ @inline const HL_START: usize = (SL_END + AL_MASK) & ~AL_MASK;
134
+ // @ts-ignore: decorator
135
+ @inline const HL_END: usize = HL_START + FL_BITS * SL_SIZE * sizeof<usize>();
136
+ // @ts-ignore: decorator
137
+ @inline const ROOT_SIZE: usize = HL_END + sizeof<usize>();
138
+
139
+ // @ts-ignore: decorator
140
+ @lazy export let ROOT: Root = changetype<Root>(0); // unsafe initializion below
141
+
142
+ /** Gets the second level map of the specified first level. */
143
+ // @ts-ignore: decorator
144
+ @inline function GETSL(root: Root, fl: usize): u32 {
145
+ return load<u32>(
146
+ changetype<usize>(root) + (fl << alignof<u32>()),
147
+ SL_START
148
+ );
149
+ }
150
+
151
+ /** Sets the second level map of the specified first level. */
152
+ // @ts-ignore: decorator
153
+ @inline function SETSL(root: Root, fl: usize, slMap: u32): void {
154
+ store<u32>(
155
+ changetype<usize>(root) + (fl << alignof<u32>()),
156
+ slMap,
157
+ SL_START
158
+ );
159
+ }
160
+
161
+ /** Gets the head of the free list for the specified combination of first and second level. */
162
+ // @ts-ignore: decorator
163
+ @inline function GETHEAD(root: Root, fl: usize, sl: u32): Block | null {
164
+ return load<Block>(
165
+ changetype<usize>(root) + (((fl << SL_BITS) + <usize>sl) << alignof<usize>()),
166
+ HL_START
167
+ );
168
+ }
169
+
170
+ /** Sets the head of the free list for the specified combination of first and second level. */
171
+ // @ts-ignore: decorator
172
+ @inline function SETHEAD(root: Root, fl: usize, sl: u32, head: Block | null): void {
173
+ store<Block | null>(
174
+ changetype<usize>(root) + (((fl << SL_BITS) + <usize>sl) << alignof<usize>()),
175
+ head,
176
+ HL_START
177
+ );
178
+ }
179
+
180
+ /** Gets the tail block.. */
181
+ // @ts-ignore: decorator
182
+ @inline function GETTAIL(root: Root): Block {
183
+ return load<Block>(
184
+ changetype<usize>(root),
185
+ HL_END
186
+ );
187
+ }
188
+
189
+ /** Sets the tail block. */
190
+ // @ts-ignore: decorator
191
+ @inline function SETTAIL(root: Root, tail: Block): void {
192
+ store<Block>(
193
+ changetype<usize>(root),
194
+ tail,
195
+ HL_END
196
+ );
197
+ }
198
+
199
+ /** Inserts a previously used block back into the free list. */
200
+ function insertBlock(root: Root, block: Block): void {
201
+ if (DEBUG) assert(block); // cannot be null
202
+ let blockInfo = block.mmInfo;
203
+ if (DEBUG) assert(blockInfo & FREE); // must be free
204
+
205
+ let right = GETRIGHT(block);
206
+ let rightInfo = right.mmInfo;
207
+
208
+ // merge with right block if also free
209
+ if (rightInfo & FREE) {
210
+ removeBlock(root, right);
211
+ block.mmInfo = blockInfo = blockInfo + BLOCK_OVERHEAD + (rightInfo & ~TAGS_MASK); // keep block tags
212
+ right = GETRIGHT(block);
213
+ rightInfo = right.mmInfo;
214
+ // 'back' is set below
215
+ }
216
+
217
+ // merge with left block if also free
218
+ if (blockInfo & LEFTFREE) {
219
+ let left = GETFREELEFT(block);
220
+ let leftInfo = left.mmInfo;
221
+ if (DEBUG) assert(leftInfo & FREE); // must be free according to right tags
222
+ removeBlock(root, left);
223
+ block = left;
224
+ block.mmInfo = blockInfo = leftInfo + BLOCK_OVERHEAD + (blockInfo & ~TAGS_MASK); // keep left tags
225
+ // 'back' is set below
226
+ }
227
+
228
+ right.mmInfo = rightInfo | LEFTFREE;
229
+ // reference to right is no longer used now, hence rightInfo is not synced
230
+
231
+ // we now know the size of the block
232
+ let size = blockInfo & ~TAGS_MASK;
233
+ if (DEBUG) assert(size >= BLOCK_MINSIZE); // must be a valid size
234
+ if (DEBUG) assert(changetype<usize>(block) + BLOCK_OVERHEAD + size == changetype<usize>(right)); // must match
235
+
236
+ // set 'back' to itself at the end of block
237
+ store<Block>(changetype<usize>(right) - sizeof<usize>(), block);
238
+
239
+ // mapping_insert
240
+ let fl: usize, sl: u32;
241
+ if (size < SB_SIZE) {
242
+ fl = 0;
243
+ sl = <u32>(size >> AL_BITS);
244
+ } else {
245
+ const inv: usize = sizeof<usize>() * 8 - 1;
246
+ let boundedSize = min(size, BLOCK_MAXSIZE);
247
+ fl = inv - clz<usize>(boundedSize);
248
+ sl = <u32>((boundedSize >> (fl - SL_BITS)) ^ (1 << SL_BITS));
249
+ fl -= SB_BITS - 1;
250
+ }
251
+ if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range
252
+
253
+ // perform insertion
254
+ let head = GETHEAD(root, fl, sl);
255
+ block.prev = null;
256
+ block.next = head;
257
+ if (head) head.prev = block;
258
+ SETHEAD(root, fl, sl, block);
259
+
260
+ // update first and second level maps
261
+ root.flMap |= (1 << fl);
262
+ SETSL(root, fl, GETSL(root, fl) | (1 << sl));
263
+ }
264
+
265
+ /** Removes a free block from internal lists. */
266
+ function removeBlock(root: Root, block: Block): void {
267
+ let blockInfo = block.mmInfo;
268
+ if (DEBUG) assert(blockInfo & FREE); // must be free
269
+ let size = blockInfo & ~TAGS_MASK;
270
+ if (DEBUG) assert(size >= BLOCK_MINSIZE); // must be valid
271
+
272
+ // mapping_insert
273
+ let fl: usize, sl: u32;
274
+ if (size < SB_SIZE) {
275
+ fl = 0;
276
+ sl = <u32>(size >> AL_BITS);
277
+ } else {
278
+ const inv: usize = sizeof<usize>() * 8 - 1;
279
+ let boundedSize = min(size, BLOCK_MAXSIZE);
280
+ fl = inv - clz<usize>(boundedSize);
281
+ sl = <u32>((boundedSize >> (fl - SL_BITS)) ^ (1 << SL_BITS));
282
+ fl -= SB_BITS - 1;
283
+ }
284
+ if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range
285
+
286
+ // link previous and next free block
287
+ let prev = block.prev;
288
+ let next = block.next;
289
+ if (prev) prev.next = next;
290
+ if (next) next.prev = prev;
291
+
292
+ // update head if we are removing it
293
+ if (block == GETHEAD(root, fl, sl)) {
294
+ SETHEAD(root, fl, sl, next);
295
+
296
+ // clear second level map if head is empty now
297
+ if (!next) {
298
+ let slMap = GETSL(root, fl);
299
+ SETSL(root, fl, slMap &= ~(1 << sl));
300
+
301
+ // clear first level map if second level is empty now
302
+ if (!slMap) root.flMap &= ~(1 << fl);
303
+ }
304
+ }
305
+ // note: does not alter left/back because it is likely that splitting
306
+ // is performed afterwards, invalidating those changes. so, the caller
307
+ // must perform those updates.
308
+ }
309
+
310
+ function roundSize(size: usize): usize {
311
+ const halfMaxSize = BLOCK_MAXSIZE >> 1; // don't round last fl
312
+ const inv: usize = sizeof<usize>() * 8 - 1;
313
+ const invRound = inv - SL_BITS;
314
+ return size < halfMaxSize
315
+ ? size + (1 << (invRound - clz<usize>(size))) - 1
316
+ : size;
317
+ }
318
+
319
+ /** Searches for a free block of at least the specified size. */
320
+ function searchBlock(root: Root, size: usize): Block | null {
321
+ // size was already asserted by caller
322
+
323
+ // mapping_search
324
+ let fl: usize, sl: u32;
325
+ if (size < SB_SIZE) {
326
+ fl = 0;
327
+ sl = <u32>(size >> AL_BITS);
328
+ } else {
329
+ const requestSize = roundSize(size);
330
+ fl = sizeof<usize>() * 8 - 1 - clz<usize>(requestSize);
331
+ sl = <u32>((requestSize >> (fl - SL_BITS)) ^ (1 << SL_BITS));
332
+ fl -= SB_BITS - 1;
333
+ }
334
+ if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range
335
+
336
+ // search second level
337
+ let slMap = GETSL(root, fl) & (~0 << sl);
338
+ let head: Block | null = null;
339
+ if (!slMap) {
340
+ // search next larger first level
341
+ let flMap = root.flMap & (~0 << (fl + 1));
342
+ if (!flMap) {
343
+ head = null;
344
+ } else {
345
+ fl = ctz<usize>(flMap);
346
+ slMap = GETSL(root, fl);
347
+ if (DEBUG) assert(slMap); // can't be zero if fl points here
348
+ head = GETHEAD(root, fl, ctz<u32>(slMap));
349
+ }
350
+ } else {
351
+ head = GETHEAD(root, fl, ctz<u32>(slMap));
352
+ }
353
+ return head;
354
+ }
355
+
356
+ /** Prepares the specified block before (re-)use, possibly splitting it. */
357
+ function prepareBlock(root: Root, block: Block, size: usize): void {
358
+ // size was already asserted by caller
359
+
360
+ let blockInfo = block.mmInfo;
361
+ if (DEBUG) assert(!((size + BLOCK_OVERHEAD) & AL_MASK)); // size must be aligned so the new block is
362
+
363
+ // split if the block can hold another MINSIZE block incl. overhead
364
+ let remaining = (blockInfo & ~TAGS_MASK) - size;
365
+ if (remaining >= BLOCK_OVERHEAD + BLOCK_MINSIZE) {
366
+ block.mmInfo = size | (blockInfo & LEFTFREE); // also discards FREE
367
+
368
+ let spare = changetype<Block>(changetype<usize>(block) + BLOCK_OVERHEAD + size);
369
+ spare.mmInfo = (remaining - BLOCK_OVERHEAD) | FREE; // not LEFTFREE
370
+ insertBlock(root, spare); // also sets 'back'
371
+
372
+ // otherwise tag block as no longer FREE and right as no longer LEFTFREE
373
+ } else {
374
+ block.mmInfo = blockInfo & ~FREE;
375
+ GETRIGHT(block).mmInfo &= ~LEFTFREE;
376
+ }
377
+ }
378
+
379
+ /** Adds more memory to the pool. */
380
+ function addMemory(root: Root, start: usize, endU64: u64): bool {
381
+ let end = <usize>endU64;
382
+ if (DEBUG) assert(<u64>start <= endU64); // must be valid
383
+ start = ((start + BLOCK_OVERHEAD + AL_MASK) & ~AL_MASK) - BLOCK_OVERHEAD;
384
+ end &= ~AL_MASK;
385
+
386
+ let tail = GETTAIL(root);
387
+ let tailInfo: usize = 0;
388
+ if (tail) { // more memory
389
+ if (DEBUG) assert(start >= changetype<usize>(tail) + BLOCK_OVERHEAD);
390
+
391
+ // merge with current tail if adjacent
392
+ const offsetToTail = AL_SIZE;
393
+ if (start - offsetToTail == changetype<usize>(tail)) {
394
+ start -= offsetToTail;
395
+ tailInfo = tail.mmInfo;
396
+ } else {
397
+ // We don't do this, but a user might `memory.grow` manually
398
+ // leading to non-adjacent pages managed by TLSF.
399
+ }
400
+
401
+ } else if (DEBUG) { // first memory
402
+ assert(start >= changetype<usize>(root) + ROOT_SIZE); // starts after root
403
+ }
404
+
405
+ // check if size is large enough for a free block and the tail block
406
+ let size = end - start;
407
+ if (size < BLOCK_OVERHEAD + BLOCK_MINSIZE + BLOCK_OVERHEAD) {
408
+ return false;
409
+ }
410
+
411
+ // left size is total minus its own and the zero-length tail's header
412
+ let leftSize = size - 2 * BLOCK_OVERHEAD;
413
+ let left = changetype<Block>(start);
414
+ left.mmInfo = leftSize | FREE | (tailInfo & LEFTFREE);
415
+ left.prev = null;
416
+ left.next = null;
417
+
418
+ // tail is a zero-length used block
419
+ tail = changetype<Block>(start + BLOCK_OVERHEAD + leftSize);
420
+ tail.mmInfo = 0 | LEFTFREE;
421
+ SETTAIL(root, tail);
422
+
423
+ insertBlock(root, left); // also merges with free left before tail / sets 'back'
424
+
425
+ return true;
426
+ }
427
+
428
+ /** Grows memory to fit at least another block of the specified size. */
429
+ function growMemory(root: Root, size: usize): void {
430
+ if (ASC_LOW_MEMORY_LIMIT) {
431
+ unreachable();
432
+ return;
433
+ }
434
+ // Here, both rounding performed in searchBlock ...
435
+ if (size >= SB_SIZE) {
436
+ size = roundSize(size);
437
+ }
438
+ // and additional BLOCK_OVERHEAD must be taken into account. If we are going
439
+ // to merge with the tail block, that's one time, otherwise it's two times.
440
+ let pagesBefore = memory.size();
441
+ size += BLOCK_OVERHEAD << usize((<usize>pagesBefore << 16) - BLOCK_OVERHEAD != changetype<usize>(GETTAIL(root)));
442
+ let pagesNeeded = <i32>(((size + 0xffff) & ~0xffff) >>> 16);
443
+ let pagesWanted = max(pagesBefore, pagesNeeded); // double memory
444
+ if (memory.grow(pagesWanted) < 0) {
445
+ if (memory.grow(pagesNeeded) < 0) unreachable();
446
+ }
447
+ let pagesAfter = memory.size();
448
+ addMemory(root, <usize>pagesBefore << 16, <u64>pagesAfter << 16);
449
+ }
450
+
451
+ /** Computes the size (excl. header) of a block. */
452
+ function computeSize(size: usize): usize {
453
+ // Size must be large enough and aligned minus preceeding overhead
454
+ return size <= BLOCK_MINSIZE
455
+ ? BLOCK_MINSIZE
456
+ : ((size + BLOCK_OVERHEAD + AL_MASK) & ~AL_MASK) - BLOCK_OVERHEAD;
457
+ }
458
+
459
+ /** Prepares and checks an allocation size. */
460
+ function prepareSize(size: usize): usize {
461
+ if (size > BLOCK_MAXSIZE) throw new Error(E_ALLOCATION_TOO_LARGE);
462
+ return computeSize(size);
463
+ }
464
+
465
+ /** Initializes the root structure. */
466
+ function initialize(): void {
467
+ if (isDefined(ASC_RTRACE)) oninit(__heap_base);
468
+ let rootOffset = (__heap_base + AL_MASK) & ~AL_MASK;
469
+ let pagesBefore = memory.size();
470
+ let pagesNeeded = <i32>((((rootOffset + ROOT_SIZE) + 0xffff) & ~0xffff) >>> 16);
471
+ if (pagesNeeded > pagesBefore && memory.grow(pagesNeeded - pagesBefore) < 0) unreachable();
472
+ let root = changetype<Root>(rootOffset);
473
+ root.flMap = 0;
474
+ SETTAIL(root, changetype<Block>(0));
475
+ for (let fl: usize = 0; fl < FL_BITS; ++fl) {
476
+ SETSL(root, fl, 0);
477
+ for (let sl: u32 = 0; sl < SL_SIZE; ++sl) {
478
+ SETHEAD(root, fl, sl, null);
479
+ }
480
+ }
481
+ let memStart = rootOffset + ROOT_SIZE;
482
+ if (ASC_LOW_MEMORY_LIMIT) {
483
+ const memEnd = <u64>ASC_LOW_MEMORY_LIMIT & ~AL_MASK;
484
+ if (memStart <= memEnd) addMemory(root, memStart, memEnd);
485
+ else unreachable(); // low memory limit already exceeded
486
+ } else {
487
+ addMemory(root, memStart, <u64>memory.size() << 16);
488
+ }
489
+ ROOT = root;
490
+ }
491
+
492
+ /** Allocates a block of the specified size. */
493
+ export function allocateBlock(root: Root, size: usize): Block {
494
+ let payloadSize = prepareSize(size);
495
+ let block = searchBlock(root, payloadSize);
496
+ if (!block) {
497
+ growMemory(root, payloadSize);
498
+ block = changetype<Block>(searchBlock(root, payloadSize));
499
+ if (DEBUG) assert(block); // must be found now
500
+ }
501
+ if (DEBUG) assert((block.mmInfo & ~TAGS_MASK) >= payloadSize); // must fit
502
+ removeBlock(root, block);
503
+ prepareBlock(root, block, payloadSize);
504
+ if (isDefined(ASC_RTRACE)) onalloc(block);
505
+ return block;
506
+ }
507
+
508
+ /** Reallocates a block to the specified size. */
509
+ export function reallocateBlock(root: Root, block: Block, size: usize): Block {
510
+ let payloadSize = prepareSize(size);
511
+ let blockInfo = block.mmInfo;
512
+ let blockSize = blockInfo & ~TAGS_MASK;
513
+
514
+ // possibly split and update runtime size if it still fits
515
+ if (payloadSize <= blockSize) {
516
+ prepareBlock(root, block, payloadSize);
517
+ if (isDefined(ASC_RTRACE)) {
518
+ if (payloadSize != blockSize) onresize(block, BLOCK_OVERHEAD + blockSize);
519
+ }
520
+ return block;
521
+ }
522
+
523
+ // merge with right free block if merger is large enough
524
+ let right = GETRIGHT(block);
525
+ let rightInfo = right.mmInfo;
526
+ if (rightInfo & FREE) {
527
+ let mergeSize = blockSize + BLOCK_OVERHEAD + (rightInfo & ~TAGS_MASK);
528
+ if (mergeSize >= payloadSize) {
529
+ removeBlock(root, right);
530
+ block.mmInfo = (blockInfo & TAGS_MASK) | mergeSize;
531
+ prepareBlock(root, block, payloadSize);
532
+ if (isDefined(ASC_RTRACE)) onresize(block, BLOCK_OVERHEAD + blockSize);
533
+ return block;
534
+ }
535
+ }
536
+
537
+ // otherwise move the block
538
+ return moveBlock(root, block, size);
539
+ }
540
+
541
+ /** Moves a block to a new one of the specified size. */
542
+ function moveBlock(root: Root, block: Block, newSize: usize): Block {
543
+ let newBlock = allocateBlock(root, newSize);
544
+ memory.copy(changetype<usize>(newBlock) + BLOCK_OVERHEAD, changetype<usize>(block) + BLOCK_OVERHEAD, block.mmInfo & ~TAGS_MASK);
545
+ if (changetype<usize>(block) >= __heap_base) {
546
+ if (isDefined(ASC_RTRACE)) onmove(block, newBlock);
547
+ freeBlock(root, block);
548
+ }
549
+ return newBlock;
550
+ }
551
+
552
+ /** Frees a block. */
553
+ export function freeBlock(root: Root, block: Block): void {
554
+ if (isDefined(ASC_RTRACE)) onfree(block);
555
+ block.mmInfo = block.mmInfo | FREE;
556
+ insertBlock(root, block);
557
+ }
558
+
559
+ /** Checks that a used block is valid to be freed or reallocated. */
560
+ function checkUsedBlock(ptr: usize): Block {
561
+ let block = changetype<Block>(ptr - BLOCK_OVERHEAD);
562
+ assert(
563
+ ptr != 0 && !(ptr & AL_MASK) && // must exist and be aligned
564
+ !(block.mmInfo & FREE) // must be used
565
+ );
566
+ return block;
567
+ }
568
+
569
+ // @ts-ignore: decorator
570
+ @global @unsafe
571
+ export function __alloc(size: usize): usize {
572
+ if (!ROOT) initialize();
573
+ return changetype<usize>(allocateBlock(ROOT, size)) + BLOCK_OVERHEAD;
574
+ }
575
+
576
+ // @ts-ignore: decorator
577
+ @global @unsafe
578
+ export function __realloc(ptr: usize, size: usize): usize {
579
+ if (!ROOT) initialize();
580
+ return (ptr < __heap_base
581
+ ? changetype<usize>(moveBlock(ROOT, checkUsedBlock(ptr), size))
582
+ : changetype<usize>(reallocateBlock(ROOT, checkUsedBlock(ptr), size))
583
+ ) + BLOCK_OVERHEAD;
584
+ }
585
+
586
+ // @ts-ignore: decorator
587
+ @global @unsafe
588
+ export function __free(ptr: usize): void {
589
+ if (ptr < __heap_base) return;
590
+ if (!ROOT) initialize();
591
+ freeBlock(ROOT, checkUsedBlock(ptr));
592
+ }
@@ -0,0 +1,90 @@
1
+ import { Typeinfo, TypeinfoFlags } from "./shared/typeinfo";
2
+ import { Runtime } from "shared/runtime";
3
+ import { E_INDEXOUTOFRANGE } from "./util/error";
4
+ import { ArrayBufferView } from "./arraybuffer";
5
+
6
+ // @ts-ignore: decorator
7
+ @builtin
8
+ export declare const __rtti_base: usize;
9
+
10
+ // @ts-ignore: decorator
11
+ @builtin @unsafe
12
+ export declare function __visit_globals(cookie: u32): void;
13
+
14
+ // @ts-ignore: decorator
15
+ @builtin @unsafe
16
+ export declare function __visit_members(ref: usize, cookie: u32): void;
17
+
18
+ // @ts-ignore: decorator
19
+ @unsafe
20
+ export function __typeinfo(id: u32): TypeinfoFlags {
21
+ let ptr = __rtti_base;
22
+ if (id > load<u32>(ptr)) throw new Error(E_INDEXOUTOFRANGE);
23
+ return changetype<Typeinfo>(ptr + sizeof<u32>() + id * offsetof<Typeinfo>()).flags;
24
+ }
25
+
26
+ // @ts-ignore: decorator
27
+ @unsafe
28
+ export function __newBuffer(size: usize, id: u32, data: usize = 0): usize {
29
+ let buffer = __new(size, id);
30
+ if (data) memory.copy(buffer, data, size);
31
+ return buffer;
32
+ }
33
+
34
+ // @ts-ignore: decorator
35
+ @unsafe
36
+ export function __newArray(length: i32, alignLog2: usize, id: u32, data: usize = 0): usize {
37
+ let bufferSize = <usize>length << alignLog2;
38
+ // make sure `buffer` is tracked by the shadow stack
39
+ let buffer = changetype<ArrayBuffer>(__newBuffer(bufferSize, idof<ArrayBuffer>(), data));
40
+ // ...since allocating the array may trigger GC steps
41
+ let array = __new(offsetof<i32[]>(), id);
42
+ store<usize>(array, changetype<usize>(buffer), offsetof<ArrayBufferView>("buffer"));
43
+ if (ASC_RUNTIME != Runtime.Memory) {
44
+ __link(array, changetype<usize>(buffer), false);
45
+ }
46
+ store<usize>(array, changetype<usize>(buffer), offsetof<ArrayBufferView>("dataStart"));
47
+ store<i32>(array, bufferSize, offsetof<ArrayBufferView>("byteLength"));
48
+ store<i32>(array, length, offsetof<i32[]>("length_"));
49
+ return array;
50
+ }
51
+
52
+ // @ts-ignore: decorator
53
+ @global @unsafe
54
+ export function __tostack(ptr: usize): usize {
55
+ return ptr;
56
+ }
57
+
58
+ // These are provided by the respective implementation, included as another entry file by asc:
59
+
60
+ // // @ts-ignore: decorator
61
+ // @builtin @unsafe
62
+ // export declare function __alloc(size: usize): usize;
63
+
64
+ // // @ts-ignore: decorator
65
+ // @builtin @unsafe
66
+ // export declare function __realloc(ptr: usize, size: usize): usize;
67
+
68
+ // // @ts-ignore: decorator
69
+ // @builtin @unsafe
70
+ // export declare function __free(ptr: usize): void;
71
+
72
+ // // @ts-ignore: decorator
73
+ // @builtin @unsafe
74
+ // export declare function __new(size: usize, id: u32): usize;
75
+
76
+ // // @ts-ignore: decorator
77
+ // @builtin @unsafe
78
+ // export declare function __renew(ptr: usize, size: usize): usize;
79
+
80
+ // // @ts-ignore: decorator
81
+ // @builtin @unsafe
82
+ // export declare function __link(parentPtr: usize, childPtr: usize, expectMultiple: bool): void;
83
+
84
+ // // @ts-ignore: decorator
85
+ // @builtin @unsafe
86
+ // export declare function __collect(): void;
87
+
88
+ // // @ts-ignore: decorator
89
+ // @builtin @unsafe
90
+ // export declare function __visit(ptr: usize, cookie: u32): void;