Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

TypedArray#set #516

Closed
wants to merge 14 commits into from
2 changes: 2 additions & 0 deletions std/assembly/index.d.ts
Expand Up @@ -609,6 +609,8 @@ declare abstract class TypedArray<T> implements ArrayBufferView<T> {
findIndex(callbackfn: (value: T, index: i32, self: this) => bool): i32;
/** The every() method tests whether all elements in the typed array pass the test implemented by the provided function. This method has the same algorithm as Array.prototype.every(). */
every(callbackfn: (value: T, index: i32, self: this) => bool): i32;
/** The set() method stores multiple values in the typed array, reading input values from a specified array. */
set<SourceT, SourceU>(value: SourceT, offset?: i32): void;
}

/** An array of twos-complement 8-bit signed integers. */
Expand Down
132 changes: 132 additions & 0 deletions std/assembly/internal/typedarray.ts
Expand Up @@ -231,3 +231,135 @@ export function EVERY<TArray extends TypedArray<T>, T>(
}
return true;
}

@inline
export function SET<T extends TypedArray<U>, U extends number, SourceT, SourceU extends number>(
target: T,
source: SourceT,
offset: i32): void {
// assert target and source are not null
if (isReference<T>(target)) {
assert(target != null, "TypeError: Target is null.");
} else {
assert(false, "TypeError: T is not a reference.");
jtenner marked this conversation as resolved.
Show resolved Hide resolved
}

if (isReference<SourceT>(source)) {
assert(source != null, "TypeError: Source is null.");
} else {
assert(false, "TypeError: SourceT is not a reference.");
jtenner marked this conversation as resolved.
Show resolved Hide resolved
}

if (isArray<SourceT>(source)) {
// check to see if the offsets are in range
let sourceLength = source.length;
let targetLength = target.length;
assert((sourceLength + offset) <= targetLength, "RangeError: Offset is too large.");

// cache the buffer and the byteOffset
let targetBuffer = target.buffer;
let targetByteOffset = target.byteOffset;

// for each source value, write it to the ArrayBuffer
for (let i = 0; i < sourceLength; i++) {
STORE<U>(
targetBuffer,
i + offset,
<U>unchecked(source[i]),
jtenner marked this conversation as resolved.
Show resolved Hide resolved
targetByteOffset,
);
}
// fast path: source has the same backing type as targe
} else if (ArrayBuffer.isView<SourceT>(source)) {
// validate the lengths are within range
let sourceLength = source.length;
let targetLength = target.length;
assert((sourceLength + offset) <= targetLength, "RangeError: Offset is too large.");

if (isFloat<U>()) {
if (isFloat<SourceU>()) {
if (sizeof<U>() == sizeof<SourceU>()) {
SET_SAME<T, U>(target, <T>source, offset);
} else {
SET_DIFFERENT<T, U, SourceT, SourceU>(target, source, sourceLength, offset);
}
} else {
SET_DIFFERENT<T, U, SourceT, SourceU>(target, source, sourceLength, offset);
}
} else if (isInteger<U>()) {
if (isInteger<SourceU>()) {
if (sizeof<U>() == sizeof<SourceU>()) {
SET_SAME<T, U>(target, <T>source, offset);
} else {
SET_DIFFERENT<T, U, SourceT, SourceU>(target, source, sourceLength, offset);
}
} else {
SET_DIFFERENT<T, U, SourceT, SourceU>(target, source, sourceLength, offset);
}
}

} else {
// validate the lengths are within range
// @ts-ignore: source is assumed to have a length property
let sourceLength = source.length;
let targetLength = target.length;
assert((sourceLength + offset) <= targetLength, "RangeError: Offset is too large.");

// cache the buffer and the offset
let targetBuffer = target.buffer;
let targetByteOffset = target.byteOffset;

/**
* In order for the source to be ArrayLike, it has to have a length property, and a
* `@operator("[]=")` getter. This is very slow because it doesn't allow for unchecked gets,
* but it is as standard compliant as we can make it.
*/
// @ts-ignore: Source is expected to have a length property
for (let i = source.length - 1; i >= 0; i--) {
STORE<U>(
targetBuffer,
i + offset,
// @ts-ignore: Source is expected to have a getter signature
<U>source[i], // if the object does not have a getter this throws a compiler error
targetByteOffset,
);
}
}
}


@inline
function SET_SAME<T extends TypedArray<U>, U extends number>(target: T, source: T, offset: i32): void {
// perform a memory.copy
memory.copy(
// store the data at the target pointer + byteOFfset + offset << alignOf<U>()
target.buffer.data + target.byteOffset + (offset << alignof<U>()),
// read the data from source pointer + byteOffset
// @ts-ignore: source has a buffer and a byteOffset property because it's instanceof T
source.buffer.data + source.byteOffset,
// @ts-ignore: store source.buffer.byteLength number of bytes
source.buffer.byteLength,
);
}

@inline
function SET_DIFFERENT<
T extends TypedArray<U>,
U extends number,
SourceT extends TypedArray<SourceU>,
SourceU extends number
>(target: T, source: SourceT, sourceLength: i32, offset: i32): void {
let sourceBuffer = source.buffer;
let targetBuffer = target.buffer;
let sourceOffset = source.byteOffset;
let targetOffset = target.byteOffset;
for (let i = 0; i < sourceLength; i++) {
STORE<U>(
targetBuffer,
i + offset,
// @ts-ignore: Number values can be cast to each other
<U>LOAD<SourceU>(sourceBuffer, i, sourceOffset),
targetOffset,
);
}
}
45 changes: 45 additions & 0 deletions std/assembly/typedarray.ts
Expand Up @@ -9,6 +9,7 @@ import {
FIND_INDEX,
SOME,
EVERY,
SET,
} from "./internal/typedarray";

