Skip to content

Refactor ArrayBufferView #865

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

Merged
merged 13 commits into from
Sep 29, 2019
4 changes: 2 additions & 2 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1592,9 +1592,9 @@ export class Compiler extends DiagnosticEmitter {

var bufferAddress32 = i64_low(bufferSegment.offset) + runtimeHeaderSize;
assert(!program.options.isWasm64); // TODO
assert(arrayInstance.writeField("data", bufferAddress32, buf, runtimeHeaderSize));
assert(arrayInstance.writeField("buffer", bufferAddress32, buf, runtimeHeaderSize));
assert(arrayInstance.writeField("dataStart", bufferAddress32, buf, runtimeHeaderSize));
assert(arrayInstance.writeField("dataLength", bufferLength, buf, runtimeHeaderSize));
assert(arrayInstance.writeField("byteLength", bufferLength, buf, runtimeHeaderSize));
assert(arrayInstance.writeField("length_", arrayLength, buf, runtimeHeaderSize));

return this.addMemorySegment(buf);
Expand Down
20 changes: 8 additions & 12 deletions std/assembly/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@

import { BLOCK_MAXSIZE } from "./rt/common";
import { COMPARATOR, SORT } from "./util/sort";
import { ArrayBuffer, ArrayBufferView } from "./arraybuffer";
import { ArrayBufferView } from "./arraybuffer";
import { joinBooleanArray, joinIntegerArray, joinFloatArray, joinStringArray, joinArrays, joinObjectArray } from "./util/string";
import { idof, isArray as builtin_isArray } from "./builtins";
import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH, E_EMPTYARRAY, E_HOLEYARRAY } from "./util/error";

/** Ensures that the given array has _at least_ the specified backing size. */
function ensureSize(array: usize, minSize: usize, alignLog2: u32): void {
var oldCapacity = changetype<ArrayBufferView>(array).dataLength;
if (minSize > oldCapacity >>> alignLog2) {
var oldCapacity = changetype<ArrayBufferView>(array).byteLength;
if (minSize > <usize>oldCapacity >>> alignLog2) {
if (minSize > BLOCK_MAXSIZE >>> alignLog2) throw new RangeError(E_INVALIDLENGTH);
let oldData = changetype<usize>(changetype<ArrayBufferView>(array).data);
let oldData = changetype<usize>(changetype<ArrayBufferView>(array).buffer);
let newCapacity = minSize << alignLog2;
let newData = __realloc(oldData, newCapacity);
memory.fill(newData + oldCapacity, 0, newCapacity - oldCapacity);
if (newData !== oldData) { // oldData has been free'd
store<usize>(changetype<usize>(array), __retain(newData), offsetof<ArrayBufferView>("data"));
changetype<ArrayBufferView>(array).dataStart = newData;
store<usize>(array, __retain(newData), offsetof<ArrayBufferView>("buffer"));
store<usize>(array, newData, offsetof<ArrayBufferView>("dataStart"));
}
changetype<ArrayBufferView>(array).dataLength = <u32>newCapacity;
store<u32>(array, newCapacity, offsetof<ArrayBufferView>("byteLength"));
}
}

Expand Down Expand Up @@ -53,10 +53,6 @@ export class Array<T> extends ArrayBufferView {
this.length_ = length;
}

@unsafe get buffer(): ArrayBuffer {
return this.data;
}

get length(): i32 {
return this.length_;
}
Expand Down Expand Up @@ -492,6 +488,6 @@ export class Array<T> extends ArrayBufferView {
cur += sizeof<usize>();
}
}
// automatically visits ArrayBufferView (.data) next
// automatically visits ArrayBufferView (.buffer) next
}
}
30 changes: 13 additions & 17 deletions std/assembly/arraybuffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,27 @@ import { E_INVALIDLENGTH } from "./util/error";

export abstract class ArrayBufferView {

@unsafe data: ArrayBuffer;
@unsafe dataStart: usize;
@unsafe dataLength: u32;

protected constructor(length: i32, alignLog2: i32) {
if (<u32>length > <u32>BLOCK_MAXSIZE >>> alignLog2) throw new RangeError(E_INVALIDLENGTH);
var buffer = __alloc(length = length << alignLog2, idof<ArrayBuffer>());
memory.fill(buffer, 0, <usize>length);
this.data = changetype<ArrayBuffer>(buffer); // retains
this.dataStart = buffer;
this.dataLength = length;
}
readonly buffer: ArrayBuffer;
@unsafe readonly dataStart: usize;
readonly byteLength: i32;

get byteOffset(): i32 {
return <i32>(this.dataStart - changetype<usize>(this.data));
}

get byteLength(): i32 {
return this.dataLength;
return <i32>(this.dataStart - changetype<usize>(this.buffer));
}

get length(): i32 {
ERROR("missing implementation: subclasses must implement ArrayBufferView#length");
return unreachable();
}

protected constructor(length: i32, alignLog2: i32) {
if (<u32>length > <u32>BLOCK_MAXSIZE >>> alignLog2) throw new RangeError(E_INVALIDLENGTH);
var buffer = __alloc(length = length << alignLog2, idof<ArrayBuffer>());
memory.fill(buffer, 0, <usize>length);
this.buffer = changetype<ArrayBuffer>(buffer); // retains
this.dataStart = buffer;
this.byteLength = length;
}
}

