@cartbc/codecs-strings
Version:
Codecs for strings of different sizes and encodings
252 lines (242 loc) • 9.47 kB
JavaScript
import { combineCodec, mapEncoder, mapDecoder, fixEncoder, mergeBytes, fixDecoder, assertByteArrayIsNotEmptyForCodec, assertByteArrayHasEnoughBytesForCodec } from '@cartbc/codecs-core';
import { getU32Encoder, getU32Decoder } from '@cartbc/codecs-numbers';
// src/assertions.ts
function assertValidBaseString(alphabet4, testValue, givenValue = testValue) {
if (!testValue.match(new RegExp(`^[${alphabet4}]*$`))) {
throw new Error(`Expected a string of base ${alphabet4.length}, got [${givenValue}].`);
}
}
var getBaseXEncoder = (alphabet4) => {
const base = alphabet4.length;
const baseBigInt = BigInt(base);
return {
description: `base${base}`,
encode(value) {
assertValidBaseString(alphabet4, value);
if (value === "")
return new Uint8Array();
const chars = [...value];
let trailIndex = chars.findIndex((c) => c !== alphabet4[0]);
trailIndex = trailIndex === -1 ? chars.length : trailIndex;
const leadingZeroes = Array(trailIndex).fill(0);
if (trailIndex === chars.length)
return Uint8Array.from(leadingZeroes);
const tailChars = chars.slice(trailIndex);
let base10Number = 0n;
let baseXPower = 1n;
for (let i = tailChars.length - 1; i >= 0; i -= 1) {
base10Number += baseXPower * BigInt(alphabet4.indexOf(tailChars[i]));
baseXPower *= baseBigInt;
}
const tailBytes = [];
while (base10Number > 0n) {
tailBytes.unshift(Number(base10Number % 256n));
base10Number /= 256n;
}
return Uint8Array.from(leadingZeroes.concat(tailBytes));
},
fixedSize: null,
maxSize: null
};
};
var getBaseXDecoder = (alphabet4) => {
const base = alphabet4.length;
const baseBigInt = BigInt(base);
return {
decode(rawBytes, offset = 0) {
const bytes = offset === 0 ? rawBytes : rawBytes.slice(offset);
if (bytes.length === 0)
return ["", 0];
let trailIndex = bytes.findIndex((n) => n !== 0);
trailIndex = trailIndex === -1 ? bytes.length : trailIndex;
const leadingZeroes = alphabet4[0].repeat(trailIndex);
if (trailIndex === bytes.length)
return [leadingZeroes, rawBytes.length];
let base10Number = bytes.slice(trailIndex).reduce((sum, byte) => sum * 256n + BigInt(byte), 0n);
const tailChars = [];
while (base10Number > 0n) {
tailChars.unshift(alphabet4[Number(base10Number % baseBigInt)]);
base10Number /= baseBigInt;
}
return [leadingZeroes + tailChars.join(""), rawBytes.length];
},
description: `base${base}`,
fixedSize: null,
maxSize: null
};
};
var getBaseXCodec = (alphabet4) => combineCodec(getBaseXEncoder(alphabet4), getBaseXDecoder(alphabet4));
// src/base10.ts
var alphabet = "0123456789";
var getBase10Encoder = () => getBaseXEncoder(alphabet);
var getBase10Decoder = () => getBaseXDecoder(alphabet);
var getBase10Codec = () => getBaseXCodec(alphabet);
var getBase16Encoder = () => ({
description: "base16",
encode(value) {
const lowercaseValue = value.toLowerCase();
assertValidBaseString("0123456789abcdef", lowercaseValue, value);
const matches = lowercaseValue.match(/.{1,2}/g);
return Uint8Array.from(matches ? matches.map((byte) => parseInt(byte, 16)) : []);
},
fixedSize: null,
maxSize: null
});
var getBase16Decoder = () => ({
decode(bytes, offset = 0) {
const value = bytes.slice(offset).reduce((str, byte) => str + byte.toString(16).padStart(2, "0"), "");
return [value, bytes.length];
},
description: "base16",
fixedSize: null,
maxSize: null
});
var getBase16Codec = () => combineCodec(getBase16Encoder(), getBase16Decoder());
// src/base58.ts
var alphabet2 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
var getBase58Encoder = () => getBaseXEncoder(alphabet2);
var getBase58Decoder = () => getBaseXDecoder(alphabet2);
var getBase58Codec = () => getBaseXCodec(alphabet2);
var getBaseXResliceEncoder = (alphabet4, bits) => ({
description: `base${alphabet4.length}`,
encode(value) {
assertValidBaseString(alphabet4, value);
if (value === "")
return new Uint8Array();
const charIndices = [...value].map((c) => alphabet4.indexOf(c));
return new Uint8Array(reslice(charIndices, bits, 8, false));
},
fixedSize: null,
maxSize: null
});
var getBaseXResliceDecoder = (alphabet4, bits) => ({
decode(rawBytes, offset = 0) {
const bytes = offset === 0 ? rawBytes : rawBytes.slice(offset);
if (bytes.length === 0)
return ["", rawBytes.length];
const charIndices = reslice([...bytes], 8, bits, true);
return [charIndices.map((i) => alphabet4[i]).join(""), rawBytes.length];
},
description: `base${alphabet4.length}`,
fixedSize: null,
maxSize: null
});
var getBaseXResliceCodec = (alphabet4, bits) => combineCodec(getBaseXResliceEncoder(alphabet4, bits), getBaseXResliceDecoder(alphabet4, bits));
function reslice(input, inputBits, outputBits, useRemainder) {
const output = [];
let accumulator = 0;
let bitsInAccumulator = 0;
const mask = (1 << outputBits) - 1;
for (const value of input) {
accumulator = accumulator << inputBits | value;
bitsInAccumulator += inputBits;
while (bitsInAccumulator >= outputBits) {
bitsInAccumulator -= outputBits;
output.push(accumulator >> bitsInAccumulator & mask);
}
}
if (useRemainder && bitsInAccumulator > 0) {
output.push(accumulator << outputBits - bitsInAccumulator & mask);
}
return output;
}
// src/base64.ts
var alphabet3 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var getBase64Encoder = () => {
return mapEncoder(getBaseXResliceEncoder(alphabet3, 6), (value) => value.replace(/=/g, ""));
};
var getBase64Decoder = () => {
return mapDecoder(
getBaseXResliceDecoder(alphabet3, 6),
(value) => value.padEnd(Math.ceil(value.length / 4) * 4, "=")
);
};
var getBase64Codec = () => combineCodec(getBase64Encoder(), getBase64Decoder());
// src/null-characters.ts
var removeNullCharacters = (value) => (
// eslint-disable-next-line no-control-regex
value.replace(/\u0000/g, "")
);
var padNullCharacters = (value, chars) => value.padEnd(chars, "\0");
// ../text-encoding-impl/dist/index.browser.js
var e = globalThis.TextDecoder;
var o = globalThis.TextEncoder;
// src/utf8.ts
var getUtf8Encoder = () => {
let textEncoder;
return {
description: "utf8",
encode: (value) => new Uint8Array((textEncoder || (textEncoder = new o())).encode(value)),
fixedSize: null,
maxSize: null
};
};
var getUtf8Decoder = () => {
let textDecoder;
return {
decode(bytes, offset = 0) {
const value = (textDecoder || (textDecoder = new e())).decode(bytes.slice(offset));
return [removeNullCharacters(value), bytes.length];
},
description: "utf8",
fixedSize: null,
maxSize: null
};
};
var getUtf8Codec = () => combineCodec(getUtf8Encoder(), getUtf8Decoder());
// src/string.ts
var getStringEncoder = (options = {}) => {
const size = options.size ?? getU32Encoder();
const encoding = options.encoding ?? getUtf8Encoder();
const description = options.description ?? `string(${encoding.description}; ${getSizeDescription(size)})`;
if (size === "variable") {
return { ...encoding, description };
}
if (typeof size === "number") {
return fixEncoder(encoding, size, description);
}
return {
description,
encode: (value) => {
const contentBytes = encoding.encode(value);
const lengthBytes = size.encode(contentBytes.length);
return mergeBytes([lengthBytes, contentBytes]);
},
fixedSize: null,
maxSize: null
};
};
var getStringDecoder = (options = {}) => {
const size = options.size ?? getU32Decoder();
const encoding = options.encoding ?? getUtf8Decoder();
const description = options.description ?? `string(${encoding.description}; ${getSizeDescription(size)})`;
if (size === "variable") {
return { ...encoding, description };
}
if (typeof size === "number") {
return fixDecoder(encoding, size, description);
}
return {
decode: (bytes, offset = 0) => {
assertByteArrayIsNotEmptyForCodec("string", bytes, offset);
const [lengthBigInt, lengthOffset] = size.decode(bytes, offset);
const length = Number(lengthBigInt);
offset = lengthOffset;
const contentBytes = bytes.slice(offset, offset + length);
assertByteArrayHasEnoughBytesForCodec("string", length, contentBytes);
const [value, contentOffset] = encoding.decode(contentBytes);
offset += contentOffset;
return [value, offset];
},
description,
fixedSize: null,
maxSize: null
};
};
var getStringCodec = (options = {}) => combineCodec(getStringEncoder(options), getStringDecoder(options));
function getSizeDescription(size) {
return typeof size === "object" ? size.description : `${size}`;
}
export { assertValidBaseString, getBase10Codec, getBase10Decoder, getBase10Encoder, getBase16Codec, getBase16Decoder, getBase16Encoder, getBase58Codec, getBase58Decoder, getBase58Encoder, getBase64Codec, getBase64Decoder, getBase64Encoder, getBaseXCodec, getBaseXDecoder, getBaseXEncoder, getBaseXResliceCodec, getBaseXResliceDecoder, getBaseXResliceEncoder, getStringCodec, getStringDecoder, getStringEncoder, getUtf8Codec, getUtf8Decoder, getUtf8Encoder, padNullCharacters, removeNullCharacters };
//# sourceMappingURL=out.js.map
//# sourceMappingURL=index.native.js.map