slowly but steadily

This commit is contained in:
dcode 2019-03-12 04:35:01 +01:00
parent 8e9586783f
commit 36d54d63d5
11 changed files with 268 additions and 641 deletions

View File

@ -1,34 +1,20 @@
// import {
// MAX_BLENGTH,
// HEADER_SIZE,
// allocateUnsafe,
// reallocateUnsafe,
// LOAD,
// STORE
// } from "./internal/arraybuffer";
import { import {
ALLOC, ALLOC,
REALLOC, REALLOC,
REGISTER, REGISTER,
LINK, LINK,
ArrayBufferView ArrayBufferView,
FREE
} from "./runtime"; } from "./runtime";
import { import {
ArrayBuffer ArrayBuffer
} from "./arraybuffer"; } from "./arraybuffer";
// import {
// allocateUnsafe as allocateUnsafeString,
// freeUnsafe as freeUnsafeString,
// copyUnsafe as copyUnsafeString
// } from "./internal/string";
import { import {
COMPARATOR, COMPARATOR,
SORT SORT
} from "./internal/sort"; } from "./util/sort";
import { import {
itoa, itoa,
@ -36,7 +22,7 @@ import {
itoa_stream, itoa_stream,
dtoa_stream, dtoa_stream,
MAX_DOUBLE_LENGTH MAX_DOUBLE_LENGTH
} from "./internal/number"; } from "./util/number";
import { import {
isArray as builtin_isArray isArray as builtin_isArray
@ -372,186 +358,233 @@ export class Array<T> extends ArrayBufferView {
} }
reverse(): Array<T> { reverse(): Array<T> {
var buffer = this.buffer_; var base = this.dataStart;
for (let front = 0, back = this.length_ - 1; front < back; ++front, --back) { for (let front = 0, back = this.length_ - 1; front < back; ++front, --back) {
let temp = LOAD<T>(buffer, front); let temp: T = load<T>(base, front);
STORE<T>(buffer, front, LOAD<T>(buffer, back)); store<T>(base + (<usize>front << alignof<T>()), load<T>(base + (<usize>back << alignof<T>())));
STORE<T>(buffer, back, temp); store<T>(base + (<usize>back << alignof<T>()), temp);
} }
return this; return this;
} }
sort(comparator: (a: T, b: T) => i32 = COMPARATOR<T>()): this { sort(comparator: (a: T, b: T) => i32 = COMPARATOR<T>()): 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 assert(comparator); // The comparison function must be a function
var length = this.length_; var length = this.length_;
if (length <= 1) return this; if (length <= 1) return this;
var buffer = this.buffer_; var base = this.dataStart;
if (length == 2) { if (length == 2) {
let a = LOAD<T>(buffer, 1); // a = arr[1] let a: T = load<T>(base, sizeof<T>()); // a = arr[1]
let b = LOAD<T>(buffer, 0); // b = arr[0] let b: T = load<T>(base); // b = arr[0]
if (comparator(a, b) < 0) { if (comparator(a, b) < 0) {
STORE<T>(buffer, 1, b); // arr[1] = b; store<T>(base, b, sizeof<T>()); // arr[1] = b;
STORE<T>(buffer, 0, a); // arr[0] = a; store<T>(base, a); // arr[0] = a;
} }
return this; return this;
} }
SORT<T>(buffer, 0, length, comparator); SORT<T>(base, length, comparator);
return this; return this;
} }
// FIXME: refactor into multiple functions?
join(separator: string = ","): string { join(separator: string = ","): string {
var lastIndex = this.length_ - 1; var lastIndex = this.length_ - 1;
if (lastIndex < 0) return ""; if (lastIndex < 0) return "";
var result = ""; var result = "";
var value: T; var value: T;
var buffer = this.buffer_; var base = this.dataStart;
// var buffer = this.buffer_;
var sepLen = separator.length; var sepLen = separator.length;
var hasSeparator = sepLen != 0; var hasSeparator = sepLen != 0;
if (value instanceof bool) { if (value instanceof bool) {
if (!lastIndex) return select<string>("true", "false", LOAD<T,bool>(buffer, 0)); if (!lastIndex) return select("true", "false", load<T>(base));
let valueLen = 5; // max possible length of element len("false") let valueLen = 5; // max possible length of element len("false")
let estLen = (valueLen + sepLen) * lastIndex + valueLen; let estLen = (valueLen + sepLen) * lastIndex + valueLen;
let result = allocateUnsafeString(estLen); let result = ALLOC(estLen << 1);
let offset = 0; let offset = 0;
for (let i = 0; i < lastIndex; ++i) { for (let i = 0; i < lastIndex; ++i) {
value = LOAD<T,bool>(buffer, i); value = load<T>(base + i);
valueLen = 4 + <i32>(!value); valueLen = 4 + <i32>(!value);
copyUnsafeString(result, offset, select<string>("true", "false", value), 0, valueLen); memory.copy(
result + (<usize>offset << 1),
changetype<usize>(select("true", "false", value)),
<usize>valueLen << 1
);
offset += valueLen; offset += valueLen;
if (hasSeparator) { if (hasSeparator) {
copyUnsafeString(result, offset, changetype<String>(separator), 0, sepLen); memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen; offset += sepLen;
} }
} }
value = LOAD<T,bool>(buffer, lastIndex); value = load<T>(base + <usize>lastIndex);
valueLen = 4 + <i32>(!value); valueLen = 4 + <i32>(!value);
copyUnsafeString(result, offset, select<string>("true", "false", value), 0, valueLen); memory.copy(
result + (<usize>offset << 1),
changetype<usize>(select("true", "false", value)),
valueLen << 1
);
offset += valueLen; offset += valueLen;
let out = result;
if (estLen > offset) { if (estLen > offset) {
out = result.substring(0, offset); let trimmed = changetype<string>(result).substring(0, offset);
freeUnsafeString(result); FREE(result);
return trimmed; // registered in .substring
} }
return out; return REGISTER<string>(result);
} else if (isInteger<T>()) { } else if (isInteger<T>()) {
if (!lastIndex) return changetype<string>(itoa<T>(LOAD<T>(buffer, 0))); if (!lastIndex) return changetype<string>(itoa<T>(load<T>(base)));
const valueLen = (sizeof<T>() <= 4 ? 10 : 20) + <i32>isSigned<T>(); const valueLen = (sizeof<T>() <= 4 ? 10 : 20) + <i32>isSigned<T>();
let estLen = (valueLen + sepLen) * lastIndex + valueLen; let estLen = (valueLen + sepLen) * lastIndex + valueLen;
let result = allocateUnsafeString(estLen); let result = ALLOC(estLen << 1);
let offset = 0; let offset = 0;
for (let i = 0; i < lastIndex; ++i) { for (let i = 0; i < lastIndex; ++i) {
value = LOAD<T>(buffer, i); value = load<T>(base + (<usize>i << alignof<T>()));
offset += itoa_stream<T>(changetype<usize>(result), offset, value); offset += itoa_stream<T>(result, offset, value);
if (hasSeparator) { if (hasSeparator) {
copyUnsafeString(result, offset, separator, 0, sepLen); memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen; offset += sepLen;
} }
} }
value = LOAD<T>(buffer, lastIndex); value = load<T>(base + (<usize>lastIndex << alignof<T>()));
offset += itoa_stream<T>(changetype<usize>(result), offset, value); offset += itoa_stream<T>(result, offset, value);
let out = result;
if (estLen > offset) { if (estLen > offset) {
out = result.substring(0, offset); let trimmed = changetype<string>(result).substring(0, offset);
freeUnsafeString(result); FREE(result);
return trimmed; // registered in .substring
} }
return out; return REGISTER<string>(result);
} else if (isFloat<T>()) { } else if (isFloat<T>()) {
if (!lastIndex) return changetype<string>(dtoa(LOAD<T,f64>(buffer, 0))); if (!lastIndex) return changetype<string>(dtoa(load<T>(base)));
const valueLen = MAX_DOUBLE_LENGTH; const valueLen = MAX_DOUBLE_LENGTH;
let estLen = (valueLen + sepLen) * lastIndex + valueLen; let estLen = (valueLen + sepLen) * lastIndex + valueLen;
let result = allocateUnsafeString(estLen); let result = ALLOC(estLen << 1);
let offset = 0; let offset = 0;
for (let i = 0; i < lastIndex; ++i) { for (let i = 0; i < lastIndex; ++i) {
value = LOAD<T,f64>(buffer, i); value = load<T>(base + (<usize>i << alignof<T>()));
offset += dtoa_stream(changetype<usize>(result), offset, value); offset += dtoa_stream(result, offset, value);
if (hasSeparator) { if (hasSeparator) {
copyUnsafeString(result, offset, separator, 0, sepLen); memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen; offset += sepLen;
} }
} }
value = LOAD<T,f64>(buffer, lastIndex); value = load<T>(base + (<usize>lastIndex << alignof<T>()));
offset += dtoa_stream(changetype<usize>(result), offset, value); offset += dtoa_stream(result, offset, value);
let out = result;
if (estLen > offset) { if (estLen > offset) {
out = result.substring(0, offset); let trimmed = changetype<string>(result).substring(0, offset);
freeUnsafeString(result); FREE(result);
return trimmed; // registered in .substring
} }
return out; return REGISTER<string>(result);
} else if (isString<T>()) { } else if (isString<T>()) {
if (!lastIndex) return LOAD<string>(buffer, 0); if (!lastIndex) return load<string>(base);
let estLen = 0; let estLen = 0;
for (let i = 0, len = lastIndex + 1; i < len; ++i) { for (let i = 0, len = lastIndex + 1; i < len; ++i) {
estLen += LOAD<string>(buffer, i).length; estLen += load<string>(base + (<usize>i << alignof<T>())).length;
} }
let offset = 0; let offset = 0;
let result = allocateUnsafeString(estLen + sepLen * lastIndex); let result = ALLOC((estLen + sepLen * lastIndex) << 1);
for (let i = 0; i < lastIndex; ++i) { for (let i = 0; i < lastIndex; ++i) {
value = LOAD<string>(buffer, i); value = load<string>(base + (<usize>i << alignof<T>()));
if (value) { if (value) {
let valueLen = value.length; // tslint:disable-line:no-unsafe-any let valueLen = changetype<string>(value).length;
copyUnsafeString(result, offset, value, 0, valueLen); // tslint:disable-line:no-unsafe-any memory.copy(
offset += valueLen; // tslint:disable-line:no-unsafe-any result + (<usize>offset << 1),
changetype<usize>(value),
<usize>valueLen << 1
);
offset += valueLen;
} }
if (hasSeparator) { if (hasSeparator) {
copyUnsafeString(result, offset, separator, 0, sepLen); memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen; offset += sepLen;
} }
} }
value = LOAD<string>(buffer, lastIndex); value = load<string>(base + (<usize>lastIndex << alignof<T>()));
if (value) { if (value) {
let valueLen = value.length; // tslint:disable-line:no-unsafe-any let valueLen = changetype<string>(value).length;
copyUnsafeString(result, offset, value, 0, valueLen); // tslint:disable-line:no-unsafe-any memory.copy(
result + (<usize>offset << 1),
changetype<usize>(value),
<usize>valueLen << 1
);
} }
return result; return REGISTER<string>(result);
} else if (isArray<T>()) { } else if (isArray<T>()) {
if (!lastIndex) { if (!lastIndex) {
value = LOAD<T>(buffer, 0); value = load<T>(base);
return value ? value.join(separator) : ""; // tslint:disable-line:no-unsafe-any return value ? value.join(separator) : "";
} }
for (let i = 0; i < lastIndex; ++i) { for (let i = 0; i < lastIndex; ++i) {
value = LOAD<T>(buffer, i); value = load<T>(base + (<usize>i << alignof<T>()));
if (value) result += value.join(separator); // tslint:disable-line:no-unsafe-any if (value) result += value.join(separator);
if (hasSeparator) result += separator; if (hasSeparator) result += separator;
} }
value = LOAD<T>(buffer, lastIndex); value = load<T>(base + (<usize>lastIndex << alignof<T>()));
if (value) result += value.join(separator); // tslint:disable-line:no-unsafe-any if (value) result += value.join(separator);
return result; return result; // registered by concatenation (FIXME: lots of garbage)
} else if (isReference<T>()) { // References } else if (isReference<T>()) { // References
if (!lastIndex) return "[object Object]"; if (!lastIndex) return "[object Object]";
const valueLen = 15; // max possible length of element len("[object Object]") const valueLen = 15; // max possible length of element len("[object Object]")
let estLen = (valueLen + sepLen) * lastIndex + valueLen; let estLen = (valueLen + sepLen) * lastIndex + valueLen;
let result = allocateUnsafeString(estLen); let result = ALLOC(estLen << 1);
let offset = 0; let offset = 0;
for (let i = 0; i < lastIndex; ++i) { for (let i = 0; i < lastIndex; ++i) {
value = LOAD<T>(buffer, i); value = load<T>(base + (<usize>i << alignof<T>()));
if (value) { if (value) {
copyUnsafeString(result, offset, changetype<String>("[object Object]"), 0, valueLen); memory.copy(
result + (<usize>offset << 1),
changetype<usize>("[object Object]"),
<usize>valueLen << 1
);
offset += valueLen; offset += valueLen;
} }
if (hasSeparator) { if (hasSeparator) {
copyUnsafeString(result, offset, changetype<String>(separator), 0, sepLen); memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen; offset += sepLen;
} }
} }
if (LOAD<T>(buffer, lastIndex)) { if (load<T>(base + (<usize>lastIndex << alignof<T>()))) {
copyUnsafeString(result, offset, changetype<String>("[object Object]"), 0, valueLen); memory.copy(
result + (<usize>offset << 1),
changetype<usize>("[object Object]"),
<usize>valueLen << 1
);
offset += valueLen; offset += valueLen;
} }
let out = result;
if (estLen > offset) { if (estLen > offset) {
out = result.substring(0, offset); let out = changetype<string>(result).substring(0, offset);
freeUnsafeString(result); FREE(result);
return out; // registered in .substring
} }
return out; return REGISTER<string>(result);
} else { } else {
assert(false); // Unsupported generic typename ERROR("unspported type");
assert(false);
} }
} }
@ -560,16 +593,16 @@ export class Array<T> extends ArrayBufferView {
return this.join(); return this.join();
} }
private __gc(): void { // private __gc(): void {
var buffer = this.buffer_; // var buffer = this.buffer_;
__gc_mark(changetype<usize>(buffer)); // tslint:disable-line // __gc_mark(changetype<usize>(buffer)); // tslint:disable-line
if (isManaged<T>()) { // if (isManaged<T>()) {
let offset: usize = 0; // let offset: usize = 0;
let end = <usize>this.length_ << alignof<usize>(); // let end = <usize>this.length_ << alignof<usize>();
while (offset < end) { // while (offset < end) {
__gc_mark(load<usize>(changetype<usize>(buffer) + offset, HEADER_SIZE)); // tslint:disable-line // __gc_mark(load<usize>(changetype<usize>(buffer) + offset, HEADER_SIZE)); // tslint:disable-line
offset += sizeof<usize>(); // offset += sizeof<usize>();
} // }
} // }
} // }
} }

