Skip to content

Commit

Permalink
Refactor ArrayBufferView (AssemblyScript#865)
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxGraey authored and dcodeIO committed Sep 29, 2019
1 parent f1b13a4 commit bd78683
Show file tree
Hide file tree
Showing 40 changed files with 2,845 additions and 4,311 deletions.
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

0 comments on commit bd78683

Please sign in to comment.