@cartbc/codecs-core
Version:
Core types and helpers for encoding and decoding byte arrays on Cartes
186 lines (178 loc) • 6.22 kB
JavaScript
// 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));
}
exports.assertByteArrayHasEnoughBytesForCodec = assertByteArrayHasEnoughBytesForCodec;
exports.assertByteArrayIsNotEmptyForCodec = assertByteArrayIsNotEmptyForCodec;
exports.assertFixedSizeCodec = assertFixedSizeCodec;
exports.combineCodec = combineCodec;
exports.fixBytes = fixBytes;
exports.fixCodec = fixCodec;
exports.fixDecoder = fixDecoder;
exports.fixEncoder = fixEncoder;
exports.mapCodec = mapCodec;
exports.mapDecoder = mapDecoder;
exports.mapEncoder = mapEncoder;
exports.mergeBytes = mergeBytes;
exports.padBytes = padBytes;
exports.reverseCodec = reverseCodec;
exports.reverseDecoder = reverseDecoder;
exports.reverseEncoder = reverseEncoder;
//# sourceMappingURL=out.js.map
//# sourceMappingURL=index.browser.cjs.map
;