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__ENCODED_BYTES_MUST_NOT_INCLUDE_SENTINEL, SOLANA_ERROR__CODECS__SENTINEL_MISSING_IN_DECODED_BYTES } 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.native.mjs.map
//# sourceMappingURL=index.native.mjs.map