import {
Expand Down Expand Up @@ -63,6 +64,10 @@ export class Int8Array extends TypedArray<i8> {
every(callbackfn: (value: i8, index: i32, self: Int8Array) => bool): bool {
return EVERY<Int8Array, i8>(this, callbackfn);
}

set<SourceT, SourceU>(source: SourceT, offset: i32 = 0): void {
SET<Int8Array, i8, SourceT, SourceU>(this, source, offset);
}
}

export class Uint8Array extends TypedArray<u8> {
Expand Down Expand Up @@ -109,6 +114,10 @@ export class Uint8Array extends TypedArray<u8> {
every(callbackfn: (value: u8, index: i32, self: Uint8Array) => bool): bool {
return EVERY<Uint8Array, u8>(this, callbackfn);
}

set<SourceT, SourceU>(source: SourceT, offset: i32 = 0): void {
jtenner marked this conversation as resolved.
Show resolved Hide resolved
SET<Uint8Array, u8, SourceT, SourceU>(this, source, offset);
}
}

export class Uint8ClampedArray extends Uint8Array {
Expand Down Expand Up @@ -151,6 +160,10 @@ export class Uint8ClampedArray extends Uint8Array {
every(callbackfn: (value: u8, index: i32, self: Uint8ClampedArray) => bool): bool {
return EVERY<Uint8ClampedArray, u8>(this, callbackfn);
}

set<SourceT, SourceU>(source: SourceT, offset: i32 = 0): void {
SET<Uint8ClampedArray, u8, SourceT, SourceU>(this, source, offset);
}
}

export class Int16Array extends TypedArray<i16> {
Expand Down Expand Up @@ -197,6 +210,10 @@ export class Int16Array extends TypedArray<i16> {
every(callbackfn: (value: i16, index: i32, self: Int16Array) => bool): bool {
return EVERY<Int16Array, i16>(this, callbackfn);
}

set<SourceT, SourceU>(source: SourceT, offset: i32 = 0): void {
SET<Int16Array, i16, SourceT, SourceU>(this, source, offset);
}
}

export class Uint16Array extends TypedArray<u16> {
Expand Down Expand Up @@ -243,6 +260,10 @@ export class Uint16Array extends TypedArray<u16> {
every(callbackfn: (value: u16, index: i32, self: Uint16Array) => bool): bool {
return EVERY<Uint16Array, u16>(this, callbackfn);
}

set<SourceT, SourceU>(source: SourceT, offset: i32 = 0): void {
SET<Uint16Array, u16, SourceT, SourceU>(this, source, offset);
}
}

export class Int32Array extends TypedArray<i32> {
Expand Down Expand Up @@ -289,6 +310,10 @@ export class Int32Array extends TypedArray<i32> {
every(callbackfn: (value: i32, index: i32, self: Int32Array) => bool): bool {
return EVERY<Int32Array, i32>(this, callbackfn);
}

set<SourceT, SourceU>(source: SourceT, offset: i32 = 0): void {
SET<Int32Array, i32, SourceT, SourceU>(this, source, offset);
}
}

export class Uint32Array extends TypedArray<u32> {
Expand Down Expand Up @@ -335,6 +360,10 @@ export class Uint32Array extends TypedArray<u32> {
every(callbackfn: (value: u32, index: i32, self: Uint32Array) => bool): bool {
return EVERY<Uint32Array, u32>(this, callbackfn);
}

set<SourceT, SourceU>(source: SourceT, offset: i32 = 0): void {
SET<Uint32Array, u32, SourceT, SourceU>(this, source, offset);
}
}

export class Int64Array extends TypedArray<i64> {
Expand Down Expand Up @@ -381,6 +410,10 @@ export class Int64Array extends TypedArray<i64> {
every(callbackfn: (value: i64, index: i32, self: Int64Array) => bool): bool {
return EVERY<Int64Array, i64>(this, callbackfn);
}

set<SourceT, SourceU>(source: SourceT, offset: i32 = 0): void {
SET<Int64Array, i64, SourceT, SourceU>(this, source, offset);
}
}

export class Uint64Array extends TypedArray<u64> {
Expand Down Expand Up @@ -427,6 +460,10 @@ export class Uint64Array extends TypedArray<u64> {
every(callbackfn: (value: u64, index: i32, self: Uint64Array) => bool): bool {
return EVERY<Uint64Array, u64>(this, callbackfn);
}

set<SourceT, SourceU>(source: SourceT, offset: i32 = 0): void {
SET<Uint64Array, u64, SourceT, SourceU>(this, source, offset);
}
}

export class Float32Array extends TypedArray<f32> {
Expand Down Expand Up @@ -473,6 +510,10 @@ export class Float32Array extends TypedArray<f32> {
every(callbackfn: (value: f32, index: i32, self: Float32Array) => bool): bool {
return EVERY<Float32Array, f32>(this, callbackfn);
}

set<SourceT, SourceU>(source: SourceT, offset: i32 = 0): void {
SET<Float32Array, f32, SourceT, SourceU>(this, source, offset);
}
}

export class Float64Array extends TypedArray<f64> {
Expand Down Expand Up @@ -519,4 +560,8 @@ export class Float64Array extends TypedArray<f64> {
every(callbackfn: (value: f64, index: i32, self: Float64Array) => bool): bool {
return EVERY<Float64Array, f64>(this, callbackfn);
}

set<SourceT, SourceU>(source: SourceT, offset: i32 = 0): void {
SET<Float64Array, f64, SourceT, SourceU>(this, source, offset);
}
}