Skip to content

Commit c500305

Browse files
committed
Add DataView to standard library
1 parent d93ca84 commit c500305

File tree

4 files changed

+8675
-0
lines changed

4 files changed

+8675
-0
lines changed

std/assembly/dataview.ts

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { HEADER_SIZE } from "./internal/arraybuffer";
2+
3+
@inline
4+
function get<T>(buffer: ArrayBuffer, byteOffset: i32, littleEndian: boolean): T {
5+
var result: T = load<T>(changetype<usize>(buffer) + byteOffset, HEADER_SIZE);
6+
return littleEndian ? result : bswap<T>(result);
7+
}
8+
9+
@inline
10+
function set<T>(buffer: ArrayBuffer, byteOffset: i32, value: T, littleEndian: boolean): void {
11+
var input = littleEndian ? value : bswap<T>(value);
12+
store<T>(changetype<usize>(buffer) + byteOffset, input, HEADER_SIZE);
13+
}
14+
15+
export class DataView {
16+
constructor(
17+
readonly buffer: ArrayBuffer,
18+
readonly byteOffset: i32 = 0,
19+
readonly byteLength: i32 = i32.MIN_VALUE,
20+
) {
21+
if (byteLength === i32.MIN_VALUE) byteLength = buffer.byteLength - byteOffset;
22+
23+
if (byteOffset < 0) throw new RangeError("byteOffset cannot be negative");
24+
if (byteLength < 0) throw new RangeError("byteLength cannot be negative");
25+
if (byteOffset + byteLength > buffer.byteLength) throw new RangeError("Length out of range of buffer");
26+
}
27+
28+
@inline
29+
getFloat32(byteOffset: i32, littleEndian: boolean = false): f32 {
30+
var result: u32 = load<u32>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE);
31+
return reinterpret<f32>(littleEndian ? result : bswap<u32>(result));
32+
}
33+
34+
@inline
35+
getFloat64(byteOffset: i32, littleEndian: boolean = false): f64 {
36+
var result: u64 = load<u64>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, HEADER_SIZE);
37+
return reinterpret<f64>(littleEndian ? result : bswap<u64>(result));
38+
}
39+
40+
@inline
41+
getInt8(byteOffset: i32): i8 {
42+
return get<i8>(this.buffer, this.byteOffset + byteOffset, true);
43+
}
44+
45+
@inline
46+
getInt16(byteOffset: i32, littleEndian: boolean = false): i16 {
47+
return get<i16>(this.buffer, this.byteOffset + byteOffset, littleEndian);
48+
}
49+
50+
@inline
51+
getInt32(byteOffset: i32, littleEndian: boolean = false): i32 {
52+
return get<i32>(this.buffer, this.byteOffset + byteOffset, littleEndian);
53+
}
54+
55+
@inline
56+
getUint8(byteOffset: i32): u8 {
57+
return get<u8>(this.buffer, this.byteOffset + byteOffset, true);
58+
}
59+
60+
@inline
61+
getUint16(byteOffset: i32, littleEndian: boolean = false): u16 {
62+
return get<u16>(this.buffer, this.byteOffset + byteOffset, littleEndian);
63+
}
64+
65+
@inline
66+
getUint32(byteOffset: i32, littleEndian: boolean = false): u32 {
67+
return get<u32>(this.buffer, this.byteOffset + byteOffset, littleEndian);
68+
}
69+
70+
@inline
71+
setFloat32(byteOffset: i32, value: f32, littleEndian: boolean = false): void {
72+
var input: f32 = littleEndian ? value : reinterpret<f32>(bswap<u32>(reinterpret<u32>(value)));
73+
store<f32>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, input, HEADER_SIZE);
74+
}
75+
76+
@inline
77+
setFloat64(byteOffset: i32, value: f64, littleEndian: boolean = false): void {
78+
var input: f64 = littleEndian ? value : reinterpret<f64>(bswap<u64>(reinterpret<u64>(value)));
79+
store<f64>(changetype<usize>(this.buffer) + this.byteOffset + byteOffset, input, HEADER_SIZE);
80+
}
81+
82+
@inline
83+
setInt8(byteOffset: i32, value: i8): void {
84+
set<i8>(this.buffer, this.byteOffset + byteOffset, value, true);
85+
}
86+
87+
@inline
88+
setInt16(byteOffset: i32, value: i16, littleEndian: boolean = false): void {
89+
set<i16>(this.buffer, this.byteOffset + byteOffset, value, littleEndian);
90+
}
91+
92+
@inline
93+
setInt32(byteOffset: i32, value: i32, littleEndian: boolean = false): void {
94+
set<i32>(this.buffer, this.byteOffset + byteOffset, value, littleEndian);
95+
}
96+
97+
@inline
98+
setUint8(byteOffset: i32, value: u8): void {
99+
set<u8>(this.buffer, this.byteOffset + byteOffset, value, true);
100+
}
101+
102+
@inline
103+
setUint16(byteOffset: i32, value: u16, littleEndian: boolean = false): void {
104+
set<u16>(this.buffer, this.byteOffset + byteOffset, value, littleEndian);
105+
}
106+
107+
@inline
108+
setUint32(byteOffset: i32, value: u32, littleEndian: boolean = false): void {
109+
set<u32>(this.buffer, this.byteOffset + byteOffset, value, littleEndian);
110+
}
111+
112+
/**
113+
* Non-standard additions that makes sense in WebAssembly, but won't work in JS
114+
*/
115+
116+
@inline
117+
getInt64(byteOffset: i32, littleEndian: boolean = false): i64 {
118+
return get<i64>(this.buffer, this.byteOffset + byteOffset, littleEndian);
119+
}
120+
121+
@inline
122+
getUint64(byteOffset: i32, littleEndian: boolean = false): u64 {
123+
return get<u64>(this.buffer, this.byteOffset + byteOffset, littleEndian);
124+
}
125+
126+
@inline
127+
setInt64(byteOffset: i32, value: i64, littleEndian: boolean = false): void {
128+
set<i64>(this.buffer, this.byteOffset + byteOffset, value, littleEndian);
129+
}
130+
131+
@inline
132+
setUint64(byteOffset: i32, value: u64, littleEndian: boolean = false): void {
133+
set<u64>(this.buffer, this.byteOffset + byteOffset, value, littleEndian);
134+
}
135+
}

