Skip to content

TypedArray#set #516

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’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 14 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions std/assembly/index.d.ts
Original file line number Diff line number Diff line change
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>(value: SourceT, offset?: i32): void;
}

/** An array of twos-complement 8-bit signed integers. */
Expand Down
315 changes: 314 additions & 1 deletion std/assembly/internal/typedarray.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
MAX_BLENGTH as AB_MAX_BLENGTH,
allocateUnsafe,
LOAD,
STORE
STORE,
HEADER_SIZE
} from "./arraybuffer";

import {
Expand Down Expand Up @@ -231,3 +232,315 @@ export function EVERY<TArray extends TypedArray<T>, T>(
}
return true;
}

@inline
export function SET<T extends TypedArray<U>, U extends number, SourceT>(
target: T,
source: SourceT,
offset: i32): void {
if (isReference<T>()) {
if (isReference<SourceT>()) {
assert(target != null, "TypeError: target is null.");
assert(source != null, "TypeError: source is null.");
// @ts-ignore it must have a length property
if (source.length == 0) return;
if (source instanceof Int8Array) {
if (target instanceof Int8Array) {
SET_SAME<Int8Array, i8>(target, source, offset);
} else if (target instanceof Uint8Array) {
SET_SAME<Int8Array, i8>(<Int8Array>target, source, offset);
} else if (target instanceof Uint8ClampedArray) {
SET_SAME<Int8Array, i8>(<Int8Array>target, source, offset);
} else {
SET_COPY<U, SourceU>(
target.buffer,
target.byteOffset,
source.buffer,
source.byteOffset,
offset,
source.length,
);
}
} else if (source instanceof Uint8Array) {
if (target instanceof Int8Array) {
SET_SAME<Uint8Array, u8>(<Uint8Array>target, source, offset);
} else if (target instanceof Uint8Array) {
SET_SAME<Uint8Array, u8>(target, source, offset);
} else if (target instanceof Uint8ClampedArray) {
SET_SAME<Uint8Array, u8>(<Uint8Array>target, source, offset);
} else {
SET_COPY<U, SourceU>(
target.buffer,
target.byteOffset,
source.buffer,
source.byteOffset,
offset,
source.length,
);
}
} else if (source instanceof Uint8ClampedArray) {
if (target instanceof Int8Array) {
SET_SAME<Uint8ClampedArray, u8>(<Uint8ClampedArray>target, source, offset);
} else if (target instanceof Uint8Array) {
SET_SAME<Uint8ClampedArray, u8>(<Uint8ClampedArray>target, source, offset);
} else if (target instanceof Uint8ClampedArray) {
SET_SAME<Uint8ClampedArray, u8>(target, source, offset);
} else {
SET_COPY<U, SourceU>(
target.buffer,
target.byteOffset,
source.buffer,
source.byteOffset,
offset,
source.length,
);
}
} else if (source instanceof Int16Array) {
if (target instanceof Int16Array) {
SET_SAME<Int16Array, i16>(target, source, offset);
} else if (target instanceof Uint16Array) {
SET_SAME<Int16Array, i16>(<Int16Array>target, source, offset);
} else {
SET_COPY<U, i16>(
target.buffer,
target.byteOffset,
source.buffer,
source.byteOffset,
offset,
source.length,
);
}
} else if (source instanceof Uint16Array) {
if (target instanceof Int16Array) {
SET_SAME<Uint16Array, u16>(<Uint16Array>target, source, offset);
} else if (target instanceof Uint16Array) {
SET_SAME<Uint16Array, u16>(target, source, offset);
} else {
SET_COPY<U, u16>(
target.buffer,
target.byteOffset,
source.buffer,
source.byteOffset,
offset,
source.length,
);
}
} else if (source instanceof Int32Array) {
if (target instanceof Int32Array) {
SET_SAME<Int32Array, i32>(target, source, offset);
} else if (target instanceof Uint32Array) {
SET_SAME<Int32Array, i32>(<Int32Array>target, source, offset);
} else {
SET_COPY<U, fi32>(
target.buffer,
target.byteOffset,
source.buffer,
source.byteOffset,
offset,
source.length,
);
}
} else if (source instanceof Uint32Array) {
if (target instanceof Int32Array) {
SET_SAME<Uint32Array, u32>(<Uint32Array>target, source, offset);
} else if (target instanceof Uint32Array) {
SET_SAME<Uint32Array, u32>(target, source, offset);
} else {
SET_COPY<U, u32>(
target.buffer,
target.byteOffset,
source.buffer,
source.byteOffset,
offset,
source.length,
);
}
} else if (source instanceof Int64Array) {
if (target instanceof Int64Array) {
SET_SAME<Int64Array, i64>(target, source, offset);
} else if (target instanceof Uint64Array) {
SET_SAME<Int64Array, i64>(<Int64Array>target, source, offset);
} else {
SET_COPY<U, i64>(
target.buffer,
target.byteOffset,
source.buffer,
source.byteOffset,
offset,
source.length,
);
}
} else if (source instanceof Uint64Array) {
if (target instanceof Int64Array) {
SET_SAME<Uint64Array, u64>(<Uint64Array>target, source, offset);
} else if (target instanceof Uint64Array) {
SET_SAME<Uint64Array, u64>(target, source, offset);
} else {
SET_COPY<U, u64>(
target.buffer,
target.byteOffset,
source.buffer,
source.byteOffset,
offset,
source.length,
);
}
} else if (source instanceof Float32Array) {
if (target instanceof Float32Array) {
SET_SAME<Float32Array, f32>(target, source, offset);
} else {
SET_COPY<U, f32>(
target.buffer,
target.byteOffset,
source.buffer,
source.byteOffset,
offset,
source.length,
);
}
} else if (source instanceof Float64Array) {
if (target instanceof Float64Array) {
SET_SAME<Float64Array, f64>(target, source, offset);
} else {
SET_COPY<U, f64>(
target.buffer,
target.byteOffset,
source.buffer,
source.byteOffset,
offset,
source.length,
);
}
} else if (isArray<SourceT>()) {
let targetBuffer = target.buffer;
let targetByteOffset = target.byteOffset;
let sourceBuffer = load<ArrayBuffer>(changetype<usize>(source), offsetof<SourceT>("buffer_"));
// @ts-ignore: this is an array, and has a length property, this is not unsafe.
// tslint:disable-next-line
let length = <i32>source.length; // adding `i32` type here supresses tslint unsafe any

// @ts-ignore: this is an array, and has an indexed getter
// tslint:disable-next-line
var stub = unchecked(source[0]);

// Detect stub type using instanceof. Not ergonomic but a valid workaround
if (stub instanceof i8) {
SET_COPY<U, i8>(targetBuffer, targetByteOffset, sourceBuffer, 0, offset, length);
} else if (stub instanceof u8) {
SET_COPY<U, u8>(targetBuffer, targetByteOffset, sourceBuffer, 0, offset, length);
} else if (stub instanceof i16) {
SET_COPY<U, i16>(targetBuffer, targetByteOffset, sourceBuffer, 0, offset, length);
} else if (stub instanceof u16) {
SET_COPY<U, u16>(targetBuffer, targetByteOffset, sourceBuffer, 0, offset, length);
} else if (stub instanceof i32) {
SET_COPY<U, i32>(targetBuffer, targetByteOffset, sourceBuffer, 0, offset, length);
} else if (stub instanceof u32) {
SET_COPY<U, u32>(targetBuffer, targetByteOffset, sourceBuffer, 0, offset, length);
} else if (stub instanceof i64) {
SET_COPY<U, i64>(targetBuffer, targetByteOffset, sourceBuffer, 0, offset, length);
} else if (stub instanceof u64) {
SET_COPY<U, u64>(targetBuffer, targetByteOffset, sourceBuffer, 0, offset, length);
} else if (stub instanceof f32) {
SET_COPY<U, f32>(targetBuffer, targetByteOffset, sourceBuffer, 0, offset, length);
} else if (stub instanceof f64) {
SET_COPY<U, f64>(targetBuffer, targetByteOffset, sourceBuffer, 0, offset, length);
}
} 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;

// tslint:disable-next-line
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
// tslint:disable-next-line
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,
);
}
}
} else {
assert(false, "TypeError: source is not a reference.");
}
} else {
assert(false, "TypeError: target is not a reference.");
}
}

