Skip to content

Commit 45a61c5

Browse files
committed
[Implement] rest of buffer.from functions per dcode
1 parent a333399 commit 45a61c5

File tree

3 files changed

+155
-13
lines changed

3 files changed

+155
-13
lines changed

assembly/buffer/index.ts

Lines changed: 80 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { BLOCK_MAXSIZE, BLOCK, BLOCK_OVERHEAD } from "rt/common";
22
import { E_INVALIDLENGTH, E_INDEXOUTOFRANGE } from "util/error";
33
import { Uint8Array } from "typedarray";
44
import { ArrayBufferView } from "arraybuffer";
5+
import { Array } from "array";
6+
7+
58

69
export class Buffer extends Uint8Array {
710
constructor(size: i32) {
@@ -24,18 +27,85 @@ export class Buffer extends Uint8Array {
2427
return result;
2528
}
2629

30+
public static fromArrayBuffer(buffer: ArrayBuffer, byteOffset: i32 = 0, length: i32 = -1): Buffer {
31+
length = select(buffer.byteLength, length, length < 0);
32+
if (i32(byteOffset < 0) | i32(byteOffset > buffer.byteLength - length)) throw new RangeError(E_INDEXOUTOFRANGE);
33+
if (length == 0) return new Buffer(0);
34+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
35+
result.data = buffer; // retains
36+
result.dataStart = changetype<usize>(buffer) + <usize>byteOffset;
37+
result.dataLength = <usize>length;
38+
return result;
39+
}
40+
41+
public static fromString(value: string, encoding: string = "utf8"): Buffer {
42+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>())); // retains
43+
let buffer: ArrayBuffer;
44+
if (encoding == "utf8" || encoding == "utf-8") {
45+
buffer = String.UTF8.encode(value);
46+
} else if (encoding == "utf16le") {
47+
buffer = String.UTF16.encode(value);
48+
} else if (encoding == "hex") {
49+
buffer = Buffer.HEX.encode(value);
50+
// TODO: add ascii encoding
51+
} else {
52+
throw new TypeError("Invalid string encoding.");
53+
}
54+
55+
result.data = buffer; // retains
56+
result.dataStart = changetype<usize>(buffer);
57+
result.dataLength = buffer.byteLength;
58+
return result;
59+
}
60+
61+
public static fromArray<T extends ArrayBufferView>(value: T, offset: i32 = 0, length: i32 = -1): Buffer {
62+
length = select(value.length, length, length < 0);
63+
if (i32(offset < 0) | i32(offset > value.length)) throw new RangeError(E_INDEXOUTOFRANGE);
64+
if (length > value.length - offset) throw new RangeError(E_INVALIDLENGTH);
65+
if (length == 0) return new Buffer(0);
66+
let abv = __alloc(offsetof<Buffer>(), idof<Buffer>());
67+
let buffer = __alloc(length, idof<ArrayBuffer>());
68+
if (value instanceof Array<String>) {
69+
for (let i = 0; i < length; i++) {
70+
let index = i + offset;
71+
let byteValue = parseFloat(unchecked(value[index]));
72+
store<u8>(buffer + <usize>i, <u8>select(0, <u8>byteValue, isNaN(byteValue)));
73+
}
74+
} else {
75+
for (let i = 0; i < length; i++) {
76+
let index = i + offset;
77+
store<u8>(buffer + <usize>i, <u8>unchecked(value[index]));
78+
}
79+
}
80+
let result = changetype<Buffer>(abv); // retains
81+
result.data = changetype<ArrayBuffer>(buffer); // retains
82+
result.dataStart = buffer;
83+
result.dataLength = length;
84+
return result;
85+
}
86+
87+
public static fromBuffer(source: Buffer): Buffer {
88+
let length = source.dataLength;
89+
let data = changetype<ArrayBuffer>(__alloc(length, idof<ArrayBuffer>())); // retains
90+
memory.copy(changetype<usize>(data), source.dataStart, length);
91+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>())); // retains
92+
result.data = data;
93+
result.dataStart = changetype<usize>(data);
94+
result.dataLength = length;
95+
return result;
96+
}
97+
2798
// @ts-ignore: Buffer returns on all valid branches
28-
public static from<T extends ArrayBufferView>(value: T): Buffer {
99+
public static from<T>(value: T): Buffer {
29100
// @ts-ignore: AssemblyScript treats this statement correctly
30-
if (value instanceof String[]) {
101+
if (value instanceof Array<String>) {
31102
let length = <usize>value.length;
32103
let buffer = __alloc(length, idof<ArrayBuffer>());
33104
let sourceStart = value.dataStart;
34105
for (let i: usize = 0; i < length; i++) {
35106
let str = changetype<string>(load<usize>(sourceStart + (<usize>i << alignof<usize>())));
36107
let value = parseFloat(str); // parseFloat is still naive
37-
trace("float values", 2, value, u8(value));
38-
store<u8>(buffer + i, isFinite(value) ? u8(value) : u8(0));
108+
store<u8>(buffer + i, <u8>select(value, 0.0, isFinite(value)));
39109
}
40110
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
41111
result.data = changetype<ArrayBuffer>(buffer);
@@ -57,20 +127,20 @@ export class Buffer extends Uint8Array {
57127
result.dataLength = buffer.byteLength;
58128
return result;
59129
} else if (value instanceof Buffer) {
60-
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
130+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>())); // retains
61131
result.data = value.buffer;
62132
result.dataStart = value.dataStart;
63133
result.dataLength = value.dataLength;
64134
return result;
65135
} else if (value instanceof ArrayBufferView) {
66-
let length = value.length;
136+
let length = <usize>value.length;
67137
let buffer = __alloc(length, idof<ArrayBuffer>());
68-
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
138+
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>())); // retains
69139
// @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);
140+
for (let i = <usize>0; i < length; i++) store<u8>(buffer + i, <u8>unchecked(value[i]));
141+
result.data = changetype<ArrayBuffer>(buffer); // retains
72142
result.dataStart = buffer;
73-
result.dataLength = u32(length);
143+
result.dataLength = <u32>length;
74144
return result;
75145
}
76146
ERROR("Cannot call Buffer.from<T>() where T is not a string, Buffer, ArrayBuffer, Array, or Array-like Object.");

