rabin-wasm
Version:
Rabin fingerprinting implemented in AssemblyScript
1 lines • 295 kB
Source Map (JSON)
{"version":3,"sources":["~lib/rt/common.ts","~lib/shared/typeinfo.ts","~lib/rt/pure.ts","~lib/rt/tlsf.ts","~lib/gc.ts","assembly/index.ts","~lib/arraybuffer.ts","~lib/util/error.ts","~lib/memory.ts","~lib/util/memory.ts","~lib/util/number.ts","~lib/util/math.ts","~lib/util/string.ts","~lib/typedarray.ts"],"names":[],"mappings":"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","sourceRoot":"./rabin.wasm","sourcesContent":["// Alignment guarantees\n\n// @ts-ignore: decorator\n@inline export const AL_BITS: u32 = 4; // 16 bytes to fit up to v128\n// @ts-ignore: decorator\n@inline export const AL_SIZE: usize = 1 << <usize>AL_BITS;\n// @ts-ignore: decorator\n@inline export const AL_MASK: usize = AL_SIZE - 1;\n\n// Extra debugging\n\n// @ts-ignore: decorator\n@inline export const DEBUG = true;\n\n// ╒════════════════ Common block layout (32-bit) ═════════════════╕\n// 3 2 1\n// 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\n// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤\n// │ MM info │ -16\n// ├───────────────────────────────────────────────────────────────┤\n// │ GC info │ -12\n// ├───────────────────────────────────────────────────────────────┤\n// │ runtime id │ -8\n// ├───────────────────────────────────────────────────────────────┤\n// │ runtime size │ -4\n// ╞═══════════════════════════════════════════════════════════════╡\n// │ ... │ ref\n@unmanaged export class BLOCK {\n /** Memory manager info. */\n mmInfo: usize; // WASM64 needs adaption\n /** Garbage collector info. */\n gcInfo: u32;\n /** Runtime class id. */\n rtId: u32;\n /** Runtime object size. */\n rtSize: u32;\n}\n\n// @ts-ignore: decorator\n@inline export const BLOCK_OVERHEAD: usize = (offsetof<BLOCK>() + AL_MASK) & ~AL_MASK;\n\n// @ts-ignore: decorator\n@inline export const BLOCK_MAXSIZE: usize = (1 << 30) - BLOCK_OVERHEAD;\n","// This file is shared with the compiler and must remain portable\n\n// ╒═══════════════════ Typeinfo interpretation ═══════════════════╕\n// 3 2 1\n// 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\n// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤ ◄─ __rtti_base\n// │ count │\n// ╞═══════════════════════════════════════════════════════════════╡ ┐\n// │ Typeinfo#flags [id=0] │ id < count\n// ├ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┤\n// │ Typeinfo#base [id=0] │\n// ├───────────────────────────────────────────────────────────────┤\n// │ ... │\n\n/** Runtime type information data structure. */\n@unmanaged\nexport class Typeinfo {\n /** Flags describing the shape of this class type. */\n flags: TypeinfoFlags;\n /** Base class id or `0` if none. */\n base: u32;\n}\n\n/** Runtime type information flags. */\nexport const enum TypeinfoFlags {\n /** No specific flags. */\n NONE = 0,\n /** Type is an `ArrayBufferView`. */\n ARRAYBUFFERVIEW = 1 << 0,\n /** Type is an `Array`. */\n ARRAY = 1 << 1,\n /** Type is a `Set`. */\n SET = 1 << 2,\n /** Type is a `Map`. */\n MAP = 1 << 3,\n /** Type is inherently acyclic. */\n ACYCLIC = 1 << 4,\n /** Value alignment of 1 byte. */\n VALUE_ALIGN_0 = 1 << 5,\n /** Value alignment of 2 bytes. */\n VALUE_ALIGN_1 = 1 << 6,\n /** Value alignment of 4 bytes. */\n VALUE_ALIGN_2 = 1 << 7,\n /** Value alignment of 8 bytes. */\n VALUE_ALIGN_3 = 1 << 8,\n /** Value alignment of 16 bytes. */\n VALUE_ALIGN_4 = 1 << 9,\n /** Value is a signed type. */\n VALUE_SIGNED = 1 << 10,\n /** Value is a float type. */\n VALUE_FLOAT = 1 << 11,\n /** Value type is nullable. */\n VALUE_NULLABLE = 1 << 12,\n /** Value type is managed. */\n VALUE_MANAGED = 1 << 13,\n /** Key alignment of 1 byte. */\n KEY_ALIGN_0 = 1 << 14,\n /** Key alignment of 2 bytes. */\n KEY_ALIGN_1 = 1 << 15,\n /** Key alignment of 4 bytes. */\n KEY_ALIGN_2 = 1 << 16,\n /** Key alignment of 8 bytes. */\n KEY_ALIGN_3 = 1 << 17,\n /** Key alignment of 16 bytes. */\n KEY_ALIGN_4 = 1 << 18,\n /** Key is a signed type. */\n KEY_SIGNED = 1 << 19,\n /** Key is a float type. */\n KEY_FLOAT = 1 << 20,\n /** Key type is nullable. */\n KEY_NULLABLE = 1 << 21,\n /** Key type is managed. */\n KEY_MANAGED = 1 << 22\n}\n","import { DEBUG, BLOCK_OVERHEAD } from \"rt/common\";\nimport { Block, freeBlock, ROOT } from \"rt/tlsf\";\nimport { TypeinfoFlags } from \"shared/typeinfo\";\nimport { onincrement, ondecrement, onfree, onalloc } from \"./rtrace\";\n\n/////////////////////////// A Pure Reference Counting Garbage Collector ///////////////////////////\n// see: https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon03Pure.pdf\n\n// ╒══════════════════════ GC Info structure ══════════════════════╕\n// │ 3 2 1 │\n// │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│\n// ├─┼─┴─┴─┼─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤\n// │B│color│ refCount │\n// └─┴─────┴───────────────────────────────────────────────────────┘\n// B: buffered\n\n// @ts-ignore: decorator\n@inline const BUFFERED_MASK: u32 = 1 << ((sizeof<u32>() * 8) - 1);\n// @ts-ignore: decorator\n@inline const COLOR_BITS = 3;\n// @ts-ignore: decorator\n@inline const COLOR_SHIFT: u32 = ctz(BUFFERED_MASK) - COLOR_BITS;\n// @ts-ignore: decorator\n@inline const COLOR_MASK: u32 = ((1 << COLOR_BITS) - 1) << COLOR_SHIFT;\n// @ts-ignore: decorator\n@inline export const REFCOUNT_MASK: u32 = (1 << COLOR_SHIFT) - 1;\n\n// ╒════════╤═══════════════════ Colors ═══════════════════════════╕\n// │ Color │ Meaning │\n// ├────────┼──────────────────────────────────────────────────────┤\n// │ BLACK │ In use or free │\n// │ GRAY │ Possible member of cycle │\n// │ WHITE │ Member of garbage cycle │\n// │ PURPLE │ Possible root of cycle │\n// │ RED │ Candidate cycle undergoing Σ-computation *concurrent │\n// │ ORANGE │ Candidate cycle awaiting epoch boundary *concurrent │\n// └────────┴──────────────────────────────────────────────────────┘\n// Acyclic detection has been decoupled, hence no GREEN.\n\n// @ts-ignore: decorator\n@inline const COLOR_BLACK: u32 = 0 << COLOR_SHIFT;\n// @ts-ignore: decorator\n@inline const COLOR_GRAY: u32 = 1 << COLOR_SHIFT;\n// @ts-ignore: decorator\n@inline const COLOR_WHITE: u32 = 2 << COLOR_SHIFT;\n// @ts-ignore: decorator\n@inline const COLOR_PURPLE: u32 = 3 << COLOR_SHIFT;\n// @ts-ignore: decorator\n// @inline const COLOR_RED: u32 = 4 << COLOR_SHIFT;\n// @ts-ignore: decorator\n// @inline const COLOR_ORANGE: u32 = 5 << COLOR_SHIFT;\n\n// @ts-ignore: decorator\n@inline const VISIT_DECREMENT = 1; // guard 0\n// @ts-ignore: decorator\n@inline const VISIT_MARKGRAY = 2;\n// @ts-ignore: decorator\n@inline const VISIT_SCAN = 3;\n// @ts-ignore: decorator\n@inline const VISIT_SCANBLACK = 4;\n// @ts-ignore: decorator\n@inline const VISIT_COLLECTWHITE = 5;\n\n// @ts-ignore: decorator\n@global @unsafe @lazy\nfunction __visit(ref: usize, cookie: i32): void {\n if (ref < __heap_base) return;\n if (isDefined(__GC_ALL_ACYCLIC)) {\n if (DEBUG) assert(cookie == VISIT_DECREMENT);\n decrement(changetype<Block>(ref - BLOCK_OVERHEAD));\n } else {\n let s = changetype<Block>(ref - BLOCK_OVERHEAD);\n switch (cookie) {\n case VISIT_DECREMENT: {\n decrement(s);\n break;\n }\n case VISIT_MARKGRAY: {\n if (DEBUG) assert((s.gcInfo & REFCOUNT_MASK) > 0);\n s.gcInfo = s.gcInfo - 1;\n markGray(s);\n break;\n }\n case VISIT_SCAN: {\n scan(s);\n break;\n }\n case VISIT_SCANBLACK: {\n let info = s.gcInfo;\n assert((info & ~REFCOUNT_MASK) == ((info + 1) & ~REFCOUNT_MASK)); // overflow\n s.gcInfo = info + 1;\n if ((info & COLOR_MASK) != COLOR_BLACK) {\n scanBlack(s);\n }\n break;\n }\n case VISIT_COLLECTWHITE: {\n collectWhite(s);\n break;\n }\n default: if (DEBUG) assert(false);\n }\n }\n}\n\n/** Increments the reference count of the specified block by one.*/\nfunction increment(s: Block): void {\n var info = s.gcInfo;\n assert((info & ~REFCOUNT_MASK) == ((info + 1) & ~REFCOUNT_MASK)); // overflow\n s.gcInfo = info + 1;\n if (isDefined(ASC_RTRACE)) onincrement(s);\n if (DEBUG) assert(!(s.mmInfo & 1)); // used\n}\n\n/** Decrements the reference count of the specified block by one, possibly freeing it. */\n// @ts-ignore: decorator\n@lazy\nfunction decrement(s: Block): void {\n var info = s.gcInfo;\n var rc = info & REFCOUNT_MASK;\n if (isDefined(ASC_RTRACE)) ondecrement(s);\n if (DEBUG) assert(!(s.mmInfo & 1)); // used\n if (rc == 1) {\n __visit_members(changetype<usize>(s) + BLOCK_OVERHEAD, VISIT_DECREMENT);\n if (isDefined(__GC_ALL_ACYCLIC)) {\n if (DEBUG) assert(!(info & BUFFERED_MASK));\n freeBlock(ROOT, s);\n } else {\n if (!(info & BUFFERED_MASK)) {\n freeBlock(ROOT, s);\n } else {\n s.gcInfo = BUFFERED_MASK | COLOR_BLACK | 0;\n }\n }\n } else {\n if (DEBUG) assert(rc > 0);\n if (isDefined(__GC_ALL_ACYCLIC)) {\n s.gcInfo = (info & ~REFCOUNT_MASK) | (rc - 1);\n } else {\n if (!(__typeinfo(s.rtId) & TypeinfoFlags.ACYCLIC)) {\n s.gcInfo = BUFFERED_MASK | COLOR_PURPLE | (rc - 1);\n if (!(info & BUFFERED_MASK)) {\n appendRoot(s);\n }\n } else {\n s.gcInfo = (info & ~REFCOUNT_MASK) | (rc - 1);\n }\n }\n }\n}\n\n/** Buffer of possible roots. */\n// @ts-ignore: decorator\n@lazy var ROOTS: usize;\n/** Current absolute offset into the `ROOTS` buffer. */\n// @ts-ignore: decorator\n@lazy var CUR: usize = 0;\n/** Current absolute end offset into the `ROOTS` buffer. */\n// @ts-ignore: decorator\n@lazy var END: usize = 0;\n\n/** Appends a block to possible roots. */\nfunction appendRoot(s: Block): void {\n var cur = CUR;\n if (cur >= END) {\n growRoots(); // TBD: either that or pick a default and force collection on overflow\n cur = CUR;\n }\n store<Block>(cur, s);\n CUR = cur + sizeof<usize>();\n}\n\n/** Grows the roots buffer if it ran full. */\nfunction growRoots(): void {\n var oldRoots = ROOTS;\n var oldSize = CUR - oldRoots;\n var newSize = max(oldSize * 2, 64 << alignof<usize>());\n var newRoots = __alloc(newSize, 0);\n if (isDefined(ASC_RTRACE)) onfree(changetype<Block>(newRoots - BLOCK_OVERHEAD)); // neglect unmanaged\n memory.copy(newRoots, oldRoots, oldSize);\n if (oldRoots) {\n if (isDefined(ASC_RTRACE)) onalloc(changetype<Block>(oldRoots - BLOCK_OVERHEAD)); // neglect unmanaged\n __free(oldRoots);\n }\n ROOTS = newRoots;\n CUR = newRoots + oldSize;\n END = newRoots + newSize;\n}\n\n/** Collects cyclic garbage. */\n// @ts-ignore: decorator\n@global @unsafe @lazy\nexport function __collect(): void {\n if (isDefined(__GC_ALL_ACYCLIC)) return;\n\n // markRoots\n var roots = ROOTS;\n var cur = roots;\n for (let pos = cur, end = CUR; pos < end; pos += sizeof<usize>()) {\n let s = load<Block>(pos);\n let info = s.gcInfo;\n if ((info & COLOR_MASK) == COLOR_PURPLE && (info & REFCOUNT_MASK) > 0) {\n markGray(s);\n store<Block>(cur, s);\n cur += sizeof<usize>();\n } else {\n if ((info & COLOR_MASK) == COLOR_BLACK && !(info & REFCOUNT_MASK)) {\n freeBlock(ROOT, s);\n } else {\n s.gcInfo = info & ~BUFFERED_MASK;\n }\n }\n }\n CUR = cur;\n\n // scanRoots\n for (let pos = roots; pos < cur; pos += sizeof<usize>()) {\n scan(load<Block>(pos));\n }\n\n // collectRoots\n for (let pos = roots; pos < cur; pos += sizeof<usize>()) {\n let s = load<Block>(pos);\n s.gcInfo = s.gcInfo & ~BUFFERED_MASK;\n collectWhite(s);\n }\n CUR = roots;\n}\n\n/** Marks a block as gray (possible member of cycle) during the collection phase. */\nfunction markGray(s: Block): void {\n var info = s.gcInfo;\n if ((info & COLOR_MASK) != COLOR_GRAY) {\n s.gcInfo = (info & ~COLOR_MASK) | COLOR_GRAY;\n __visit_members(changetype<usize>(s) + BLOCK_OVERHEAD, VISIT_MARKGRAY);\n }\n}\n\n/** Scans a block during the collection phase, determining whether it is garbage or not. */\nfunction scan(s: Block): void {\n var info = s.gcInfo;\n if ((info & COLOR_MASK) == COLOR_GRAY) {\n if ((info & REFCOUNT_MASK) > 0) {\n scanBlack(s);\n } else {\n s.gcInfo = (info & ~COLOR_MASK) | COLOR_WHITE;\n __visit_members(changetype<usize>(s) + BLOCK_OVERHEAD, VISIT_SCAN);\n }\n }\n}\n\n/** Marks a block as black (in use) if it was found to be reachable during the collection phase. */\nfunction scanBlack(s: Block): void {\n s.gcInfo = (s.gcInfo & ~COLOR_MASK) | COLOR_BLACK;\n __visit_members(changetype<usize>(s) + BLOCK_OVERHEAD, VISIT_SCANBLACK);\n}\n\n/** Collects all white (member of a garbage cycle) nodes when completing the collection phase. */\nfunction collectWhite(s: Block): void {\n var info = s.gcInfo;\n if ((info & COLOR_MASK) == COLOR_WHITE && !(info & BUFFERED_MASK)) {\n s.gcInfo = (info & ~COLOR_MASK) | COLOR_BLACK;\n __visit_members(changetype<usize>(s) + BLOCK_OVERHEAD, VISIT_COLLECTWHITE);\n freeBlock(ROOT, s);\n }\n}\n\n// @ts-ignore: decorator\n@global @unsafe\nexport function __retain(ptr: usize): usize {\n if (ptr > __heap_base) increment(changetype<Block>(ptr - BLOCK_OVERHEAD));\n return ptr;\n}\n\n// @ts-ignore: decorator\n@global @unsafe\nexport function __release(ptr: usize): void {\n if (ptr > __heap_base) decrement(changetype<Block>(ptr - BLOCK_OVERHEAD));\n}\n","import { AL_BITS, AL_MASK, DEBUG, BLOCK, BLOCK_OVERHEAD, BLOCK_MAXSIZE } from \"rt/common\";\nimport { onfree, onalloc, onrealloc } from \"./rtrace\";\nimport { REFCOUNT_MASK } from \"./pure\";\n\n/////////////////////// The TLSF (Two-Level Segregate Fit) memory allocator ///////////////////////\n// see: http://www.gii.upv.es/tlsf/\n\n// - `ffs(x)` is equivalent to `ctz(x)` with x != 0\n// - `fls(x)` is equivalent to `sizeof(x) * 8 - clz(x) - 1`\n\n// ╒══════════════ Block size interpretation (32-bit) ═════════════╕\n// 3 2 1\n// 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\n// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┴─┴─┴─╫─┴─┴─┴─┤\n// │ | FL │ SB = SL + AL │ ◄─ usize\n// └───────────────────────────────────────────────┴───────╨───────┘\n// FL: first level, SL: second level, AL: alignment, SB: small block\n\n// @ts-ignore: decorator\n@inline const SL_BITS: u32 = 4;\n// @ts-ignore: decorator\n@inline const SL_SIZE: u32 = 1 << SL_BITS;\n\n// @ts-ignore: decorator\n@inline const SB_BITS: u32 = SL_BITS + AL_BITS;\n// @ts-ignore: decorator\n@inline const SB_SIZE: u32 = 1 << SB_BITS;\n\n// @ts-ignore: decorator\n@inline const FL_BITS: u32 = 31 - SB_BITS;\n\n// [00]: < 256B (SB) [12]: < 1M\n// [01]: < 512B [13]: < 2M\n// [02]: < 1K [14]: < 4M\n// [03]: < 2K [15]: < 8M\n// [04]: < 4K [16]: < 16M\n// [05]: < 8K [17]: < 32M\n// [06]: < 16K [18]: < 64M\n// [07]: < 32K [19]: < 128M\n// [08]: < 64K [20]: < 256M\n// [09]: < 128K [21]: < 512M\n// [10]: < 256K [22]: <= 1G - OVERHEAD\n// [11]: < 512K\n// VMs limit to 2GB total (currently), making one 1G block max (or three 512M etc.) due to block overhead\n\n// Tags stored in otherwise unused alignment bits\n\n// @ts-ignore: decorator\n@inline const FREE: usize = 1 << 0;\n// @ts-ignore: decorator\n@inline const LEFTFREE: usize = 1 << 1;\n// @ts-ignore: decorator\n@inline const TAGS_MASK: usize = FREE | LEFTFREE; // <= AL_MASK\n\n// ╒════════════════════ Block layout (32-bit) ════════════════════╕\n// 3 2 1\n// 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\n// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┼─┼─┼─┤ overhead ┐\n// │ size │0│L│F│ ◄─┐ info\n// ├─────────────────────────────────────────────────────────┴─┴─┴─┤ │\n// │ │ │\n// │ ... additional runtime overhead ... │ │\n// │ │ │\n// ╞═══════════════════════════════════════════════════════════════╡ │ ┐ ┘\n// │ if free: ◄ prev │ ◄─┤ usize\n// ├───────────────────────────────────────────────────────────────┤ │\n// │ if free: next ► │ ◄─┤\n// ├───────────────────────────────────────────────────────────────┤ │\n// │ ... │ │ = 0\n// ├───────────────────────────────────────────────────────────────┤ │\n// │ if free: back ▲ │ ◄─┘\n// └───────────────────────────────────────────────────────────────┘ payload ┘ >= MIN SIZE\n// F: FREE, L: LEFTFREE\n@unmanaged export class Block extends BLOCK {\n\n /** Previous free block, if any. Only valid if free, otherwise part of payload. */\n prev: Block | null;\n /** Next free block, if any. Only valid if free, otherwise part of payload. */\n next: Block | null;\n\n // If the block is free, there is a 'back'reference at its end pointing at its start.\n}\n\n// Block constants. A block must have a minimum size of three pointers so it can hold `prev`,\n// `next` and `back` if free.\n\n// @ts-ignore: decorator\n@inline const BLOCK_MINSIZE: usize = (3 * sizeof<usize>() + AL_MASK) & ~AL_MASK; // prev + next + back\n// @ts-ignore: decorator\n// @inline const BLOCK_MAXSIZE: usize = 1 << (FL_BITS + SB_BITS - 1); // exclusive, lives in common.ts\n\n/** Gets the left block of a block. Only valid if the left block is free. */\n// @ts-ignore: decorator\n@inline function GETFREELEFT(block: Block): Block {\n return load<Block>(changetype<usize>(block) - sizeof<usize>());\n}\n\n/** Gets the right block of of a block by advancing to the right by its size. */\n// @ts-ignore: decorator\n@inline function GETRIGHT(block: Block): Block {\n return changetype<Block>(changetype<usize>(block) + BLOCK_OVERHEAD + (block.mmInfo & ~TAGS_MASK));\n}\n\n// ╒═════════════════════ Root layout (32-bit) ════════════════════╕\n// 3 2 1\n// 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\n// ├─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┴─┤ ┐\n// │ 0 | flMap S│ ◄────┐\n// ╞═══════════════════════════════════════════════════════════════╡ │\n// │ slMap[0] S │ ◄─┐ │\n// ├───────────────────────────────────────────────────────────────┤ │ │\n// │ slMap[1] │ ◄─┤ │\n// ├───────────────────────────────────────────────────────────────┤ u32 │\n// │ slMap[22] │ ◄─┘ │\n// ╞═══════════════════════════════════════════════════════════════╡ usize\n// │ head[0] │ ◄────┤\n// ├───────────────────────────────────────────────────────────────┤ │\n// │ ... │ ◄────┤\n// ├───────────────────────────────────────────────────────────────┤ │\n// │ head[367] │ ◄────┤\n// ╞═══════════════════════════════════════════════════════════════╡ │\n// │ tail │ ◄────┘\n// └───────────────────────────────────────────────────────────────┘ SIZE ┘\n// S: Small blocks map\n@unmanaged class Root {\n /** First level bitmap. */\n flMap: usize;\n}\n\n// Root constants. Where stuff is stored inside of the root structure.\n\n// @ts-ignore: decorator\n@inline const SL_START: usize = sizeof<usize>();\n// @ts-ignore: decorator\n@inline const SL_END: usize = SL_START + (FL_BITS << alignof<u32>());\n// @ts-ignore: decorator\n@inline const HL_START: usize = (SL_END + AL_MASK) & ~AL_MASK;\n// @ts-ignore: decorator\n@inline const HL_END: usize = HL_START + FL_BITS * SL_SIZE * sizeof<usize>();\n// @ts-ignore: decorator\n@inline const ROOT_SIZE: usize = HL_END + sizeof<usize>();\n\n// @ts-ignore: decorator\n@lazy export var ROOT: Root;\n\n/** Gets the second level map of the specified first level. */\n// @ts-ignore: decorator\n@inline function GETSL(root: Root, fl: usize): u32 {\n return load<u32>(\n changetype<usize>(root) + (fl << alignof<u32>()),\n SL_START\n );\n}\n\n/** Sets the second level map of the specified first level. */\n// @ts-ignore: decorator\n@inline function SETSL(root: Root, fl: usize, slMap: u32): void {\n store<u32>(\n changetype<usize>(root) + (fl << alignof<u32>()),\n slMap,\n SL_START\n );\n}\n\n/** Gets the head of the free list for the specified combination of first and second level. */\n// @ts-ignore: decorator\n@inline function GETHEAD(root: Root, fl: usize, sl: u32): Block | null {\n return load<Block>(\n changetype<usize>(root) + (((fl << SL_BITS) + <usize>sl) << alignof<usize>()),\n HL_START\n );\n}\n\n/** Sets the head of the free list for the specified combination of first and second level. */\n// @ts-ignore: decorator\n@inline function SETHEAD(root: Root, fl: usize, sl: u32, head: Block | null): void {\n store<Block>(\n changetype<usize>(root) + (((fl << SL_BITS) + <usize>sl) << alignof<usize>()),\n head,\n HL_START\n );\n}\n\n/** Gets the tail block.. */\n// @ts-ignore: decorator\n@inline function GETTAIL(root: Root): Block {\n return load<Block>(\n changetype<usize>(root),\n HL_END\n );\n}\n\n/** Sets the tail block. */\n// @ts-ignore: decorator\n@inline function SETTAIL(root: Root, tail: Block): void {\n store<Block>(\n changetype<usize>(root),\n tail,\n HL_END\n );\n}\n\n/** Inserts a previously used block back into the free list. */\nfunction insertBlock(root: Root, block: Block): void {\n if (DEBUG) assert(block); // cannot be null\n var blockInfo = block.mmInfo;\n if (DEBUG) assert(blockInfo & FREE); // must be free\n\n var right = GETRIGHT(block);\n var rightInfo = right.mmInfo;\n\n // merge with right block if also free\n if (rightInfo & FREE) {\n let newSize = (blockInfo & ~TAGS_MASK) + BLOCK_OVERHEAD + (rightInfo & ~TAGS_MASK);\n if (newSize < BLOCK_MAXSIZE) {\n removeBlock(root, right);\n block.mmInfo = blockInfo = (blockInfo & TAGS_MASK) | newSize;\n right = GETRIGHT(block);\n rightInfo = right.mmInfo;\n // 'back' is set below\n }\n }\n\n // merge with left block if also free\n if (blockInfo & LEFTFREE) {\n let left = GETFREELEFT(block);\n let leftInfo = left.mmInfo;\n if (DEBUG) assert(leftInfo & FREE); // must be free according to right tags\n let newSize = (leftInfo & ~TAGS_MASK) + BLOCK_OVERHEAD + (blockInfo & ~TAGS_MASK);\n if (newSize < BLOCK_MAXSIZE) {\n removeBlock(root, left);\n left.mmInfo = blockInfo = (leftInfo & TAGS_MASK) | newSize;\n block = left;\n // 'back' is set below\n }\n }\n\n right.mmInfo = rightInfo | LEFTFREE;\n // right is no longer used now, hence rightInfo is not synced\n\n // we now know the size of the block\n var size = blockInfo & ~TAGS_MASK;\n if (DEBUG) assert(size >= BLOCK_MINSIZE && size < BLOCK_MAXSIZE); // must be a valid size\n if (DEBUG) assert(changetype<usize>(block) + BLOCK_OVERHEAD + size == changetype<usize>(right)); // must match\n\n // set 'back' to itself at the end of block\n store<Block>(changetype<usize>(right) - sizeof<usize>(), block);\n\n // mapping_insert\n var fl: usize, sl: u32;\n if (size < SB_SIZE) {\n fl = 0;\n sl = <u32>(size >> AL_BITS);\n } else {\n const inv: usize = sizeof<usize>() * 8 - 1;\n fl = inv - clz<usize>(size);\n sl = <u32>((size >> (fl - SL_BITS)) ^ (1 << SL_BITS));\n fl -= SB_BITS - 1;\n }\n if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range\n\n // perform insertion\n var head = GETHEAD(root, fl, sl);\n block.prev = null;\n block.next = head;\n if (head) head.prev = block;\n SETHEAD(root, fl, sl, block);\n\n // update first and second level maps\n root.flMap |= (1 << fl);\n SETSL(root, fl, GETSL(root, fl) | (1 << sl));\n}\n\n/** Removes a free block from internal lists. */\nfunction removeBlock(root: Root, block: Block): void {\n var blockInfo = block.mmInfo;\n if (DEBUG) assert(blockInfo & FREE); // must be free\n var size = blockInfo & ~TAGS_MASK;\n if (DEBUG) assert(size >= BLOCK_MINSIZE && size < BLOCK_MAXSIZE); // must be valid\n\n // mapping_insert\n var fl: usize, sl: u32;\n if (size < SB_SIZE) {\n fl = 0;\n sl = <u32>(size >> AL_BITS);\n } else {\n const inv: usize = sizeof<usize>() * 8 - 1;\n fl = inv - clz<usize>(size);\n sl = <u32>((size >> (fl - SL_BITS)) ^ (1 << SL_BITS));\n fl -= SB_BITS - 1;\n }\n if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range\n\n // link previous and next free block\n var prev = block.prev;\n var next = block.next;\n if (prev) prev.next = next;\n if (next) next.prev = prev;\n\n // update head if we are removing it\n if (block == GETHEAD(root, fl, sl)) {\n SETHEAD(root, fl, sl, next);\n\n // clear second level map if head is empty now\n if (!next) {\n let slMap = GETSL(root, fl);\n SETSL(root, fl, slMap &= ~(1 << sl));\n\n // clear first level map if second level is empty now\n if (!slMap) root.flMap &= ~(1 << fl);\n }\n }\n // note: does not alter left/back because it is likely that splitting\n // is performed afterwards, invalidating those changes. so, the caller\n // must perform those updates.\n}\n\n/** Searches for a free block of at least the specified size. */\nfunction searchBlock(root: Root, size: usize): Block | null {\n // size was already asserted by caller\n\n // mapping_search\n var fl: usize, sl: u32;\n if (size < SB_SIZE) {\n fl = 0;\n sl = <u32>(size >> AL_BITS);\n } else {\n const halfMaxSize = BLOCK_MAXSIZE >> 1; // don't round last fl\n const inv: usize = sizeof<usize>() * 8 - 1;\n const invRound = inv - SL_BITS;\n let requestSize = size < halfMaxSize\n ? size + (1 << (invRound - clz<usize>(size))) - 1\n : size;\n fl = inv - clz<usize>(requestSize);\n sl = <u32>((requestSize >> (fl - SL_BITS)) ^ (1 << SL_BITS));\n fl -= SB_BITS - 1;\n }\n if (DEBUG) assert(fl < FL_BITS && sl < SL_SIZE); // fl/sl out of range\n\n // search second level\n var slMap = GETSL(root, fl) & (~0 << sl);\n var head: Block | null = null;\n if (!slMap) {\n // search next larger first level\n let flMap = root.flMap & (~0 << (fl + 1));\n if (!flMap) {\n head = null;\n } else {\n fl = ctz<usize>(flMap);\n slMap = GETSL(root, fl);\n if (DEBUG) assert(slMap); // can't be zero if fl points here\n head = GETHEAD(root, fl, ctz<u32>(slMap));\n }\n } else {\n head = GETHEAD(root, fl, ctz<u32>(slMap));\n }\n return head;\n}\n\n/** Prepares the specified block before (re-)use, possibly splitting it. */\nfunction prepareBlock(root: Root, block: Block, size: usize): void {\n // size was already asserted by caller\n\n var blockInfo = block.mmInfo;\n if (DEBUG) assert(!(size & AL_MASK)); // size must be aligned so the new block is\n\n // split if the block can hold another MINSIZE block incl. overhead\n var remaining = (blockInfo & ~TAGS_MASK) - size;\n if (remaining >= BLOCK_OVERHEAD + BLOCK_MINSIZE) {\n block.mmInfo = size | (blockInfo & LEFTFREE); // also discards FREE\n\n let spare = changetype<Block>(changetype<usize>(block) + BLOCK_OVERHEAD + size);\n spare.mmInfo = (remaining - BLOCK_OVERHEAD) | FREE; // not LEFTFREE\n insertBlock(root, spare); // also sets 'back'\n\n // otherwise tag block as no longer FREE and right as no longer LEFTFREE\n } else {\n block.mmInfo = blockInfo & ~FREE;\n GETRIGHT(block).mmInfo &= ~LEFTFREE;\n }\n}\n\n/** Adds more memory to the pool. */\nfunction addMemory(root: Root, start: usize, end: usize): bool {\n if (DEBUG) {\n assert(\n start <= end && // must be valid\n !(start & AL_MASK) && // must be aligned\n !(end & AL_MASK) // must be aligned\n );\n }\n\n var tail = GETTAIL(root);\n var tailInfo: usize = 0;\n if (tail) { // more memory\n if (DEBUG) assert(start >= changetype<usize>(tail) + BLOCK_OVERHEAD);\n\n // merge with current tail if adjacent\n if (start - BLOCK_OVERHEAD == changetype<usize>(tail)) {\n start -= BLOCK_OVERHEAD;\n tailInfo = tail.mmInfo;\n } else {\n // We don't do this, but a user might `memory.grow` manually\n // leading to non-adjacent pages managed by TLSF.\n }\n\n } else if (DEBUG) { // first memory\n assert(start >= changetype<usize>(root) + ROOT_SIZE); // starts after root\n }\n\n // check if size is large enough for a free block and the tail block\n var size = end - start;\n if (size < BLOCK_OVERHEAD + BLOCK_MINSIZE + BLOCK_OVERHEAD) {\n return false;\n }\n\n // left size is total minus its own and the zero-length tail's header\n var leftSize = size - (BLOCK_OVERHEAD << 1);\n var left = changetype<Block>(start);\n left.mmInfo = leftSize | FREE | (tailInfo & LEFTFREE);\n left.prev = null;\n left.next = null;\n\n // tail is a zero-length used block\n tail = changetype<Block>(start + size - BLOCK_OVERHEAD);\n tail.mmInfo = 0 | LEFTFREE;\n SETTAIL(root, tail);\n\n insertBlock(root, left); // also merges with free left before tail / sets 'back'\n\n return true;\n}\n\n/** Grows memory to fit at least another block of the specified size. */\nfunction growMemory(root: Root, size: usize): void {\n // Here, both rounding performed in searchBlock ...\n const halfMaxSize = BLOCK_MAXSIZE >> 1;\n if (size < halfMaxSize) { // don't round last fl\n const invRound = (sizeof<usize>() * 8 - 1) - SL_BITS;\n size += (1 << (invRound - clz<usize>(size))) - 1;\n }\n // and additional BLOCK_OVERHEAD must be taken into account. If we are going\n // to merge with the tail block, that's one time, otherwise it's two times.\n var pagesBefore = memory.size();\n size += BLOCK_OVERHEAD << usize((<usize>pagesBefore << 16) - BLOCK_OVERHEAD != changetype<usize>(GETTAIL(root)));\n var pagesNeeded = <i32>(((size + 0xffff) & ~0xffff) >>> 16);\n var pagesWanted = max(pagesBefore, pagesNeeded); // double memory\n if (memory.grow(pagesWanted) < 0) {\n if (memory.grow(pagesNeeded) < 0) unreachable();\n }\n var pagesAfter = memory.size();\n addMemory(root, <usize>pagesBefore << 16, <usize>pagesAfter << 16);\n}\n\n/** Prepares and checks an allocation size. */\nfunction prepareSize(size: usize): usize {\n if (size >= BLOCK_MAXSIZE) throw new Error(\"allocation too large\");\n return max<usize>((size + AL_MASK) & ~AL_MASK, BLOCK_MINSIZE); // align and ensure min size\n}\n\n/** Initilizes the root structure. */\nexport function maybeInitialize(): Root {\n var root = ROOT;\n if (!root) {\n const rootOffset = (__heap_base + AL_MASK) & ~AL_MASK;\n let pagesBefore = memory.size();\n let pagesNeeded = <i32>((((rootOffset + ROOT_SIZE) + 0xffff) & ~0xffff) >>> 16);\n if (pagesNeeded > pagesBefore && memory.grow(pagesNeeded - pagesBefore) < 0) unreachable();\n root = changetype<Root>(rootOffset);\n root.flMap = 0;\n SETTAIL(root, changetype<Block>(0));\n for (let fl: usize = 0; fl < FL_BITS; ++fl) {\n SETSL(root, fl, 0);\n for (let sl: u32 = 0; sl < SL_SIZE; ++sl) {\n SETHEAD(root, fl, sl, null);\n }\n }\n addMemory(root, (rootOffset + ROOT_SIZE + AL_MASK) & ~AL_MASK, memory.size() << 16);\n ROOT = root;\n }\n return root;\n}\n\n// @ts-ignore: decorator\n@lazy\nvar collectLock: bool = false;\n\n/** Allocates a block of the specified size. */\nexport function allocateBlock(root: Root, size: usize, id: u32): Block {\n if (DEBUG) assert(!collectLock); // must not allocate while collecting\n var payloadSize = prepareSize(size);\n var block = searchBlock(root, payloadSize);\n if (!block) {\n if (gc.auto) {\n if (DEBUG) collectLock = true;\n __collect();\n if (DEBUG) collectLock = false;\n block = searchBlock(root, payloadSize);\n if (!block) {\n growMemory(root, payloadSize);\n block = changetype<Block>(searchBlock(root, payloadSize));\n if (DEBUG) assert(block); // must be found now\n }\n } else {\n growMemory(root, payloadSize);\n block = changetype<Block>(searchBlock(root, payloadSize));\n if (DEBUG) assert(block); // must be found now\n }\n }\n if (DEBUG) assert((block.mmInfo & ~TAGS_MASK) >= payloadSize); // must fit\n block.gcInfo = 0; // RC=0\n block.rtId = id;\n block.rtSize = <u32>size;\n removeBlock(root, <Block>block);\n prepareBlock(root, <Block>block, payloadSize);\n if (isDefined(ASC_RTRACE)) onalloc(<Block>block);\n return <Block>block;\n}\n\n/** Reallocates a block to the specified size. */\nexport function reallocateBlock(root: Root, block: Block, size: usize): Block {\n var payloadSize = prepareSize(size);\n var blockInfo = block.mmInfo;\n\n // possibly split and update runtime size if it still fits\n if (payloadSize <= (blockInfo & ~TAGS_MASK)) {\n prepareBlock(root, block, payloadSize);\n block.rtSize = <u32>size;\n return block;\n }\n\n // merge with right free block if merger is large enough\n var right = GETRIGHT(block);\n var rightInfo = right.mmInfo;\n if (rightInfo & FREE) {\n let mergeSize = (blockInfo & ~TAGS_MASK) + BLOCK_OVERHEAD + (rightInfo & ~TAGS_MASK);\n if (mergeSize >= payloadSize) {\n removeBlock(root, right);\n // TODO: this can yield an intermediate block larger than BLOCK_MAXSIZE, which\n // is immediately split though. does this trigger any assertions / issues?\n block.mmInfo = (blockInfo & TAGS_MASK) | mergeSize;\n block.rtSize = <u32>size;\n prepareBlock(root, block, payloadSize);\n return block;\n }\n }\n\n // otherwise move the block\n var newBlock = allocateBlock(root, size, block.rtId); // may invalidate cached blockInfo\n newBlock.gcInfo = block.gcInfo; // keep RC\n memory.copy(changetype<usize>(newBlock) + BLOCK_OVERHEAD, changetype<usize>(block) + BLOCK_OVERHEAD, size);\n if (changetype<usize>(block) >= __heap_base) {\n if (isDefined(ASC_RTRACE)) onrealloc(block, newBlock);\n freeBlock(root, block);\n }\n return newBlock;\n}\n\n/** Frees a block. */\nexport function freeBlock(root: Root, block: Block): void {\n var blockInfo = block.mmInfo;\n block.mmInfo = blockInfo | FREE;\n insertBlock(root, block);\n if (isDefined(ASC_RTRACE)) onfree(block);\n}\n\n/** Checks that a used block is valid to be freed or reallocated. */\nfunction checkUsedBlock(ref: usize): Block {\n var block = changetype<Block>(ref - BLOCK_OVERHEAD);\n assert(\n ref != 0 && !(ref & AL_MASK) && // must exist and be aligned\n !(block.mmInfo & FREE) && // must be used\n !(block.gcInfo & ~REFCOUNT_MASK) // not buffered or != BLACK\n );\n return block;\n}\n\n// @ts-ignore: decorator\n@global @unsafe\nexport function __alloc(size: usize, id: u32): usize {\n return changetype<usize>(\n allocateBlock(maybeInitialize(), size, id)\n ) + BLOCK_OVERHEAD;\n}\n\n// @ts-ignore: decorator\n@global @unsafe\nexport function __realloc(ref: usize, size: usize): usize {\n return changetype<usize>(\n reallocateBlock(maybeInitialize(), checkUsedBlock(ref), size)\n ) + BLOCK_OVERHEAD;\n}\n\n// @ts-ignore: decorator\n@global @unsafe\nexport function __free(ref: usize): void {\n freeBlock(maybeInitialize(), checkUsedBlock(ref));\n}\n","/// <reference path=\"./rt/index.d.ts\" />\n\n/** Garbage collector interface. */\nexport namespace gc {\n\n /** Can be set to `false` to disable automatic collection. Defaults to `true`. */\n export var auto: bool = true;\n\n /** Performs a full garbage collection cycle. */\n export function collect(): void {\n __collect();\n }\n}\n","/// <reference path=\"../node_modules/@as-pect/assembly/types/as-pect.d.ts\" />\nexport const Int32Array_ID = idof<Int32Array>();\nexport const Uint8Array_ID = idof<Uint8Array>();\n\nconst POLYNOMIAL_DEGREE = 53;\nconst POLYNOMIAL_SHIFT = POLYNOMIAL_DEGREE - 8;\n\nlet tables_initialized = false;\n\nconst modTable = new Uint64Array(256);\nconst outTable = new Uint64Array(256);\n\n\n@inline\nexport function degree(polynom: u64): i32 {\n return 63 - <i32>clz(polynom);\n}\n\n@inline\nexport function mod(x: u64, p: u64): u64 {\n let shift: i32;\n let dp = degree(p);\n while ((shift = degree(x) - dp) >= 0) {\n x ^= p << shift;\n }\n return x;\n}\n\n@inline\nfunction append_byte(hash: u64, b: u8, pol: u64): u64 {\n hash <<= 8;\n hash |= <u64>b;\n\n return mod(hash, pol);\n}\n\n@inline\nfunction calc_tables(h: Rabin): void {\n for (let b = 0; b < 256; b++) {\n let hash: u64 = 0;\n\n hash = append_byte(hash, <u8>b, h.polynomial);\n for (let i = 0; i < h.window_size-1; i++) {\n hash = append_byte(hash, 0, h.polynomial);\n }\n unchecked(outTable[b] = hash);\n }\n\n let k = <u64>degree(h.polynomial);\n for (let b = 0; b < 256; b++) {\n const bk = (<u64>b) << k;\n unchecked(modTable[b] = mod(bk, h.polynomial) | bk);\n }\n}\n\n@inline\nfunction rabin_append(h: Rabin, b: u8): void {\n let digest = h.digest;\n let index = i32(digest >> POLYNOMIAL_SHIFT);\n\n h.digest = ((digest << 8) | <u64>b) ^ unchecked(modTable[index]);\n}\n\n@inline\nfunction rabin_slide(h: Rabin, b: u8): void {\n let out = h.window[h.wpos];\n unchecked(h.window[h.wpos] = b);\n h.digest ^= unchecked(outTable[out]);\n h.wpos = (h.wpos + 1) % h.window_size;\n rabin_append(h, b);\n}\n\n@inline\nfunction rabin_reset(h: Rabin): void {\n for (let i = 0; i < h.window_size; i++) {\n h.window[i] = 0;\n }\n h.digest = 0;\n h.wpos = 0;\n h.count = 0;\n h.digest = 0;\n\n rabin_slide(h, 1);\n}\n\n@inline\nfunction rabin_next_chunk(h: Rabin, buf: usize, len: i32): i32 {\n for (let i = 0; i < len; i++) {\n let b = load<u8>(buf + i);\n\n rabin_slide(h, b);\n\n h.count++;\n h.pos++;\n if ((h.count >= h.minsize && ((h.digest & h.mask) == 0)) || h.count >= h.maxsize) {\n h.chunk_start = h.start;\n h.chunk_length = h.count;\n h.chunk_cut_fingerprint = h.digest;\n\n rabin_reset(h);\n return i + 1;\n }\n }\n\n return -1;\n}\n\n@inline\nfunction rabin_init(h: Rabin): Rabin {\n if (!tables_initialized) {\n calc_tables(h);\n tables_initialized = true;\n }\n\n h.pos = 0;\n h.start = 0;\n rabin_reset(h);\n\n return h;\n}\n\nexport class Rabin {\n window: Uint8Array\n window_size: i32\n wpos: i32\n count: u64\n pos: u64\n start: u64\n digest: u64\n chunk_start: u64\n chunk_length: u64\n chunk_cut_fingerprint: u64\n polynomial: u64\n minsize: u64\n maxsize: u64\n mask: u64\n\n constructor(average_bits: u32, minsize: u32, maxsize: u32, window_size: i32) {\n this.minsize = <u64>minsize;\n this.maxsize = <u64>maxsize;\n this.window = new Uint8Array(window_size);\n this.window_size = window_size;\n this.mask = (1 << average_bits) - 1;\n this.polynomial = 0x3DA3358B4DC173;\n\n rabin_init(this);\n }\n\n fingerprint(buf: Uint8Array, lengths: Int32Array): Int32Array {\n let idx = 0;\n let len = buf.length;\n let ptr = buf.dataStart;\n while (true) {\n let remaining = rabin_next_chunk(this, ptr, len);\n if (remaining < 0) {\n break;\n }\n len -= remaining;\n ptr += remaining;\n let c = idx++;\n // lengths.push(<i32>this.chunk_length)\n unchecked(lengths[c] = <i32>this.chunk_length);\n }\n return lengths;\n }\n}\n","/// <reference path=\"./rt/index.d.ts\" />\n\nimport { BLOCK, BLOCK_MAXSIZE, BLOCK_OVERHEAD } from \"./rt/common\";\nimport { idof } from \"./builtins\";\nimport { E_INVALIDLENGTH } from \"./util/error\";\n\nexport abstract class ArrayBufferView {\n\n readonly buffer: ArrayBuffer;\n @unsafe readonly dataStart: usize;\n readonly byteLength: i32;\n\n get byteOffset(): i32 {\n return <i32>(this.dataStart - changetype<usize>(this.buffer));\n }\n\n get length(): i32 {\n ERROR(\"missing implementation: subclasses must implement ArrayBufferView#length\");\n return unreachable();\n }\n\n protected constructor(length: i32, alignLog2: i32) {\n if (<u32>length > <u32>BLOCK_MAXSIZE >>> alignLog2) throw new RangeError(E_INVALIDLENGTH);\n var buffer = __alloc(length = length << alignLog2, idof<ArrayBuffer>());\n memory.fill(buffer, 0, <usize>length);\n this.buffer = changetype<ArrayBuffer>(buffer); // retains\n this.dataStart = buffer;\n this.byteLength = length;\n }\n}\n\n@sealed export class ArrayBuffer {\n\n static isView<T>(value: T): bool {\n if (isNullable<T>()) {\n if (value === null) return false;\n }\n if (value instanceof Int8Array) return true;\n if (value instanceof Uint8Array) return true;\n if (value instanceof Uint8ClampedArray) return true;\n if (value instanceof Int16Array) return true;\n if (value instanceof Uint16Array) return true;\n if (value instanceof Int32Array) return true;\n if (value instanceof Uint32Array) return true;\n if (value instanceof Int64Array) return true;\n if (value instanceof Uint64Array) return true;\n if (value instanceof Float32Array) return true;\n if (value instanceof Float64Array) return true;\n if (value instanceof DataView) return true;\n return false;\n }\n\n constructor(length: i32) {\n if (<u32>length > <u32>BLOCK_MAXSIZE) throw new RangeError(E_INVALIDLENGTH);\n var buffer = __alloc(<usize>length, idof<ArrayBuffer>());\n memory.fill(buffer, 0, <usize>length);\n return changetype<ArrayBuffer>(buffer); // retains\n }\n\n get byteLength(): i32 {\n return changetype<BLOCK>(changetype<usize>(this) - BLOCK_OVERHEAD).rtSize;\n }\n\n slice(begin: i32 = 0, end: i32 = BLOCK_MAXSIZE): ArrayBuffer {\n var length = this.byteLength;\n begin = begin < 0 ? max(length + begin, 0) : min(begin, length);\n end = end < 0 ? max(length + end , 0) : min(end , length);\n var outSize = <usize>max(end - begin, 0);\n var out = __alloc(outSize, idof<ArrayBuffer>());\n memory.copy(out, changetype<usize>(this) + <usize>begin, outSize);\n return changetype<ArrayBuffer>(out); // retains\n }\n\n toString(): string {\n return \"[object ArrayBuffer]\";\n }\n}\n","// Common error messages for use accross the standard library. Keeping error messages compact\n// and reusing them where possible ensures minimal static data in binaries.\n\n// @ts-ignore: decorator\n@lazy @inline\nexport const E_INDEXOUTOFRANGE: string = \"Index out of range\";\n\n// @ts-ignore: decorator\n@lazy @inline\nexport const E_INVALIDLENGTH: string = \"Invalid length\";\n\n// @ts-ignore: decorator\n@lazy @inline\nexport const E_EMPTYARRAY: string = \"Array is empty\";\n\n// @ts-ignore: decorator\n@lazy @inline\nexport const E_HOLEYARRAY: string = \"Element type must be nullable if array is holey\";\n\n// @ts-ignore: decorator\n@lazy @inline\nexport const E_NOTIMPLEMENTED: string = \"Not implemented\";\n\n// @ts-ignore: decorator\n@lazy @inline\nexport const E_KEYNOTFOUND: string = \"Key does not exist\";\n","import { memcmp, memmove, memset } from \"./util/memory\";\nimport { E_