mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-04-27 16:02:16 +00:00
slowly but steadily
This commit is contained in:
parent
8e9586783f
commit
36d54d63d5
@ -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>();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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>(
|
||||||
|
@ -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;
|
||||||
}
|
}
|
@ -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[] = [
|
@ -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
124
std/assembly/util/string.ts
Normal 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;
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user