mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-11 14:01:26 +00:00
arraybufferview
This commit is contained in:
224
src/builtins.ts
224
src/builtins.ts
@ -21,7 +21,8 @@ import {
|
||||
LiteralKind,
|
||||
LiteralExpression,
|
||||
StringLiteralExpression,
|
||||
CallExpression
|
||||
CallExpression,
|
||||
ElementAccessExpression
|
||||
} from "./ast";
|
||||
|
||||
import {
|
||||
@ -48,7 +49,10 @@ import {
|
||||
getConstValueI64Low,
|
||||
getConstValueI32,
|
||||
getConstValueF32,
|
||||
getConstValueF64
|
||||
getConstValueF64,
|
||||
getBinaryOp,
|
||||
getBinaryLeft,
|
||||
getBinaryRight
|
||||
} from "./module";
|
||||
|
||||
import {
|
||||
@ -476,6 +480,19 @@ export namespace BuiltinSymbols {
|
||||
export const memory_fill = "~lib/runtime/memory.fill";
|
||||
export const gc_classId = "~lib/runtime/gc.classId";
|
||||
export const gc_iterateRoots = "~lib/runtime/gc.iterateRoots";
|
||||
|
||||
// std/typedarray.ts
|
||||
export const Int8Array = "~lib/typedarray/Int8Array";
|
||||
export const Uint8Array = "~lib/typedarray/Uint8Array";
|
||||
export const Int16Array = "~lib/typedarray/Int16Array";
|
||||
export const Uint16Array = "~lib/typedarray/Uint16Array";
|
||||
export const Int32Array = "~lib/typedarray/Int32Array";
|
||||
export const Uint32Array = "~lib/typedarray/Uint32Array";
|
||||
export const Int64Array = "~lib/typedarray/Int64Array";
|
||||
export const Uint64Array = "~lib/typedarray/Uint64Array";
|
||||
export const Uint8ClampedArray = "~lib/typedarray/Uint8ClampedArray";
|
||||
export const Float32Array = "~lib/typedarray/Float32Array";
|
||||
export const Float64Array = "~lib/typedarray/Float64Array";
|
||||
}
|
||||
|
||||
/** Compiles a call to a built-in function. */
|
||||
@ -4081,6 +4098,209 @@ export function compileIterateRoots(compiler: Compiler): void {
|
||||
);
|
||||
}
|
||||
|
||||
function typedArraySymbolToType(symbol: string): Type {
|
||||
switch (symbol) {
|
||||
default: assert(false);
|
||||
case BuiltinSymbols.Int8Array: return Type.i8;
|
||||
case BuiltinSymbols.Uint8ClampedArray:
|
||||
case BuiltinSymbols.Uint8Array: return Type.u8;
|
||||
case BuiltinSymbols.Int16Array: return Type.i16;
|
||||
case BuiltinSymbols.Uint16Array: return Type.u16;
|
||||
case BuiltinSymbols.Int32Array: return Type.i32;
|
||||
case BuiltinSymbols.Uint32Array: return Type.u32;
|
||||
case BuiltinSymbols.Int64Array: return Type.i64;
|
||||
case BuiltinSymbols.Uint64Array: return Type.u64;
|
||||
case BuiltinSymbols.Float32Array: return Type.f32;
|
||||
case BuiltinSymbols.Float64Array: return Type.f64;
|
||||
}
|
||||
}
|
||||
|
||||
export function compileTypedArrayGet(
|
||||
compiler: Compiler,
|
||||
target: Class,
|
||||
thisExpression: Expression,
|
||||
elementExpression: Expression,
|
||||
contextualType: Type
|
||||
): ExpressionRef {
|
||||
var type = typedArraySymbolToType(target.internalName);
|
||||
var module = compiler.module;
|
||||
var outType = (
|
||||
type.is(TypeFlags.INTEGER) &&
|
||||
contextualType.is(TypeFlags.INTEGER) &&
|
||||
contextualType.size > type.size
|
||||
) ? contextualType : type;
|
||||
|
||||
var bufferField = assert(target.lookupInSelf("buffer"));
|
||||
assert(bufferField.kind == ElementKind.FIELD);
|
||||
var dataStart = assert(target.lookupInSelf("dataStart"));
|
||||
assert(dataStart.kind == ElementKind.FIELD);
|
||||
var dataEnd = assert(target.lookupInSelf("dataEnd"));
|
||||
assert(dataEnd.kind == ElementKind.FIELD);
|
||||
|
||||
var constantOffset: i32 = 0;
|
||||
var dynamicOffset = module.precomputeExpression(compiler.compileExpression(
|
||||
elementExpression,
|
||||
Type.i32,
|
||||
ConversionKind.IMPLICIT,
|
||||
WrapMode.NONE
|
||||
));
|
||||
if (getExpressionId(dynamicOffset) == ExpressionId.Const) {
|
||||
constantOffset = getConstValueI32(dynamicOffset);
|
||||
dynamicOffset = 0;
|
||||
} else if (getExpressionId(dynamicOffset) == ExpressionId.Binary) {
|
||||
if (getBinaryOp(dynamicOffset) == BinaryOp.AddI32) {
|
||||
let left = getBinaryLeft(dynamicOffset);
|
||||
let right = getBinaryRight(dynamicOffset);
|
||||
if (getExpressionId(left) == ExpressionId.Const) {
|
||||
constantOffset = getConstValueI32(left);
|
||||
dynamicOffset = right;
|
||||
} else if (getExpressionId(right) == ExpressionId.Const) {
|
||||
constantOffset = getConstValueI32(right);
|
||||
dynamicOffset = left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var usizeType = compiler.options.usizeType;
|
||||
var nativeSizeType = compiler.options.nativeSizeType;
|
||||
var dataStartExpr = module.createLoad(usizeType.byteSize, true,
|
||||
compiler.compileExpression(
|
||||
thisExpression,
|
||||
target.type,
|
||||
ConversionKind.IMPLICIT,
|
||||
WrapMode.NONE
|
||||
),
|
||||
nativeSizeType, (<Field>dataStart).memoryOffset
|
||||
);
|
||||
if (dynamicOffset) {
|
||||
if (nativeSizeType == NativeType.I64) {
|
||||
dataStartExpr = module.createBinary(BinaryOp.AddI64,
|
||||
dataStartExpr,
|
||||
module.createUnary(UnaryOp.ExtendU32, dynamicOffset)
|
||||
);
|
||||
} else {
|
||||
dataStartExpr = module.createBinary(BinaryOp.AddI32,
|
||||
dataStartExpr,
|
||||
dynamicOffset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: check offset
|
||||
|
||||
compiler.currentType = outType;
|
||||
return module.createLoad(
|
||||
type.byteSize,
|
||||
type.is(TypeFlags.SIGNED),
|
||||
dataStartExpr,
|
||||
outType.toNativeType(),
|
||||
constantOffset
|
||||
);
|
||||
}
|
||||
|
||||
export function compileTypedArraySet(
|
||||
compiler: Compiler,
|
||||
target: Class,
|
||||
thisExpression: Expression,
|
||||
elementExpression: Expression,
|
||||
valueExpression: Expression,
|
||||
contextualType: Type
|
||||
): ExpressionRef {
|
||||
var type = typedArraySymbolToType(target.internalName);
|
||||
var module = compiler.module;
|
||||
|
||||
var bufferField = assert(target.lookupInSelf("buffer"));
|
||||
assert(bufferField.kind == ElementKind.FIELD);
|
||||
var dataStart = assert(target.lookupInSelf("dataStart"));
|
||||
assert(dataStart.kind == ElementKind.FIELD);
|
||||
var dataEnd = assert(target.lookupInSelf("dataEnd"));
|
||||
assert(dataEnd.kind == ElementKind.FIELD);
|
||||
|
||||
var constantOffset: i32 = 0;
|
||||
var dynamicOffset = module.precomputeExpression(compiler.compileExpression(
|
||||
elementExpression,
|
||||
Type.i32,
|
||||
ConversionKind.IMPLICIT,
|
||||
WrapMode.NONE
|
||||
));
|
||||
if (getExpressionId(dynamicOffset) == ExpressionId.Const) {
|
||||
constantOffset = getConstValueI32(dynamicOffset);
|
||||
dynamicOffset = 0;
|
||||
} else if (getExpressionId(dynamicOffset) == ExpressionId.Binary) {
|
||||
if (getBinaryOp(dynamicOffset) == BinaryOp.AddI32) {
|
||||
let left = getBinaryLeft(dynamicOffset);
|
||||
let right = getBinaryRight(dynamicOffset);
|
||||
if (getExpressionId(left) == ExpressionId.Const) {
|
||||
constantOffset = getConstValueI32(left);
|
||||
dynamicOffset = right;
|
||||
} else if (getExpressionId(right) == ExpressionId.Const) {
|
||||
constantOffset = getConstValueI32(right);
|
||||
dynamicOffset = left;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var usizeType = compiler.options.usizeType;
|
||||
var nativeSizeType = compiler.options.nativeSizeType;
|
||||
var dataStartExpr = module.createLoad(usizeType.byteSize, true,
|
||||
compiler.compileExpression(
|
||||
thisExpression,
|
||||
target.type,
|
||||
ConversionKind.IMPLICIT,
|
||||
WrapMode.NONE
|
||||
),
|
||||
nativeSizeType, (<Field>dataStart).memoryOffset
|
||||
);
|
||||
if (dynamicOffset) {
|
||||
if (nativeSizeType == NativeType.I64) {
|
||||
dataStartExpr = module.createBinary(BinaryOp.AddI64,
|
||||
dataStartExpr,
|
||||
module.createUnary(UnaryOp.ExtendU32, dynamicOffset)
|
||||
);
|
||||
} else {
|
||||
dataStartExpr = module.createBinary(BinaryOp.AddI32,
|
||||
dataStartExpr,
|
||||
dynamicOffset
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
var valueExpr = compiler.compileExpression(
|
||||
valueExpression,
|
||||
type.is(TypeFlags.SIGNED) ? Type.i32 : Type.u32,
|
||||
ConversionKind.IMPLICIT,
|
||||
WrapMode.NONE
|
||||
);
|
||||
var nativeType = type.toNativeType();
|
||||
|
||||
// TODO: check offset, clamp
|
||||
|
||||
if (contextualType == Type.void) {
|
||||
compiler.currentType = Type.void;
|
||||
return module.createStore(
|
||||
type.byteSize,
|
||||
dataStartExpr,
|
||||
valueExpr,
|
||||
nativeType,
|
||||
constantOffset
|
||||
);
|
||||
} else {
|
||||
let flow = compiler.currentFlow;
|
||||
let tempLocal = flow.getAndFreeTempLocal(type, false);
|
||||
compiler.currentType = type;
|
||||
return module.createBlock(null, [
|
||||
module.createStore(
|
||||
type.byteSize,
|
||||
dataStartExpr,
|
||||
module.createTeeLocal(tempLocal.index, valueExpr),
|
||||
nativeType,
|
||||
constantOffset
|
||||
),
|
||||
module.createGetLocal(tempLocal.index, nativeType)
|
||||
], nativeType);
|
||||
}
|
||||
}
|
||||
|
||||
/** Ensures that the specified class's GC hook exists and returns its function table index. */
|
||||
export function ensureGCHook(
|
||||
compiler: Compiler,
|
||||
|
@ -174,6 +174,7 @@ export namespace LibrarySymbols {
|
||||
export const V128 = "V128";
|
||||
export const String = "String";
|
||||
export const Array = "Array";
|
||||
export const ArrayBufferView = "ArrayBufferView";
|
||||
export const ArrayBuffer = "ArrayBuffer";
|
||||
export const Math = "Math";
|
||||
export const Mathf = "Mathf";
|
||||
|
@ -8,7 +8,9 @@ import {
|
||||
compileAbort,
|
||||
compileIterateRoots,
|
||||
ensureGCHook,
|
||||
BuiltinSymbols
|
||||
BuiltinSymbols,
|
||||
compileTypedArrayGet,
|
||||
compileTypedArraySet
|
||||
} from "./builtins";
|
||||
|
||||
import {
|
||||
@ -4683,7 +4685,21 @@ export class Compiler extends DiagnosticEmitter {
|
||||
break;
|
||||
}
|
||||
case ElementKind.CLASS: {
|
||||
if (resolver.currentElementExpression) { // indexed access
|
||||
let elementExpression = resolver.currentElementExpression;
|
||||
if (elementExpression) { // indexed access
|
||||
let arrayBufferView = this.program.arrayBufferView;
|
||||
if (arrayBufferView) {
|
||||
if ((<Class>target).prototype.extends(arrayBufferView.prototype)) {
|
||||
return compileTypedArraySet(
|
||||
this,
|
||||
<Class>target,
|
||||
assert(this.resolver.currentThisExpression),
|
||||
elementExpression,
|
||||
valueExpression,
|
||||
contextualType
|
||||
);
|
||||
}
|
||||
}
|
||||
let isUnchecked = flow.is(FlowFlags.UNCHECKED_CONTEXT);
|
||||
let indexedSet = (<Class>target).lookupOverload(OperatorKind.INDEXED_SET, isUnchecked);
|
||||
if (!indexedSet) {
|
||||
@ -5886,6 +5902,18 @@ export class Compiler extends DiagnosticEmitter {
|
||||
if (!target) return this.module.createUnreachable();
|
||||
switch (target.kind) {
|
||||
case ElementKind.CLASS: {
|
||||
let arrayBufferView = this.program.arrayBufferView;
|
||||
if (arrayBufferView) {
|
||||
if ((<Class>target).prototype.extends(arrayBufferView.prototype)) {
|
||||
return compileTypedArrayGet(
|
||||
this,
|
||||
<Class>target,
|
||||
expression.expression,
|
||||
expression.elementExpression,
|
||||
contextualType
|
||||
);
|
||||
}
|
||||
}
|
||||
let isUnchecked = this.currentFlow.is(FlowFlags.UNCHECKED_CONTEXT);
|
||||
let indexedGet = (<Class>target).lookupOverload(OperatorKind.INDEXED_GET, isUnchecked);
|
||||
if (!indexedGet) {
|
||||
|
@ -333,6 +333,8 @@ export class Program extends DiagnosticEmitter {
|
||||
|
||||
// runtime references
|
||||
|
||||
/** ArrayBufferView reference. */
|
||||
arrayBufferView: Class | null = null;
|
||||
/** ArrayBuffer instance reference. */
|
||||
arrayBufferInstance: Class | null = null;
|
||||
/** Array prototype reference. */
|
||||
@ -770,6 +772,10 @@ export class Program extends DiagnosticEmitter {
|
||||
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
|
||||
this.stringInstance = resolver.resolveClass(<ClassPrototype>element, null);
|
||||
}
|
||||
if (element = this.lookupGlobal(LibrarySymbols.ArrayBufferView)) {
|
||||
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
|
||||
this.arrayBufferView = resolver.resolveClass(<ClassPrototype>element, null);
|
||||
}
|
||||
if (element = this.lookupGlobal(LibrarySymbols.ArrayBuffer)) {
|
||||
assert(element.kind == ElementKind.CLASS_PROTOTYPE);
|
||||
this.arrayBufferInstance = resolver.resolveClass(<ClassPrototype>element, null);
|
||||
@ -1145,7 +1151,7 @@ export class Program extends DiagnosticEmitter {
|
||||
(declaration.is(CommonFlags.READONLY)
|
||||
? DecoratorFlags.INLINE
|
||||
: DecoratorFlags.NONE
|
||||
) | DecoratorFlags.LAZY
|
||||
) | DecoratorFlags.LAZY | DecoratorFlags.UNSAFE
|
||||
),
|
||||
declaration
|
||||
);
|
||||
@ -1156,7 +1162,7 @@ export class Program extends DiagnosticEmitter {
|
||||
name,
|
||||
parent,
|
||||
declaration,
|
||||
this.checkDecorators(decorators, DecoratorFlags.NONE)
|
||||
this.checkDecorators(decorators, DecoratorFlags.UNSAFE)
|
||||
);
|
||||
if (!parent.addInstance(name, element)) return;
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {
|
||||
HEADER_SIZE,
|
||||
MAX_BLENGTH,
|
||||
allocateUnsafe
|
||||
} from "./internal/arraybuffer";
|
||||
ALLOC_RAW,
|
||||
REGISTER,
|
||||
ArrayBufferBase
|
||||
} from "./runtime";
|
||||
|
||||
import {
|
||||
Uint8ClampedArray,
|
||||
@ -21,9 +21,7 @@ import {
|
||||
} from "./dataview";
|
||||
|
||||
@sealed
|
||||
export class ArrayBuffer {
|
||||
|
||||
readonly byteLength: i32; // capped to [0, MAX_LENGTH]
|
||||
export class ArrayBuffer extends ArrayBufferBase {
|
||||
|
||||
@inline static isView<T>(value: T): bool {
|
||||
if (value === null) return false;
|
||||
@ -40,28 +38,15 @@ export class ArrayBuffer {
|
||||
return false;
|
||||
}
|
||||
|
||||
// @unsafe
|
||||
@inline get data(): usize { return changetype<usize>(this) + HEADER_SIZE; }
|
||||
|
||||
constructor(length: i32, unsafe: bool = false) {
|
||||
if (<u32>length > <u32>MAX_BLENGTH) throw new RangeError("Invalid array buffer length");
|
||||
var buffer = allocateUnsafe(length);
|
||||
if (!unsafe) memory.fill(changetype<usize>(buffer) + HEADER_SIZE, 0, <usize>length);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
slice(begin: i32 = 0, end: i32 = MAX_BLENGTH): ArrayBuffer {
|
||||
slice(begin: i32 = 0, end: i32 = ArrayBuffer.MAX_BYTELENGTH): ArrayBuffer {
|
||||
var len = this.byteLength;
|
||||
begin = begin < 0 ? max(len + begin, 0) : min(begin, len);
|
||||
end = end < 0 ? max(len + end, 0) : min(end, len);
|
||||
len = max(end - begin, 0);
|
||||
var buffer = allocateUnsafe(len);
|
||||
memory.copy(
|
||||
changetype<usize>(buffer) + HEADER_SIZE,
|
||||
changetype<usize>(this) + HEADER_SIZE + begin,
|
||||
len
|
||||
);
|
||||
return buffer;
|
||||
var outSize = <usize>len;
|
||||
var out = ALLOC_RAW(outSize);
|
||||
memory.copy(out, changetype<usize>(this) + <usize>begin, outSize);
|
||||
return REGISTER<ArrayBuffer>(out);
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
|
@ -1,10 +1,12 @@
|
||||
import {
|
||||
HEADER_SIZE as AB_HEADER_SIZE,
|
||||
MAX_BLENGTH as AB_MAX_BLENGTH,
|
||||
allocateUnsafe,
|
||||
LOAD,
|
||||
STORE
|
||||
} from "./arraybuffer";
|
||||
ALLOC,
|
||||
REGISTER,
|
||||
LINK
|
||||
} from "../runtime";
|
||||
|
||||
import {
|
||||
ArrayBuffer
|
||||
} from "../arraybuffer";
|
||||
|
||||
import {
|
||||
SORT as SORT_IMPL
|
||||
@ -19,228 +21,184 @@ export abstract class TypedArray<T> {
|
||||
readonly byteLength: i32;
|
||||
|
||||
constructor(length: i32) {
|
||||
const MAX_LENGTH = <u32>AB_MAX_BLENGTH / sizeof<T>();
|
||||
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>();
|
||||
var buffer = allocateUnsafe(byteLength);
|
||||
memory.fill(changetype<usize>(buffer) + AB_HEADER_SIZE, 0, <usize>byteLength);
|
||||
this.buffer = buffer;
|
||||
this.buffer = new ArrayBuffer(byteLength);
|
||||
this.byteOffset = 0;
|
||||
this.byteLength = byteLength;
|
||||
}
|
||||
|
||||
@inline
|
||||
get length(): i32 {
|
||||
return this.byteLength >>> alignof<T>();
|
||||
}
|
||||
|
||||
@operator("[]")
|
||||
protected __get(index: i32): 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>(this.buffer, index, this.byteOffset);
|
||||
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>(this.buffer, index, this.byteOffset);
|
||||
@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 {
|
||||
@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,native<T>>(this.buffer, index, value, this.byteOffset);
|
||||
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,native<T>>(this.buffer, index, value, this.byteOffset);
|
||||
@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>(
|
||||
@inline export function FILL<TArray extends TypedArray<T>, T extends number>(
|
||||
array: TArray,
|
||||
value: native<T>,
|
||||
start: i32,
|
||||
end: i32
|
||||
): TArray {
|
||||
var buffer = array.buffer;
|
||||
var byteOffset = array.byteOffset;
|
||||
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(
|
||||
changetype<usize>(buffer) + start + byteOffset + AB_HEADER_SIZE,
|
||||
<u8>value,
|
||||
<usize>(end - start)
|
||||
);
|
||||
}
|
||||
if (start < end) memory.fill(base + <usize>start, <u8>value, <usize>(end - start));
|
||||
} else {
|
||||
for (; start < end; ++start) {
|
||||
STORE<T,native<T>>(buffer, start, value, byteOffset);
|
||||
store<T>(base + (<usize>start << alignof()), value);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@inline
|
||||
export function SORT<TArray extends TypedArray<T>, T>(
|
||||
@inline export function SORT<TArray extends TypedArray<T>, T>(
|
||||
array: TArray,
|
||||
comparator: (a: T, b: T) => i32
|
||||
): TArray {
|
||||
var byteOffset = array.byteOffset;
|
||||
var length = array.length;
|
||||
var offset = <usize>array.byteOffset;
|
||||
if (length <= 1) return array;
|
||||
var buffer = array.buffer;
|
||||
var buffer = changetype<usize>(array.buffer);
|
||||
if (length == 2) {
|
||||
let a = LOAD<T>(buffer, 1, byteOffset);
|
||||
let b = LOAD<T>(buffer, 0, byteOffset);
|
||||
let a = load<T>(buffer + offset, sizeof<T>());
|
||||
let b = load<T>(buffer + offset);
|
||||
if (comparator(a, b) < 0) {
|
||||
STORE<T>(buffer, 1, b, byteOffset);
|
||||
STORE<T>(buffer, 0, a, byteOffset);
|
||||
store<T>(buffer + offset, b, sizeof<T>());
|
||||
store<T>(buffer + offset, a);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
SORT_IMPL<T>(buffer, byteOffset, length, comparator);
|
||||
// TODO
|
||||
// SORT_IMPL<T>(buffer, byteOffset, length, comparator);
|
||||
return array;
|
||||
}
|
||||
|
||||
@inline
|
||||
export function SUBARRAY<TArray extends TypedArray<T>, T>(
|
||||
@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 slice = memory.allocate(offsetof<TArray>());
|
||||
store<usize>(slice, array.buffer, offsetof<TArray>("buffer"));
|
||||
store<i32>(slice, <i32>array.byteOffset + (begin << alignof<T>()), offsetof<TArray>("byteOffset"));
|
||||
store<i32>(slice, (end - begin) << alignof<T>(), offsetof<TArray>("byteLength"));
|
||||
return changetype<TArray>(slice);
|
||||
var out = ALLOC(offsetof<TArray>());
|
||||
store<usize>(out, buffer, offsetof<TArray>("buffer"));
|
||||
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,usize>(out)); // register first, then link
|
||||
return changetype<TArray>(out);
|
||||
}
|
||||
|
||||
@inline
|
||||
export function REDUCE<TArray extends TypedArray<T>, T, TRet>(
|
||||
@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 length = array.length;
|
||||
var buffer = array.buffer;
|
||||
var byteOffset = array.byteOffset;
|
||||
for (let i = 0; i < length; i++) {
|
||||
initialValue = callbackfn(
|
||||
initialValue,
|
||||
LOAD<T>(buffer, i, byteOffset),
|
||||
i,
|
||||
array,
|
||||
);
|
||||
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>(
|
||||
@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 buffer = array.buffer;
|
||||
var byteOffset = array.byteOffset;
|
||||
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
initialValue = callbackfn(
|
||||
initialValue,
|
||||
LOAD<T>(buffer, i, byteOffset),
|
||||
i,
|
||||
array,
|
||||
);
|
||||
initialValue = callbackfn(initialValue, load<T>(base + (<usize>i << alignof<T>())), i, array);
|
||||
}
|
||||
return initialValue;
|
||||
}
|
||||
|
||||
@inline
|
||||
export function MAP<TArray extends TypedArray<T>, T>(
|
||||
@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 buffer = array.buffer;
|
||||
var byteOffset = array.byteOffset;
|
||||
var base = changetype<usize>(array.buffer) + <usize>array.byteOffset;
|
||||
var result = instantiate<TArray>(length);
|
||||
var resultBuffer = result.buffer;
|
||||
var resultBase = changetype<usize>(result.buffer); // assumes byteOffset = 0
|
||||
for (let i = 0; i < length; i++) {
|
||||
STORE<T, native<T>>(resultBuffer, i, <native<T>>callbackfn(LOAD<T>(buffer, i, byteOffset), i, array));
|
||||
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>(
|
||||
@inline export function FIND_INDEX<TArray extends TypedArray<T>, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, array: TArray) => bool,
|
||||
): i32 {
|
||||
var length = array.length;
|
||||
var buffer = array.buffer;
|
||||
var byteOffset = array.byteOffset;
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (callbackfn(LOAD<T>(buffer, i, byteOffset), i, array)) {
|
||||
return i;
|
||||
}
|
||||
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>(
|
||||
@inline export function SOME<TArray extends TypedArray<T>, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, array: TArray) => bool,
|
||||
): bool {
|
||||
var length = array.length;
|
||||
var buffer = array.buffer;
|
||||
var byteOffset = array.byteOffset;
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (callbackfn(LOAD<T>(buffer, i, byteOffset), i, array)) {
|
||||
return true;
|
||||
}
|
||||
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>(
|
||||
@inline export function EVERY<TArray extends TypedArray<T>, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, array: TArray) => bool,
|
||||
): bool {
|
||||
var length = array.length;
|
||||
var buffer = array.buffer;
|
||||
var byteOffset = array.byteOffset;
|
||||
for (let i = 0; i < length; i++) {
|
||||
if (callbackfn(LOAD<T>(buffer, i, byteOffset), i, array)) {
|
||||
continue;
|
||||
}
|
||||
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>(
|
||||
@inline export function FOREACH<TArray extends TypedArray<T>, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, array: TArray) => void,
|
||||
): void {
|
||||
var length = array.length;
|
||||
var buffer = array.buffer;
|
||||
var byteOffset = array.byteOffset;
|
||||
for (let i = 0; i < length; i++) {
|
||||
callbackfn(LOAD<T>(buffer, i, byteOffset), i, array);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ export function ADJUST(payloadSize: usize): usize {
|
||||
return <usize>1 << <usize>(<u32>32 - clz<u32>(payloadSize + HEADER_SIZE - 1));
|
||||
}
|
||||
|
||||
/** Allocates a new object and returns a pointer to its payload. */
|
||||
@unsafe export function ALLOC(payloadSize: u32): usize {
|
||||
/** Allocates a new object and returns a pointer to its payload. Does not fill. */
|
||||
@unsafe export function ALLOC_RAW(payloadSize: u32): usize {
|
||||
var header = changetype<HEADER>(memory.allocate(ADJUST(payloadSize)));
|
||||
header.classId = HEADER_MAGIC;
|
||||
header.payloadSize = payloadSize;
|
||||
@ -52,7 +52,12 @@ export function ADJUST(payloadSize: usize): usize {
|
||||
header.gc1 = 0;
|
||||
header.gc2 = 0;
|
||||
}
|
||||
var ref = changetype<usize>(header) + HEADER_SIZE;
|
||||
return changetype<usize>(header) + HEADER_SIZE;
|
||||
}
|
||||
|
||||
/** Allocates a new object and returns a pointer to its payload. Fills with zeroes.*/
|
||||
@unsafe export function ALLOC(payloadSize: u32): usize {
|
||||
var ref = ALLOC_RAW(payloadSize);
|
||||
memory.fill(ref, 0, payloadSize);
|
||||
return ref;
|
||||
}
|
||||
@ -110,12 +115,12 @@ function unref(ref: usize): HEADER {
|
||||
}
|
||||
|
||||
/** Registers a managed object. Cannot be free'd anymore afterwards. */
|
||||
@unsafe @inline export function REGISTER<T>(ref: usize): T {
|
||||
@unsafe @inline export function REGISTER<T,TRet = T>(ref: usize): TRet {
|
||||
// see comment in REALLOC why this is useful. also inline this because
|
||||
// it's generic so we don't get a bunch of functions.
|
||||
unref(ref).classId = gc.classId<T>();
|
||||
if (GC) gc.register(ref);
|
||||
return changetype<T>(ref);
|
||||
return changetype<TRet>(ref);
|
||||
}
|
||||
|
||||
/** Links a managed object with its managed parent. */
|
||||
@ -132,6 +137,41 @@ export abstract class ArrayBufferBase {
|
||||
get byteLength(): i32 {
|
||||
return changetype<HEADER>(changetype<usize>(this) - HEADER_SIZE).payloadSize;
|
||||
}
|
||||
constructor(length: i32) {
|
||||
if (<u32>length > <u32>ArrayBufferBase.MAX_BYTELENGTH) throw new RangeError("Invalid array buffer length");
|
||||
return REGISTER<ArrayBuffer>(ALLOC(<usize>length));
|
||||
}
|
||||
}
|
||||
|
||||
/** Typed array base class. */
|
||||
export abstract class ArrayBufferView {
|
||||
[key: number]: number;
|
||||
readonly buffer: ArrayBuffer;
|
||||
@unsafe dataStart: usize;
|
||||
@unsafe dataEnd: usize;
|
||||
|
||||
constructor(length: i32, alignLog2: i32) {
|
||||
if (<u32>length > <u32>ArrayBufferBase.MAX_BYTELENGTH >> alignLog2) {
|
||||
throw new RangeError("Invalid typed array length");
|
||||
}
|
||||
var byteLength = length << alignLog2;
|
||||
var buffer = new ArrayBuffer(byteLength);
|
||||
this.buffer = buffer;
|
||||
this.dataStart = changetype<usize>(buffer);
|
||||
this.dataEnd = changetype<usize>(buffer) + length;
|
||||
}
|
||||
|
||||
get byteOffset(): i32 {
|
||||
return this.dataStart - changetype<usize>(this.buffer);
|
||||
}
|
||||
|
||||
get byteLength(): i32 {
|
||||
return this.dataEnd - this.dataStart;
|
||||
}
|
||||
|
||||
get length(): i32 {
|
||||
return <i32>unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
/** String base class. */
|
||||
|
@ -1,16 +1,9 @@
|
||||
import {
|
||||
TypedArray,
|
||||
FILL,
|
||||
SORT,
|
||||
SUBARRAY,
|
||||
REDUCE,
|
||||
REDUCE_RIGHT,
|
||||
MAP,
|
||||
FIND_INDEX,
|
||||
SOME,
|
||||
EVERY,
|
||||
FOREACH,
|
||||
} from "./internal/typedarray";
|
||||
ALLOC,
|
||||
REGISTER,
|
||||
LINK,
|
||||
ArrayBufferView
|
||||
} from "./runtime";
|
||||
|
||||
import {
|
||||
COMPARATOR
|
||||
@ -20,9 +13,15 @@ function clampToByte(value: i32): i32 {
|
||||
return ~(value >> 31) & (((255 - value) >> 31) | value); // & 255
|
||||
}
|
||||
|
||||
export class Int8Array extends TypedArray<i8> {
|
||||
export class Int8Array extends ArrayBufferView {
|
||||
@lazy static readonly BYTES_PER_ELEMENT: usize = sizeof<i8>();
|
||||
|
||||
constructor(length: i32) {
|
||||
super(length, 0);
|
||||
}
|
||||
|
||||
get length(): i32 { return this.byteLength; }
|
||||
|
||||
fill(value: i32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Int8Array {
|
||||
return FILL<Int8Array, i8>(this, value, start, end);
|
||||
}
|
||||
@ -70,9 +69,15 @@ export class Int8Array extends TypedArray<i8> {
|
||||
}
|
||||
}
|
||||
|
||||
export class Uint8Array extends TypedArray<u8> {
|
||||
export class Uint8Array extends ArrayBufferView {
|
||||
@lazy static readonly BYTES_PER_ELEMENT: usize = sizeof<u8>();
|
||||
|
||||
constructor(length: i32) {
|
||||
super(length, 0);
|
||||
}
|
||||
|
||||
get length(): i32 { return this.byteLength; }
|
||||
|
||||
fill(value: u32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Uint8Array {
|
||||
return FILL<Uint8Array, u8>(this, value, start, end);
|
||||
}
|
||||
@ -123,16 +128,6 @@ export class Uint8Array extends TypedArray<u8> {
|
||||
export class Uint8ClampedArray extends Uint8Array {
|
||||
@lazy static readonly BYTES_PER_ELEMENT: usize = sizeof<u8>();
|
||||
|
||||
@inline @operator("[]=")
|
||||
protected __set(index: i32, value: i32): void {
|
||||
super.__set(index, clampToByte(value));
|
||||
}
|
||||
|
||||
@inline @operator("{}=")
|
||||
protected __unchecked_set(index: i32, value: i32): void {
|
||||
super.__unchecked_set(index, clampToByte(value));
|
||||
}
|
||||
|
||||
fill(value: u32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Uint8ClampedArray {
|
||||
return changetype<Uint8ClampedArray>(super.fill(value, start, end)); // safe because '.fill' reuses 'this'
|
||||
}
|
||||
@ -180,9 +175,15 @@ export class Uint8ClampedArray extends Uint8Array {
|
||||
}
|
||||
}
|
||||
|
||||
export class Int16Array extends TypedArray<i16> {
|
||||
export class Int16Array extends ArrayBufferView {
|
||||
@lazy static readonly BYTES_PER_ELEMENT: usize = sizeof<i16>();
|
||||
|
||||
constructor(length: i32) {
|
||||
super(length, 1);
|
||||
}
|
||||
|
||||
get length(): i32 { return this.byteLength >>> 1; }
|
||||
|
||||
fill(value: i32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Int16Array {
|
||||
return FILL<Int16Array, i16>(this, value, start, end);
|
||||
}
|
||||
@ -230,9 +231,15 @@ export class Int16Array extends TypedArray<i16> {
|
||||
}
|
||||
}
|
||||
|
||||
export class Uint16Array extends TypedArray<u16> {
|
||||
export class Uint16Array extends ArrayBufferView {
|
||||
@lazy static readonly BYTES_PER_ELEMENT: usize = sizeof<u16>();
|
||||
|
||||
constructor(length: i32) {
|
||||
super(length, 1);
|
||||
}
|
||||
|
||||
get length(): i32 { return this.byteLength >>> 1; }
|
||||
|
||||
fill(value: u32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Uint16Array {
|
||||
return FILL<Uint16Array, u16>(this, value, start, end);
|
||||
}
|
||||
@ -280,9 +287,15 @@ export class Uint16Array extends TypedArray<u16> {
|
||||
}
|
||||
}
|
||||
|
||||
export class Int32Array extends TypedArray<i32> {
|
||||
export class Int32Array extends ArrayBufferView {
|
||||
@lazy static readonly BYTES_PER_ELEMENT: usize = sizeof<i32>();
|
||||
|
||||
constructor(length: i32) {
|
||||
super(length, 2);
|
||||
}
|
||||
|
||||
get length(): i32 { return this.byteLength >>> 2; }
|
||||
|
||||
fill(value: i32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Int32Array {
|
||||
return FILL<Int32Array, i32>(this, value, start, end);
|
||||
}
|
||||
@ -330,9 +343,15 @@ export class Int32Array extends TypedArray<i32> {
|
||||
}
|
||||
}
|
||||
|
||||
export class Uint32Array extends TypedArray<u32> {
|
||||
export class Uint32Array extends ArrayBufferView {
|
||||
@lazy static readonly BYTES_PER_ELEMENT: usize = sizeof<u32>();
|
||||
|
||||
constructor(length: i32) {
|
||||
super(length, 2);
|
||||
}
|
||||
|
||||
get length(): i32 { return this.byteLength >>> 2; }
|
||||
|
||||
fill(value: u32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Uint32Array {
|
||||
return FILL<Uint32Array, u32>(this, value, start, end);
|
||||
}
|
||||
@ -380,9 +399,15 @@ export class Uint32Array extends TypedArray<u32> {
|
||||
}
|
||||
}
|
||||
|
||||
export class Int64Array extends TypedArray<i64> {
|
||||
export class Int64Array extends ArrayBufferView {
|
||||
@lazy static readonly BYTES_PER_ELEMENT: usize = sizeof<i64>();
|
||||
|
||||
constructor(length: i32) {
|
||||
super(length, 3);
|
||||
}
|
||||
|
||||
get length(): i32 { return this.byteLength >>> 3; }
|
||||
|
||||
fill(value: i64, start: i32 = 0, end: i32 = i32.MAX_VALUE): Int64Array {
|
||||
return FILL<Int64Array, i64>(this, value, start, end);
|
||||
}
|
||||
@ -430,9 +455,15 @@ export class Int64Array extends TypedArray<i64> {
|
||||
}
|
||||
}
|
||||
|
||||
export class Uint64Array extends TypedArray<u64> {
|
||||
export class Uint64Array extends ArrayBufferView {
|
||||
@lazy static readonly BYTES_PER_ELEMENT: usize = sizeof<u64>();
|
||||
|
||||
constructor(length: i32) {
|
||||
super(length, 3);
|
||||
}
|
||||
|
||||
get length(): i32 { return this.byteLength >>> 3; }
|
||||
|
||||
fill(value: u64, start: i32 = 0, end: i32 = i32.MAX_VALUE): Uint64Array {
|
||||
return FILL<Uint64Array, u64>(this, value, start, end);
|
||||
}
|
||||
@ -480,9 +511,15 @@ export class Uint64Array extends TypedArray<u64> {
|
||||
}
|
||||
}
|
||||
|
||||
export class Float32Array extends TypedArray<f32> {
|
||||
export class Float32Array extends ArrayBufferView {
|
||||
@lazy static readonly BYTES_PER_ELEMENT: usize = sizeof<f32>();
|
||||
|
||||
constructor(length: i32) {
|
||||
super(length, 2);
|
||||
}
|
||||
|
||||
get length(): i32 { return this.byteLength >>> 2; }
|
||||
|
||||
fill(value: f32, start: i32 = 0, end: i32 = i32.MAX_VALUE): Float32Array {
|
||||
return FILL<Float32Array, f32>(this, value, start, end);
|
||||
}
|
||||
@ -530,9 +567,15 @@ export class Float32Array extends TypedArray<f32> {
|
||||
}
|
||||
}
|
||||
|
||||
export class Float64Array extends TypedArray<f64> {
|
||||
export class Float64Array extends ArrayBufferView {
|
||||
@lazy static readonly BYTES_PER_ELEMENT: usize = sizeof<f64>();
|
||||
|
||||
constructor(length: i32) {
|
||||
super(length, 3);
|
||||
}
|
||||
|
||||
get length(): i32 { return this.byteLength >>> 3; }
|
||||
|
||||
fill(value: f64, start: i32 = 0, end: i32 = i32.MAX_VALUE): Float64Array {
|
||||
return FILL<Float64Array, f64>(this, value, start, end);
|
||||
}
|
||||
@ -579,3 +622,148 @@ export class Float64Array extends TypedArray<f64> {
|
||||
FOREACH<Float64Array, f64>(this, callbackfn);
|
||||
}
|
||||
}
|
||||
|
||||
@inline function FILL<TArray extends ArrayBufferView, T extends number>(
|
||||
array: TArray,
|
||||
value: native<T>,
|
||||
start: i32,
|
||||
end: i32
|
||||
): TArray {
|
||||
var dataStart = array.dataStart;
|
||||
var length = array.length;
|
||||
start = start < 0 ? max(length + start, 0) : min(start, length);
|
||||
end = end < 0 ? max(length + end, 0) : min(end, length);
|
||||
if (sizeof<T>() == 1) {
|
||||
if (start < end) memory.fill(dataStart + <usize>start, <u8>value, <usize>(end - start));
|
||||
} else {
|
||||
for (; start < end; ++start) {
|
||||
store<T>(dataStart + (<usize>start << alignof<T>()), value);
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
@inline function SORT<TArray extends ArrayBufferView, T>(
|
||||
array: TArray,
|
||||
comparator: (a: T, b: T) => i32
|
||||
): TArray {
|
||||
var length = array.length;
|
||||
if (length <= 1) return array;
|
||||
var base = array.dataStart;
|
||||
if (length == 2) {
|
||||
let a = load<T>(base, sizeof<T>());
|
||||
let b = load<T>(base);
|
||||
if (comparator(a, b) < 0) {
|
||||
store<T>(base, b, sizeof<T>());
|
||||
store<T>(base, a);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
// TODO
|
||||
// SORT_IMPL<T>(buffer, byteOffset, length, comparator);
|
||||
return array;
|
||||
}
|
||||
|
||||
@inline function SUBARRAY<TArray extends ArrayBufferView, 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(offsetof<TArray>());
|
||||
store<usize>(out, buffer, offsetof<TArray>("buffer"));
|
||||
store<usize>(out, array.dataStart + (<usize>begin << alignof<T>()) , offsetof<TArray>("dataStart"));
|
||||
store<usize>(out, array.dataEnd + (<usize>(end - begin) << alignof<T>()), offsetof<TArray>("dataEnd"));
|
||||
LINK(buffer, REGISTER<TArray,usize>(out)); // register first, then link
|
||||
return changetype<TArray>(out);
|
||||
}
|
||||
|
||||
@inline function REDUCE<TArray extends ArrayBufferView, T, TRet>(
|
||||
array: TArray,
|
||||
callbackfn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet,
|
||||
initialValue: TRet
|
||||
): TRet {
|
||||
var dataStart = array.dataStart;
|
||||
for (let i = 0, k = array.length; i < k; i++) {
|
||||
initialValue = callbackfn(initialValue, load<T>(dataStart + (<usize>i << alignof<T>())), i, array);
|
||||
}
|
||||
return initialValue;
|
||||
}
|
||||
|
||||
@inline function REDUCE_RIGHT<TArray extends ArrayBufferView, T, TRet>(
|
||||
array: TArray,
|
||||
callbackfn: (accumulator: TRet, value: T, index: i32, array: TArray) => TRet,
|
||||
initialValue: TRet
|
||||
): TRet {
|
||||
var dataStart = array.dataStart;
|
||||
for (let i = array.length - 1; i >= 0; i--) {
|
||||
initialValue = callbackfn(initialValue, load<T>(dataStart + (<usize>i << alignof<T>())), i, array);
|
||||
}
|
||||
return initialValue;
|
||||
}
|
||||
|
||||
@inline function MAP<TArray extends ArrayBufferView, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, self: TArray) => T,
|
||||
): TArray {
|
||||
var length = array.length;
|
||||
var dataStart = array.dataStart;
|
||||
var out = instantiate<TArray>(length);
|
||||
var outDataStart = out.dataStart;
|
||||
for (let i = 0; i < length; i++) {
|
||||
store<T>(
|
||||
outDataStart + (<usize>i << alignof<T>()),
|
||||
callbackfn(load<T>(dataStart + (<usize>i << alignof<T>())), i, array)
|
||||
);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
@inline function FIND_INDEX<TArray extends ArrayBufferView, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, array: TArray) => bool,
|
||||
): i32 {
|
||||
var dataStart = array.dataStart;
|
||||
for (let i = 0, k = array.length; i < k; i++) {
|
||||
if (callbackfn(load<T>(dataStart + (<usize>i << alignof<T>())), i, array)) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@inline function SOME<TArray extends ArrayBufferView, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, array: TArray) => bool,
|
||||
): bool {
|
||||
var dataStart = array.dataStart;
|
||||
for (let i = 0, k = array.length; i < k; i++) {
|
||||
if (callbackfn(load<T>(dataStart + (<usize>i << alignof<T>())), i, array)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@inline function EVERY<TArray extends ArrayBufferView, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, array: TArray) => bool,
|
||||
): bool {
|
||||
var dataStart = array.dataStart;
|
||||
for (let i = 0, k = array.length; i < k; i++) {
|
||||
if (callbackfn(load<T>(dataStart + (<usize>i << alignof<T>())), i, array)) continue;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@inline function FOREACH<TArray extends ArrayBufferView, T>(
|
||||
array: TArray,
|
||||
callbackfn: (value: T, index: i32, array: TArray) => void,
|
||||
): void {
|
||||
var dataStart = array.dataStart;
|
||||
for (let i = 0, k = array.length; i < k; i++) {
|
||||
callbackfn(load<T>(dataStart + (<usize>i << alignof<T>())), i, array);
|
||||
}
|
||||
}
|
||||
|
@ -1173,7 +1173,34 @@
|
||||
local.get $0
|
||||
call $~lib/allocator/tlsf/Root#use
|
||||
)
|
||||
(func $~lib/internal/memory/memset (; 17 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
|
||||
(func $~lib/runtime/ALLOC_RAW (; 17 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
i32.const 1
|
||||
i32.const 32
|
||||
local.get $0
|
||||
i32.const 15
|
||||
i32.add
|
||||
i32.clz
|
||||
i32.sub
|
||||
i32.shl
|
||||
call $~lib/allocator/tlsf/memory.allocate
|
||||
local.tee $1
|
||||
i32.const -1520547049
|
||||
i32.store
|
||||
local.get $1
|
||||
local.get $0
|
||||
i32.store offset=4
|
||||
local.get $1
|
||||
i32.const 0
|
||||
i32.store offset=8
|
||||
local.get $1
|
||||
i32.const 0
|
||||
i32.store offset=12
|
||||
local.get $1
|
||||
i32.const 16
|
||||
i32.add
|
||||
)
|
||||
(func $~lib/internal/memory/memset (; 18 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
|
||||
(local $2 i32)
|
||||
local.get $1
|
||||
i32.eqz
|
||||
@ -1392,38 +1419,16 @@
|
||||
end
|
||||
end
|
||||
)
|
||||
(func $~lib/runtime/ALLOC (; 18 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(func $~lib/runtime/ALLOC (; 19 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
i32.const 1
|
||||
i32.const 32
|
||||
local.get $0
|
||||
i32.const 15
|
||||
i32.add
|
||||
i32.clz
|
||||
i32.sub
|
||||
i32.shl
|
||||
call $~lib/allocator/tlsf/memory.allocate
|
||||
local.tee $1
|
||||
i32.const -1520547049
|
||||
i32.store
|
||||
local.get $1
|
||||
local.get $0
|
||||
i32.store offset=4
|
||||
local.get $1
|
||||
i32.const 0
|
||||
i32.store offset=8
|
||||
local.get $1
|
||||
i32.const 0
|
||||
i32.store offset=12
|
||||
local.get $1
|
||||
i32.const 16
|
||||
i32.add
|
||||
call $~lib/runtime/ALLOC_RAW
|
||||
local.tee $1
|
||||
local.get $0
|
||||
call $~lib/internal/memory/memset
|
||||
local.get $1
|
||||
)
|
||||
(func $~lib/internal/memory/memcpy (; 19 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
|
||||
(func $~lib/internal/memory/memcpy (; 20 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
(local $5 i32)
|
||||
@ -2320,7 +2325,7 @@
|
||||
i32.store8
|
||||
end
|
||||
)
|
||||
(func $~lib/internal/memory/memmove (; 20 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
|
||||
(func $~lib/internal/memory/memmove (; 21 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
local.get $0
|
||||
@ -2518,7 +2523,7 @@
|
||||
end
|
||||
end
|
||||
)
|
||||
(func $~lib/allocator/tlsf/memory.free (; 21 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
(func $~lib/allocator/tlsf/memory.free (; 22 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
(local $1 i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
@ -2556,7 +2561,7 @@
|
||||
end
|
||||
end
|
||||
)
|
||||
(func $~lib/runtime/REALLOC (; 22 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(func $~lib/runtime/REALLOC (; 23 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
@ -2630,7 +2635,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 184
|
||||
i32.const 83
|
||||
i32.const 88
|
||||
i32.const 8
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2657,14 +2662,14 @@
|
||||
i32.store offset=4
|
||||
local.get $0
|
||||
)
|
||||
(func $~lib/runtime/unref (; 23 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(func $~lib/runtime/unref (; 24 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
local.get $0
|
||||
i32.const 232
|
||||
i32.lt_u
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 184
|
||||
i32.const 101
|
||||
i32.const 106
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -2679,18 +2684,18 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 184
|
||||
i32.const 103
|
||||
i32.const 108
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
local.get $0
|
||||
)
|
||||
(func $std/runtime/gc.register (; 24 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
(func $std/runtime/gc.register (; 25 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
local.get $0
|
||||
global.set $std/runtime/register_ref
|
||||
)
|
||||
(func $start:std/runtime (; 25 ;) (type $FUNCSIG$v)
|
||||
(func $start:std/runtime (; 26 ;) (type $FUNCSIG$v)
|
||||
(local $0 i32)
|
||||
(local $1 i32)
|
||||
(local $2 i32)
|
||||
@ -3015,13 +3020,13 @@
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $std/runtime/gc.link (; 26 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
|
||||
(func $std/runtime/gc.link (; 27 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
|
||||
nop
|
||||
)
|
||||
(func $~lib/runtime/gc.collect (; 27 ;) (type $FUNCSIG$v)
|
||||
(func $~lib/runtime/gc.collect (; 28 ;) (type $FUNCSIG$v)
|
||||
nop
|
||||
)
|
||||
(func $start (; 28 ;) (type $FUNCSIG$v)
|
||||
(func $start (; 29 ;) (type $FUNCSIG$v)
|
||||
call $start:std/runtime
|
||||
)
|
||||
)
|
||||
|
@ -1461,7 +1461,29 @@
|
||||
local.get $0
|
||||
call $~lib/allocator/tlsf/Root#use
|
||||
)
|
||||
(func $~lib/internal/memory/memset (; 23 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
|
||||
(func $~lib/runtime/ALLOC_RAW (; 23 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
local.get $0
|
||||
call $~lib/runtime/ADJUST
|
||||
call $~lib/allocator/tlsf/memory.allocate
|
||||
local.set $1
|
||||
local.get $1
|
||||
i32.const -1520547049
|
||||
i32.store
|
||||
local.get $1
|
||||
local.get $0
|
||||
i32.store offset=4
|
||||
local.get $1
|
||||
i32.const 0
|
||||
i32.store offset=8
|
||||
local.get $1
|
||||
i32.const 0
|
||||
i32.store offset=12
|
||||
local.get $1
|
||||
i32.const 16
|
||||
i32.add
|
||||
)
|
||||
(func $~lib/internal/memory/memset (; 24 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
(local $5 i64)
|
||||
@ -1715,47 +1737,29 @@
|
||||
end
|
||||
end
|
||||
)
|
||||
(func $~lib/runtime/ALLOC (; 24 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(func $~lib/runtime/ALLOC (; 25 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
(local $5 i32)
|
||||
local.get $0
|
||||
call $~lib/runtime/ADJUST
|
||||
call $~lib/allocator/tlsf/memory.allocate
|
||||
call $~lib/runtime/ALLOC_RAW
|
||||
local.set $1
|
||||
local.get $1
|
||||
i32.const -1520547049
|
||||
i32.store
|
||||
local.get $1
|
||||
local.get $0
|
||||
i32.store offset=4
|
||||
local.get $1
|
||||
i32.const 0
|
||||
i32.store offset=8
|
||||
local.get $1
|
||||
i32.const 0
|
||||
i32.store offset=12
|
||||
local.get $1
|
||||
i32.const 16
|
||||
i32.add
|
||||
local.set $2
|
||||
block $~lib/runtime/memory.fill|inlined.0
|
||||
local.get $2
|
||||
local.set $3
|
||||
local.get $1
|
||||
local.set $2
|
||||
i32.const 0
|
||||
local.set $4
|
||||
local.set $3
|
||||
local.get $0
|
||||
local.set $5
|
||||
local.set $4
|
||||
local.get $2
|
||||
local.get $3
|
||||
local.get $4
|
||||
local.get $5
|
||||
call $~lib/internal/memory/memset
|
||||
end
|
||||
local.get $2
|
||||
local.get $1
|
||||
)
|
||||
(func $~lib/internal/memory/memcpy (; 25 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
|
||||
(func $~lib/internal/memory/memcpy (; 26 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
(local $5 i32)
|
||||
@ -2956,7 +2960,7 @@
|
||||
i32.store8
|
||||
end
|
||||
)
|
||||
(func $~lib/internal/memory/memmove (; 26 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
|
||||
(func $~lib/internal/memory/memmove (; 27 ;) (type $FUNCSIG$viii) (param $0 i32) (param $1 i32) (param $2 i32)
|
||||
(local $3 i32)
|
||||
local.get $0
|
||||
local.get $1
|
||||
@ -3183,7 +3187,7 @@
|
||||
end
|
||||
end
|
||||
)
|
||||
(func $~lib/allocator/tlsf/memory.free (; 27 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
(func $~lib/allocator/tlsf/memory.free (; 28 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
(local $1 i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
@ -3226,7 +3230,7 @@
|
||||
end
|
||||
end
|
||||
)
|
||||
(func $~lib/runtime/REALLOC (; 28 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(func $~lib/runtime/REALLOC (; 29 ;) (type $FUNCSIG$iii) (param $0 i32) (param $1 i32) (result i32)
|
||||
(local $2 i32)
|
||||
(local $3 i32)
|
||||
(local $4 i32)
|
||||
@ -3315,7 +3319,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 184
|
||||
i32.const 83
|
||||
i32.const 88
|
||||
i32.const 8
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3351,7 +3355,7 @@
|
||||
i32.store offset=4
|
||||
local.get $0
|
||||
)
|
||||
(func $~lib/runtime/unref (; 29 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(func $~lib/runtime/unref (; 30 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(local $1 i32)
|
||||
local.get $0
|
||||
global.get $~lib/runtime/HEAP_BASE
|
||||
@ -3362,7 +3366,7 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 184
|
||||
i32.const 101
|
||||
i32.const 106
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
@ -3379,29 +3383,29 @@
|
||||
if
|
||||
i32.const 0
|
||||
i32.const 184
|
||||
i32.const 103
|
||||
i32.const 108
|
||||
i32.const 2
|
||||
call $~lib/env/abort
|
||||
unreachable
|
||||
end
|
||||
local.get $1
|
||||
)
|
||||
(func $~lib/runtime/FREE (; 30 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
(func $~lib/runtime/FREE (; 31 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
local.get $0
|
||||
call $~lib/runtime/unref
|
||||
call $~lib/allocator/tlsf/memory.free
|
||||
)
|
||||
(func $std/runtime/gc.register (; 31 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
(func $std/runtime/gc.register (; 32 ;) (type $FUNCSIG$vi) (param $0 i32)
|
||||
local.get $0
|
||||
global.set $std/runtime/register_ref
|
||||
)
|
||||
(func $~lib/runtime/ArrayBufferBase#get:byteLength (; 32 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(func $~lib/runtime/ArrayBufferBase#get:byteLength (; 33 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
local.get $0
|
||||
i32.const 16
|
||||
i32.sub
|
||||
i32.load offset=4
|
||||
)
|
||||
(func $~lib/runtime/StringBase#get:length (; 33 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
(func $~lib/runtime/StringBase#get:length (; 34 ;) (type $FUNCSIG$ii) (param $0 i32) (result i32)
|
||||
local.get $0
|
||||
i32.const 16
|
||||
i32.sub
|
||||
@ -3409,7 +3413,7 @@
|
||||
i32.const 1
|
||||
i32.shr_u
|
||||
)
|
||||
(func $start:std/runtime (; 34 ;) (type $FUNCSIG$v)
|
||||
(func $start:std/runtime (; 35 ;) (type $FUNCSIG$v)
|
||||
(local $0 i32)
|
||||
call $start:~lib/allocator/tlsf
|
||||
i32.const 2
|
||||
@ -3654,7 +3658,7 @@
|
||||
global.get $std/runtime/barrier1
|
||||
call $~lib/runtime/ALLOC
|
||||
global.set $std/runtime/ref4
|
||||
block $~lib/runtime/REGISTER<A>|inlined.0 (result i32)
|
||||
block $~lib/runtime/REGISTER<A,A>|inlined.0 (result i32)
|
||||
global.get $std/runtime/ref4
|
||||
local.set $0
|
||||
local.get $0
|
||||
@ -3738,15 +3742,15 @@
|
||||
unreachable
|
||||
end
|
||||
)
|
||||
(func $std/runtime/gc.link (; 35 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
|
||||
(func $std/runtime/gc.link (; 36 ;) (type $FUNCSIG$vii) (param $0 i32) (param $1 i32)
|
||||
nop
|
||||
)
|
||||
(func $~lib/runtime/gc.collect (; 36 ;) (type $FUNCSIG$v)
|
||||
(func $~lib/runtime/gc.collect (; 37 ;) (type $FUNCSIG$v)
|
||||
nop
|
||||
)
|
||||
(func $start (; 37 ;) (type $FUNCSIG$v)
|
||||
(func $start (; 38 ;) (type $FUNCSIG$v)
|
||||
call $start:std/runtime
|
||||
)
|
||||
(func $null (; 38 ;) (type $FUNCSIG$v)
|
||||
(func $null (; 39 ;) (type $FUNCSIG$v)
|
||||
)
|
||||
)
|
||||
|
Reference in New Issue
Block a user