Skip to content

Commit

Permalink
[Implement] Naive Buffer.from
Browse files Browse the repository at this point in the history
  • Loading branch information
jtenner committed Jul 19, 2019
1 parent c3a162d commit 96b1872
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 0 deletions.
37 changes: 37 additions & 0 deletions assembly/buffer/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { BLOCK_MAXSIZE } from "rt/common";
import { E_INVALIDLENGTH, E_INDEXOUTOFRANGE } from "util/error";
import { Uint8Array } from "typedarray";
import { ArrayBufferView } from "arraybuffer";

export class Buffer extends Uint8Array {
constructor(size: i32) {
Expand All @@ -22,4 +23,40 @@ export class Buffer extends Uint8Array {
result.dataLength = size;
return result;
}

// @ts-ignore: Buffer returns on all valid branches
public static from<T>(value: T): Buffer {
if (value instanceof ArrayBuffer) {
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
result.data = value;
result.dataStart = changetype<usize>(value);
result.dataLength = value.byteLength;
return result;
} else if (value instanceof String) {
// @ts-ignore value not instance of `string` does changetype<string>(value) work here?
let buffer = String.UTF8.encode(value);
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
result.data = buffer;
result.dataStart = changetype<usize>(buffer);
result.dataLength = buffer.byteLength;
return result;
} else if (value instanceof Buffer) {
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
result.data = value.buffer;
result.dataStart = value.dataStart;
result.dataLength = value.dataLength;
return result;
} else if (value instanceof ArrayBufferView) {
let length = value.length;
let buffer = __alloc(length, idof<ArrayBuffer>());
let result = changetype<Buffer>(__alloc(offsetof<Buffer>(), idof<Buffer>()));
// @ts-ignore: value[i] is implied to work
for (let i = 0; i < length; i++) store<u8>(buffer + usize(i), u8(unchecked(value[i])));
result.data = changetype<ArrayBuffer>(buffer);
result.dataStart = buffer;
result.dataLength = u32(length);
return result;
}
ERROR("Cannot call Buffer.from<T>() where T is not a string, Buffer, ArrayBuffer, Array, or Array-like Object.");
}
}
2 changes: 2 additions & 0 deletions assembly/node.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@ declare class Buffer extends Uint8Array {
static alloc(size: i32): Buffer;
/** This method allocates a new Buffer of indicated size. This is unsafe because the data is not zeroed. */
static allocUnsafe(size: i32): Buffer;
/** This method creates a Buffer from the given reference. This method is naive and defaults to utf8 encoding for strings. */
static from<T>(value: T): Buffer;
}
45 changes: 45 additions & 0 deletions tests/buffer.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
function bufferFrom<T>(values: valueof<T>[]): T {
let buffer = instantiate<T>(values.length);
// @ts-ignore
for (let i = 0; i < values.length; i++) buffer[i] = values[i];
return buffer;
}

/**
* This is the buffer test suite. For each prototype function, put a single test
* function call here.
Expand Down Expand Up @@ -42,4 +49,42 @@ describe("buffer", () => {
// TODO: expectFn(() => { Buffer.allocUnsafe(-1); }).toThrow();
// TODO: expectFn(() => { Buffer.allocUnsafe(BLOCK_MAXSIZE + 1); }).toThrow();
});


/**
* This specification is a tradeoff, because Buffer.from() takes _many_ parameters.
* Instead, the only common parameter is the first one, which results in Buffer.from
* acting in a very naive fashion. Perhaps an optional encoding parameter might be
* possible for strings, at least. However, this makes things more complicated.
* There are no good solutions. Only tradeoffs. Function overloading is the only
* way to fix this problem.
*/
test("#from", () => {
// TODO: Switch to expect<Buffer>() when 2.2.1 releases

// Buffer.from uses the array buffer reference
let buff = new ArrayBuffer(100);
for (let i = 0; i < 100; i++) store<u8>(changetype<usize>(buff), u8(i));
let abBuffer = Buffer.from<ArrayBuffer>(buff);
expect<ArrayBuffer>(abBuffer.buffer).toStrictEqual(buff);
expect<ArrayBuffer>(abBuffer.buffer).toBe(buff);

// strings are utf8 encoded by default
let strBuffer = Buffer.from<string>("Hello world!");
let strBufferExpected = bufferFrom<Buffer>([0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21]);
expect<ArrayBuffer>(strBuffer.buffer).toStrictEqual(strBufferExpected.buffer);

// buffer returns a new reference view to the same ArrayBuffer
let buff2 = Buffer.from<Buffer>(abBuffer);
expect<Buffer>(buff2).not.toBe(abBuffer);
expect<ArrayBuffer>(buff2.buffer).toBe(abBuffer.buffer);
expect<usize>(buff2.dataStart).toBe(abBuffer.dataStart);
expect<u32>(buff2.dataLength).toBe(abBuffer.dataLength);

// else if it extends ArrayBufferView simply converts all the values
let floats = bufferFrom<Float32Array>([1.1, 2.2, 3.3]);
let floatBuff = Buffer.from<Float32Array>(floats);
let floatBuffExpected = bufferFrom<Buffer>([1, 2, 3]);
expect<ArrayBuffer>(floatBuff.buffer).toStrictEqual(floatBuffExpected.buffer);
});
});

0 comments on commit 96b1872

Please sign in to comment.