UNPKG

@cartbc/codecs-core

Version:

Core types and helpers for encoding and decoding byte arrays on Cartes

169 lines (162 loc) 5.81 kB
// src/assertions.ts function assertByteArrayIsNotEmptyForCodec(codecDescription, bytes, offset = 0) { if (bytes.length - offset <= 0) { throw new Error(`Codec [${codecDescription}] cannot decode empty byte arrays.`); } } function assertByteArrayHasEnoughBytesForCodec(codecDescription, expected, bytes, offset = 0) { const bytesLength = bytes.length - offset; if (bytesLength < expected) { throw new Error(`Codec [${codecDescription}] expected ${expected} bytes, got ${bytesLength}.`); } } function assertFixedSizeCodec(data, message) { if (data.fixedSize === null) { throw new Error(message ?? "Expected a fixed-size codec, got a variable-size one."); } } // 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); // src/combine-codec.ts function combineCodec(encoder, decoder, description) { if (encoder.fixedSize !== decoder.fixedSize) { throw new Error( `Encoder and decoder must have the same fixed size, got [${encoder.fixedSize}] and [${decoder.fixedSize}].` ); } if (encoder.maxSize !== decoder.maxSize) { throw new Error( `Encoder and decoder must have the same max size, got [${encoder.maxSize}] and [${decoder.maxSize}].` ); } if (description === void 0 && encoder.description !== decoder.description) { throw new Error( `Encoder and decoder must have the same description, got [${encoder.description}] and [${decoder.description}]. Pass a custom description as a third argument if you want to override the description and bypass this error.` ); } return { decode: decoder.decode, description: description ?? encoder.description, encode: encoder.encode, fixedSize: encoder.fixedSize, maxSize: encoder.maxSize }; } // src/fix-codec.ts function fixCodecHelper(data, fixedBytes, description) { return { description: description ?? `fixed(${fixedBytes}, ${data.description})`, fixedSize: fixedBytes, maxSize: fixedBytes }; } function fixEncoder(encoder, fixedBytes, description) { return { ...fixCodecHelper(encoder, fixedBytes, description), encode: (value) => fixBytes(encoder.encode(value), fixedBytes) }; } function fixDecoder(decoder, fixedBytes, description) { return { ...fixCodecHelper(decoder, fixedBytes, description), decode: (bytes, offset = 0) => { assertByteArrayHasEnoughBytesForCodec("fixCodec", fixedBytes, bytes, offset); if (offset > 0 || bytes.length > fixedBytes) { bytes = bytes.slice(offset, offset + fixedBytes); } if (decoder.fixedSize !== null) { bytes = fixBytes(bytes, decoder.fixedSize); } const [value] = decoder.decode(bytes, 0); return [value, offset + fixedBytes]; } }; } function fixCodec(codec, fixedBytes, description) { return combineCodec(fixEncoder(codec, fixedBytes, description), fixDecoder(codec, fixedBytes, description)); } // src/map-codec.ts function mapEncoder(encoder, unmap) { return { description: encoder.description, encode: (value) => encoder.encode(unmap(value)), fixedSize: encoder.fixedSize, maxSize: encoder.maxSize }; } function mapDecoder(decoder, map) { return { decode: (bytes, offset = 0) => { const [value, length] = decoder.decode(bytes, offset); return [map(value, bytes, offset), length]; }, description: decoder.description, fixedSize: decoder.fixedSize, maxSize: decoder.maxSize }; } function mapCodec(codec, unmap, map) { return { decode: map ? mapDecoder(codec, map).decode : codec.decode, description: codec.description, encode: mapEncoder(codec, unmap).encode, fixedSize: codec.fixedSize, maxSize: codec.maxSize }; } // src/reverse-codec.ts function reverseEncoder(encoder) { assertFixedSizeCodec(encoder, "Cannot reverse a codec of variable size."); return { ...encoder, encode: (value) => encoder.encode(value).reverse() }; } function reverseDecoder(decoder) { assertFixedSizeCodec(decoder, "Cannot reverse a codec of variable size."); return { ...decoder, decode: (bytes, offset = 0) => { const reverseEnd = offset + decoder.fixedSize; if (offset === 0 && bytes.length === reverseEnd) { return decoder.decode(bytes.reverse(), offset); } const newBytes = mergeBytes([ ...offset === 0 ? [] : [bytes.slice(0, offset)], bytes.slice(offset, reverseEnd).reverse(), ...bytes.length === reverseEnd ? [] : [bytes.slice(reverseEnd)] ]); return decoder.decode(newBytes, offset); } }; } function reverseCodec(codec) { return combineCodec(reverseEncoder(codec), reverseDecoder(codec)); } export { assertByteArrayHasEnoughBytesForCodec, assertByteArrayIsNotEmptyForCodec, assertFixedSizeCodec, combineCodec, fixBytes, fixCodec, fixDecoder, fixEncoder, mapCodec, mapDecoder, mapEncoder, mergeBytes, padBytes, reverseCodec, reverseDecoder, reverseEncoder }; //# sourceMappingURL=out.js.map //# sourceMappingURL=index.node.js.map