@sealed export class ArrayBuffer {
Expand Down
82 changes: 29 additions & 53 deletions std/assembly/dataview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH } from "./util/error";

export class DataView {

private data: ArrayBuffer;
private dataStart: usize;
private dataLength: i32;
readonly buffer: ArrayBuffer;
@unsafe readonly dataStart: usize;
readonly byteLength: i32;

get byteOffset(): i32 {
return <i32>(this.dataStart - changetype<usize>(this.buffer));
}

constructor(
buffer: ArrayBuffer,
Expand All @@ -19,28 +23,15 @@ export class DataView {
i32(<u32>byteLength > <u32>BLOCK_MAXSIZE) |
i32(<u32>byteOffset + byteLength > <u32>buffer.byteLength)
) throw new RangeError(E_INVALIDLENGTH);
this.data = buffer; // retains
this.buffer = buffer; // retains
var dataStart = changetype<usize>(buffer) + <usize>byteOffset;
this.dataStart = dataStart;
this.dataLength = byteLength;
}

get buffer(): ArrayBuffer {
return this.data;
}

get byteOffset(): i32 {
return <i32>(this.dataStart - changetype<usize>(this.data));
}

get byteLength(): i32 {
return this.dataLength;
this.byteLength = byteLength;
}

getFloat32(byteOffset: i32, littleEndian: boolean = false): f32 {
if (
i32(byteOffset < 0) |
i32(byteOffset + 4 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 4 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
return littleEndian
? load<f32>(this.dataStart + <usize>byteOffset)
Expand All @@ -53,8 +44,7 @@ export class DataView {

getFloat64(byteOffset: i32, littleEndian: boolean = false): f64 {
if (
i32(byteOffset < 0) |
i32(byteOffset + 8 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 8 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
return littleEndian
? load<f64>(this.dataStart + <usize>byteOffset)
Expand All @@ -66,107 +56,97 @@ export class DataView {
}

getInt8(byteOffset: i32): i8 {
if (<u32>byteOffset >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
if (<u32>byteOffset >= <u32>this.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
return load<i8>(this.dataStart + <usize>byteOffset);
}

getInt16(byteOffset: i32, littleEndian: boolean = false): i16 {
if (
i32(byteOffset < 0) |
i32(byteOffset + 2 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 2 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
var result: i16 = load<i16>(this.dataStart + <usize>byteOffset);
return littleEndian ? result : bswap<i16>(result);
}

getInt32(byteOffset: i32, littleEndian: boolean = false): i32 {
if (
i32(byteOffset < 0) |
i32(byteOffset + 4 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 4 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
var result: i32 = load<i32>(this.dataStart + <usize>byteOffset);
return littleEndian ? result : bswap<i32>(result);
}

getUint8(byteOffset: i32): u8 {
if (<u32>byteOffset >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
if (<u32>byteOffset >= <u32>this.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
return load<u8>(this.dataStart + <usize>byteOffset);
}

getUint16(byteOffset: i32, littleEndian: boolean = false): u16 {
if (
i32(byteOffset < 0) |
i32(byteOffset + 2 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 2 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
var result: u16 = load<u16>(this.dataStart + <usize>byteOffset);
return littleEndian ? result : bswap<u16>(result);
}

getUint32(byteOffset: i32, littleEndian: boolean = false): u32 {
if (
i32(byteOffset < 0) |
i32(byteOffset + 4 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 4 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
var result: u32 = load<u32>(this.dataStart + <usize>byteOffset);
return littleEndian ? result : bswap<u32>(result);
}

setFloat32(byteOffset: i32, value: f32, littleEndian: boolean = false): void {
if (
i32(byteOffset < 0) |
i32(byteOffset + 4 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 4 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
if (littleEndian) store<f32>(this.dataStart + <usize>byteOffset, value);
else store<u32>(this.dataStart + <usize>byteOffset, bswap<u32>(reinterpret<u32>(value)));
}

setFloat64(byteOffset: i32, value: f64, littleEndian: boolean = false): void {
if (
i32(byteOffset < 0) |
i32(byteOffset + 8 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 8 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
if (littleEndian) store<f64>(this.dataStart + <usize>byteOffset, value);
else store<u64>(this.dataStart + <usize>byteOffset, bswap<u64>(reinterpret<u64>(value)));
}

setInt8(byteOffset: i32, value: i8): void {
if (<u32>byteOffset >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
if (<u32>byteOffset >= <u32>this.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
store<i8>(this.dataStart + <usize>byteOffset, value);
}

setInt16(byteOffset: i32, value: i16, littleEndian: boolean = false): void {
if (
i32(byteOffset < 0) |
i32(byteOffset + 2 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 2 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
store<i16>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<i16>(value));
}

setInt32(byteOffset: i32, value: i32, littleEndian: boolean = false): void {
if (
i32(byteOffset < 0) |
i32(byteOffset + 4 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 4 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
store<i32>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<i32>(value));
}

setUint8(byteOffset: i32, value: u8): void {
if (<u32>byteOffset >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
if (<u32>byteOffset >= <u32>this.byteLength) throw new RangeError(E_INDEXOUTOFRANGE);
store<u8>(this.dataStart + <usize>byteOffset, value);
}

setUint16(byteOffset: i32, value: u16, littleEndian: boolean = false): void {
if (
i32(byteOffset < 0) |
i32(byteOffset + 2 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 2 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
store<u16>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<u16>(value));
}

setUint32(byteOffset: i32, value: u32, littleEndian: boolean = false): void {
if (
i32(byteOffset < 0) |
i32(byteOffset + 4 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 4 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
store<u32>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<u32>(value));
}
Expand All @@ -175,34 +155,30 @@ export class DataView {

getInt64(byteOffset: i32, littleEndian: boolean = false): i64 {
if (
i32(byteOffset < 0) |
i32(byteOffset + 8 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 8 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
var result: i64 = load<i64>(this.dataStart + <usize>byteOffset);
return littleEndian ? result : bswap<i64>(result);
}

getUint64(byteOffset: i32, littleEndian: boolean = false): u64 {
if (
i32(byteOffset < 0) |
i32(byteOffset + 8 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 8 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
var result = load<u64>(this.dataStart + <usize>byteOffset);
return littleEndian ? result : bswap<u64>(result);
}

setInt64(byteOffset: i32, value: i64, littleEndian: boolean = false): void {
if (
i32(byteOffset < 0) |
i32(byteOffset + 8 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 8 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
store<i64>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<i64>(value));
}

setUint64(byteOffset: i32, value: u64, littleEndian: boolean = false): void {
if (
i32(byteOffset < 0) |
i32(byteOffset + 8 > this.dataLength)
(byteOffset >>> 31) | i32(byteOffset + 8 > this.byteLength)
) throw new RangeError(E_INDEXOUTOFRANGE);
store<u64>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<u64>(value));
}
Expand Down
7 changes: 3 additions & 4 deletions std/assembly/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1154,8 +1154,7 @@ interface ArrayLike<T> {
}

/** Interface for a typed view on an array buffer. */
interface ArrayBufferView<T> {
[key: number]: T;
interface ArrayBufferView {
/** The {@link ArrayBuffer} referenced by this view. */
readonly buffer: ArrayBuffer;
/** The offset in bytes from the start of the referenced {@link ArrayBuffer}. */
Expand All @@ -1165,12 +1164,12 @@ interface ArrayBufferView<T> {
}

/* @internal */
declare abstract class TypedArray<T> implements ArrayBufferView<T> {
declare abstract class TypedArray<T> implements ArrayBufferView {
[key: number]: T;
/** Number of bytes per element. */
static readonly BYTES_PER_ELEMENT: usize;
/** Wrap an ArrayBuffer */
static wrap(buffer: ArrayBuffer, byteOffset?: i32, length?: i32): TypedArray<T>;
static wrap(buffer: ArrayBuffer, byteOffset?: i32, length?: i32): ArrayBufferView;
/** Constructs a new typed array. */
constructor(length: i32);
/** The {@link ArrayBuffer} referenced by this view. */
Expand Down
6 changes: 3 additions & 3 deletions std/assembly/rt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ export function __allocArray(length: i32, alignLog2: usize, id: u32, data: usize
var array = __alloc(offsetof<i32[]>(), id);
var bufferSize = <usize>length << alignLog2;
var buffer = __alloc(bufferSize, idof<ArrayBuffer>());
store<usize>(array, __retain(buffer), offsetof<ArrayBufferView>("data"));
changetype<ArrayBufferView>(array).dataStart = buffer;
changetype<ArrayBufferView>(array).dataLength = bufferSize;
store<usize>(array, __retain(buffer), offsetof<ArrayBufferView>("buffer"));
store<usize>(array, buffer, offsetof<ArrayBufferView>("dataStart"));
store<u32>(array, bufferSize, offsetof<ArrayBufferView>("byteLength"));
store<i32>(changetype<usize>(array), length, offsetof<i32[]>("length_"));
if (data) memory.copy(buffer, data, bufferSize);
return array;
Expand Down
Loading