From 6d5b8ef6f7c09f3acb0a46371ce5e58f629fccd1 Mon Sep 17 00:00:00 2001 From: Callum Date: Thu, 28 Mar 2024 17:03:08 +0000 Subject: [PATCH] refactor(experimental): Allow decoding with a ReadonlyUint8Array --- .../codecs-core/src/__tests__/codec-test.ts | 5 +++-- .../src/__tests__/combine-codec.ts | 5 +++-- .../src/__tests__/map-codec-test.ts | 12 +++++++---- .../src/__tests__/reverse-codec-test.ts | 21 ++++++++++++++++++- packages/codecs-core/src/assertions.ts | 10 +++++++-- packages/codecs-core/src/bytes.ts | 6 ++++-- packages/codecs-core/src/codec.ts | 6 ++++-- packages/codecs-core/src/fix-codec.ts | 3 ++- packages/codecs-core/src/index.ts | 1 + packages/codecs-core/src/map-codec.ts | 19 +++++++++-------- packages/codecs-core/src/offset-codec.ts | 3 ++- .../codecs-core/src/readonly-uint8array.ts | 4 ++++ packages/codecs-core/src/reverse-codec.ts | 2 +- packages/codecs-data-structures/src/array.ts | 3 ++- packages/codecs-data-structures/src/bytes.ts | 5 +++-- .../src/discriminated-union.ts | 3 ++- .../codecs-data-structures/src/nullable.ts | 3 ++- packages/codecs-data-structures/src/struct.ts | 3 ++- packages/codecs-data-structures/src/tuple.ts | 3 ++- packages/codecs-data-structures/src/unit.ts | 3 ++- packages/codecs-numbers/src/short-u16.ts | 3 ++- packages/codecs-numbers/src/utils.ts | 3 ++- packages/codecs-strings/src/string.ts | 3 ++- packages/options/src/option-codec.ts | 3 ++- packages/sysvars/src/slot-history.ts | 3 ++- 25 files changed, 95 insertions(+), 40 deletions(-) create mode 100644 packages/codecs-core/src/readonly-uint8array.ts diff --git a/packages/codecs-core/src/__tests__/codec-test.ts b/packages/codecs-core/src/__tests__/codec-test.ts index 746ed3a98262..7a0534b09cf5 100644 --- a/packages/codecs-core/src/__tests__/codec-test.ts +++ b/packages/codecs-core/src/__tests__/codec-test.ts @@ -1,4 +1,5 @@ import { Codec, createCodec, createDecoder, createEncoder, Encoder } from '../codec'; +import { ReadonlyUint8Array } from '../readonly-uint8array'; describe('Encoder', () => { it('can define Encoder instances', () => { @@ -27,7 +28,7 @@ describe('Decoder', () => { it('can define Decoder instances', () => { const myDecoder = createDecoder({ fixedSize: 32, - read: (bytes: Uint8Array, offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => { const slice = bytes.slice(offset, offset + 32); const str = [...slice].map(charCode => String.fromCharCode(charCode)).join(''); return [str, offset + 32]; @@ -45,7 +46,7 @@ describe('Codec', () => { it('can define Codec instances', () => { const myCodec: Codec = createCodec({ fixedSize: 32, - read: (bytes: Uint8Array, offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => { const slice = bytes.slice(offset, offset + 32); const str = [...slice].map(charCode => String.fromCharCode(charCode)).join(''); return [str, offset + 32]; diff --git a/packages/codecs-core/src/__tests__/combine-codec.ts b/packages/codecs-core/src/__tests__/combine-codec.ts index f3989ccfbef2..3ac82f5eb762 100644 --- a/packages/codecs-core/src/__tests__/combine-codec.ts +++ b/packages/codecs-core/src/__tests__/combine-codec.ts @@ -6,6 +6,7 @@ import { import { createDecoder, createEncoder, FixedSizeCodec, FixedSizeDecoder, FixedSizeEncoder } from '../codec'; import { combineCodec } from '../combine-codec'; +import { ReadonlyUint8Array } from '../readonly-uint8array'; describe('combineCodec', () => { it('can join encoders and decoders with the same type', () => { @@ -19,7 +20,7 @@ describe('combineCodec', () => { const u8Decoder = createDecoder({ fixedSize: 1, - read: (bytes: Uint8Array, offset = 0) => [bytes[offset], offset + 1], + read: (bytes: ReadonlyUint8Array | Uint8Array, offset = 0) => [bytes[offset], offset + 1], }); const u8Codec = combineCodec(u8Encoder, u8Decoder); @@ -40,7 +41,7 @@ describe('combineCodec', () => { const u8Decoder: FixedSizeDecoder = createDecoder({ fixedSize: 1, - read: (bytes: Uint8Array, offset = 0) => [BigInt(bytes[offset]), offset + 1], + read: (bytes: ReadonlyUint8Array | Uint8Array, offset = 0) => [BigInt(bytes[offset]), offset + 1], }); const u8Codec: FixedSizeCodec = combineCodec(u8Encoder, u8Decoder); diff --git a/packages/codecs-core/src/__tests__/map-codec-test.ts b/packages/codecs-core/src/__tests__/map-codec-test.ts index 393fb64cf87c..d8154e0ac39d 100644 --- a/packages/codecs-core/src/__tests__/map-codec-test.ts +++ b/packages/codecs-core/src/__tests__/map-codec-test.ts @@ -1,9 +1,10 @@ import { Codec, createCodec, createDecoder, createEncoder } from '../codec'; import { mapCodec, mapDecoder, mapEncoder } from '../map-codec'; +import { ReadonlyUint8Array } from '../readonly-uint8array'; const numberCodec: Codec = createCodec({ fixedSize: 1, - read: (bytes: Uint8Array): [number, number] => [bytes[0], 1], + read: (bytes: ReadonlyUint8Array | Uint8Array): [number, number] => [bytes[0], 1], write: (value: number, bytes, offset) => { bytes.set([value], offset); return offset + 1; @@ -74,7 +75,7 @@ describe('mapCodec', () => { type Strict = { discriminator: number; label: string }; const strictCodec: Codec = createCodec({ fixedSize: 2, - read: (bytes: Uint8Array): [Strict, number] => [ + read: (bytes: ReadonlyUint8Array | Uint8Array): [Strict, number] => [ { discriminator: bytes[0], label: 'x'.repeat(bytes[1]) }, 1, ], @@ -118,7 +119,10 @@ describe('mapCodec', () => { it('can loosen a tuple codec', () => { const codec: Codec<[number, string]> = createCodec({ fixedSize: 2, - read: (bytes: Uint8Array): [[number, string], number] => [[bytes[0], 'x'.repeat(bytes[1])], 2], + read: (bytes: ReadonlyUint8Array | Uint8Array): [[number, string], number] => [ + [bytes[0], 'x'.repeat(bytes[1])], + 2, + ], write: (value: [number, string], bytes, offset) => { bytes.set([value[0], value[1].length], offset); return offset + 2; @@ -163,7 +167,7 @@ describe('mapDecoder', () => { it('can map an encoder to another encoder', () => { const decoder = createDecoder({ fixedSize: 1, - read: (bytes: Uint8Array, offset = 0) => [bytes[offset], offset + 1], + read: (bytes: ReadonlyUint8Array | Uint8Array, offset = 0) => [bytes[offset], offset + 1], }); const decoderB = mapDecoder(decoder, (value: number): string => 'x'.repeat(value)); diff --git a/packages/codecs-core/src/__tests__/reverse-codec-test.ts b/packages/codecs-core/src/__tests__/reverse-codec-test.ts index c968431ac59d..d846c1a1846e 100644 --- a/packages/codecs-core/src/__tests__/reverse-codec-test.ts +++ b/packages/codecs-core/src/__tests__/reverse-codec-test.ts @@ -2,6 +2,7 @@ import { SOLANA_ERROR__CODECS__EXPECTED_FIXED_LENGTH, SolanaError } from '@solan import { createDecoder, createEncoder } from '../codec'; import { fixCodec } from '../fix-codec'; +import { ReadonlyUint8Array } from '../readonly-uint8array'; import { reverseCodec, reverseDecoder, reverseEncoder } from '../reverse-codec'; import { b, base16 } from './__setup__'; @@ -57,7 +58,10 @@ describe('reverseDecoder', () => { it('can reverse the bytes of a fixed-size decoder', () => { const decoder = createDecoder({ fixedSize: 2, - read: (bytes: Uint8Array, offset = 0) => [`${bytes[offset]}-${bytes[offset + 1]}`, offset + 2], + read: (bytes: ReadonlyUint8Array | Uint8Array, offset = 0) => [ + `${bytes[offset]}-${bytes[offset + 1]}`, + offset + 2, + ], }); const reversedDecoder = reverseDecoder(decoder); @@ -67,4 +71,19 @@ describe('reverseDecoder', () => { // @ts-expect-error Reversed decoder should be fixed-size. expect(() => reverseDecoder(base16)).toThrow(new SolanaError(SOLANA_ERROR__CODECS__EXPECTED_FIXED_LENGTH)); }); + + it('does not modify the input bytes in-place', () => { + const decoder = createDecoder({ + fixedSize: 2, + read: (bytes: ReadonlyUint8Array | Uint8Array, offset = 0) => [ + `${bytes[offset]}-${bytes[offset + 1]}`, + offset + 2, + ], + }); + + const reversedDecoder = reverseDecoder(decoder); + const inputBytes = new Uint8Array([42, 0]); + reversedDecoder.read(inputBytes, 0); + expect(inputBytes).toStrictEqual(new Uint8Array([42, 0])); + }); }); diff --git a/packages/codecs-core/src/assertions.ts b/packages/codecs-core/src/assertions.ts index 923f4ef8a705..5ffccb3907b0 100644 --- a/packages/codecs-core/src/assertions.ts +++ b/packages/codecs-core/src/assertions.ts @@ -5,10 +5,16 @@ import { SolanaError, } from '@solana/errors'; +import { ReadonlyUint8Array } from './readonly-uint8array'; + /** * Asserts that a given byte array is not empty. */ -export function assertByteArrayIsNotEmptyForCodec(codecDescription: string, bytes: Uint8Array, offset = 0) { +export function assertByteArrayIsNotEmptyForCodec( + codecDescription: string, + bytes: ReadonlyUint8Array | Uint8Array, + offset = 0, +) { if (bytes.length - offset <= 0) { throw new SolanaError(SOLANA_ERROR__CODECS__CANNOT_DECODE_EMPTY_BYTE_ARRAY, { codecDescription, @@ -22,7 +28,7 @@ export function assertByteArrayIsNotEmptyForCodec(codecDescription: string, byte export function assertByteArrayHasEnoughBytesForCodec( codecDescription: string, expected: number, - bytes: Uint8Array, + bytes: ReadonlyUint8Array | Uint8Array, offset = 0, ) { const bytesLength = bytes.length - offset; diff --git a/packages/codecs-core/src/bytes.ts b/packages/codecs-core/src/bytes.ts index 5fefd2378b0a..1e6ec9ab1e02 100644 --- a/packages/codecs-core/src/bytes.ts +++ b/packages/codecs-core/src/bytes.ts @@ -1,3 +1,5 @@ +import { ReadonlyUint8Array } from './readonly-uint8array'; + /** * Concatenates an array of `Uint8Array`s into a single `Uint8Array`. * Reuses the original byte array when applicable. @@ -26,7 +28,7 @@ export const mergeBytes = (byteArrays: Uint8Array[]): Uint8Array => { * Pads a `Uint8Array` with zeroes to the specified length. * If the array is longer than the specified length, it is returned as-is. */ -export const padBytes = (bytes: Uint8Array, length: number): Uint8Array => { +export const padBytes = (bytes: ReadonlyUint8Array | Uint8Array, length: number): ReadonlyUint8Array | Uint8Array => { if (bytes.length >= length) return bytes; const paddedBytes = new Uint8Array(length).fill(0); paddedBytes.set(bytes); @@ -38,5 +40,5 @@ export const padBytes = (bytes: Uint8Array, length: number): Uint8Array => { * If the array is longer than the specified length, it is truncated. * If the array is shorter than the specified length, it is padded with zeroes. */ -export const fixBytes = (bytes: Uint8Array, length: number): Uint8Array => +export const fixBytes = (bytes: ReadonlyUint8Array | Uint8Array, length: number): ReadonlyUint8Array | Uint8Array => padBytes(bytes.length <= length ? bytes : bytes.slice(0, length), length); diff --git a/packages/codecs-core/src/codec.ts b/packages/codecs-core/src/codec.ts index c737d7f88c65..b4b7dd34a747 100644 --- a/packages/codecs-core/src/codec.ts +++ b/packages/codecs-core/src/codec.ts @@ -4,6 +4,8 @@ import { SolanaError, } from '@solana/errors'; +import { ReadonlyUint8Array } from './readonly-uint8array'; + /** * Defines an offset in bytes. */ @@ -38,12 +40,12 @@ export type Encoder = FixedSizeEncoder | VariableSizeEncoder = { /** Decodes the provided byte array at the given offset (or zero) and returns the value directly. */ - readonly decode: (bytes: Uint8Array, offset?: Offset) => TTo; + readonly decode: (bytes: ReadonlyUint8Array | Uint8Array, offset?: Offset) => TTo; /** * Reads the encoded value from the provided byte array at the given offset. * Returns the decoded value and the offset of the next byte after the encoded value. */ - readonly read: (bytes: Uint8Array, offset: Offset) => [TTo, Offset]; + readonly read: (bytes: ReadonlyUint8Array | Uint8Array, offset: Offset) => [TTo, Offset]; }; export type FixedSizeDecoder = BaseDecoder & { diff --git a/packages/codecs-core/src/fix-codec.ts b/packages/codecs-core/src/fix-codec.ts index 9f02126022dc..23a658c7af08 100644 --- a/packages/codecs-core/src/fix-codec.ts +++ b/packages/codecs-core/src/fix-codec.ts @@ -13,6 +13,7 @@ import { Offset, } from './codec'; import { combineCodec } from './combine-codec'; +import { ReadonlyUint8Array } from './readonly-uint8array'; /** * Creates a fixed-size encoder from a given encoder. @@ -51,7 +52,7 @@ export function fixDecoder( ): FixedSizeDecoder { return createDecoder({ fixedSize: fixedBytes, - read: (bytes: Uint8Array, offset: Offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset: Offset) => { assertByteArrayHasEnoughBytesForCodec('fixCodec', fixedBytes, bytes, offset); // Slice the byte array to the fixed size if necessary. if (offset > 0 || bytes.length > fixedBytes) { diff --git a/packages/codecs-core/src/index.ts b/packages/codecs-core/src/index.ts index 6fae759885ad..81c226d3a739 100644 --- a/packages/codecs-core/src/index.ts +++ b/packages/codecs-core/src/index.ts @@ -6,5 +6,6 @@ export * from './fix-codec'; export * from './map-codec'; export * from './offset-codec'; export * from './pad-codec'; +export * from './readonly-uint8array'; export * from './resize-codec'; export * from './reverse-codec'; diff --git a/packages/codecs-core/src/map-codec.ts b/packages/codecs-core/src/map-codec.ts index 2621c4108b24..ee31931e4e03 100644 --- a/packages/codecs-core/src/map-codec.ts +++ b/packages/codecs-core/src/map-codec.ts @@ -13,6 +13,7 @@ import { VariableSizeDecoder, VariableSizeEncoder, } from './codec'; +import { ReadonlyUint8Array } from './readonly-uint8array'; /** * Converts an encoder A to a encoder B by mapping their values. @@ -46,23 +47,23 @@ export function mapEncoder( */ export function mapDecoder( decoder: FixedSizeDecoder, - map: (value: TOldTo, bytes: Uint8Array, offset: number) => TNewTo, + map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo, ): FixedSizeDecoder; export function mapDecoder( decoder: VariableSizeDecoder, - map: (value: TOldTo, bytes: Uint8Array, offset: number) => TNewTo, + map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo, ): VariableSizeDecoder; export function mapDecoder( decoder: Decoder, - map: (value: TOldTo, bytes: Uint8Array, offset: number) => TNewTo, + map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo, ): Decoder; export function mapDecoder( decoder: Decoder, - map: (value: TOldTo, bytes: Uint8Array, offset: number) => TNewTo, + map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo, ): Decoder { return createDecoder({ ...decoder, - read: (bytes: Uint8Array, offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => { const [value, newOffset] = decoder.read(bytes, offset); return [map(value, bytes, offset), newOffset]; }, @@ -87,22 +88,22 @@ export function mapCodec( export function mapCodec( codec: FixedSizeCodec, unmap: (value: TNewFrom) => TOldFrom, - map: (value: TOldTo, bytes: Uint8Array, offset: number) => TNewTo, + map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo, ): FixedSizeCodec; export function mapCodec( codec: VariableSizeCodec, unmap: (value: TNewFrom) => TOldFrom, - map: (value: TOldTo, bytes: Uint8Array, offset: number) => TNewTo, + map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo, ): VariableSizeCodec; export function mapCodec( codec: Codec, unmap: (value: TNewFrom) => TOldFrom, - map: (value: TOldTo, bytes: Uint8Array, offset: number) => TNewTo, + map: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo, ): Codec; export function mapCodec( codec: Codec, unmap: (value: TNewFrom) => TOldFrom, - map?: (value: TOldTo, bytes: Uint8Array, offset: number) => TNewTo, + map?: (value: TOldTo, bytes: ReadonlyUint8Array | Uint8Array, offset: number) => TNewTo, ): Codec { return createCodec({ ...mapEncoder(codec, unmap), diff --git a/packages/codecs-core/src/offset-codec.ts b/packages/codecs-core/src/offset-codec.ts index b973b8c1716d..7a79538a2a09 100644 --- a/packages/codecs-core/src/offset-codec.ts +++ b/packages/codecs-core/src/offset-codec.ts @@ -1,6 +1,7 @@ import { assertByteArrayOffsetIsNotOutOfRange } from './assertions'; import { Codec, createDecoder, createEncoder, Decoder, Encoder, Offset } from './codec'; import { combineCodec } from './combine-codec'; +import { ReadonlyUint8Array } from './readonly-uint8array'; type OffsetConfig = { postOffset?: PostOffsetFunction; @@ -9,7 +10,7 @@ type OffsetConfig = { type PreOffsetFunctionScope = { /** The entire byte array. */ - bytes: Uint8Array; + bytes: ReadonlyUint8Array | Uint8Array; /** The original offset prior to encode or decode. */ preOffset: Offset; /** Wraps the offset to the byte array length. */ diff --git a/packages/codecs-core/src/readonly-uint8array.ts b/packages/codecs-core/src/readonly-uint8array.ts new file mode 100644 index 000000000000..bffd26356231 --- /dev/null +++ b/packages/codecs-core/src/readonly-uint8array.ts @@ -0,0 +1,4 @@ +type TypedArrayMutableProperties = 'copyWithin' | 'fill' | 'reverse' | 'set' | 'sort'; +export interface ReadonlyUint8Array extends Omit { + readonly [n: number]: number; +} diff --git a/packages/codecs-core/src/reverse-codec.ts b/packages/codecs-core/src/reverse-codec.ts index ff5dbb9ccc8e..6de6c73aa464 100644 --- a/packages/codecs-core/src/reverse-codec.ts +++ b/packages/codecs-core/src/reverse-codec.ts @@ -38,7 +38,7 @@ export function reverseDecoder( read: (bytes, offset) => { const reverseEnd = offset + decoder.fixedSize; if (offset === 0 && bytes.length === reverseEnd) { - return decoder.read(bytes.reverse(), offset); + return decoder.read(new Uint8Array([...bytes]).reverse(), offset); } const reversedBytes = bytes.slice(); reversedBytes.set(bytes.slice(offset, reverseEnd).reverse(), offset); diff --git a/packages/codecs-data-structures/src/array.ts b/packages/codecs-data-structures/src/array.ts index de5966d7b6aa..bd9303e5e603 100644 --- a/packages/codecs-data-structures/src/array.ts +++ b/packages/codecs-data-structures/src/array.ts @@ -9,6 +9,7 @@ import { FixedSizeDecoder, FixedSizeEncoder, getEncodedSize, + ReadonlyUint8Array, VariableSizeCodec, VariableSizeDecoder, VariableSizeEncoder, @@ -120,7 +121,7 @@ export function getArrayDecoder(item: Decoder, config: ArrayCodecConfi return createDecoder({ ...(fixedSize !== null ? { fixedSize } : { maxSize }), - read: (bytes: Uint8Array, offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => { const array: TTo[] = []; if (typeof size === 'object' && bytes.slice(offset).length === 0) { return [array, offset]; diff --git a/packages/codecs-data-structures/src/bytes.ts b/packages/codecs-data-structures/src/bytes.ts index c9549aadc3e0..a1e96a3cb166 100644 --- a/packages/codecs-data-structures/src/bytes.ts +++ b/packages/codecs-data-structures/src/bytes.ts @@ -13,6 +13,7 @@ import { FixedSizeEncoder, fixEncoder, getEncodedSize, + ReadonlyUint8Array, VariableSizeCodec, VariableSizeDecoder, VariableSizeEncoder, @@ -81,7 +82,7 @@ export function getBytesDecoder(config: BytesCodecConfig = {}): D const size = config.size ?? 'variable'; const byteDecoder: Decoder = createDecoder({ - read: (bytes: Uint8Array, offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => { const slice = bytes.slice(offset); return [slice, offset + slice.length]; }, @@ -96,7 +97,7 @@ export function getBytesDecoder(config: BytesCodecConfig = {}): D } return createDecoder({ - read: (bytes: Uint8Array, offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => { assertByteArrayIsNotEmptyForCodec('bytes', bytes, offset); const [lengthBigInt, lengthOffset] = size.read(bytes, offset); const length = Number(lengthBigInt); diff --git a/packages/codecs-data-structures/src/discriminated-union.ts b/packages/codecs-data-structures/src/discriminated-union.ts index 3c7b96a609d0..5a807a20b8a2 100644 --- a/packages/codecs-data-structures/src/discriminated-union.ts +++ b/packages/codecs-data-structures/src/discriminated-union.ts @@ -9,6 +9,7 @@ import { Encoder, getEncodedSize, isFixedSize, + ReadonlyUint8Array, } from '@solana/codecs-core'; import { getU8Decoder, getU8Encoder, NumberCodec, NumberDecoder, NumberEncoder } from '@solana/codecs-numbers'; import { @@ -174,7 +175,7 @@ export function getDiscriminatedUnionDecoder< const fixedSize = getDiscriminatedUnionFixedSize(variants, prefix); return createDecoder({ ...(fixedSize !== null ? { fixedSize } : { maxSize: getDiscriminatedUnionMaxSize(variants, prefix) }), - read: (bytes: Uint8Array, offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => { assertByteArrayIsNotEmptyForCodec('discriminatedUnion', bytes, offset); const [discriminator, dOffset] = prefix.read(bytes, offset); offset = dOffset; diff --git a/packages/codecs-data-structures/src/nullable.ts b/packages/codecs-data-structures/src/nullable.ts index 4b3c04fd97a0..de81be4cf61a 100644 --- a/packages/codecs-data-structures/src/nullable.ts +++ b/packages/codecs-data-structures/src/nullable.ts @@ -11,6 +11,7 @@ import { FixedSizeEncoder, getEncodedSize, isFixedSize, + ReadonlyUint8Array, VariableSizeCodec, VariableSizeDecoder, VariableSizeEncoder, @@ -140,7 +141,7 @@ export function getNullableDecoder( ...(fixedSize === null ? { maxSize: sumCodecSizes([prefix, item].map(getMaxSize)) ?? undefined } : { fixedSize }), - read: (bytes: Uint8Array, offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => { if (bytes.length - offset <= 0) { return [null, offset]; } diff --git a/packages/codecs-data-structures/src/struct.ts b/packages/codecs-data-structures/src/struct.ts index 94791792d1bb..cd4f60132b88 100644 --- a/packages/codecs-data-structures/src/struct.ts +++ b/packages/codecs-data-structures/src/struct.ts @@ -10,6 +10,7 @@ import { FixedSizeDecoder, FixedSizeEncoder, getEncodedSize, + ReadonlyUint8Array, VariableSizeCodec, VariableSizeDecoder, VariableSizeEncoder, @@ -87,7 +88,7 @@ export function getStructDecoder>>( return createDecoder({ ...(fixedSize === null ? { maxSize } : { fixedSize }), - read: (bytes: Uint8Array, offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => { const struct = {} as TTo; fields.forEach(([key, codec]) => { const [value, newOffset] = codec.read(bytes, offset); diff --git a/packages/codecs-data-structures/src/tuple.ts b/packages/codecs-data-structures/src/tuple.ts index e4fefe97b391..f086aaf91839 100644 --- a/packages/codecs-data-structures/src/tuple.ts +++ b/packages/codecs-data-structures/src/tuple.ts @@ -10,6 +10,7 @@ import { FixedSizeDecoder, FixedSizeEncoder, getEncodedSize, + ReadonlyUint8Array, VariableSizeCodec, VariableSizeDecoder, VariableSizeEncoder, @@ -83,7 +84,7 @@ export function getTupleDecoder[]>( return createDecoder({ ...(fixedSize === null ? { maxSize } : { fixedSize }), - read: (bytes: Uint8Array, offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => { const values = [] as Array & TTo; items.forEach(item => { const [newValue, newOffset] = item.read(bytes, offset); diff --git a/packages/codecs-data-structures/src/unit.ts b/packages/codecs-data-structures/src/unit.ts index 975fdad1d9a8..b5bf6d07b038 100644 --- a/packages/codecs-data-structures/src/unit.ts +++ b/packages/codecs-data-structures/src/unit.ts @@ -5,6 +5,7 @@ import { FixedSizeCodec, FixedSizeDecoder, FixedSizeEncoder, + ReadonlyUint8Array, } from '@solana/codecs-core'; /** @@ -23,7 +24,7 @@ export function getUnitEncoder(): FixedSizeEncoder { export function getUnitDecoder(): FixedSizeDecoder { return createDecoder({ fixedSize: 0, - read: (_bytes: Uint8Array, offset) => [undefined, offset], + read: (_bytes: ReadonlyUint8Array | Uint8Array, offset) => [undefined, offset], }); } diff --git a/packages/codecs-numbers/src/short-u16.ts b/packages/codecs-numbers/src/short-u16.ts index eb5f36af5f26..895b30afab1c 100644 --- a/packages/codecs-numbers/src/short-u16.ts +++ b/packages/codecs-numbers/src/short-u16.ts @@ -3,6 +3,7 @@ import { createDecoder, createEncoder, Offset, + ReadonlyUint8Array, VariableSizeCodec, VariableSizeDecoder, VariableSizeEncoder, @@ -52,7 +53,7 @@ export const getShortU16Encoder = (): VariableSizeEncoder => export const getShortU16Decoder = (): VariableSizeDecoder => createDecoder({ maxSize: 3, - read: (bytes: Uint8Array, offset): [number, Offset] => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset): [number, Offset] => { let value = 0; let byteCount = 0; while (++byteCount) { diff --git a/packages/codecs-numbers/src/utils.ts b/packages/codecs-numbers/src/utils.ts index 2f013d541638..04ff2ce737ec 100644 --- a/packages/codecs-numbers/src/utils.ts +++ b/packages/codecs-numbers/src/utils.ts @@ -6,6 +6,7 @@ import { FixedSizeDecoder, FixedSizeEncoder, Offset, + ReadonlyUint8Array, } from '@solana/codecs-core'; import { assertNumberIsBetweenForCodec } from './assertions'; @@ -65,7 +66,7 @@ export function numberDecoderFactory { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset = 0) => { assertByteArrayIsNotEmptyForCodec('string', bytes, offset); const [lengthBigInt, lengthOffset] = size.read(bytes, offset); const length = Number(lengthBigInt); diff --git a/packages/options/src/option-codec.ts b/packages/options/src/option-codec.ts index 7803ed8ece11..dce056677171 100644 --- a/packages/options/src/option-codec.ts +++ b/packages/options/src/option-codec.ts @@ -11,6 +11,7 @@ import { FixedSizeEncoder, getEncodedSize, isFixedSize, + ReadonlyUint8Array, VariableSizeCodec, VariableSizeDecoder, VariableSizeEncoder, @@ -148,7 +149,7 @@ export function getOptionDecoder( ...(fixedSize === null ? { maxSize: sumCodecSizes([prefix, item].map(getMaxSize)) ?? undefined } : { fixedSize }), - read: (bytes: Uint8Array, offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => { if (bytes.length - offset <= 0) { return [none(), offset]; } diff --git a/packages/sysvars/src/slot-history.ts b/packages/sysvars/src/slot-history.ts index 059136ca2768..5835b0d15926 100644 --- a/packages/sysvars/src/slot-history.ts +++ b/packages/sysvars/src/slot-history.ts @@ -10,6 +10,7 @@ import { getU64Codec, getU64Decoder, getU64Encoder, + ReadonlyUint8Array, } from '@solana/codecs'; import { SOLANA_ERROR__CODECS__ENUM_DISCRIMINATOR_OUT_OF_RANGE, @@ -103,7 +104,7 @@ export function getSysvarSlotHistoryEncoder(): FixedSizeEncoder { return createDecoder({ fixedSize: SLOT_HISTORY_ACCOUNT_DATA_STATIC_SIZE, - read: (bytes: Uint8Array, offset) => { + read: (bytes: ReadonlyUint8Array | Uint8Array, offset) => { // Byte length should be exact. if (bytes.length != SLOT_HISTORY_ACCOUNT_DATA_STATIC_SIZE) { throw new SolanaError(SOLANA_ERROR__CODECS__INVALID_BYTE_LENGTH, {