diff --git a/src/bytes.test.ts b/src/bytes.test.ts index 1fa03967..140ff08b 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,83 @@ describe('concatBytes', () => { ).toStrictEqual(Uint8Array.from([1, 2, 3, 52, 5])); }); }); + +describe('createDataView', () => { + 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); + }); + }); + + 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 6b676aaa..f31dc2a2 100644 --- a/src/bytes.ts +++ b/src/bytes.ts @@ -393,3 +393,38 @@ 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}. + * + * This function also supports Node.js {@link Buffer}s. + * + * @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. + * @returns The {@link DataView}. + */ +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); +}