std/assembly/index.d.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,58 @@ declare class ArrayBuffer {
380380
slice(begin?: i32, end?: i32): ArrayBuffer;
381381
}
382382

383+
/** The `DataView` view provides a low-level interface for reading and writing multiple number types in a binary `ArrayBuffer`, without having to care about the platform's endianness. */
384+
declare class DataView {
385+
/** The `buffer` accessor property represents the `ArrayBuffer` or `SharedArrayBuffer` referenced by the `DataView` at construction time. */
386+
readonly buffer: ArrayBuffer;
387+
/** The `byteLength` accessor property represents the length (in bytes) of this view from the start of its `ArrayBuffer` or `SharedArrayBuffer`. */
388+
readonly byteLength: i32;
389+
/** The `byteOffset` accessor property represents the offset (in bytes) of this view from the start of its `ArrayBuffer` or `SharedArrayBuffer`. */
390+
readonly byteOffset: i32;
391+
/** Constructs a new `DataView` with the given properties */
392+
constructor(buffer: ArrayBuffer, byteOffset?: i32, byteLength?: i32);
393+
/** The `getFloat32()` method gets a signed 32-bit float (float) at the specified byte offset from the start of the `DataView`. */
394+
getFloat32(byteOffset: i32, littleEndian?: boolean): f32
395+
/** The `getFloat64()` method gets a signed 64-bit float (double) at the specified byte offset from the start of the `DataView`. */
396+
getFloat64(byteOffset: i32, littleEndian?: boolean): f64
397+
/** The `getInt8()` method gets a signed 8-bit integer (byte) at the specified byte offset from the start of the `DataView`. */
398+
getInt8(byteOffset: i32): i8
399+
/** The `getInt16()` method gets a signed 16-bit integer (short) at the specified byte offset from the start of the `DataView`. */
400+
getInt16(byteOffset: i32, littleEndian?: boolean): i16
401+
/** The `getInt32()` method gets a signed 32-bit integer (long) at the specified byte offset from the start of the `DataView`. */
402+
getInt32(byteOffset: i32, littleEndian?: boolean): i32
403+
/** The `getInt64()` method gets a signed 64-bit integer (long long) at the specified byte offset from the start of the `DataView`. */
404+
getInt64(byteOffset: i32, littleEndian?: boolean): i64
405+
/** The `getUint8()` method gets an unsigned 8-bit integer (unsigned byte) at the specified byte offset from the start of the `DataView`. */
406+
getUint8(byteOffset: i32): u8
407+
/** The `getUint16()` method gets an unsigned 16-bit integer (unsigned short) at the specified byte offset from the start of the `DataView`. */
408+
getUint16(byteOffset: i32, littleEndian?: boolean): u16
409+
/** The `getUint32()` method gets an unsigned 32-bit integer (unsigned long) at the specified byte offset from the start of the `DataView`. */
410+
getUint32(byteOffset: i32, littleEndian?: boolean): u32
411+
/** The `getUint64()` method gets an unsigned 64-bit integer (unsigned long long) at the specified byte offset from the start of the `DataView`. */
412+
getUint64(byteOffset: i32, littleEndian?: boolean): u64
413+
/** The `setFloat32()` method stores a signed 32-bit float (float) value at the specified byte offset from the start of the `DataView`. */
414+
setFloat32(byteOffset: i32, value: f32, littleEndian?: boolean): void
415+
/** The `setFloat64()` method stores a signed 64-bit float (double) value at the specified byte offset from the start of the `DataView`. */
416+
setFloat64(byteOffset: i32, value: f64, littleEndian?: boolean): void
417+
/** The `setInt8()` method stores a signed 8-bit integer (byte) value at the specified byte offset from the start of the `DataView`. */
418+
setInt8(byteOffset: i32, value: i8): void
419+
/** The `setInt16()` method stores a signed 16-bit integer (short) value at the specified byte offset from the start of the `DataView`. */
420+
setInt16(byteOffset: i32, value: i16, littleEndian?: boolean): void
421+
/** The `setInt32()` method stores a signed 32-bit integer (long) value at the specified byte offset from the start of the `DataView`. */
422+
setInt32(byteOffset: i32, value: i32, littleEndian?: boolean): void
423+
/** The `setInt64()` method stores a signed 64-bit integer (long long) value at the specified byte offset from the start of the `DataView`. */
424+
setInt64(byteOffset: i32, value: i64, littleEndian?: boolean): void
425+
/** The `setUint8()` method stores an unsigned 8-bit integer (byte) value at the specified byte offset from the start of the `DataView`. */
426+
setUint8(byteOffset: i32, value: u8): void
427+
/** The `setUint16()` method stores an unsigned 16-bit integer (unsigned short) value at the specified byte offset from the start of the `DataView`. */
428+
setUint16(byteOffset: i32, value: u16, littleEndian?: boolean): void
429+
/** The `setUint32()` method stores an unsigned 32-bit integer (unsigned long) value at the specified byte offset from the start of the `DataView`. */
430+
setUint32(byteOffset: i32, value: u32, littleEndian?: boolean): void
431+
/** The `setUint64()` method stores an unsigned 64-bit integer (unsigned long long) value at the specified byte offset from the start of the `DataView`. */
432+
setUint64(byteOffset: i32, value: u64, littleEndian?: boolean): void
433+
}
434+
383435
/** Interface for a typed view on an array buffer. */
384436
interface ArrayBufferView<T> {
385437
[key: number]: T;

tests/compiler/std/dataview.ts

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import "allocator/arena";
2+
3+
var array = new Uint8Array(8);
4+
5+
array[0] = 246;
6+
array[1] = 224;
7+
array[2] = 88;
8+
array[3] = 159;
9+
array[4] = 130;
10+
array[5] = 101;
11+
array[6] = 67;
12+
array[7] = 95;
13+
14+
var view = new DataView(array.buffer, array.byteOffset, array.byteLength);
15+
16+
assert(view.getFloat32(0, true) === -4.592586247781397e-20);
17+
assert(view.getFloat32(1, true) === -2.3413961970849473e-37);
18+
assert(view.getFloat32(2, true) === 7.710587701863113e+22);
19+
assert(view.getFloat32(3, true) === 229.51023864746094);
20+
assert(view.getFloat32(4, true) === 14079802746555335000.0);
21+
22+
assert(view.getFloat32(0, false) === -2.2751405188178955e+33);
23+
assert(view.getFloat32(1, false) === -62437351080004160000.0);
24+
assert(view.getFloat32(2, false) === 1403059112509440.0);
25+
assert(view.getFloat32(3, false) === -5.522466503261712e-20);
26+
assert(view.getFloat32(4, false) === -1.6843597451835358e-37);
27+
28+
assert(view.getFloat64(0, true) === 7.936550095674706e+150);
29+
assert(view.getFloat64(0, false) === -4.1177747581885255e+264);
30+
31+
assert(view.getInt8(0) === -10);
32+
assert(view.getInt8(1) === -32);
33+
assert(view.getInt8(2) === 88);
34+
assert(view.getInt8(3) === -97);
35+
assert(view.getInt8(4) === -126);
36+
assert(view.getInt8(5) === 101);
37+
assert(view.getInt8(6) === 67);
38+
assert(view.getInt8(7) === 95);
39+
40+
assert(view.getInt16(0, true) === -7946);
41+
assert(view.getInt16(1, true) === 22752);
42+
assert(view.getInt16(2, true) === -24744);
43+
assert(view.getInt16(3, true) === -32097);
44+
assert(view.getInt16(4, true) === 25986);
45+
assert(view.getInt16(5, true) === 17253);
46+
assert(view.getInt16(6, true) === 24387);
47+
48+
assert(view.getInt16(0, false) === -2336);
49+
assert(view.getInt16(1, false) === -8104);
50+
assert(view.getInt16(2, false) === 22687);
51+
assert(view.getInt16(3, false) === -24702);
52+
assert(view.getInt16(4, false) === -32155);
53+
assert(view.getInt16(5, false) === 25923);
54+
assert(view.getInt16(6, false) === 17247);
55+
56+
assert(view.getInt32(0, true) === -1621565194);
57+
assert(view.getInt32(1, true) === -2103486240);
58+
assert(view.getInt32(2, true) === 1703059288);
59+
assert(view.getInt32(3, true) === 1130726047);
60+
assert(view.getInt32(4, true) === 1598252418);
61+
62+
assert(view.getInt32(0, false) === -153069409);
63+
assert(view.getInt32(1, false) === -531062910);
64+
assert(view.getInt32(2, false) === 1486848613);
65+
assert(view.getInt32(3, false) === -1618844349);
66+
assert(view.getInt32(4, false) === -2107292833);
67+
68+
assert(view.getInt64(0, true) === 6864441868736323830)
69+
assert(view.getInt64(0, false) === -657428103485373601)
70+
71+
assert(view.getUint8(0) === 246);
72+
assert(view.getUint8(1) === 224);
73+
assert(view.getUint8(2) === 88);
74+
assert(view.getUint8(3) === 159);
75+
assert(view.getUint8(4) === 130);
76+
assert(view.getUint8(5) === 101);
77+
assert(view.getUint8(6) === 67);
78+
assert(view.getUint8(7) === 95);
79+
80+
assert(view.getUint16(0, true) === 57590);
81+
assert(view.getUint16(1, true) === 22752);
82+
assert(view.getUint16(2, true) === 40792);
83+
assert(view.getUint16(3, true) === 33439);
84+
assert(view.getUint16(4, true) === 25986);
85+
assert(view.getUint16(5, true) === 17253);
86+
assert(view.getUint16(6, true) === 24387);
87+
88+
assert(view.getUint16(0, false) === 63200);
89+
assert(view.getUint16(1, false) === 57432);
90+
assert(view.getUint16(2, false) === 22687);
91+
assert(view.getUint16(3, false) === 40834);
92+
assert(view.getUint16(4, false) === 33381);
93+
assert(view.getUint16(5, false) === 25923);
94+
assert(view.getUint16(6, false) === 17247);
95+
96+
assert(view.getUint32(0, true) === 2673402102);
97+
assert(view.getUint32(1, true) === 2191481056);
98+
assert(view.getUint32(2, true) === 1703059288);
99+
assert(view.getUint32(3, true) === 1130726047);
100+
assert(view.getUint32(4, true) === 1598252418);
101+
102+
assert(view.getUint32(0, false) === 4141897887);
103+
assert(view.getUint32(1, false) === 3763904386);
104+
assert(view.getUint32(2, false) === 1486848613);
105+
assert(view.getUint32(3, false) === 2676122947);
106+
assert(view.getUint32(4, false) === 2187674463);
107+
108+
assert(view.getUint64(0, true) === 6864441868736323830)
109+
assert(view.getUint64(0, false) === 17789315970224178015)
110+
111+
view.setFloat32(0, 1.5976661625240943e-18, true)
112+
assert(view.getFloat32(0, true) === 1.5976661625240943e-18)
113+
114+
view.setFloat32(0, 1.9762819733816963e+21, false)
115+
assert(view.getFloat32(0, false) === 1.9762819733816963e+21)
116+
117+
view.setFloat64(0, -1.094252199637739e+148, true)
118+
assert(view.getFloat64(0, true) === -1.094252199637739e+148)
119+
120+
view.setFloat64(0, 6.022586634778589e-103, false)
121+
assert(view.getFloat64(0, false) === 6.022586634778589e-103)
122+
123+
view.setInt8(0, 108)
124+
assert(view.getInt8(0) === 108)
125+
126+
view.setInt16(0, -13360, true)
127+
assert(view.getInt16(0, true) === -13360)
128+
129+
view.setInt16(0, 14689, false)
130+
assert(view.getInt16(0, false) === 14689)
131+
132+
view.setInt32(0, 1204680201, true)
133+
assert(view.getInt32(0, true) === 1204680201)
134+
135+
view.setInt32(0, 660673230, false)
136+
assert(view.getInt32(0, false) === 660673230)
137+
138+
view.setInt64(0, -3290739641816099749, true)
139+
assert(view.getInt64(0, true) === -3290739641816099749)
140+
141+
view.setInt64(0, 8178932412950708047, false)
142+
assert(view.getInt64(0, false) === 8178932412950708047)
143+
144+
view.setUint8(0, 238)
145+
assert(view.getUint8(0) === 238)
146+
147+
view.setUint16(0, 58856, true)
148+
assert(view.getUint16(0, true) === 58856)
149+
150+
view.setUint16(0, 60400, false)
151+
assert(view.getUint16(0, false) === 60400)
152+
153+
view.setUint32(0, 3448161552, true)
154+
assert(view.getUint32(0, true) === 3448161552)
155+
156+
view.setUint32(0, 2784175665, false)
157+
assert(view.getUint32(0, false) === 2784175665)
158+
159+
view.setUint64(0, 2334704782995986958, true)
160+
assert(view.getUint64(0, true) === 2334704782995986958)
161+
162+
view.setUint64(0, 11323557176419695287, false)
163+
assert(view.getUint64(0, false) === 11323557176419695287)

0 commit comments

Comments
 (0)