View File

@ -1,84 +0,0 @@
import {
AL_MASK,
MAX_SIZE_32
} from "./allocator";
/** Size of an ArrayBuffer header. */
@inline export const HEADER_SIZE: usize = (offsetof<ArrayBuffer>() + AL_MASK) & ~AL_MASK;
/** Maximum byte length of an ArrayBuffer. */
@inline export const MAX_BLENGTH: i32 = <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 <usize>1 << <usize>(<u32>32 - clz<u32>(byteLength + HEADER_SIZE - 1));
}
// Low-level utility
function __gc(ref: usize): void {}
export function allocateUnsafe(byteLength: i32): ArrayBuffer {
assert(<u32>byteLength <= <u32>MAX_BLENGTH);
var buffer: usize;
if (isManaged<ArrayBuffer>()) {
buffer = __gc_allocate(computeSize(byteLength), __gc); // tslint:disable-line
} else {
buffer = memory.allocate(computeSize(byteLength));
}
store<i32>(buffer, byteLength, offsetof<ArrayBuffer>("byteLength"));
return changetype<ArrayBuffer>(buffer);
}
export function reallocateUnsafe(buffer: ArrayBuffer, newByteLength: i32): ArrayBuffer {
var oldByteLength = buffer.byteLength;
if (newByteLength > oldByteLength) {
assert(newByteLength <= MAX_BLENGTH);
if (newByteLength <= <i32>(computeSize(oldByteLength) - HEADER_SIZE)) { // fast path: zero out additional space
store<i32>(changetype<usize>(buffer), newByteLength, offsetof<ArrayBuffer>("byteLength"));
} else { // slow path: copy to new buffer
let newBuffer = allocateUnsafe(newByteLength);
memory.copy(
changetype<usize>(newBuffer) + HEADER_SIZE,
changetype<usize>(buffer) + HEADER_SIZE,
<usize>oldByteLength
);
if (!isManaged<ArrayBuffer>()) {
memory.free(changetype<usize>(buffer));
}
buffer = newBuffer;
}
memory.fill(
changetype<usize>(buffer) + HEADER_SIZE + <usize>oldByteLength,
0,
<usize>(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<i32>(changetype<usize>(buffer), newByteLength, offsetof<ArrayBuffer>("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` ^= `<i32>load<i8>(...)` that reads an i8 but returns an i32, or
// * `i64.load32_s` ^= `<i64>load<i32>(...)`) 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<T,TOut = T>(buffer: ArrayBuffer, index: i32, byteOffset: i32 = 0): TOut {
return <TOut>load<T>(changetype<usize>(buffer) + (<usize>index << alignof<T>()) + <usize>byteOffset, HEADER_SIZE);
}
@inline export function STORE<T,TIn = T>(buffer: ArrayBuffer, index: i32, value: TIn, byteOffset: i32 = 0): void {
store<T>(changetype<usize>(buffer) + (<usize>index << alignof<T>()) + <usize>byteOffset, value, HEADER_SIZE);
}

View File

@ -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<usize>(str1) + (index1 << 1);
var ptr2 = changetype<usize>(str2) + (index2 << 1);
while (len && !(result = <i32>load<u16>(ptr1) - <i32>load<u16>(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<String>() + 1) & ~1; // 2 byte aligned
/** Maximum length of a String. */
@inline export const MAX_LENGTH = (<i32>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<String>()) {
buffer = __gc_allocate(HEADER_SIZE + (<usize>length << 1), __gc); // tslint:disable-line
} else {
buffer = memory.allocate(HEADER_SIZE + (<usize>length << 1));
}
store<i32>(buffer, length);
return changetype<String>(buffer);
}
@inline
export function freeUnsafe(buffer: String): void {
if (!isManaged<String>()) {
assert(buffer);
memory.free(changetype<usize>(buffer));
}
}
export function copyUnsafe(dest: String, destOffset: usize, src: String, srcOffset: usize, len: usize): void {
memory.copy(
changetype<usize>(dest) + (destOffset << 1) + HEADER_SIZE,
changetype<usize>(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<usize>(dest) + HEADER_SIZE + (destOffset << 1);
let from = changetype<usize>(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<u16>(changetype<usize>(src), HEADER_SIZE);
let out = changetype<usize>(dest) + (destOffset << 1);
for (let i = 0; i < count; ++i) {
store<u16>(out + (i << 1), cc, HEADER_SIZE);
}
break;
}
case 2: {
let cc = load<u32>(changetype<usize>(src), HEADER_SIZE);
let out = changetype<usize>(dest) + (destOffset << 1);
for (let i = 0; i < count; ++i) {
store<u32>(out + (i << 2), cc, HEADER_SIZE);
}
break;
}
case 3: {
let cc1 = load<u32>(changetype<usize>(src), HEADER_SIZE + 0);
let cc2 = load<u16>(changetype<usize>(src), HEADER_SIZE + 4);
let out = changetype<usize>(dest) + (destOffset << 1);
for (let i = 0; i < count; ++i) {
store<u32>(out + (i << 2), cc1, HEADER_SIZE + 0);
store<u16>(out + (i << 1), cc2, HEADER_SIZE + 4);
}
break;
}
case 4: {
let cc = load<u64>(changetype<usize>(src), HEADER_SIZE);
let out = changetype<usize>(dest) + (destOffset << 1);
for (let i = 0; i < count; ++i) {
store<u64>(out + (i << 3), cc, HEADER_SIZE);
}
break;
}
default: {
let strLen = length << 1;
let to = changetype<usize>(dest) + HEADER_SIZE + (destOffset << 1);
let from = changetype<usize>(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: // <TAB>
case 10: // <LF>
case 13: // <CR>
case 11: // <VT>
case 12: // <FF>
case 32: // <SP>
case 160: // <NBSP>
case 8232: // <LS>
case 8233: // <PS>
case 65279: return true; // <ZWNBSP>
default: return false;
}
}
/** Parses a string to an integer (usually), using the specified radix. */
export function parse<T>(str: String, radix: i32 = 0): T {
var len: i32 = str.length;
if (!len) return <T>NaN;
var ptr = changetype<usize>(str) /* + HEAD -> offset */;
var code = <i32>load<u16>(ptr, HEADER_SIZE);
// determine sign
var sign: T;
if (code == CharCode.MINUS) {
if (!--len) return <T>NaN;
code = <i32>load<u16>(ptr += 2, HEADER_SIZE);
sign = -1;
} else if (code == CharCode.PLUS) {
if (!--len) return <T>NaN;
code = <i32>load<u16>(ptr += 2, HEADER_SIZE);
sign = 1;
} else {
sign = 1;
}
// determine radix
if (!radix) {
if (code == CharCode._0 && len > 2) {
switch (<i32>load<u16>(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 <T>NaN;
}
// calculate value
var num: T = 0;
while (len--) {
code = <i32>load<u16>(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;
}

View File

@ -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<T> {
[key: number]: T; // compatibility only
readonly buffer: ArrayBuffer;
readonly byteOffset: i32;
readonly byteLength: i32;
constructor(length: i32) {
const MAX_LENGTH = <u32>ArrayBuffer.MAX_BYTELENGTH / sizeof<T>();
if (<u32>length > MAX_LENGTH) throw new RangeError("Invalid typed array length");
var byteLength = length << alignof<T>();
this.buffer = new ArrayBuffer(byteLength);
this.byteOffset = 0;
this.byteLength = byteLength;
}
get length(): i32 {
return this.byteLength >>> alignof<T>();
}
// TODO: could compute load/store offset from index and emit an immediate -> make this a builtin?
@operator("[]") protected __get(index: i32): T {
if (<u32>index >= <u32>(this.byteLength >>> alignof<T>())) throw new Error("Index out of bounds");
return load<T>(changetype<usize>(this.buffer) + <usize>this.byteOffset, <usize>index << alignof<T>());
}
@inline @operator("{}") protected __unchecked_get(index: i32): T {
return load<T>(changetype<usize>(this.buffer) + <usize>this.byteOffset + (<usize>index << alignof<T>()));
}
@operator("[]=") protected __set(index: i32, value: native<T>): void {
if (<u32>index >= <u32>(this.byteLength >>> alignof<T>())) throw new Error("Index out of bounds");
store<T>(changetype<usize>(this.buffer) + <usize>this.byteOffset + (<usize>index << alignof<T>()), value);
}
@inline @operator("{}=") protected __unchecked_set(index: i32, value: native<T>): void {
store<T>(changetype<usize>(this.buffer) + <usize>this.byteOffset + (<usize>index << alignof<T>()), value);
}
// copyWithin(target: i32, start: i32, end: i32 = this.length): this
}
@inline export function FILL<TArray extends TypedArray<T>, T extends number>(
array: TArray,
value: native<T>,
start: i32,
end: i32
): TArray {
var base = changetype<usize>(array.buffer) + <usize>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<T>() == 1) {
if (start < end) memory.fill(base + <usize>start, <u8>value, <usize>(end - start));
} else {
for (; start < end; ++start) {
store<T>(base + (<usize>start << alignof()), value);
}
}
return array;
}
@inline export function SORT<TArray extends TypedArray<T>, T>(
array: TArray,
comparator: (a: T, b: T) => i32
): TArray {
var length = array.length;
var offset = <usize>array.byteOffset;
if (length <= 1) return array;
var buffer = changetype<usize>(array.buffer);
if (length == 2) {
let a = load<T>(buffer + offset, sizeof<T>());
let b = load<T>(buffer + offset);
if (comparator(a, b) < 0) {
store<T>(buffer + offset, b, sizeof<T>());
store<T>(buffer + offset, a);
}
return array;
}
// TODO
// SORT_IMPL<T>(buffer, byteOffset, length, comparator);
return array;
}
@inline export function SUBARRAY<TArray extends TypedArray<T>, T>(
array: TArray,
begin: i32,
end: i32
): TArray {
var buffer = changetype<usize>(array.buffer);
var length = <i32>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<TArray>());
store<usize>(out, buffer, offsetof<TArray>("buffer"));
store<usize>(out, buffer + (<usize>begin << alignof<T>()), offsetof<TArray>("dataStart"));
store<usize>(out, buffer + (<usize>end << alignof<T>()), offsetof<TArray>("dataStart"));
store<i32>(out, <i32>array.byteOffset + (begin << alignof<T>()), offsetof<TArray>("byteOffset"));
store<i32>(out, (end - begin) << alignof<T>(), offsetof<TArray>("byteLength"));
LINK(buffer, REGISTER<TArray>(out)); // register first, then link
return changetype<TArray>(out);
}
@inline export function REDUCE<TArray extends TypedArray<T>, T, TRet>(
array: TArray,
callbackfn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet,
initialValue: TRet
): TRet {
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
for (let i = 0, k = array.length; i < k; i++) {
initialValue = callbackfn(initialValue, load<T>(base + (<usize>i << alignof<T>())), i, array);
}
return initialValue;
}
@inline export function REDUCE_RIGHT<TArray extends TypedArray<T>, T, TRet>(
array: TArray,
callbackfn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet,
initialValue: TRet
): TRet {
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
for (let i = array.length - 1; i >= 0; i--) {
initialValue = callbackfn(initialValue, load<T>(base + (<usize>i << alignof<T>())), i, array);
}
return initialValue;
}
@inline export function MAP<TArray extends TypedArray<T>, T>(
array: TArray,
callbackfn: (value: T, index: i32, self: TArray) => T,
): TArray {
var length = array.length;
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
var result = instantiate<TArray>(length);
var resultBase = changetype<usize>(result.buffer); // assumes byteOffset = 0
for (let i = 0; i < length; i++) {
store<T>(
resultBase + (<usize>i << alignof<T>()),
callbackfn(load<T>(base + (<usize>i << alignof<T>())), i, array)
);
}
return result;
}
@inline export function FIND_INDEX<TArray extends TypedArray<T>, T>(
array: TArray,
callbackfn: (value: T, index: i32, array: TArray) => bool,
): i32 {
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
for (let i = 0, k = array.length; i < k; i++) {
if (callbackfn(load<T>(base + (<usize>i << alignof<T>())), i, array)) return i;
}
return -1;
}
@inline export function SOME<TArray extends TypedArray<T>, T>(
array: TArray,
callbackfn: (value: T, index: i32, array: TArray) => bool,
): bool {
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
for (let i = 0, k = array.length; i < k; i++) {
if (callbackfn(load<T>(base + (<usize>i << alignof<T>())), i, array)) return true;
}
return false;
}
@inline export function EVERY<TArray extends TypedArray<T>, T>(
array: TArray,
callbackfn: (value: T, index: i32, array: TArray) => bool,
): bool {
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
for (let i = 0, k = array.length; i < k; i++) {
if (callbackfn(load<T>(base + (<usize>i << alignof<T>())), i, array)) continue;
return false;
}
return true;
}
@inline export function FOREACH<TArray extends TypedArray<T>, T>(
array: TArray,
callbackfn: (value: T, index: i32, array: TArray) => void,
): void {
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
for (let i = 0, k = array.length; i < k; i++) {
callbackfn(load<T>(base + (<usize>i << alignof<T>())), i, array);
}
}

View File

@ -355,7 +355,8 @@ import {
// split by chars // split by chars
length = min<isize>(length, <isize>limit); length = min<isize>(length, <isize>limit);
let result = new Array<String>(length); let result = new Array<String>(length);
let buffer = <ArrayBuffer>result.buffer_; let buffer = unreachable(); // TODO
// let buffer = <ArrayBuffer>result.buffer_;
for (let i: isize = 0; i < length; ++i) { for (let i: isize = 0; i < length; ++i) {
let char = ALLOC(2); let char = ALLOC(2);
store<u16>( store<u16>(

View File

@ -1,11 +1,4 @@
import { @inline export function HASH<T>(key: T): u32 {
HEADER_SIZE
} from "./string";
/** Computes the 32-bit hash of a value of any type. */
@inline
export function HASH<T>(key: T): u32 {
// branch-level tree-shaking makes this a `(return (call ...))`
if (isString(key)) { if (isString(key)) {
return hashStr(key); return hashStr(key);
} else if (isReference<T>()) { } else if (isReference<T>()) {
@ -66,7 +59,7 @@ function hash64(key: u64): u32 {
function hashStr(key: string): u32 { function hashStr(key: string): u32 {
var v = FNV_OFFSET; var v = FNV_OFFSET;
for (let i: usize = 0, k: usize = key.length << 1; i < k; ++i) { for (let i: usize = 0, k: usize = key.length << 1; i < k; ++i) {
v = (v ^ <u32>load<u8>(changetype<usize>(key) + i, HEADER_SIZE)) * FNV_PRIME; v = (v ^ <u32>load<u8>(changetype<usize>(key) + i)) * FNV_PRIME;
} }
return v; return v;
} }

View File

@ -8,10 +8,6 @@ import {
FREE FREE
} from "../runtime"; } from "../runtime";
import {
LOAD
} from "./arraybuffer";
@inline export const MAX_DOUBLE_LENGTH = 28; @inline export const MAX_DOUBLE_LENGTH = 28;
@lazy @inline const POWERS10: u32[] = [ @lazy @inline const POWERS10: u32[] = [

View File

@ -1,6 +1,4 @@
/** Obtains the default comparator for the specified value type. */ @inline export function COMPARATOR<T>(): (a: T, b: T) => i32 {
@inline
export function COMPARATOR<T>(): (a: T, b: T) => i32 {
if (isInteger<T>()) { if (isInteger<T>()) {
if (isSigned<T>() && sizeof<T>() <= 4) { if (isSigned<T>() && sizeof<T>() <= 4) {
return (a: T, b: T): i32 => (<i32>(a - b)); return (a: T, b: T): i32 => (<i32>(a - b));
@ -40,8 +38,7 @@ export function COMPARATOR<T>(): (a: T, b: T) => i32 {
} }
} }
@inline @inline export function SORT<T>(
export function SORT<T>(
dataStart: usize, dataStart: usize,
length: i32, length: i32,
comparator: (a: T, b: T) => i32 comparator: (a: T, b: T) => i32
@ -58,7 +55,6 @@ export function SORT<T>(
} }
} }
/** Sorts an Array with the 'Insertion Sort' algorithm. */
function insertionSort<T>( function insertionSort<T>(
dataStart: usize, dataStart: usize,
length: i32, length: i32,
@ -77,7 +73,6 @@ function insertionSort<T>(
} }
} }
/** Sorts an Array with the 'Weak Heap Sort' algorithm. */
function weakHeapSort<T>( function weakHeapSort<T>(
dataStart: usize, dataStart: usize,
length: i32, length: i32,

124
std/assembly/util/string.ts Normal file
View File

@ -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<usize>(str1) + (index1 << 1);
var ptr2 = changetype<usize>(str2) + (index2 << 1);
while (len && !(result = <i32>load<u16>(ptr1) - <i32>load<u16>(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: // <TAB>
case 10: // <LF>
case 13: // <CR>
case 11: // <VT>
case 12: // <FF>
case 32: // <SP>
case 160: // <NBSP>
case 8232: // <LS>
case 8233: // <PS>
case 65279: return true; // <ZWNBSP>
default: return false;
}
}
/** Parses a string to an integer (usually), using the specified radix. */
export function parse<T>(str: string, radix: i32 = 0): T {
var len: i32 = str.length;
if (!len) return <T>NaN;
var ptr = changetype<usize>(str) /* + HEAD -> offset */;
var code = <i32>load<u16>(ptr, HEADER_SIZE);
// determine sign
var sign: T;
if (code == CharCode.MINUS) {
if (!--len) return <T>NaN;
code = <i32>load<u16>(ptr += 2, HEADER_SIZE);
sign = -1;
} else if (code == CharCode.PLUS) {
if (!--len) return <T>NaN;
code = <i32>load<u16>(ptr += 2, HEADER_SIZE);
sign = 1;
} else {
sign = 1;
}
// determine radix
if (!radix) {
if (code == CharCode._0 && len > 2) {
switch (<i32>load<u16>(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 <T>NaN;
}
// calculate value
var num: T = 0;
while (len--) {
code = <i32>load<u16>(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;
}