From ceefef688ab9b87cd91f39d805196ec51808ea7b Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Thu, 13 Oct 2022 18:10:38 +0200 Subject: [PATCH 1/2] Add convenience function for creating a DataView --- src/bytes.test.ts | 55 +++++++++++++++++++++++++++++++++++++++++++++++ src/bytes.ts | 32 +++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/src/bytes.test.ts b/src/bytes.test.ts index 1fa03967..aeca2412 100644 --- a/src/bytes.test.ts +++ b/src/bytes.test.ts @@ -7,6 +7,7 @@ import { bytesToSignedBigInt, bytesToString, concatBytes, + createDataView, hexToBytes, isBytes, numberToBytes, @@ -414,3 +415,57 @@ describe('concatBytes', () => { ).toStrictEqual(Uint8Array.from([1, 2, 3, 52, 5])); }); }); + +describe('createDataView', () => { + it('returns a DataView from a byte array', () => { + const dataView = createDataView(new Uint8Array([1, 2, 3])); + + expect(dataView).toBeInstanceOf(DataView); + expect(dataView.getUint8(0)).toBe(1); + expect(dataView.getUint8(1)).toBe(2); + expect(dataView.getUint8(2)).toBe(3); + }); + + it('returns a DataView from a subarray of a byte array', () => { + const original = new Uint8Array([1, 2, 3, 4, 5]); + const subset = original.subarray(1, 4); + + const dataView = createDataView(subset); + + expect(dataView).toBeInstanceOf(DataView); + expect(dataView.byteOffset).toBe(1); + expect(dataView.byteLength).toBe(3); + expect(dataView.getUint8(0)).toBe(2); + expect(dataView.getUint8(1)).toBe(3); + expect(dataView.getUint8(2)).toBe(4); + }); + + it('returns a DataView from a slice of a byte array', () => { + const original = new Uint8Array([1, 2, 3, 4, 5]); + const subset = original.slice(1, 4); + + const dataView = createDataView(subset); + + expect(dataView).toBeInstanceOf(DataView); + expect(dataView.byteOffset).toBe(0); + expect(dataView.byteLength).toBe(3); + expect(dataView.getUint8(0)).toBe(2); + expect(dataView.getUint8(1)).toBe(3); + expect(dataView.getUint8(2)).toBe(4); + }); + + it('allows overriding the byte offset and length', () => { + const original = new Uint8Array([1, 2, 3, 4, 5]); + const subset = original.slice(1, 4); + + const dataView = createDataView(subset, 1, 1); + + expect(dataView).toBeInstanceOf(DataView); + expect(dataView.byteOffset).toBe(1); + expect(dataView.byteLength).toBe(1); + expect(dataView.getUint8(0)).toBe(3); + expect(() => dataView.getUint8(1)).toThrow( + 'Offset is outside the bounds of the DataView', + ); + }); +}); diff --git a/src/bytes.ts b/src/bytes.ts index 6b676aaa..bca78d78 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -393,3 +393,35 @@ export function concatBytes(values: Bytes[]): Uint8Array { return bytes; } + +/** + * Create a {@link DataView} from a {@link Uint8Array}. This is a convenience + * function that avoids having to create a {@link DataView} manually, which + * requires passing the `byteOffset` and `byteLength` parameters every time. + * + * Not passing the `byteOffset` and `byteLength` parameters can result in + * unexpected behavior when the {@link Uint8Array} is a view of a larger + * {@link ArrayBuffer}, e.g., when using {@link Uint8Array.subarray}. + * + * @example + * ```typescript + * const bytes = new Uint8Array([1, 2, 3]); + * + * // This is equivalent to: + * // const dataView = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); + * const dataView = createDataView(bytes); + * ``` + * @param bytes - The bytes to create the {@link DataView} from. + * @param byteOffset - The offset of the first byte to read. Defaults to + * `bytes.byteOffset`. + * @param byteLength - The length of the bytes to read. Defaults to + * `bytes.byteLength`. + * @returns The {@link DataView}. + */ +export function createDataView( + bytes: Uint8Array, + byteOffset = bytes.byteOffset, + byteLength = bytes.byteLength, +): DataView { + return new DataView(bytes.buffer, byteOffset, byteLength); +} From 2347810fce1adc52bf8f214e8201853765624f80 Mon Sep 17 00:00:00 2001 From: Maarten Zuidhoorn Date: Thu, 13 Oct 2022 22:03:55 +0200 Subject: [PATCH 2/2] Support Node.js Buffer --- src/bytes.test.ts | 122 ++++++++++++++++++++++++++++------------------ src/bytes.ts | 23 +++++---- 2 files changed, 87 insertions(+), 58 deletions(-) diff --git a/src/bytes.test.ts b/src/bytes.test.ts index aeca2412..140ff08b 100644 --- a/src/bytes.test.ts +++ b/src/bytes.test.ts @@ -417,55 +417,81 @@ describe('concatBytes', () => { }); describe('createDataView', () => { - it('returns a DataView from a byte array', () => { - const dataView = createDataView(new Uint8Array([1, 2, 3])); - - expect(dataView).toBeInstanceOf(DataView); - expect(dataView.getUint8(0)).toBe(1); - expect(dataView.getUint8(1)).toBe(2); - expect(dataView.getUint8(2)).toBe(3); - }); - - it('returns a DataView from a subarray of a byte array', () => { - const original = new Uint8Array([1, 2, 3, 4, 5]); - const subset = original.subarray(1, 4); - - const dataView = createDataView(subset); - - expect(dataView).toBeInstanceOf(DataView); - expect(dataView.byteOffset).toBe(1); - expect(dataView.byteLength).toBe(3); - expect(dataView.getUint8(0)).toBe(2); - expect(dataView.getUint8(1)).toBe(3); - expect(dataView.getUint8(2)).toBe(4); - }); - - it('returns a DataView from a slice of a byte array', () => { - const original = new Uint8Array([1, 2, 3, 4, 5]); - const subset = original.slice(1, 4); - - const dataView = createDataView(subset); - - expect(dataView).toBeInstanceOf(DataView); - expect(dataView.byteOffset).toBe(0); - expect(dataView.byteLength).toBe(3); - expect(dataView.getUint8(0)).toBe(2); - expect(dataView.getUint8(1)).toBe(3); - expect(dataView.getUint8(2)).toBe(4); + describe('Uint8Array', () => { + it('returns a DataView from a byte array', () => { + const dataView = createDataView(new Uint8Array([1, 2, 3])); + + expect(dataView).toBeInstanceOf(DataView); + expect(dataView.getUint8(0)).toBe(1); + expect(dataView.getUint8(1)).toBe(2); + expect(dataView.getUint8(2)).toBe(3); + }); + + it('returns a DataView from a subarray of a byte array', () => { + const original = new Uint8Array([1, 2, 3, 4, 5]); + const subset = original.subarray(1, 4); + + const dataView = createDataView(subset); + + expect(dataView).toBeInstanceOf(DataView); + expect(dataView.byteOffset).toBe(1); + expect(dataView.byteLength).toBe(3); + expect(dataView.getUint8(0)).toBe(2); + expect(dataView.getUint8(1)).toBe(3); + expect(dataView.getUint8(2)).toBe(4); + }); + + it('returns a DataView from a slice of a byte array', () => { + const original = new Uint8Array([1, 2, 3, 4, 5]); + const subset = original.slice(1, 4); + + const dataView = createDataView(subset); + + expect(dataView).toBeInstanceOf(DataView); + expect(dataView.byteOffset).toBe(0); + expect(dataView.byteLength).toBe(3); + expect(dataView.getUint8(0)).toBe(2); + expect(dataView.getUint8(1)).toBe(3); + expect(dataView.getUint8(2)).toBe(4); + }); }); - it('allows overriding the byte offset and length', () => { - const original = new Uint8Array([1, 2, 3, 4, 5]); - const subset = original.slice(1, 4); - - const dataView = createDataView(subset, 1, 1); - - expect(dataView).toBeInstanceOf(DataView); - expect(dataView.byteOffset).toBe(1); - expect(dataView.byteLength).toBe(1); - expect(dataView.getUint8(0)).toBe(3); - expect(() => dataView.getUint8(1)).toThrow( - 'Offset is outside the bounds of the DataView', - ); + describe('Node.js Buffer', () => { + it('returns a DataView from a byte array', () => { + const dataView = createDataView(Buffer.from([1, 2, 3])); + + expect(dataView).toBeInstanceOf(DataView); + expect(dataView.getUint8(0)).toBe(1); + expect(dataView.getUint8(1)).toBe(2); + expect(dataView.getUint8(2)).toBe(3); + }); + + it('returns a DataView from a subarray of a byte array', () => { + const original = Buffer.from([1, 2, 3, 4, 5]); + const subset = original.subarray(1, 4); + + const dataView = createDataView(subset); + + expect(dataView).toBeInstanceOf(DataView); + expect(dataView.byteOffset).toBe(0); + expect(dataView.byteLength).toBe(3); + expect(dataView.getUint8(0)).toBe(2); + expect(dataView.getUint8(1)).toBe(3); + expect(dataView.getUint8(2)).toBe(4); + }); + + it('returns a DataView from a slice of a byte array', () => { + const original = Buffer.from([1, 2, 3, 4, 5]); + const subset = original.slice(1, 4); + + const dataView = createDataView(subset); + + expect(dataView).toBeInstanceOf(DataView); + expect(dataView.byteOffset).toBe(0); + expect(dataView.byteLength).toBe(3); + expect(dataView.getUint8(0)).toBe(2); + expect(dataView.getUint8(1)).toBe(3); + expect(dataView.getUint8(2)).toBe(4); + }); }); }); diff --git a/src/bytes.ts b/src/bytes.ts index bca78d78..f31dc2a2 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -403,6 +403,8 @@ export function concatBytes(values: Bytes[]): Uint8Array { * unexpected behavior when the {@link Uint8Array} is a view of a larger * {@link ArrayBuffer}, e.g., when using {@link Uint8Array.subarray}. * + * This function also supports Node.js {@link Buffer}s. + * * @example * ```typescript * const bytes = new Uint8Array([1, 2, 3]); @@ -412,16 +414,17 @@ export function concatBytes(values: Bytes[]): Uint8Array { * const dataView = createDataView(bytes); * ``` * @param bytes - The bytes to create the {@link DataView} from. - * @param byteOffset - The offset of the first byte to read. Defaults to - * `bytes.byteOffset`. - * @param byteLength - The length of the bytes to read. Defaults to - * `bytes.byteLength`. * @returns The {@link DataView}. */ -export function createDataView( - bytes: Uint8Array, - byteOffset = bytes.byteOffset, - byteLength = bytes.byteLength, -): DataView { - return new DataView(bytes.buffer, byteOffset, byteLength); +export function createDataView(bytes: Uint8Array): DataView { + if (typeof Buffer !== 'undefined' && bytes instanceof Buffer) { + const buffer = bytes.buffer.slice( + bytes.byteOffset, + bytes.byteOffset + bytes.byteLength, + ); + + return new DataView(buffer); + } + + return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength); }