export namespace memory { export function size(): i32 { return __memory_size(); // tslint:disable-line } export function grow(pages: i32): i32 { return __memory_grow(pages); // tslint:disable-line } export function fill(dest: usize, c: u8, n: usize): void { // see: musl/src/string/memset if (isDefined(__memory_fill)) { __memory_fill(dest, c, n); return; } // tslint:disable-line memset(dest, c, n); } export function copy(dest: usize, src: usize, n: usize): void { // see: musl/src/string/memmove.c if (isDefined(__memory_copy)) { __memory_copy(dest, src, n); return; } // tslint:disable-line memmove(dest, src, n); } export function compare(vl: usize, vr: usize, n: usize): i32 { // see: musl/src/string/memcmp.c if (isDefined(__memory_compare)) return __memory_compare(vl, vr, n); // tslint:disable-line return memcmp(vl, vr, n); } // Passive segments // export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void { // __memory_init(segmentIndex, srcOffset, dstOffset); // } // export function drop(segmentIndex: u32): void { // __memory_drop(segmentIndex); // } // Allocator export function allocate(size: usize): usize { if (isDefined(__memory_allocate)) return __memory_allocate(size); // tslint:disable-line WARNING("Calling 'memory.allocate' requires a memory manager to be present."); return unreachable(); } export function free(ptr: usize): void { if (isDefined(__memory_free)) { __memory_free(ptr); return; } // tslint:disable-line WARNING("Calling 'memory.free' requires a memory manager to be present."); unreachable(); } export function reset(): void { if (isDefined(__memory_reset)) { __memory_reset(); return; } // tslint:disable-line unreachable(); } } // this function will go away once `memory.copy` becomes an intrinsic function memcpy(dest: usize, src: usize, n: usize): void { // see: musl/src/string/memcpy.c var w: u32, x: u32; // copy 1 byte each until src is aligned to 4 bytes while (n && (src & 3)) { store(dest++, load(src++)); n--; } // if dst is aligned to 4 bytes as well, copy 4 bytes each if ((dest & 3) == 0) { while (n >= 16) { store(dest , load(src )); store(dest + 4, load(src + 4)); store(dest + 8, load(src + 8)); store(dest + 12, load(src + 12)); src += 16; dest += 16; n -= 16; } if (n & 8) { store(dest , load(src )); store(dest + 4, load(src + 4)); dest += 8; src += 8; } if (n & 4) { store(dest, load(src)); dest += 4; src += 4; } if (n & 2) { // drop to 2 bytes each store(dest, load(src)); dest += 2; src += 2; } if (n & 1) { // drop to 1 byte store(dest++, load(src++)); } return; } // if dst is not aligned to 4 bytes, use alternating shifts to copy 4 bytes each // doing shifts if faster when copying enough bytes (here: 32 or more) if (n >= 32) { switch (dest & 3) { // known to be != 0 case 1: { w = load(src); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); n -= 3; while (n >= 17) { x = load(src + 1); store(dest, w >> 24 | x << 8); w = load(src + 5); store(dest + 4, x >> 24 | w << 8); x = load(src + 9); store(dest + 8, w >> 24 | x << 8); w = load(src + 13); store(dest + 12, x >> 24 | w << 8); src += 16; dest += 16; n -= 16; } break; } case 2: { w = load(src); store(dest++, load(src++)); store(dest++, load(src++)); n -= 2; while (n >= 18) { x = load(src + 2); store(dest, w >> 16 | x << 16); w = load(src + 6); store(dest + 4, x >> 16 | w << 16); x = load(src + 10); store(dest + 8, w >> 16 | x << 16); w = load(src + 14); store(dest + 12, x >> 16 | w << 16); src += 16; dest += 16; n -= 16; } break; } case 3: { w = load(src); store(dest++, load(src++)); n -= 1; while (n >= 19) { x = load(src + 3); store(dest, w >> 8 | x << 24); w = load(src + 7); store(dest + 4, x >> 8 | w << 24); x = load(src + 11); store(dest + 8, w >> 8 | x << 24); w = load(src + 15); store(dest + 12, x >> 8 | w << 24); src += 16; dest += 16; n -= 16; } break; } } } // copy remaining bytes one by one if (n & 16) { store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); } if (n & 8) { store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); } if (n & 4) { store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); store(dest++, load(src++)); } if (n & 2) { store(dest++, load(src++)); store(dest++, load(src++)); } if (n & 1) { store(dest++, load(src++)); } } // this function will go away once `memory.copy` becomes an intrinsic function memmove(dest: usize, src: usize, n: usize): void { // see: musl/src/string/memmove.c if (dest == src) return; if (src + n <= dest || dest + n <= src) { memcpy(dest, src, n); return; } if (dest < src) { if ((src & 7) == (dest & 7)) { while (dest & 7) { if (!n) return; --n; store(dest++, load(src++)); } while (n >= 8) { store(dest, load(src)); n -= 8; dest += 8; src += 8; } } while (n) { store(dest++, load(src++)); --n; } } else { if ((src & 7) == (dest & 7)) { while ((dest + n) & 7) { if (!n) return; store(dest + --n, load(src + n)); } while (n >= 8) { n -= 8; store(dest + n, load(src + n)); } } while (n) { store(dest + --n, load(src + n)); } } } // this function will go away once `memory.fill` becomes an intrinsic function memset(dest: usize, c: u8, n: usize): void { // see: musl/src/string/memset // fill head and tail with minimal branching if (!n) return; store(dest, c); store(dest + n - 1, c); if (n <= 2) return; store(dest + 1, c); store(dest + 2, c); store(dest + n - 2, c); store(dest + n - 3, c); if (n <= 6) return; store(dest + 3, c); store(dest + n - 4, c); if (n <= 8) return; // advance pointer to align it at 4-byte boundary var k: usize = -dest & 3; dest += k; n -= k; n &= -4; var c32: u32 = -1 / 255 * c; // fill head/tail up to 28 bytes each in preparation store(dest, c32); store(dest + n - 4, c32); if (n <= 8) return; store(dest + 4, c32); store(dest + 8, c32); store(dest + n - 12, c32); store(dest + n - 8, c32); if (n <= 24) return; store(dest + 12, c32); store(dest + 16, c32); store(dest + 20, c32); store(dest + 24, c32); store(dest + n - 28, c32); store(dest + n - 24, c32); store(dest + n - 20, c32); store(dest + n - 16, c32); // align to a multiple of 8 k = 24 + (dest & 4); dest += k; n -= k; // copy 32 bytes each var c64: u64 = c32 | (c32 << 32); while (n >= 32) { store(dest, c64); store(dest + 8, c64); store(dest + 16, c64); store(dest + 24, c64); n -= 32; dest += 32; } } function memcmp(vl: usize, vr: usize, n: usize): i32 { // see: musl/src/string/memcmp.c if (vl == vr) return 0; while (n != 0 && load(vl) == load(vr)) { n--; vl++; vr++; } return n ? load(vl) - load(vr) : 0; }