Skip to content

Commit 96b1872

Browse files
committed
[Implement] Naive Buffer.from
1 parent c3a162d commit 96b1872

File tree

3 files changed

+84
-0
lines changed

3 files changed

+84
-0
lines changed

assembly/buffer/index.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { BLOCK_MAXSIZE } from "rt/common";
22
import { E_INVALIDLENGTH, E_INDEXOUTOFRANGE } from "util/error";
33
import { Uint8Array } from "typedarray";
4+
import { ArrayBufferView } from "arraybuffer";
45

56
export class Buffer extends Uint8Array {
67
constructor(size: i32) {
@@ -22,4 +23,40 @@ export class Buffer extends Uint8Array {
2223
result.dataLength = size;
2324
return result;
2425
}
26+
27+
// @ts-ignore: Buffer returns on all valid branches
28+
public static from<T>(value: T): Buffer {
29+
if (value instanceof ArrayBuffer) {
30+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
31+
result.data = value;
32+
result.dataStart = changetype<usize>(value);
33+
result.dataLength = value.byteLength;
34+
return result;
35+
} else if (value instanceof String) {
36+
// @ts-ignore value not instance of `string` does changetype<string>(value) work here?
37+
let buffer = String.UTF8.encode(value);
38+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
39+
result.data = buffer;
40+
result.dataStart = changetype<usize>(buffer);
41+
result.dataLength = buffer.byteLength;
42+
return result;
43+
} else if (value instanceof Buffer) {
44+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
45+
result.data = value.buffer;
46+
result.dataStart = value.dataStart;
47+
result.dataLength = value.dataLength;
48+
return result;
49+
} else if (value instanceof ArrayBufferView) {
50+
let length = value.length;
51+
let buffer = __alloc(length, idof<ArrayBuffer>());
52+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
53+
// @ts-ignore: value[i] is implied to work
54+
for (let i = 0; i < length; i++) store<u8>(buffer + usize(i), u8(unchecked(value[i])));
55+
result.data = changetype<ArrayBuffer>(buffer);
56+
result.dataStart = buffer;
57+
result.dataLength = u32(length);
58+
return result;
59+
}
60+
ERROR("Cannot call Buffer.from<T>() where T is not a string, Buffer, ArrayBuffer, Array, or Array-like Object.");
61+
}
2562
}

assembly/node.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,6 @@ declare class Buffer extends Uint8Array {
33
static alloc(size: i32): Buffer;
44
/** This method allocates a new Buffer of indicated size. This is unsafe because the data is not zeroed. */
55
static allocUnsafe(size: i32): Buffer;
6+
/** This method creates a Buffer from the given reference. This method is naive and defaults to utf8 encoding for strings. */
7+
static from<T>(value: T): Buffer;
68
}

tests/buffer.spec.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
function bufferFrom<T>(values: valueof<T>[]): T {
2+
let buffer = instantiate<T>(values.length);
3+
// @ts-ignore
4+
for (let i = 0; i < values.length; i++) buffer[i] = values[i];
5+
return buffer;
6+
}
7+
18
/**
29
* This is the buffer test suite. For each prototype function, put a single test
310
* function call here.
@@ -42,4 +49,42 @@ describe("buffer", () => {
4249
// TODO: expectFn(() => { Buffer.allocUnsafe(-1); }).toThrow();
4350
// TODO: expectFn(() => { Buffer.allocUnsafe(BLOCK_MAXSIZE + 1); }).toThrow();
4451
});
52+
53+
54+
/**
55+
* This specification is a tradeoff, because Buffer.from() takes _many_ parameters.
56+
* Instead, the only common parameter is the first one, which results in Buffer.from
57+
* acting in a very naive fashion. Perhaps an optional encoding parameter might be
58+
* possible for strings, at least. However, this makes things more complicated.
59+
* There are no good solutions. Only tradeoffs. Function overloading is the only
60+
* way to fix this problem.
61+
*/
62+
test("#from", () => {
63+
// TODO: Switch to expect<Buffer>() when 2.2.1 releases
64+
65+
// Buffer.from uses the array buffer reference
66+
let buff = new ArrayBuffer(100);
67+
for (let i = 0; i < 100; i++) store<u8>(changetype<usize>(buff), u8(i));
68+
let abBuffer = Buffer.from<ArrayBuffer>(buff);
69+
expect<ArrayBuffer>(abBuffer.buffer).toStrictEqual(buff);
70+
expect<ArrayBuffer>(abBuffer.buffer).toBe(buff);
71+
72+
// strings are utf8 encoded by default
73+
let strBuffer = Buffer.from<string>("Hello world!");
74+
let strBufferExpected = bufferFrom<Buffer>([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21]);
75+
expect<ArrayBuffer>(strBuffer.buffer).toStrictEqual(strBufferExpected.buffer);
76+
77+
// buffer returns a new reference view to the same ArrayBuffer
78+
let buff2 = Buffer.from<Buffer>(abBuffer);
79+
expect<Buffer>(buff2).not.toBe(abBuffer);
80+
expect<ArrayBuffer>(buff2.buffer).toBe(abBuffer.buffer);
81+
expect<usize>(buff2.dataStart).toBe(abBuffer.dataStart);
82+
expect<u32>(buff2.dataLength).toBe(abBuffer.dataLength);
83+
84+
// else if it extends ArrayBufferView simply converts all the values
85+
let floats = bufferFrom<Float32Array>([1.1, 2.2, 3.3]);
86+
let floatBuff = Buffer.from<Float32Array>(floats);
87+
let floatBuffExpected = bufferFrom<Buffer>([1, 2, 3]);
88+
expect<ArrayBuffer>(floatBuff.buffer).toStrictEqual(floatBuffExpected.buffer);
89+
});
4590
});

0 commit comments

Comments
 (0)