assembly/node.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@ declare class Buffer extends Uint8Array {
55
static allocUnsafe(size: i32): Buffer;
66
/** This method creates a Buffer from the given reference. This method is naive and defaults to utf8 encoding for strings. */
77
static from<T>(value: T): Buffer;
8+
/** This method creates a buffer from a given string. This method defaults to utf8 encoding. */
9+
public static fromString(value: string, encoding?: string): Buffer;
10+
/** This method creates a buffer that uses the given ArrayBuffer as an underlying value. */
11+
public static fromArrayBuffer(buffer: ArrayBuffer, byteOffset?: i32 , length?: i32): Buffer;
12+
/** This method creates a copy of the buffer using memory.copy(). */
13+
public static fromBuffer(source: Buffer): Buffer;
14+
/** This method creates a new Buffer by copying the underlying values to a new ArrayBuffer and coercing each one to an 8 bit integer value. */
15+
public static fromArray<T extends ArrayBufferView<number | string>>(value: T, offset?: i32, length?: i32): Buffer;
816
/** This method asserts a value is a Buffer object via `value instanceof Buffer`. */
917
static isBuffer<T>(value: T): bool;
1018
/** Reads a signed integer at the designated offset. */

tests/buffer.spec.ts

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function create<T>(values: valueof<T>[]): T {
2121
}
2222

2323
describe("buffer", () => {
24-
test("#constructor", () => {
24+
test(".constructor", () => {
2525
expect<Buffer>(new Buffer(0)).toBeTruthy();
2626
expect<Buffer>(new Buffer(10)).toHaveLength(10);
2727
let myBuffer = new Buffer(10);
@@ -31,7 +31,7 @@ describe("buffer", () => {
3131
// TODO: expectFn(() => { new Buffer(BLOCK_MAXSIZE + 1); }).toThrow();
3232
});
3333

34-
test("#alloc", () => {
34+
test(".alloc", () => {
3535
expect<Buffer>(Buffer.alloc(10)).toBeTruthy();
3636
expect<Buffer>(Buffer.alloc(10)).toHaveLength(10);
3737
let buff = Buffer.alloc(100);
@@ -42,7 +42,7 @@ describe("buffer", () => {
4242
// TODO: expectFn(() => { Buffer.alloc(BLOCK_MAXSIZE + 1); }).toThrow();
4343
});
4444

45-
test("#allocUnsafe", () => {
45+
test(".allocUnsafe", () => {
4646
expect<Buffer>(Buffer.allocUnsafe(10)).toBeTruthy();
4747
expect<Buffer>(Buffer.allocUnsafe(10)).toHaveLength(10);
4848
let buff = Buffer.allocUnsafe(100);
@@ -92,6 +92,70 @@ describe("buffer", () => {
9292
expect<Buffer>(strArrayActual).toStrictEqual(strArrayExpected, "Array Of Strings");
9393
});
9494

95+
test(".fromString", () => {
96+
// public static fromString(value: string, encoding: string = "utf8"): Buffer {
97+
// default encoding is utf8
98+
let expected = create<Buffer>([0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x61, 0x20, 0x74, 0xc3, 0xa9, 0x73, 0x74])
99+
expect<Buffer>(Buffer.from('this is a tést'))
100+
.toStrictEqual(expected);
101+
102+
expect<Buffer>(Buffer.fromString('7468697320697320612074c3a97374', 'hex'))
103+
.toStrictEqual(expected);
104+
});
105+
106+
test(".fromArrayBuffer", () => {
107+
const arr = new Uint16Array(2);
108+
109+
arr[0] = 5000;
110+
arr[1] = 4000;
111+
112+
// Shares memory with `arr`.
113+
const buf = Buffer.fromArrayBuffer(arr.buffer);
114+
115+
116+
expect<Buffer>(buf).toStrictEqual(create<Buffer>([0x88, 0x13, 0xa0, 0x0f]));
117+
118+
// Changing the original Uint16Array changes the Buffer also.
119+
arr[1] = 6000;
120+
expect<Buffer>(buf).toStrictEqual(create<Buffer>([0x88, 0x13, 0x70, 0x17]));
121+
122+
// test optional parameters
123+
expect<Buffer>(Buffer.fromArrayBuffer(arr.buffer, 1, 2)).toStrictEqual(create<Buffer>([0x13, 0x70]));
124+
125+
// TODO:
126+
// expectFn(() => {
127+
// let value = create<Uint16Array>([5000, 4000]); // 4 bytes
128+
// Buffer.fromArrayBuffer(value.buffer, 5);
129+
// }).toThrow("offset out of bounds should throw");
130+
// expectFn(() => {
131+
// let value = create<Uint16Array>([5000, 4000]); // 4 bytes
132+
// Buffer.fromArrayBuffer(value.buffer, 2, 3);
133+
// }).toThrow("length out of bounds should throw");
134+
});
135+
136+
test(".fromBuffer", () => {
137+
let buff1 = create<Buffer>([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
138+
let buff2 = Buffer.fromBuffer(buff1);
139+
140+
expect<Buffer>(buff1).not.toBe(buff2);
141+
expect<ArrayBuffer>(buff1.buffer).not.toBe(buff2.buffer);
142+
expect<Buffer>(buff1).toStrictEqual(buff2);
143+
});
144+
145+
test(".fromArray", () => {
146+
let buff1 = create<Uint16Array>([3, 6, 9, 12, 15, 18, 21]);
147+
let buff2 = Buffer.fromArray(buff1, 2, 4);
148+
let expected = create<Buffer>([9, 12, 15, 18]);
149+
expect<Buffer>(buff2).toStrictEqual(expected);
150+
151+
// test string values
152+
buff2 = Buffer.fromArray<string[]>(["9.2", "12.1", "15.3", "18.8"]);
153+
expect<Buffer>(buff2).toStrictEqual(expected);
154+
});
155+
156+
// todo: fromArray
157+
// todo: fromBuffer
158+
95159
test("#isBuffer", () => {
96160
let a = "";
97161
let b = new Uint8Array(0);

0 commit comments

Comments
 (0)