// SLow set copy method
@inline
function SET_COPY<U, SourceU>(
targetBuffer: ArrayBuffer,
targetBufferOffset: i32,
sourceBuffer: ArrayBuffer,
sourceBufferOffset: i32,
offset: i32,
length: i32): void {

// slowest path, a buffer writing to itself, despite the types not lining up
if (targetBuffer == sourceBuffer) {
// copy source data to a new copy to prevent data thrashing
let bytes = length << alignof<SourceU>();
let sourceCopy = allocateUnsafe(bytes);
memory.copy(
sourceCopy.data,
sourceBuffer.data + sourceBufferOffset,
bytes,
);

// copy the data to itself
for (let i = 0; i < length; i++) {
STORE<U>(
targetBuffer,
i + offset,
<U>LOAD<SourceU>(sourceCopy, i, 0),
targetBufferOffset,
);
}

// cleanup
if (isManaged<ArrayBuffer>(sourceCopy)) {
memory.free(changetype<usize>(sourceCopy));
}
} else {
// copy the data directly
for (let i = 0; i < length; i++) {
STORE<U>(
targetBuffer,
i + offset,
<U>LOAD<SourceU>(sourceBuffer, i, sourceBufferOffset),
targetBufferOffset,
);
}
}
}

@inline
function SET_SAME<T extends TypedArray<U>, U extends number>(target: T, source: T, offset: i32): void {
// perform a memcpy
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,
);
}
Loading