460 lines
17 KiB
Plaintext
460 lines
17 KiB
Plaintext
import { SolanaError, SOLANA_ERROR__CODECS__EXPECTED_FIXED_LENGTH, SOLANA_ERROR__CODECS__EXPECTED_VARIABLE_LENGTH, SOLANA_ERROR__CODECS__ENCODER_DECODER_SIZE_COMPATIBILITY_MISMATCH, SOLANA_ERROR__CODECS__ENCODER_DECODER_FIXED_SIZE_MISMATCH, SOLANA_ERROR__CODECS__ENCODER_DECODER_MAX_SIZE_MISMATCH, SOLANA_ERROR__CODECS__CANNOT_DECODE_EMPTY_BYTE_ARRAY, SOLANA_ERROR__CODECS__INVALID_BYTE_LENGTH, SOLANA_ERROR__CODECS__OFFSET_OUT_OF_RANGE, SOLANA_ERROR__CODECS__EXPECTED_POSITIVE_BYTE_LENGTH, SOLANA_ERROR__CODECS__SENTINEL_MISSING_IN_DECODED_BYTES, SOLANA_ERROR__CODECS__ENCODED_BYTES_MUST_NOT_INCLUDE_SENTINEL } from '@solana/errors';
|
|
|
|
// src/add-codec-sentinel.ts
|
|
|
|
// src/bytes.ts
|
|
var mergeBytes = (byteArrays) => {
|
|
const nonEmptyByteArrays = byteArrays.filter((arr) => arr.length);
|
|
if (nonEmptyByteArrays.length === 0) {
|
|
return byteArrays.length ? byteArrays[0] : new Uint8Array();
|
|
}
|
|
if (nonEmptyByteArrays.length === 1) {
|
|
return nonEmptyByteArrays[0];
|
|
}
|
|
const totalLength = nonEmptyByteArrays.reduce((total, arr) => total + arr.length, 0);
|
|
const result = new Uint8Array(totalLength);
|
|
let offset = 0;
|
|
nonEmptyByteArrays.forEach((arr) => {
|
|
result.set(arr, offset);
|
|
offset += arr.length;
|
|
});
|
|
return result;
|
|
};
|
|
var padBytes = (bytes, length) => {
|
|
if (bytes.length >= length) return bytes;
|
|
const paddedBytes = new Uint8Array(length).fill(0);
|
|
paddedBytes.set(bytes);
|
|
return paddedBytes;
|
|
};
|
|
var fixBytes = (bytes, length) => padBytes(bytes.length <= length ? bytes : bytes.slice(0, length), length);
|
|
function containsBytes(data, bytes, offset) {
|
|
const slice = offset === 0 && data.length === bytes.length ? data : data.slice(offset, offset + bytes.length);
|
|
if (slice.length !== bytes.length) return false;
|
|
return bytes.every((b, i) => b === slice[i]);
|
|
}
|
|
function getEncodedSize(value, encoder) {
|
|
return "fixedSize" in encoder ? encoder.fixedSize : encoder.getSizeFromValue(value);
|
|
}
|
|
function createEncoder(encoder) {
|
|
return Object.freeze({
|
|
...encoder,
|
|
encode: (value) => {
|
|
const bytes = new Uint8Array(getEncodedSize(value, encoder));
|
|
encoder.write(value, bytes, 0);
|
|
return bytes;
|
|
}
|
|
});
|
|
}
|
|
function createDecoder(decoder) {
|
|
return Object.freeze({
|
|
...decoder,
|
|
decode: (bytes, offset = 0) => decoder.read(bytes, offset)[0]
|
|
});
|
|
}
|
|
function createCodec(codec) {
|
|
return Object.freeze({
|
|
...codec,
|
|
decode: (bytes, offset = 0) => codec.read(bytes, offset)[0],
|
|
encode: (value) => {
|
|
const bytes = new Uint8Array(getEncodedSize(value, codec));
|
|
codec.write(value, bytes, 0);
|
|
return bytes;
|
|
}
|
|
});
|
|
}
|
|
function isFixedSize(codec) {
|
|
return "fixedSize" in codec && typeof codec.fixedSize === "number";
|
|
}
|
|
function assertIsFixedSize(codec) {
|
|
if (!isFixedSize(codec)) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__EXPECTED_FIXED_LENGTH);
|
|
}
|
|
}
|
|
function isVariableSize(codec) {
|
|
return !isFixedSize(codec);
|
|
}
|
|
function assertIsVariableSize(codec) {
|
|
if (!isVariableSize(codec)) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__EXPECTED_VARIABLE_LENGTH);
|
|
}
|
|
}
|
|
function combineCodec(encoder, decoder) {
|
|
if (isFixedSize(encoder) !== isFixedSize(decoder)) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__ENCODER_DECODER_SIZE_COMPATIBILITY_MISMATCH);
|
|
}
|
|
if (isFixedSize(encoder) && isFixedSize(decoder) && encoder.fixedSize !== decoder.fixedSize) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__ENCODER_DECODER_FIXED_SIZE_MISMATCH, {
|
|
decoderFixedSize: decoder.fixedSize,
|
|
encoderFixedSize: encoder.fixedSize
|
|
});
|
|
}
|
|
if (!isFixedSize(encoder) && !isFixedSize(decoder) && encoder.maxSize !== decoder.maxSize) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__ENCODER_DECODER_MAX_SIZE_MISMATCH, {
|
|
decoderMaxSize: decoder.maxSize,
|
|
encoderMaxSize: encoder.maxSize
|
|
});
|
|
}
|
|
return {
|
|
...decoder,
|
|
...encoder,
|
|
decode: decoder.decode,
|
|
encode: encoder.encode,
|
|
read: decoder.read,
|
|
write: encoder.write
|
|
};
|
|
}
|
|
|
|
// src/add-codec-sentinel.ts
|
|
function addEncoderSentinel(encoder, sentinel) {
|
|
const write = (value, bytes, offset) => {
|
|
const encoderBytes = encoder.encode(value);
|
|
if (findSentinelIndex(encoderBytes, sentinel) >= 0) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__ENCODED_BYTES_MUST_NOT_INCLUDE_SENTINEL, {
|
|
encodedBytes: encoderBytes,
|
|
hexEncodedBytes: hexBytes(encoderBytes),
|
|
hexSentinel: hexBytes(sentinel),
|
|
sentinel
|
|
});
|
|
}
|
|
bytes.set(encoderBytes, offset);
|
|
offset += encoderBytes.length;
|
|
bytes.set(sentinel, offset);
|
|
offset += sentinel.length;
|
|
return offset;
|
|
};
|
|
if (isFixedSize(encoder)) {
|
|
return createEncoder({ ...encoder, fixedSize: encoder.fixedSize + sentinel.length, write });
|
|
}
|
|
return createEncoder({
|
|
...encoder,
|
|
...encoder.maxSize != null ? { maxSize: encoder.maxSize + sentinel.length } : {},
|
|
getSizeFromValue: (value) => encoder.getSizeFromValue(value) + sentinel.length,
|
|
write
|
|
});
|
|
}
|
|
function addDecoderSentinel(decoder, sentinel) {
|
|
const read = (bytes, offset) => {
|
|
const candidateBytes = offset === 0 ? bytes : bytes.slice(offset);
|
|
const sentinelIndex = findSentinelIndex(candidateBytes, sentinel);
|
|
if (sentinelIndex === -1) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__SENTINEL_MISSING_IN_DECODED_BYTES, {
|
|
decodedBytes: candidateBytes,
|
|
hexDecodedBytes: hexBytes(candidateBytes),
|
|
hexSentinel: hexBytes(sentinel),
|
|
sentinel
|
|
});
|
|
}
|
|
const preSentinelBytes = candidateBytes.slice(0, sentinelIndex);
|
|
return [decoder.decode(preSentinelBytes), offset + preSentinelBytes.length + sentinel.length];
|
|
};
|
|
if (isFixedSize(decoder)) {
|
|
return createDecoder({ ...decoder, fixedSize: decoder.fixedSize + sentinel.length, read });
|
|
}
|
|
return createDecoder({
|
|
...decoder,
|
|
...decoder.maxSize != null ? { maxSize: decoder.maxSize + sentinel.length } : {},
|
|
read
|
|
});
|
|
}
|
|
function addCodecSentinel(codec, sentinel) {
|
|
return combineCodec(addEncoderSentinel(codec, sentinel), addDecoderSentinel(codec, sentinel));
|
|
}
|
|
function findSentinelIndex(bytes, sentinel) {
|
|
return bytes.findIndex((byte, index, arr) => {
|
|
if (sentinel.length === 1) return byte === sentinel[0];
|
|
return containsBytes(arr, sentinel, index);
|
|
});
|
|
}
|
|
function hexBytes(bytes) {
|
|
return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
|
|
}
|
|
function assertByteArrayIsNotEmptyForCodec(codecDescription, bytes, offset = 0) {
|
|
if (bytes.length - offset <= 0) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__CANNOT_DECODE_EMPTY_BYTE_ARRAY, {
|
|
codecDescription
|
|
});
|
|
}
|
|
}
|
|
function assertByteArrayHasEnoughBytesForCodec(codecDescription, expected, bytes, offset = 0) {
|
|
const bytesLength = bytes.length - offset;
|
|
if (bytesLength < expected) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__INVALID_BYTE_LENGTH, {
|
|
bytesLength,
|
|
codecDescription,
|
|
expected
|
|
});
|
|
}
|
|
}
|
|
function assertByteArrayOffsetIsNotOutOfRange(codecDescription, offset, bytesLength) {
|
|
if (offset < 0 || offset > bytesLength) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__OFFSET_OUT_OF_RANGE, {
|
|
bytesLength,
|
|
codecDescription,
|
|
offset
|
|
});
|
|
}
|
|
}
|
|
|
|
// src/add-codec-size-prefix.ts
|
|
function addEncoderSizePrefix(encoder, prefix) {
|
|
const write = (value, bytes, offset) => {
|
|
const encoderBytes = encoder.encode(value);
|
|
offset = prefix.write(encoderBytes.length, bytes, offset);
|
|
bytes.set(encoderBytes, offset);
|
|
return offset + encoderBytes.length;
|
|
};
|
|
if (isFixedSize(prefix) && isFixedSize(encoder)) {
|
|
return createEncoder({ ...encoder, fixedSize: prefix.fixedSize + encoder.fixedSize, write });
|
|
}
|
|
const prefixMaxSize = isFixedSize(prefix) ? prefix.fixedSize : prefix.maxSize ?? null;
|
|
const encoderMaxSize = isFixedSize(encoder) ? encoder.fixedSize : encoder.maxSize ?? null;
|
|
const maxSize = prefixMaxSize !== null && encoderMaxSize !== null ? prefixMaxSize + encoderMaxSize : null;
|
|
return createEncoder({
|
|
...encoder,
|
|
...maxSize !== null ? { maxSize } : {},
|
|
getSizeFromValue: (value) => {
|
|
const encoderSize = getEncodedSize(value, encoder);
|
|
return getEncodedSize(encoderSize, prefix) + encoderSize;
|
|
},
|
|
write
|
|
});
|
|
}
|
|
function addDecoderSizePrefix(decoder, prefix) {
|
|
const read = (bytes, offset) => {
|
|
const [bigintSize, decoderOffset] = prefix.read(bytes, offset);
|
|
const size = Number(bigintSize);
|
|
offset = decoderOffset;
|
|
if (offset > 0 || bytes.length > size) {
|
|
bytes = bytes.slice(offset, offset + size);
|
|
}
|
|
assertByteArrayHasEnoughBytesForCodec("addDecoderSizePrefix", size, bytes);
|
|
return [decoder.decode(bytes), offset + size];
|
|
};
|
|
if (isFixedSize(prefix) && isFixedSize(decoder)) {
|
|
return createDecoder({ ...decoder, fixedSize: prefix.fixedSize + decoder.fixedSize, read });
|
|
}
|
|
const prefixMaxSize = isFixedSize(prefix) ? prefix.fixedSize : prefix.maxSize ?? null;
|
|
const decoderMaxSize = isFixedSize(decoder) ? decoder.fixedSize : decoder.maxSize ?? null;
|
|
const maxSize = prefixMaxSize !== null && decoderMaxSize !== null ? prefixMaxSize + decoderMaxSize : null;
|
|
return createDecoder({ ...decoder, ...maxSize !== null ? { maxSize } : {}, read });
|
|
}
|
|
function addCodecSizePrefix(codec, prefix) {
|
|
return combineCodec(addEncoderSizePrefix(codec, prefix), addDecoderSizePrefix(codec, prefix));
|
|
}
|
|
|
|
// src/fix-codec-size.ts
|
|
function fixEncoderSize(encoder, fixedBytes) {
|
|
return createEncoder({
|
|
fixedSize: fixedBytes,
|
|
write: (value, bytes, offset) => {
|
|
const variableByteArray = encoder.encode(value);
|
|
const fixedByteArray = variableByteArray.length > fixedBytes ? variableByteArray.slice(0, fixedBytes) : variableByteArray;
|
|
bytes.set(fixedByteArray, offset);
|
|
return offset + fixedBytes;
|
|
}
|
|
});
|
|
}
|
|
function fixDecoderSize(decoder, fixedBytes) {
|
|
return createDecoder({
|
|
fixedSize: fixedBytes,
|
|
read: (bytes, offset) => {
|
|
assertByteArrayHasEnoughBytesForCodec("fixCodecSize", fixedBytes, bytes, offset);
|
|
if (offset > 0 || bytes.length > fixedBytes) {
|
|
bytes = bytes.slice(offset, offset + fixedBytes);
|
|
}
|
|
if (isFixedSize(decoder)) {
|
|
bytes = fixBytes(bytes, decoder.fixedSize);
|
|
}
|
|
const [value] = decoder.read(bytes, 0);
|
|
return [value, offset + fixedBytes];
|
|
}
|
|
});
|
|
}
|
|
function fixCodecSize(codec, fixedBytes) {
|
|
return combineCodec(fixEncoderSize(codec, fixedBytes), fixDecoderSize(codec, fixedBytes));
|
|
}
|
|
|
|
// src/offset-codec.ts
|
|
function offsetEncoder(encoder, config) {
|
|
return createEncoder({
|
|
...encoder,
|
|
write: (value, bytes, preOffset) => {
|
|
const wrapBytes = (offset) => modulo(offset, bytes.length);
|
|
const newPreOffset = config.preOffset ? config.preOffset({ bytes, preOffset, wrapBytes }) : preOffset;
|
|
assertByteArrayOffsetIsNotOutOfRange("offsetEncoder", newPreOffset, bytes.length);
|
|
const postOffset = encoder.write(value, bytes, newPreOffset);
|
|
const newPostOffset = config.postOffset ? config.postOffset({ bytes, newPreOffset, postOffset, preOffset, wrapBytes }) : postOffset;
|
|
assertByteArrayOffsetIsNotOutOfRange("offsetEncoder", newPostOffset, bytes.length);
|
|
return newPostOffset;
|
|
}
|
|
});
|
|
}
|
|
function offsetDecoder(decoder, config) {
|
|
return createDecoder({
|
|
...decoder,
|
|
read: (bytes, preOffset) => {
|
|
const wrapBytes = (offset) => modulo(offset, bytes.length);
|
|
const newPreOffset = config.preOffset ? config.preOffset({ bytes, preOffset, wrapBytes }) : preOffset;
|
|
assertByteArrayOffsetIsNotOutOfRange("offsetDecoder", newPreOffset, bytes.length);
|
|
const [value, postOffset] = decoder.read(bytes, newPreOffset);
|
|
const newPostOffset = config.postOffset ? config.postOffset({ bytes, newPreOffset, postOffset, preOffset, wrapBytes }) : postOffset;
|
|
assertByteArrayOffsetIsNotOutOfRange("offsetDecoder", newPostOffset, bytes.length);
|
|
return [value, newPostOffset];
|
|
}
|
|
});
|
|
}
|
|
function offsetCodec(codec, config) {
|
|
return combineCodec(offsetEncoder(codec, config), offsetDecoder(codec, config));
|
|
}
|
|
function modulo(dividend, divisor) {
|
|
if (divisor === 0) return 0;
|
|
return (dividend % divisor + divisor) % divisor;
|
|
}
|
|
function resizeEncoder(encoder, resize) {
|
|
if (isFixedSize(encoder)) {
|
|
const fixedSize = resize(encoder.fixedSize);
|
|
if (fixedSize < 0) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__EXPECTED_POSITIVE_BYTE_LENGTH, {
|
|
bytesLength: fixedSize,
|
|
codecDescription: "resizeEncoder"
|
|
});
|
|
}
|
|
return createEncoder({ ...encoder, fixedSize });
|
|
}
|
|
return createEncoder({
|
|
...encoder,
|
|
getSizeFromValue: (value) => {
|
|
const newSize = resize(encoder.getSizeFromValue(value));
|
|
if (newSize < 0) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__EXPECTED_POSITIVE_BYTE_LENGTH, {
|
|
bytesLength: newSize,
|
|
codecDescription: "resizeEncoder"
|
|
});
|
|
}
|
|
return newSize;
|
|
}
|
|
});
|
|
}
|
|
function resizeDecoder(decoder, resize) {
|
|
if (isFixedSize(decoder)) {
|
|
const fixedSize = resize(decoder.fixedSize);
|
|
if (fixedSize < 0) {
|
|
throw new SolanaError(SOLANA_ERROR__CODECS__EXPECTED_POSITIVE_BYTE_LENGTH, {
|
|
bytesLength: fixedSize,
|
|
codecDescription: "resizeDecoder"
|
|
});
|
|
}
|
|
return createDecoder({ ...decoder, fixedSize });
|
|
}
|
|
return decoder;
|
|
}
|
|
function resizeCodec(codec, resize) {
|
|
return combineCodec(resizeEncoder(codec, resize), resizeDecoder(codec, resize));
|
|
}
|
|
|
|
// src/pad-codec.ts
|
|
function padLeftEncoder(encoder, offset) {
|
|
return offsetEncoder(
|
|
resizeEncoder(encoder, (size) => size + offset),
|
|
{ preOffset: ({ preOffset }) => preOffset + offset }
|
|
);
|
|
}
|
|
function padRightEncoder(encoder, offset) {
|
|
return offsetEncoder(
|
|
resizeEncoder(encoder, (size) => size + offset),
|
|
{ postOffset: ({ postOffset }) => postOffset + offset }
|
|
);
|
|
}
|
|
function padLeftDecoder(decoder, offset) {
|
|
return offsetDecoder(
|
|
resizeDecoder(decoder, (size) => size + offset),
|
|
{ preOffset: ({ preOffset }) => preOffset + offset }
|
|
);
|
|
}
|
|
function padRightDecoder(decoder, offset) {
|
|
return offsetDecoder(
|
|
resizeDecoder(decoder, (size) => size + offset),
|
|
{ postOffset: ({ postOffset }) => postOffset + offset }
|
|
);
|
|
}
|
|
function padLeftCodec(codec, offset) {
|
|
return combineCodec(padLeftEncoder(codec, offset), padLeftDecoder(codec, offset));
|
|
}
|
|
function padRightCodec(codec, offset) {
|
|
return combineCodec(padRightEncoder(codec, offset), padRightDecoder(codec, offset));
|
|
}
|
|
|
|
// src/reverse-codec.ts
|
|
function copySourceToTargetInReverse(source, target_WILL_MUTATE, sourceOffset, sourceLength, targetOffset = 0) {
|
|
while (sourceOffset < --sourceLength) {
|
|
const leftValue = source[sourceOffset];
|
|
target_WILL_MUTATE[sourceOffset + targetOffset] = source[sourceLength];
|
|
target_WILL_MUTATE[sourceLength + targetOffset] = leftValue;
|
|
sourceOffset++;
|
|
}
|
|
if (sourceOffset === sourceLength) {
|
|
target_WILL_MUTATE[sourceOffset + targetOffset] = source[sourceOffset];
|
|
}
|
|
}
|
|
function reverseEncoder(encoder) {
|
|
assertIsFixedSize(encoder);
|
|
return createEncoder({
|
|
...encoder,
|
|
write: (value, bytes, offset) => {
|
|
const newOffset = encoder.write(value, bytes, offset);
|
|
copySourceToTargetInReverse(
|
|
bytes,
|
|
bytes,
|
|
offset,
|
|
offset + encoder.fixedSize
|
|
);
|
|
return newOffset;
|
|
}
|
|
});
|
|
}
|
|
function reverseDecoder(decoder) {
|
|
assertIsFixedSize(decoder);
|
|
return createDecoder({
|
|
...decoder,
|
|
read: (bytes, offset) => {
|
|
const reversedBytes = bytes.slice();
|
|
copySourceToTargetInReverse(
|
|
bytes,
|
|
reversedBytes,
|
|
offset,
|
|
offset + decoder.fixedSize
|
|
);
|
|
return decoder.read(reversedBytes, offset);
|
|
}
|
|
});
|
|
}
|
|
function reverseCodec(codec) {
|
|
return combineCodec(reverseEncoder(codec), reverseDecoder(codec));
|
|
}
|
|
|
|
// src/transform-codec.ts
|
|
function transformEncoder(encoder, unmap) {
|
|
return createEncoder({
|
|
...isVariableSize(encoder) ? { ...encoder, getSizeFromValue: (value) => encoder.getSizeFromValue(unmap(value)) } : encoder,
|
|
write: (value, bytes, offset) => encoder.write(unmap(value), bytes, offset)
|
|
});
|
|
}
|
|
function transformDecoder(decoder, map) {
|
|
return createDecoder({
|
|
...decoder,
|
|
read: (bytes, offset) => {
|
|
const [value, newOffset] = decoder.read(bytes, offset);
|
|
return [map(value, bytes, offset), newOffset];
|
|
}
|
|
});
|
|
}
|
|
function transformCodec(codec, unmap, map) {
|
|
return createCodec({
|
|
...transformEncoder(codec, unmap),
|
|
read: map ? transformDecoder(codec, map).read : codec.read
|
|
});
|
|
}
|
|
|
|
export { addCodecSentinel, addCodecSizePrefix, addDecoderSentinel, addDecoderSizePrefix, addEncoderSentinel, addEncoderSizePrefix, assertByteArrayHasEnoughBytesForCodec, assertByteArrayIsNotEmptyForCodec, assertByteArrayOffsetIsNotOutOfRange, assertIsFixedSize, assertIsVariableSize, combineCodec, containsBytes, createCodec, createDecoder, createEncoder, fixBytes, fixCodecSize, fixDecoderSize, fixEncoderSize, getEncodedSize, isFixedSize, isVariableSize, mergeBytes, offsetCodec, offsetDecoder, offsetEncoder, padBytes, padLeftCodec, padLeftDecoder, padLeftEncoder, padRightCodec, padRightDecoder, padRightEncoder, resizeCodec, resizeDecoder, resizeEncoder, reverseCodec, reverseDecoder, reverseEncoder, transformCodec, transformDecoder, transformEncoder };
|
|
//# sourceMappingURL=index.node.mjs.map
|
|
//# sourceMappingURL=index.node.mjs.map |