Skip to content

Commit 62b4dc9

Browse files
committed
[Implement] Naive Buffer.from
1 parent c3a162d commit 62b4dc9

File tree

3 files changed

+106
-0
lines changed

3 files changed

+106
-0
lines changed

assembly/buffer/index.ts

Lines changed: 54 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,57 @@ 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+
// @ts-ignore: AssemblyScript treats this statement correctly
30+
if (value instanceof String[]) {
31+
let length = value.length;
32+
log<i32>(length);
33+
let buffer = __alloc(length, idof<ArrayBuffer>());
34+
let sourceStart = value.dataStart;
35+
for (let i = 0; i < length; i++) {
36+
let str = changetype<string>(load<usize>(sourceStart + (usize(i) << alignof<usize>())));
37+
let value = parseFloat(str); // parseFloat is still naive
38+
store<u8>(buffer + usize(i), isFinite<f64>(value) ? u8(value) : u8(0));
39+
}
40+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
41+
result.data = changetype<ArrayBuffer>(buffer);
42+
result.dataStart = changetype<usize>(value);
43+
result.dataLength = length;
44+
return result;
45+
} else if (value instanceof ArrayBuffer) {
46+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
47+
result.data = value;
48+
result.dataStart = changetype<usize>(value);
49+
result.dataLength = value.byteLength;
50+
return result;
51+
} else if (value instanceof String) {
52+
// @ts-ignore value not instance of `string` does changetype<string>(value) work here?
53+
let buffer = String.UTF8.encode(value);
54+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
55+
result.data = buffer;
56+
result.dataStart = changetype<usize>(buffer);
57+
result.dataLength = buffer.byteLength;
58+
return result;
59+
} else if (value instanceof Buffer) {
60+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
61+
result.data = value.buffer;
62+
result.dataStart = value.dataStart;
63+
result.dataLength = value.dataLength;
64+
return result;
65+
} else if (value instanceof ArrayBufferView) {
66+
let length = value.length;
67+
let buffer = __alloc(length, idof<ArrayBuffer>());
68+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
69+
// @ts-ignore: value[i] is implied to work
70+
for (let i = 0; i < length; i++) store<u8>(buffer + usize(i), u8(unchecked(value[i])));
71+
result.data = changetype<ArrayBuffer>(buffer);
72+
result.dataStart = buffer;
73+
result.dataLength = u32(length);
74+
return result;
75+
76+
}
77+
ERROR("Cannot call Buffer.from<T>() where T is not a string, Buffer, ArrayBuffer, Array, or Array-like Object.");
78+
}
2579
}

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: 50 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,47 @@ 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+
90+
let strArrayExpected = bufferFrom<Buffer>([1, 2, 3, 4, 5, 6, 7, 0, 0, 0]);
91+
let stringValues: string[] = ["1.1", "2.2", "3.3", "4.4", "5.5", "6.6", "7.7", "Infinity", "NaN", "-Infinity"];
92+
let strArrayActual = Buffer.from<Array<String>>(stringValues);
93+
expect<ArrayBuffer>(strArrayActual.buffer).toStrictEqual(strArrayExpected.buffer);
94+
});
4595
});

0 commit comments

Comments
 (0)