Skip to content

Commit

Permalink
feat(std/node): support hex / base64 encoding in Buffer (#6414)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcosc90 authored Jun 21, 2020
1 parent 40866d7 commit 86448fd
Show file tree
Hide file tree
Showing 2 changed files with 203 additions and 5 deletions.
64 changes: 59 additions & 5 deletions std/node/buffer.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
import * as hex from "../encoding/hex.ts";
import * as base64 from "../encoding/base64.ts";
import { notImplemented } from "./_utils.ts";

const validEncodings = ["utf8", "hex", "base64"];
const notImplementedEncodings = [
"utf16le",
"latin1",
"ascii",
"binary",
"ucs2",
];

function checkEncoding(encoding = "utf8", strict = true): string {
if (typeof encoding !== "string" || (strict && encoding === "")) {
if (!strict) return "utf8";
throw new TypeError(`Unkown encoding: ${encoding}`);
}

encoding = encoding.toLowerCase();
if (encoding === "utf-8" || encoding === "") {
return "utf8";
}

if (notImplementedEncodings.includes(encoding)) {
notImplemented(`"${encoding}" encoding`);
}

if (!validEncodings.includes(encoding)) {
throw new TypeError(`Unkown encoding: ${encoding}`);
}

return encoding;
}

/**
* See also https://nodejs.org/api/buffer.html
*/
Expand Down Expand Up @@ -66,11 +101,24 @@ export default class Buffer extends Uint8Array {
/**
* Creates a new Buffer containing string.
*/
static from(string: string): Buffer;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
static from(value: any, offset?: number, length?: number): Buffer {
if (typeof value == "string")
static from(string: string, encoding?: string): Buffer;
static from(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any,
offsetOrEncoding?: number | string,
length?: number
): Buffer {
const offset =
typeof offsetOrEncoding === "string" ? undefined : offsetOrEncoding;
let encoding =
typeof offsetOrEncoding === "string" ? offsetOrEncoding : undefined;

if (typeof value == "string") {
encoding = checkEncoding(encoding, false);
if (encoding === "hex") return new Buffer(hex.decodeString(value).buffer);
if (encoding === "base64") return new Buffer(base64.decode(value));
return new Buffer(new TextEncoder().encode(value).buffer);
}

// workaround for https://github.com/microsoft/TypeScript/issues/38446
return new Buffer(value, offset!, length);
Expand Down Expand Up @@ -246,7 +294,13 @@ export default class Buffer extends Uint8Array {
* encoding. start and end may be passed to decode only a subset of buf.
*/
toString(encoding = "utf8", start = 0, end = this.length): string {
return new TextDecoder(encoding).decode(this.subarray(start, end));
encoding = checkEncoding(encoding);

const b = this.subarray(start, end);
if (encoding === "hex") return hex.encodeToString(b);
if (encoding === "base64") return base64.encode(b.buffer);

return new TextDecoder(encoding).decode(b);
}

/**
Expand Down
144 changes: 144 additions & 0 deletions std/node/buffer_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,150 @@ Deno.test({
},
});

Deno.test({
name: "Buffer from string hex",
fn() {
for (const encoding of ["hex", "HEX"]) {
const buffer: Buffer = Buffer.from(
"7468697320697320612074c3a97374",
encoding
);
assertEquals(buffer.length, 15, "Buffer length should be 15");
assertEquals(
buffer.toString(),
"this is a tést",
"Buffer to string should recover the string"
);
}
},
});

Deno.test({
name: "Buffer from string base64",
fn() {
for (const encoding of ["base64", "BASE64"]) {
const buffer: Buffer = Buffer.from("dGhpcyBpcyBhIHTDqXN0", encoding);
assertEquals(buffer.length, 15, "Buffer length should be 15");
assertEquals(
buffer.toString(),
"this is a tést",
"Buffer to string should recover the string"
);
}
},
});

Deno.test({
name: "Buffer to string base64",
fn() {
for (const encoding of ["base64", "BASE64"]) {
const buffer: Buffer = Buffer.from("deno land");
assertEquals(
buffer.toString(encoding),
"ZGVubyBsYW5k",
"Buffer to string should recover the string in base64"
);
}
const b64 = "dGhpcyBpcyBhIHTDqXN0";
assertEquals(Buffer.from(b64, "base64").toString("base64"), b64);
},
});

Deno.test({
name: "Buffer to string hex",
fn() {
for (const encoding of ["hex", "HEX"]) {
const buffer: Buffer = Buffer.from("deno land");
assertEquals(
buffer.toString(encoding),
"64656e6f206c616e64",
"Buffer to string should recover the string"
);
}
const hex = "64656e6f206c616e64";
assertEquals(Buffer.from(hex, "hex").toString("hex"), hex);
},
});

Deno.test({
name: "Buffer to string invalid encoding",
fn() {
const buffer: Buffer = Buffer.from("deno land");
const invalidEncodings = [null, 5, {}, true, false, "foo", ""];

for (const encoding of invalidEncodings) {
assertThrows(
() => {
// deno-lint-ignore ban-ts-comment
// @ts-ignore
buffer.toString(encoding);
},
TypeError,
`Unkown encoding: ${encoding}`,
"Should throw on invalid encoding"
);
}
},
});

Deno.test({
name: "Buffer from string invalid encoding",
fn() {
const defaultToUtf8Encodings = [null, 5, {}, true, false, ""];
const invalidEncodings = ["deno", "base645"];

for (const encoding of defaultToUtf8Encodings) {
// deno-lint-ignore ban-ts-comment
// @ts-ignore
assertEquals(Buffer.from("yes", encoding).toString(), "yes");
}

for (const encoding of invalidEncodings) {
assertThrows(
() => {
// deno-lint-ignore ban-ts-comment
// @ts-ignore
Buffer.from("yes", encoding);
},
TypeError,
`Unkown encoding: ${encoding}`
);
}
},
});

Deno.test({
name: "Buffer to/from string not implemented encodings",
fn() {
const buffer: Buffer = Buffer.from("deno land");
const notImplemented = ["ascii", "binary"];

for (const encoding of notImplemented) {
assertThrows(
() => {
// deno-lint-ignore ban-ts-comment
// @ts-ignore
buffer.toString(encoding);
},
Error,
`"${encoding}" encoding`,
"Should throw on invalid encoding"
);

assertThrows(
() => {
// deno-lint-ignore ban-ts-comment
// @ts-ignore
Buffer.from("", encoding);
},
Error,
`"${encoding}" encoding`,
"Should throw on invalid encoding"
);
}
},
});

Deno.test({
name: "Buffer from another buffer creates a Buffer",
fn() {
Expand Down

0 comments on commit 86448fd

Please sign in to comment.