diff --git a/std/assembly/array.ts b/std/assembly/array.ts index 5e453f05..3811d5a6 100644 --- a/std/assembly/array.ts +++ b/std/assembly/array.ts @@ -1,34 +1,20 @@ -// import { -// MAX_BLENGTH, -// HEADER_SIZE, -// allocateUnsafe, -// reallocateUnsafe, -// LOAD, -// STORE -// } from "./internal/arraybuffer"; - import { ALLOC, REALLOC, REGISTER, LINK, - ArrayBufferView + ArrayBufferView, + FREE } from "./runtime"; import { ArrayBuffer } from "./arraybuffer"; -// import { -// allocateUnsafe as allocateUnsafeString, -// freeUnsafe as freeUnsafeString, -// copyUnsafe as copyUnsafeString -// } from "./internal/string"; - import { COMPARATOR, SORT -} from "./internal/sort"; +} from "./util/sort"; import { itoa, @@ -36,7 +22,7 @@ import { itoa_stream, dtoa_stream, MAX_DOUBLE_LENGTH -} from "./internal/number"; +} from "./util/number"; import { isArray as builtin_isArray @@ -372,186 +358,233 @@ export class Array extends ArrayBufferView { } reverse(): Array { - var buffer = this.buffer_; + var base = this.dataStart; for (let front = 0, back = this.length_ - 1; front < back; ++front, --back) { - let temp = LOAD(buffer, front); - STORE(buffer, front, LOAD(buffer, back)); - STORE(buffer, back, temp); + let temp: T = load(base, front); + store(base + (front << alignof()), load(base + (back << alignof()))); + store(base + (back << alignof()), temp); } return this; } sort(comparator: (a: T, b: T) => i32 = COMPARATOR()): this { - // TODO remove this when flow will allow trackcing null + // TODO remove this when flow will allow tracking null assert(comparator); // The comparison function must be a function var length = this.length_; if (length <= 1) return this; - var buffer = this.buffer_; + var base = this.dataStart; if (length == 2) { - let a = LOAD(buffer, 1); // a = arr[1] - let b = LOAD(buffer, 0); // b = arr[0] + let a: T = load(base, sizeof()); // a = arr[1] + let b: T = load(base); // b = arr[0] if (comparator(a, b) < 0) { - STORE(buffer, 1, b); // arr[1] = b; - STORE(buffer, 0, a); // arr[0] = a; + store(base, b, sizeof()); // arr[1] = b; + store(base, a); // arr[0] = a; } return this; } - SORT(buffer, 0, length, comparator); + SORT(base, length, comparator); return this; } + // FIXME: refactor into multiple functions? join(separator: string = ","): string { var lastIndex = this.length_ - 1; if (lastIndex < 0) return ""; var result = ""; var value: T; - var buffer = this.buffer_; + var base = this.dataStart; + // var buffer = this.buffer_; var sepLen = separator.length; var hasSeparator = sepLen != 0; if (value instanceof bool) { - if (!lastIndex) return select("true", "false", LOAD(buffer, 0)); + if (!lastIndex) return select("true", "false", load(base)); let valueLen = 5; // max possible length of element len("false") let estLen = (valueLen + sepLen) * lastIndex + valueLen; - let result = allocateUnsafeString(estLen); + let result = ALLOC(estLen << 1); let offset = 0; for (let i = 0; i < lastIndex; ++i) { - value = LOAD(buffer, i); + value = load(base + i); valueLen = 4 + (!value); - copyUnsafeString(result, offset, select("true", "false", value), 0, valueLen); + memory.copy( + result + (offset << 1), + changetype(select("true", "false", value)), + valueLen << 1 + ); offset += valueLen; if (hasSeparator) { - copyUnsafeString(result, offset, changetype(separator), 0, sepLen); + memory.copy( + result + (offset << 1), + changetype(separator), + sepLen << 1 + ); offset += sepLen; } } - value = LOAD(buffer, lastIndex); + value = load(base + lastIndex); valueLen = 4 + (!value); - copyUnsafeString(result, offset, select("true", "false", value), 0, valueLen); + memory.copy( + result + (offset << 1), + changetype(select("true", "false", value)), + valueLen << 1 + ); offset += valueLen; - let out = result; if (estLen > offset) { - out = result.substring(0, offset); - freeUnsafeString(result); + let trimmed = changetype(result).substring(0, offset); + FREE(result); + return trimmed; // registered in .substring } - return out; + return REGISTER(result); } else if (isInteger()) { - if (!lastIndex) return changetype(itoa(LOAD(buffer, 0))); + if (!lastIndex) return changetype(itoa(load(base))); const valueLen = (sizeof() <= 4 ? 10 : 20) + isSigned(); let estLen = (valueLen + sepLen) * lastIndex + valueLen; - let result = allocateUnsafeString(estLen); + let result = ALLOC(estLen << 1); let offset = 0; for (let i = 0; i < lastIndex; ++i) { - value = LOAD(buffer, i); - offset += itoa_stream(changetype(result), offset, value); + value = load(base + (i << alignof())); + offset += itoa_stream(result, offset, value); if (hasSeparator) { - copyUnsafeString(result, offset, separator, 0, sepLen); + memory.copy( + result + (offset << 1), + changetype(separator), + sepLen << 1 + ); offset += sepLen; } } - value = LOAD(buffer, lastIndex); - offset += itoa_stream(changetype(result), offset, value); - let out = result; + value = load(base + (lastIndex << alignof())); + offset += itoa_stream(result, offset, value); if (estLen > offset) { - out = result.substring(0, offset); - freeUnsafeString(result); + let trimmed = changetype(result).substring(0, offset); + FREE(result); + return trimmed; // registered in .substring } - return out; + return REGISTER(result); } else if (isFloat()) { - if (!lastIndex) return changetype(dtoa(LOAD(buffer, 0))); + if (!lastIndex) return changetype(dtoa(load(base))); const valueLen = MAX_DOUBLE_LENGTH; let estLen = (valueLen + sepLen) * lastIndex + valueLen; - let result = allocateUnsafeString(estLen); + let result = ALLOC(estLen << 1); let offset = 0; for (let i = 0; i < lastIndex; ++i) { - value = LOAD(buffer, i); - offset += dtoa_stream(changetype(result), offset, value); + value = load(base + (i << alignof())); + offset += dtoa_stream(result, offset, value); if (hasSeparator) { - copyUnsafeString(result, offset, separator, 0, sepLen); + memory.copy( + result + (offset << 1), + changetype(separator), + sepLen << 1 + ); offset += sepLen; } } - value = LOAD(buffer, lastIndex); - offset += dtoa_stream(changetype(result), offset, value); - let out = result; + value = load(base + (lastIndex << alignof())); + offset += dtoa_stream(result, offset, value); if (estLen > offset) { - out = result.substring(0, offset); - freeUnsafeString(result); + let trimmed = changetype(result).substring(0, offset); + FREE(result); + return trimmed; // registered in .substring } - return out; + return REGISTER(result); } else if (isString()) { - if (!lastIndex) return LOAD(buffer, 0); + if (!lastIndex) return load(base); let estLen = 0; for (let i = 0, len = lastIndex + 1; i < len; ++i) { - estLen += LOAD(buffer, i).length; + estLen += load(base + (i << alignof())).length; } let offset = 0; - let result = allocateUnsafeString(estLen + sepLen * lastIndex); + let result = ALLOC((estLen + sepLen * lastIndex) << 1); for (let i = 0; i < lastIndex; ++i) { - value = LOAD(buffer, i); + value = load(base + (i << alignof())); if (value) { - let valueLen = value.length; // tslint:disable-line:no-unsafe-any - copyUnsafeString(result, offset, value, 0, valueLen); // tslint:disable-line:no-unsafe-any - offset += valueLen; // tslint:disable-line:no-unsafe-any + let valueLen = changetype(value).length; + memory.copy( + result + (offset << 1), + changetype(value), + valueLen << 1 + ); + offset += valueLen; } if (hasSeparator) { - copyUnsafeString(result, offset, separator, 0, sepLen); + memory.copy( + result + (offset << 1), + changetype(separator), + sepLen << 1 + ); offset += sepLen; } } - value = LOAD(buffer, lastIndex); + value = load(base + (lastIndex << alignof())); if (value) { - let valueLen = value.length; // tslint:disable-line:no-unsafe-any - copyUnsafeString(result, offset, value, 0, valueLen); // tslint:disable-line:no-unsafe-any + let valueLen = changetype(value).length; + memory.copy( + result + (offset << 1), + changetype(value), + valueLen << 1 + ); } - return result; + return REGISTER(result); } else if (isArray()) { if (!lastIndex) { - value = LOAD(buffer, 0); - return value ? value.join(separator) : ""; // tslint:disable-line:no-unsafe-any + value = load(base); + return value ? value.join(separator) : ""; } for (let i = 0; i < lastIndex; ++i) { - value = LOAD(buffer, i); - if (value) result += value.join(separator); // tslint:disable-line:no-unsafe-any + value = load(base + (i << alignof())); + if (value) result += value.join(separator); if (hasSeparator) result += separator; } - value = LOAD(buffer, lastIndex); - if (value) result += value.join(separator); // tslint:disable-line:no-unsafe-any - return result; + value = load(base + (lastIndex << alignof())); + if (value) result += value.join(separator); + return result; // registered by concatenation (FIXME: lots of garbage) } else if (isReference()) { // References if (!lastIndex) return "[object Object]"; const valueLen = 15; // max possible length of element len("[object Object]") let estLen = (valueLen + sepLen) * lastIndex + valueLen; - let result = allocateUnsafeString(estLen); + let result = ALLOC(estLen << 1); let offset = 0; for (let i = 0; i < lastIndex; ++i) { - value = LOAD(buffer, i); + value = load(base + (i << alignof())); if (value) { - copyUnsafeString(result, offset, changetype("[object Object]"), 0, valueLen); + memory.copy( + result + (offset << 1), + changetype("[object Object]"), + valueLen << 1 + ); offset += valueLen; } if (hasSeparator) { - copyUnsafeString(result, offset, changetype(separator), 0, sepLen); + memory.copy( + result + (offset << 1), + changetype(separator), + sepLen << 1 + ); offset += sepLen; } } - if (LOAD(buffer, lastIndex)) { - copyUnsafeString(result, offset, changetype("[object Object]"), 0, valueLen); + if (load(base + (lastIndex << alignof()))) { + memory.copy( + result + (offset << 1), + changetype("[object Object]"), + valueLen << 1 + ); offset += valueLen; } - let out = result; if (estLen > offset) { - out = result.substring(0, offset); - freeUnsafeString(result); + let out = changetype(result).substring(0, offset); + FREE(result); + return out; // registered in .substring } - return out; + return REGISTER(result); } else { - assert(false); // Unsupported generic typename + ERROR("unspported type"); + assert(false); } } @@ -560,16 +593,16 @@ export class Array extends ArrayBufferView { return this.join(); } - private __gc(): void { - var buffer = this.buffer_; - __gc_mark(changetype(buffer)); // tslint:disable-line - if (isManaged()) { - let offset: usize = 0; - let end = this.length_ << alignof(); - while (offset < end) { - __gc_mark(load(changetype(buffer) + offset, HEADER_SIZE)); // tslint:disable-line - offset += sizeof(); - } - } - } + // private __gc(): void { + // var buffer = this.buffer_; + // __gc_mark(changetype(buffer)); // tslint:disable-line + // if (isManaged()) { + // let offset: usize = 0; + // let end = this.length_ << alignof(); + // while (offset < end) { + // __gc_mark(load(changetype(buffer) + offset, HEADER_SIZE)); // tslint:disable-line + // offset += sizeof(); + // } + // } + // } } diff --git a/std/assembly/internal/arraybuffer.ts b/std/assembly/internal/arraybuffer.ts deleted file mode 100644 index 4545d8e0..00000000 --- a/std/assembly/internal/arraybuffer.ts +++ /dev/null @@ -1,84 +0,0 @@ -import { - AL_MASK, - MAX_SIZE_32 - } from "./allocator"; - -/** Size of an ArrayBuffer header. */ -@inline export const HEADER_SIZE: usize = (offsetof() + AL_MASK) & ~AL_MASK; -/** Maximum byte length of an ArrayBuffer. */ -@inline export const MAX_BLENGTH: i32 = MAX_SIZE_32 - HEADER_SIZE; - -function computeSize(byteLength: i32): usize { - // round up to power of 2, with HEADER_SIZE=8: - // 0 -> 2^3 = 8 - // 1..8 -> 2^4 = 16 - // 9..24 -> 2^5 = 32 - // ... - // MAX_LENGTH -> 2^30 = 0x40000000 (MAX_SIZE_32) - return 1 << (32 - clz(byteLength + HEADER_SIZE - 1)); -} - -// Low-level utility - -function __gc(ref: usize): void {} - -export function allocateUnsafe(byteLength: i32): ArrayBuffer { - assert(byteLength <= MAX_BLENGTH); - var buffer: usize; - if (isManaged()) { - buffer = __gc_allocate(computeSize(byteLength), __gc); // tslint:disable-line - } else { - buffer = memory.allocate(computeSize(byteLength)); - } - store(buffer, byteLength, offsetof("byteLength")); - return changetype(buffer); -} - -export function reallocateUnsafe(buffer: ArrayBuffer, newByteLength: i32): ArrayBuffer { - var oldByteLength = buffer.byteLength; - if (newByteLength > oldByteLength) { - assert(newByteLength <= MAX_BLENGTH); - if (newByteLength <= (computeSize(oldByteLength) - HEADER_SIZE)) { // fast path: zero out additional space - store(changetype(buffer), newByteLength, offsetof("byteLength")); - } else { // slow path: copy to new buffer - let newBuffer = allocateUnsafe(newByteLength); - memory.copy( - changetype(newBuffer) + HEADER_SIZE, - changetype(buffer) + HEADER_SIZE, - oldByteLength - ); - if (!isManaged()) { - memory.free(changetype(buffer)); - } - buffer = newBuffer; - } - memory.fill( - changetype(buffer) + HEADER_SIZE + oldByteLength, - 0, - (newByteLength - oldByteLength) - ); - } else if (newByteLength < oldByteLength) { // fast path: override size - // TBD: worth to copy and release if size is significantly less than before? - assert(newByteLength >= 0); - store(changetype(buffer), newByteLength, offsetof("byteLength")); - } - return buffer; -} - -// The helpers below use two different types in order to emit loads and stores that load respectively -// store one type to/from memory while returning/taking the desired output/input type. This allows to -// emit instructions like -// -// * `i32.load8` ^= `load(...)` that reads an i8 but returns an i32, or -// * `i64.load32_s` ^= `load(...)`) that reads a 32-bit as a 64-bit integer -// -// without having to emit an additional instruction for conversion purposes. The second parameter -// can be omitted for references and other loads and stores that simply return the exact type. - -@inline export function LOAD(buffer: ArrayBuffer, index: i32, byteOffset: i32 = 0): TOut { - return load(changetype(buffer) + (index << alignof()) + byteOffset, HEADER_SIZE); -} - -@inline export function STORE(buffer: ArrayBuffer, index: i32, value: TIn, byteOffset: i32 = 0): void { - store(changetype(buffer) + (index << alignof()) + byteOffset, value, HEADER_SIZE); -} diff --git a/std/assembly/internal/string.ts b/std/assembly/internal/string.ts deleted file mode 100644 index 2e4fd81f..00000000 --- a/std/assembly/internal/string.ts +++ /dev/null @@ -1,225 +0,0 @@ -export function compareImpl(str1: String, index1: usize, str2: String, index2: usize, len: usize): i32 { - var result: i32 = 0; - var ptr1 = changetype(str1) + (index1 << 1); - var ptr2 = changetype(str2) + (index2 << 1); - while (len && !(result = load(ptr1) - load(ptr2))) { - --len, ptr1 += 2, ptr2 += 2; - } - return result; -} - -import { MAX_SIZE_32 } from "./allocator"; -import { String } from "../string"; - -/** Size of a String header. */ -@inline export const HEADER_SIZE = (offsetof() + 1) & ~1; // 2 byte aligned -/** Maximum length of a String. */ -@inline export const MAX_LENGTH = (MAX_SIZE_32 - HEADER_SIZE) >>> 1; - -// Low-level utility - -function __gc(ref: usize): void {} - -export function allocateUnsafe(length: i32): String { - assert(length > 0 && length <= MAX_LENGTH); - var buffer: usize; - if (isManaged()) { - buffer = __gc_allocate(HEADER_SIZE + (length << 1), __gc); // tslint:disable-line - } else { - buffer = memory.allocate(HEADER_SIZE + (length << 1)); - } - store(buffer, length); - return changetype(buffer); -} - -@inline -export function freeUnsafe(buffer: String): void { - if (!isManaged()) { - assert(buffer); - memory.free(changetype(buffer)); - } -} - -export function copyUnsafe(dest: String, destOffset: usize, src: String, srcOffset: usize, len: usize): void { - memory.copy( - changetype(dest) + (destOffset << 1) + HEADER_SIZE, - changetype(src) + (srcOffset << 1) + HEADER_SIZE, - len << 1 - ); -} - -export function repeatUnsafe(dest: String, destOffset: usize, src: String, count: i32): void { - var length = src.length; - if (ASC_SHRINK_LEVEL > 1) { - let strLen = length << 1; - let to = changetype(dest) + HEADER_SIZE + (destOffset << 1); - let from = changetype(src) + HEADER_SIZE; - for (let i = 0, len = strLen * count; i < len; i += strLen) { - memory.copy(to + i, from, strLen); - } - } else { - switch (length) { - case 0: break; - case 1: { - let cc = load(changetype(src), HEADER_SIZE); - let out = changetype(dest) + (destOffset << 1); - for (let i = 0; i < count; ++i) { - store(out + (i << 1), cc, HEADER_SIZE); - } - break; - } - case 2: { - let cc = load(changetype(src), HEADER_SIZE); - let out = changetype(dest) + (destOffset << 1); - for (let i = 0; i < count; ++i) { - store(out + (i << 2), cc, HEADER_SIZE); - } - break; - } - case 3: { - let cc1 = load(changetype(src), HEADER_SIZE + 0); - let cc2 = load(changetype(src), HEADER_SIZE + 4); - let out = changetype(dest) + (destOffset << 1); - for (let i = 0; i < count; ++i) { - store(out + (i << 2), cc1, HEADER_SIZE + 0); - store(out + (i << 1), cc2, HEADER_SIZE + 4); - } - break; - } - case 4: { - let cc = load(changetype(src), HEADER_SIZE); - let out = changetype(dest) + (destOffset << 1); - for (let i = 0; i < count; ++i) { - store(out + (i << 3), cc, HEADER_SIZE); - } - break; - } - default: { - let strLen = length << 1; - let to = changetype(dest) + HEADER_SIZE + (destOffset << 1); - let from = changetype(src) + HEADER_SIZE; - for (let i = 0, len = strLen * count; i < len; i += strLen) { - memory.copy(to + i, from, strLen); - } - break; - } - } - } -} - -// Helpers - -@inline export const enum CharCode { - PLUS = 0x2B, - MINUS = 0x2D, - DOT = 0x2E, - _0 = 0x30, - _1 = 0x31, - _2 = 0x32, - _3 = 0x33, - _4 = 0x34, - _5 = 0x35, - _6 = 0x36, - _7 = 0x37, - _8 = 0x38, - _9 = 0x39, - A = 0x41, - B = 0x42, - E = 0x45, - N = 0x4E, - O = 0x4F, - X = 0x58, - Z = 0x5a, - a = 0x61, - b = 0x62, - e = 0x65, - n = 0x6E, - o = 0x6F, - x = 0x78, - z = 0x7A -} - -export function isWhiteSpaceOrLineTerminator(c: u16): bool { - switch (c) { - case 9: // - case 10: // - case 13: // - case 11: // - case 12: // - case 32: // - case 160: // - case 8232: // - case 8233: // - case 65279: return true; // - default: return false; - } -} - -/** Parses a string to an integer (usually), using the specified radix. */ -export function parse(str: String, radix: i32 = 0): T { - var len: i32 = str.length; - if (!len) return NaN; - - var ptr = changetype(str) /* + HEAD -> offset */; - var code = load(ptr, HEADER_SIZE); - - // determine sign - var sign: T; - if (code == CharCode.MINUS) { - if (!--len) return NaN; - code = load(ptr += 2, HEADER_SIZE); - sign = -1; - } else if (code == CharCode.PLUS) { - if (!--len) return NaN; - code = load(ptr += 2, HEADER_SIZE); - sign = 1; - } else { - sign = 1; - } - - // determine radix - if (!radix) { - if (code == CharCode._0 && len > 2) { - switch (load(ptr + 2, HEADER_SIZE)) { - case CharCode.B: - case CharCode.b: { - ptr += 4; len -= 2; - radix = 2; - break; - } - case CharCode.O: - case CharCode.o: { - ptr += 4; len -= 2; - radix = 8; - break; - } - case CharCode.X: - case CharCode.x: { - ptr += 4; len -= 2; - radix = 16; - break; - } - default: radix = 10; - } - } else radix = 10; - } else if (radix < 2 || radix > 36) { - return NaN; - } - - // calculate value - var num: T = 0; - while (len--) { - code = load(ptr, HEADER_SIZE); - if (code >= CharCode._0 && code <= CharCode._9) { - code -= CharCode._0; - } else if (code >= CharCode.A && code <= CharCode.Z) { - code -= CharCode.A - 10; - } else if (code >= CharCode.a && code <= CharCode.z) { - code -= CharCode.a - 10; - } else break; - if (code >= radix) break; - num = (num * radix) + code; - ptr += 2; - } - return sign * num; -} diff --git a/std/assembly/internal/typedarray.ts b/std/assembly/internal/typedarray.ts deleted file mode 100644 index 2364f325..00000000 --- a/std/assembly/internal/typedarray.ts +++ /dev/null @@ -1,206 +0,0 @@ -import { - ALLOC_RAW, - REGISTER, - LINK -} from "../runtime"; - -import { - ArrayBuffer -} from "../arraybuffer"; - -import { - SORT as SORT_IMPL -} from "./sort"; - -/** Typed array base class. Not a global object. */ -export abstract class TypedArray { - [key: number]: T; // compatibility only - - readonly buffer: ArrayBuffer; - readonly byteOffset: i32; - readonly byteLength: i32; - - constructor(length: i32) { - const MAX_LENGTH = ArrayBuffer.MAX_BYTELENGTH / sizeof(); - if (length > MAX_LENGTH) throw new RangeError("Invalid typed array length"); - var byteLength = length << alignof(); - this.buffer = new ArrayBuffer(byteLength); - this.byteOffset = 0; - this.byteLength = byteLength; - } - - get length(): i32 { - return this.byteLength >>> alignof(); - } - - // TODO: could compute load/store offset from index and emit an immediate -> make this a builtin? - - @operator("[]") protected __get(index: i32): T { - if (index >= (this.byteLength >>> alignof())) throw new Error("Index out of bounds"); - return load(changetype(this.buffer) + this.byteOffset, index << alignof()); - } - - @inline @operator("{}") protected __unchecked_get(index: i32): T { - return load(changetype(this.buffer) + this.byteOffset + (index << alignof())); - } - - @operator("[]=") protected __set(index: i32, value: native): void { - if (index >= (this.byteLength >>> alignof())) throw new Error("Index out of bounds"); - store(changetype(this.buffer) + this.byteOffset + (index << alignof()), value); - } - - @inline @operator("{}=") protected __unchecked_set(index: i32, value: native): void { - store(changetype(this.buffer) + this.byteOffset + (index << alignof()), value); - } - - // copyWithin(target: i32, start: i32, end: i32 = this.length): this -} - -@inline export function FILL, T extends number>( - array: TArray, - value: native, - start: i32, - end: i32 -): TArray { - var base = changetype(array.buffer) + array.byteOffset; - var len = array.length; - start = start < 0 ? max(len + start, 0) : min(start, len); - end = end < 0 ? max(len + end, 0) : min(end, len); - if (sizeof() == 1) { - if (start < end) memory.fill(base + start, value, (end - start)); - } else { - for (; start < end; ++start) { - store(base + (start << alignof()), value); - } - } - return array; -} - -@inline export function SORT, T>( - array: TArray, - comparator: (a: T, b: T) => i32 -): TArray { - var length = array.length; - var offset = array.byteOffset; - if (length <= 1) return array; - var buffer = changetype(array.buffer); - if (length == 2) { - let a = load(buffer + offset, sizeof()); - let b = load(buffer + offset); - if (comparator(a, b) < 0) { - store(buffer + offset, b, sizeof()); - store(buffer + offset, a); - } - return array; - } - // TODO - // SORT_IMPL(buffer, byteOffset, length, comparator); - return array; -} - -@inline export function SUBARRAY, T>( - array: TArray, - begin: i32, - end: i32 -): TArray { - var buffer = changetype(array.buffer); - var length = array.length; - if (begin < 0) begin = max(length + begin, 0); - else begin = min(begin, length); - if (end < 0) end = max(length + end, begin); - else end = max(min(end, length), begin); - var out = ALLOC_RAW(offsetof()); - store(out, buffer, offsetof("buffer")); - store(out, buffer + (begin << alignof()), offsetof("dataStart")); - store(out, buffer + (end << alignof()), offsetof("dataStart")); - store(out, array.byteOffset + (begin << alignof()), offsetof("byteOffset")); - store(out, (end - begin) << alignof(), offsetof("byteLength")); - LINK(buffer, REGISTER(out)); // register first, then link - return changetype(out); -} - -@inline export function REDUCE, T, TRet>( - array: TArray, - callbackfn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet, - initialValue: TRet -): TRet { - var base = changetype(array.buffer) + array.byteOffset; - for (let i = 0, k = array.length; i < k; i++) { - initialValue = callbackfn(initialValue, load(base + (i << alignof())), i, array); - } - return initialValue; -} - -@inline export function REDUCE_RIGHT, T, TRet>( - array: TArray, - callbackfn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet, - initialValue: TRet -): TRet { - var base = changetype(array.buffer) + array.byteOffset; - for (let i = array.length - 1; i >= 0; i--) { - initialValue = callbackfn(initialValue, load(base + (i << alignof())), i, array); - } - return initialValue; -} - -@inline export function MAP, T>( - array: TArray, - callbackfn: (value: T, index: i32, self: TArray) => T, -): TArray { - var length = array.length; - var base = changetype(array.buffer) + array.byteOffset; - var result = instantiate(length); - var resultBase = changetype(result.buffer); // assumes byteOffset = 0 - for (let i = 0; i < length; i++) { - store( - resultBase + (i << alignof()), - callbackfn(load(base + (i << alignof())), i, array) - ); - } - - return result; -} - -@inline export function FIND_INDEX, T>( - array: TArray, - callbackfn: (value: T, index: i32, array: TArray) => bool, -): i32 { - var base = changetype(array.buffer) + array.byteOffset; - for (let i = 0, k = array.length; i < k; i++) { - if (callbackfn(load(base + (i << alignof())), i, array)) return i; - } - return -1; -} - -@inline export function SOME, T>( - array: TArray, - callbackfn: (value: T, index: i32, array: TArray) => bool, -): bool { - var base = changetype(array.buffer) + array.byteOffset; - for (let i = 0, k = array.length; i < k; i++) { - if (callbackfn(load(base + (i << alignof())), i, array)) return true; - } - return false; -} - -@inline export function EVERY, T>( - array: TArray, - callbackfn: (value: T, index: i32, array: TArray) => bool, -): bool { - var base = changetype(array.buffer) + array.byteOffset; - for (let i = 0, k = array.length; i < k; i++) { - if (callbackfn(load(base + (i << alignof())), i, array)) continue; - return false; - } - return true; -} - -@inline export function FOREACH, T>( - array: TArray, - callbackfn: (value: T, index: i32, array: TArray) => void, -): void { - var base = changetype(array.buffer) + array.byteOffset; - for (let i = 0, k = array.length; i < k; i++) { - callbackfn(load(base + (i << alignof())), i, array); - } -} diff --git a/std/assembly/string.ts b/std/assembly/string.ts index 26c8368e..bc4a2e28 100644 --- a/std/assembly/string.ts +++ b/std/assembly/string.ts @@ -355,7 +355,8 @@ import { // split by chars length = min(length, limit); let result = new Array(length); - let buffer = result.buffer_; + let buffer = unreachable(); // TODO + // let buffer = result.buffer_; for (let i: isize = 0; i < length; ++i) { let char = ALLOC(2); store( diff --git a/std/assembly/internal/allocator.ts b/std/assembly/util/allocator.ts similarity index 100% rename from std/assembly/internal/allocator.ts rename to std/assembly/util/allocator.ts diff --git a/std/assembly/internal/hash.ts b/std/assembly/util/hash.ts similarity index 86% rename from std/assembly/internal/hash.ts rename to std/assembly/util/hash.ts index dd7c7403..f4dcea3e 100644 --- a/std/assembly/internal/hash.ts +++ b/std/assembly/util/hash.ts @@ -1,11 +1,4 @@ -import { - HEADER_SIZE -} from "./string"; - -/** Computes the 32-bit hash of a value of any type. */ -@inline -export function HASH(key: T): u32 { - // branch-level tree-shaking makes this a `(return (call ...))` +@inline export function HASH(key: T): u32 { if (isString(key)) { return hashStr(key); } else if (isReference()) { @@ -66,7 +59,7 @@ function hash64(key: u64): u32 { function hashStr(key: string): u32 { var v = FNV_OFFSET; for (let i: usize = 0, k: usize = key.length << 1; i < k; ++i) { - v = (v ^ load(changetype(key) + i, HEADER_SIZE)) * FNV_PRIME; + v = (v ^ load(changetype(key) + i)) * FNV_PRIME; } return v; } diff --git a/std/assembly/internal/memory.ts b/std/assembly/util/memory.ts similarity index 100% rename from std/assembly/internal/memory.ts rename to std/assembly/util/memory.ts diff --git a/std/assembly/internal/number.ts b/std/assembly/util/number.ts similarity index 99% rename from std/assembly/internal/number.ts rename to std/assembly/util/number.ts index 0cc7ac21..01dff985 100644 --- a/std/assembly/internal/number.ts +++ b/std/assembly/util/number.ts @@ -8,10 +8,6 @@ import { FREE } from "../runtime"; -import { - LOAD -} from "./arraybuffer"; - @inline export const MAX_DOUBLE_LENGTH = 28; @lazy @inline const POWERS10: u32[] = [ diff --git a/std/assembly/internal/sort.ts b/std/assembly/util/sort.ts similarity index 93% rename from std/assembly/internal/sort.ts rename to std/assembly/util/sort.ts index 587de405..db9e3048 100644 --- a/std/assembly/internal/sort.ts +++ b/std/assembly/util/sort.ts @@ -1,6 +1,4 @@ -/** Obtains the default comparator for the specified value type. */ -@inline -export function COMPARATOR(): (a: T, b: T) => i32 { +@inline export function COMPARATOR(): (a: T, b: T) => i32 { if (isInteger()) { if (isSigned() && sizeof() <= 4) { return (a: T, b: T): i32 => ((a - b)); @@ -40,8 +38,7 @@ export function COMPARATOR(): (a: T, b: T) => i32 { } } -@inline -export function SORT( +@inline export function SORT( dataStart: usize, length: i32, comparator: (a: T, b: T) => i32 @@ -58,7 +55,6 @@ export function SORT( } } -/** Sorts an Array with the 'Insertion Sort' algorithm. */ function insertionSort( dataStart: usize, length: i32, @@ -77,7 +73,6 @@ function insertionSort( } } -/** Sorts an Array with the 'Weak Heap Sort' algorithm. */ function weakHeapSort( dataStart: usize, length: i32, diff --git a/std/assembly/util/string.ts b/std/assembly/util/string.ts new file mode 100644 index 00000000..a3374134 --- /dev/null +++ b/std/assembly/util/string.ts @@ -0,0 +1,124 @@ +export function compareImpl(str1: string, index1: usize, str2: string, index2: usize, len: usize): i32 { + var result: i32 = 0; + var ptr1 = changetype(str1) + (index1 << 1); + var ptr2 = changetype(str2) + (index2 << 1); + while (len && !(result = load(ptr1) - load(ptr2))) { + --len, ptr1 += 2, ptr2 += 2; + } + return result; +} + +@inline export const enum CharCode { + PLUS = 0x2B, + MINUS = 0x2D, + DOT = 0x2E, + _0 = 0x30, + _1 = 0x31, + _2 = 0x32, + _3 = 0x33, + _4 = 0x34, + _5 = 0x35, + _6 = 0x36, + _7 = 0x37, + _8 = 0x38, + _9 = 0x39, + A = 0x41, + B = 0x42, + E = 0x45, + N = 0x4E, + O = 0x4F, + X = 0x58, + Z = 0x5a, + a = 0x61, + b = 0x62, + e = 0x65, + n = 0x6E, + o = 0x6F, + x = 0x78, + z = 0x7A +} + +export function isWhiteSpaceOrLineTerminator(c: u16): bool { + switch (c) { + case 9: // + case 10: // + case 13: // + case 11: // + case 12: // + case 32: // + case 160: // + case 8232: // + case 8233: // + case 65279: return true; // + default: return false; + } +} + +/** Parses a string to an integer (usually), using the specified radix. */ +export function parse(str: string, radix: i32 = 0): T { + var len: i32 = str.length; + if (!len) return NaN; + + var ptr = changetype(str) /* + HEAD -> offset */; + var code = load(ptr, HEADER_SIZE); + + // determine sign + var sign: T; + if (code == CharCode.MINUS) { + if (!--len) return NaN; + code = load(ptr += 2, HEADER_SIZE); + sign = -1; + } else if (code == CharCode.PLUS) { + if (!--len) return NaN; + code = load(ptr += 2, HEADER_SIZE); + sign = 1; + } else { + sign = 1; + } + + // determine radix + if (!radix) { + if (code == CharCode._0 && len > 2) { + switch (load(ptr + 2, HEADER_SIZE)) { + case CharCode.B: + case CharCode.b: { + ptr += 4; len -= 2; + radix = 2; + break; + } + case CharCode.O: + case CharCode.o: { + ptr += 4; len -= 2; + radix = 8; + break; + } + case CharCode.X: + case CharCode.x: { + ptr += 4; len -= 2; + radix = 16; + break; + } + default: radix = 10; + } + } else radix = 10; + } else if (radix < 2 || radix > 36) { + return NaN; + } + + // calculate value + var num: T = 0; + while (len--) { + code = load(ptr, HEADER_SIZE); + if (code >= CharCode._0 && code <= CharCode._9) { + code -= CharCode._0; + } else if (code >= CharCode.A && code <= CharCode.Z) { + code -= CharCode.A - 10; + } else if (code >= CharCode.a && code <= CharCode.z) { + code -= CharCode.a - 10; + } else break; + if (code >= radix) break; + num = (num * radix) + code; + ptr += 2; + } + return sign * num; +}