diff --git a/package.json b/package.json index 241b102..1f6f13d 100644 --- a/package.json +++ b/package.json @@ -15,10 +15,12 @@ "node": { "module": "./src/mjs/index.js", "require": "./src/cjs/index.cjs", - "import": "./src/mjs/index.js" + "import": "./src/mjs/index.js", + "types": "./src/cjs/index.d.ts" }, "browser": "./src/mjs/browser.js", - "default": "./src/mjs/browser.js" + "default": "./src/mjs/browser.js", + "types": "./src/cjs/index.d.ts" }, "types": "src/cjs/index.d.ts", "type": "module", diff --git a/src/cjs/browser.cjs b/src/cjs/browser.cjs index 24ce7e8..8c1facf 100644 --- a/src/cjs/browser.cjs +++ b/src/cjs/browser.cjs @@ -1,6 +1,6 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.readUInt64 = exports.readUInt32 = exports.readUInt16 = exports.readUInt8 = exports.writeUInt64 = exports.writeUInt32 = exports.writeUInt16 = exports.writeUInt8 = exports.compare = exports.fromHex = exports.toHex = exports.toUtf8 = void 0; +exports.readUInt64 = exports.readUInt32 = exports.readUInt16 = exports.readUInt8 = exports.writeUInt64 = exports.writeUInt32 = exports.writeUInt16 = exports.writeUInt8 = exports.compare = exports.fromBase64 = exports.toBase64 = exports.fromHex = exports.toHex = exports.concat = exports.fromUtf8 = exports.toUtf8 = void 0; const HEX_STRINGS = "0123456789abcdefABCDEF"; const HEX_CODES = HEX_STRINGS.split("").map((c) => c.codePointAt(0)); const HEX_CODEPOINTS = Array(256) @@ -17,6 +17,21 @@ function toUtf8(bytes) { return DECODER.decode(bytes); } exports.toUtf8 = toUtf8; +function fromUtf8(s) { + return ENCODER.encode(s); +} +exports.fromUtf8 = fromUtf8; +function concat(arrays) { + const totalLength = arrays.reduce((a, b) => a + b.length, 0); + const result = new Uint8Array(totalLength); + let offset = 0; + for (const array of arrays) { + result.set(array, offset); + offset += array.length; + } + return result; +} +exports.concat = concat; // There are two implementations. // One optimizes for length of the bytes, and uses TextDecoder. // One optimizes for iteration count, and appends strings. @@ -60,6 +75,19 @@ function fromHex(hexString) { return i === resultBytes.length ? resultBytes : resultBytes.slice(0, i); } exports.fromHex = fromHex; +function toBase64(bytes) { + return btoa(String.fromCharCode(...bytes)); +} +exports.toBase64 = toBase64; +function fromBase64(base64) { + const binaryString = atob(base64); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes; +} +exports.fromBase64 = fromBase64; // Same behavior as Buffer.compare() function compare(v1, v2) { const minLength = Math.min(v1.length, v2.length); diff --git a/src/cjs/index.cjs b/src/cjs/index.cjs index 597469a..8665c71 100644 --- a/src/cjs/index.cjs +++ b/src/cjs/index.cjs @@ -1,10 +1,18 @@ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); -exports.readUInt64 = exports.readUInt32 = exports.readUInt16 = exports.readUInt8 = exports.writeUInt64 = exports.writeUInt32 = exports.writeUInt16 = exports.writeUInt8 = exports.compare = exports.fromHex = exports.toHex = exports.toUtf8 = void 0; +exports.readUInt64 = exports.readUInt32 = exports.readUInt16 = exports.readUInt8 = exports.writeUInt64 = exports.writeUInt32 = exports.writeUInt16 = exports.writeUInt8 = exports.compare = exports.fromBase64 = exports.toBase64 = exports.fromHex = exports.toHex = exports.concat = exports.fromUtf8 = exports.toUtf8 = void 0; function toUtf8(bytes) { return Buffer.from(bytes || []).toString(); } exports.toUtf8 = toUtf8; +function fromUtf8(s) { + return Uint8Array.from(Buffer.from(s || "", "utf8")); +} +exports.fromUtf8 = fromUtf8; +function concat(arrays) { + return Uint8Array.from(Buffer.concat(arrays)); +} +exports.concat = concat; function toHex(bytes) { return Buffer.from(bytes || []).toString("hex"); } @@ -13,6 +21,14 @@ function fromHex(hexString) { return Uint8Array.from(Buffer.from(hexString || "", "hex")); } exports.fromHex = fromHex; +function toBase64(bytes) { + return Buffer.from(bytes).toString("base64"); +} +exports.toBase64 = toBase64; +function fromBase64(base64) { + return Uint8Array.from(Buffer.from(base64 || "", "base64")); +} +exports.fromBase64 = fromBase64; function compare(v1, v2) { return Buffer.from(v1).compare(Buffer.from(v2)); } diff --git a/src/cjs/index.d.ts b/src/cjs/index.d.ts index d618b5b..5cbe104 100644 --- a/src/cjs/index.d.ts +++ b/src/cjs/index.d.ts @@ -1,6 +1,10 @@ export declare function toUtf8(bytes: Uint8Array): string; +export declare function fromUtf8(s: string): Uint8Array; +export declare function concat(arrays: Uint8Array[]): Uint8Array; export declare function toHex(bytes: Uint8Array): string; export declare function fromHex(hexString: string): Uint8Array; +export declare function toBase64(bytes: Uint8Array): string; +export declare function fromBase64(base64: string): Uint8Array; export declare type CompareResult = -1 | 0 | 1; export declare function compare(v1: Uint8Array, v2: Uint8Array): CompareResult; export declare type endian = "LE" | "BE" | "le" | "be"; diff --git a/src/mjs/browser.js b/src/mjs/browser.js index 171ab80..aaab7e4 100644 --- a/src/mjs/browser.js +++ b/src/mjs/browser.js @@ -13,6 +13,19 @@ const DECODER = new TextDecoder(); export function toUtf8(bytes) { return DECODER.decode(bytes); } +export function fromUtf8(s) { + return ENCODER.encode(s); +} +export function concat(arrays) { + const totalLength = arrays.reduce((a, b) => a + b.length, 0); + const result = new Uint8Array(totalLength); + let offset = 0; + for (const array of arrays) { + result.set(array, offset); + offset += array.length; + } + return result; +} // There are two implementations. // One optimizes for length of the bytes, and uses TextDecoder. // One optimizes for iteration count, and appends strings. @@ -54,6 +67,17 @@ export function fromHex(hexString) { } return i === resultBytes.length ? resultBytes : resultBytes.slice(0, i); } +export function toBase64(bytes) { + return btoa(String.fromCharCode(...bytes)); +} +export function fromBase64(base64) { + const binaryString = atob(base64); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes; +} // Same behavior as Buffer.compare() export function compare(v1, v2) { const minLength = Math.min(v1.length, v2.length); diff --git a/src/mjs/index.js b/src/mjs/index.js index 53004d5..8e41336 100644 --- a/src/mjs/index.js +++ b/src/mjs/index.js @@ -1,12 +1,24 @@ export function toUtf8(bytes) { return Buffer.from(bytes || []).toString(); } +export function fromUtf8(s) { + return Uint8Array.from(Buffer.from(s || "", "utf8")); +} +export function concat(arrays) { + return Uint8Array.from(Buffer.concat(arrays)); +} export function toHex(bytes) { return Buffer.from(bytes || []).toString("hex"); } export function fromHex(hexString) { return Uint8Array.from(Buffer.from(hexString || "", "hex")); } +export function toBase64(bytes) { + return Buffer.from(bytes).toString("base64"); +} +export function fromBase64(base64) { + return Uint8Array.from(Buffer.from(base64 || "", "base64")); +} export function compare(v1, v2) { return Buffer.from(v1).compare(Buffer.from(v2)); } diff --git a/ts_src/browser.ts b/ts_src/browser.ts index f8b6c7f..b2bcad5 100644 --- a/ts_src/browser.ts +++ b/ts_src/browser.ts @@ -15,6 +15,21 @@ export function toUtf8(bytes: Uint8Array): string { return DECODER.decode(bytes); } +export function fromUtf8(s: string): Uint8Array { + return ENCODER.encode(s); +} + +export function concat(arrays: Uint8Array[]): Uint8Array { + const totalLength = arrays.reduce((a, b) => a + b.length, 0); + const result = new Uint8Array(totalLength); + let offset = 0; + for (const array of arrays) { + result.set(array, offset); + offset += array.length; + } + return result; +} + // There are two implementations. // One optimizes for length of the bytes, and uses TextDecoder. // One optimizes for iteration count, and appends strings. @@ -58,6 +73,19 @@ export function fromHex(hexString: string): Uint8Array { return i === resultBytes.length ? resultBytes : resultBytes.slice(0, i); } +export function toBase64(bytes: Uint8Array): string { + return btoa(String.fromCharCode(...bytes)); +} + +export function fromBase64(base64: string): Uint8Array { + const binaryString = atob(base64); + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes; +} + export type CompareResult = -1 | 0 | 1; // Same behavior as Buffer.compare() export function compare(v1: Uint8Array, v2: Uint8Array): CompareResult { diff --git a/ts_src/index.ts b/ts_src/index.ts index cd5cbeb..d69ffbb 100644 --- a/ts_src/index.ts +++ b/ts_src/index.ts @@ -2,6 +2,14 @@ export function toUtf8(bytes: Uint8Array): string { return Buffer.from(bytes || []).toString(); } +export function fromUtf8(s: string): Uint8Array { + return Uint8Array.from(Buffer.from(s || "", "utf8")); +} + +export function concat(arrays: Uint8Array[]): Uint8Array { + return Uint8Array.from(Buffer.concat(arrays)); +} + export function toHex(bytes: Uint8Array): string { return Buffer.from(bytes || []).toString("hex"); } @@ -10,6 +18,14 @@ export function fromHex(hexString: string): Uint8Array { return Uint8Array.from(Buffer.from(hexString || "", "hex")); } +export function toBase64(bytes: Uint8Array): string { + return Buffer.from(bytes).toString("base64"); +} + +export function fromBase64(base64: string): Uint8Array { + return Uint8Array.from(Buffer.from(base64 || "", "base64")); +} + export type CompareResult = -1 | 0 | 1; export function compare(v1: Uint8Array, v2: Uint8Array): CompareResult { return Buffer.from(v1).compare(Buffer.from(v2)) as CompareResult; diff --git a/ts_src/tests.spec.ts b/ts_src/tests.spec.ts index ef95876..d92aaf0 100644 --- a/ts_src/tests.spec.ts +++ b/ts_src/tests.spec.ts @@ -53,6 +53,12 @@ describe(`Uint8Array tools`, () => { expect(tools.toUtf8(longBytes2)).toEqual(longUtf8); expect((tools.toUtf8 as any)()).toEqual(""); }); + it("should read utf8 with fromUtf8", () => { + expect(tools.fromUtf8(utf8)).toEqual(bytes3); + expect(tools.fromUtf8(str)).toEqual(testBytes); + expect(tools.fromUtf8(longUtf8)).toEqual(longBytes2); + expect((tools.fromUtf8 as any)()).toEqual(f([])); + }); it(`should compare Uint8Arrays`, () => { expect(tools.compare(bytes, bytes2)).toBe(-1); expect(tools.compare(bytes, bytes)).toBe(0); @@ -62,6 +68,71 @@ describe(`Uint8Array tools`, () => { expect(tools.compare(bytes2, bytes2LargerLeft)).toBe(1); expect(tools.compare(bytes2LargerLeft, bytes2)).toBe(-1); }); + it("should concat Uint8Arrays", () => { + const fixtures = [ + [new Uint8Array([]), new Uint8Array([])], + [new Uint8Array([1]), new Uint8Array([1])], + [new Uint8Array([])], + [new Uint8Array()], + [...new Array(1000)].map(() => new Uint8Array([123])), + ]; + + for (const fixture of fixtures) { + expect(new Uint8Array(Buffer.concat(fixture))).toEqual( + tools.concat(fixture) + ); + } + }); + it("should read from base64", () => { + const fixtures = [ + Buffer.from("").toString("base64"), + Buffer.from("a").toString("base64"), + Buffer.from("ab").toString("base64"), + Buffer.from("abc").toString("base64"), + Buffer.from("abcd").toString("base64"), + Buffer.from("abcde").toString("base64"), + Buffer.from("abcdef").toString("base64"), + Buffer.from("abcdefg").toString("base64"), + Buffer.from("abcdefgh").toString("base64"), + Buffer.from("abcdefghi").toString("base64"), + Buffer.from("abcdefghij").toString("base64"), + Buffer.from("abcdefghijk").toString("base64"), + Buffer.from("abcdefghijkl").toString("base64"), + Buffer.from("abcdefghijklm").toString("base64"), + Buffer.from("abcdefghijklmn").toString("base64"), + ]; + + for (const fixture of fixtures) { + expect(tools.fromBase64(fixture)).toEqual( + Uint8Array.from(Buffer.from(fixture, "base64")) + ); + } + }); + + it("should write to base64", () => { + const fixtures = [ + Buffer.from("").toString("base64"), + Buffer.from("a").toString("base64"), + Buffer.from("ab").toString("base64"), + Buffer.from("abc").toString("base64"), + Buffer.from("abcd").toString("base64"), + Buffer.from("abcde").toString("base64"), + Buffer.from("abcdef").toString("base64"), + Buffer.from("abcdefg").toString("base64"), + Buffer.from("abcdefgh").toString("base64"), + Buffer.from("abcdefghi").toString("base64"), + Buffer.from("abcdefghij").toString("base64"), + Buffer.from("abcdefghijk").toString("base64"), + Buffer.from("abcdefghijkl").toString("base64"), + Buffer.from("abcdefghijklm").toString("base64"), + Buffer.from("abcdefghijklmn").toString("base64"), + ]; + + for (const fixture of fixtures) { + expect(tools.toBase64(tools.fromBase64(fixture))).toEqual(fixture); + } + }); + it("should writeUint8", () => { const hexs = ["03", "fd"];