Add comprehensive installation and setup documentation

- Add GETTING_STARTED.md with quick start guide and development modes
- Add INSTALL.sh automated installation script
- Add INSTALLATION_CHECKLIST.md, INSTALLATION_SUCCESS.md, and INSTALLATION_SUMMARY.md
- Add QUICK_REFERENCE.md for common commands
- Add SETUP_GUIDE.md with detailed setup instructions
- Update README.md with improved project overview
- Add did-wallet app dependencies and node_modules
This commit is contained in:
Dorian
2026-01-27 17:18:21 +00:00
parent a81f655133
commit 0d073fa89e
22658 changed files with 4494151 additions and 6 deletions
+152
View File
@@ -0,0 +1,152 @@
import {
Token,
Type
} from './token.js';
import {
decodeErrPrefix,
assertEnoughData
} from './common.js';
export const uintBoundaries = [
24,
256,
65536,
4294967296,
BigInt('18446744073709551616')
];
export function readUint8(data, offset, options) {
assertEnoughData(data, offset, 1);
const value = data[offset];
if (options.strict === true && value < uintBoundaries[0]) {
throw new Error(`${ decodeErrPrefix } integer encoded in more bytes than necessary (strict decode)`);
}
return value;
}
export function readUint16(data, offset, options) {
assertEnoughData(data, offset, 2);
const value = data[offset] << 8 | data[offset + 1];
if (options.strict === true && value < uintBoundaries[1]) {
throw new Error(`${ decodeErrPrefix } integer encoded in more bytes than necessary (strict decode)`);
}
return value;
}
export function readUint32(data, offset, options) {
assertEnoughData(data, offset, 4);
const value = data[offset] * 16777216 + (data[offset + 1] << 16) + (data[offset + 2] << 8) + data[offset + 3];
if (options.strict === true && value < uintBoundaries[2]) {
throw new Error(`${ decodeErrPrefix } integer encoded in more bytes than necessary (strict decode)`);
}
return value;
}
export function readUint64(data, offset, options) {
assertEnoughData(data, offset, 8);
const hi = data[offset] * 16777216 + (data[offset + 1] << 16) + (data[offset + 2] << 8) + data[offset + 3];
const lo = data[offset + 4] * 16777216 + (data[offset + 5] << 16) + (data[offset + 6] << 8) + data[offset + 7];
const value = (BigInt(hi) << BigInt(32)) + BigInt(lo);
if (options.strict === true && value < uintBoundaries[3]) {
throw new Error(`${ decodeErrPrefix } integer encoded in more bytes than necessary (strict decode)`);
}
if (value <= Number.MAX_SAFE_INTEGER) {
return Number(value);
}
if (options.allowBigInt === true) {
return value;
}
throw new Error(`${ decodeErrPrefix } integers outside of the safe integer range are not supported`);
}
export function decodeUint8(data, pos, _minor, options) {
return new Token(Type.uint, readUint8(data, pos + 1, options), 2);
}
export function decodeUint16(data, pos, _minor, options) {
return new Token(Type.uint, readUint16(data, pos + 1, options), 3);
}
export function decodeUint32(data, pos, _minor, options) {
return new Token(Type.uint, readUint32(data, pos + 1, options), 5);
}
export function decodeUint64(data, pos, _minor, options) {
return new Token(Type.uint, readUint64(data, pos + 1, options), 9);
}
export function encodeUint(buf, token) {
return encodeUintValue(buf, 0, token.value);
}
export function encodeUintValue(buf, major, uint) {
if (uint < uintBoundaries[0]) {
const nuint = Number(uint);
buf.push([major | nuint]);
} else if (uint < uintBoundaries[1]) {
const nuint = Number(uint);
buf.push([
major | 24,
nuint
]);
} else if (uint < uintBoundaries[2]) {
const nuint = Number(uint);
buf.push([
major | 25,
nuint >>> 8,
nuint & 255
]);
} else if (uint < uintBoundaries[3]) {
const nuint = Number(uint);
buf.push([
major | 26,
nuint >>> 24 & 255,
nuint >>> 16 & 255,
nuint >>> 8 & 255,
nuint & 255
]);
} else {
const buint = BigInt(uint);
if (buint < uintBoundaries[4]) {
const set = [
major | 27,
0,
0,
0,
0,
0,
0,
0
];
let lo = Number(buint & BigInt(4294967295));
let hi = Number(buint >> BigInt(32) & BigInt(4294967295));
set[8] = lo & 255;
lo = lo >> 8;
set[7] = lo & 255;
lo = lo >> 8;
set[6] = lo & 255;
lo = lo >> 8;
set[5] = lo & 255;
set[4] = hi & 255;
hi = hi >> 8;
set[3] = hi & 255;
hi = hi >> 8;
set[2] = hi & 255;
hi = hi >> 8;
set[1] = hi & 255;
buf.push(set);
} else {
throw new Error(`${ decodeErrPrefix } encountered BigInt larger than allowable range`);
}
}
}
encodeUint.encodedSize = function encodedSize(token) {
return encodeUintValue.encodedSize(token.value);
};
encodeUintValue.encodedSize = function encodedSize(uint) {
if (uint < uintBoundaries[0]) {
return 1;
}
if (uint < uintBoundaries[1]) {
return 2;
}
if (uint < uintBoundaries[2]) {
return 3;
}
if (uint < uintBoundaries[3]) {
return 5;
}
return 9;
};
encodeUint.compareTokens = function compareTokens(tok1, tok2) {
return tok1.value < tok2.value ? -1 : tok1.value > tok2.value ? 1 : 0;
};
+55
View File
@@ -0,0 +1,55 @@
import {
Token,
Type
} from './token.js';
import * as uint from './0uint.js';
import { decodeErrPrefix } from './common.js';
export function decodeNegint8(data, pos, _minor, options) {
return new Token(Type.negint, -1 - uint.readUint8(data, pos + 1, options), 2);
}
export function decodeNegint16(data, pos, _minor, options) {
return new Token(Type.negint, -1 - uint.readUint16(data, pos + 1, options), 3);
}
export function decodeNegint32(data, pos, _minor, options) {
return new Token(Type.negint, -1 - uint.readUint32(data, pos + 1, options), 5);
}
const neg1b = BigInt(-1);
const pos1b = BigInt(1);
export function decodeNegint64(data, pos, _minor, options) {
const int = uint.readUint64(data, pos + 1, options);
if (typeof int !== 'bigint') {
const value = -1 - int;
if (value >= Number.MIN_SAFE_INTEGER) {
return new Token(Type.negint, value, 9);
}
}
if (options.allowBigInt !== true) {
throw new Error(`${ decodeErrPrefix } integers outside of the safe integer range are not supported`);
}
return new Token(Type.negint, neg1b - BigInt(int), 9);
}
export function encodeNegint(buf, token) {
const negint = token.value;
const unsigned = typeof negint === 'bigint' ? negint * neg1b - pos1b : negint * -1 - 1;
uint.encodeUintValue(buf, token.type.majorEncoded, unsigned);
}
encodeNegint.encodedSize = function encodedSize(token) {
const negint = token.value;
const unsigned = typeof negint === 'bigint' ? negint * neg1b - pos1b : negint * -1 - 1;
if (unsigned < uint.uintBoundaries[0]) {
return 1;
}
if (unsigned < uint.uintBoundaries[1]) {
return 2;
}
if (unsigned < uint.uintBoundaries[2]) {
return 3;
}
if (unsigned < uint.uintBoundaries[3]) {
return 5;
}
return 9;
};
encodeNegint.compareTokens = function compareTokens(tok1, tok2) {
return tok1.value < tok2.value ? 1 : tok1.value > tok2.value ? -1 : 0;
};
+59
View File
@@ -0,0 +1,59 @@
import {
Token,
Type
} from './token.js';
import {
assertEnoughData,
decodeErrPrefix
} from './common.js';
import * as uint from './0uint.js';
import {
compare,
fromString,
slice
} from './byte-utils.js';
function toToken(data, pos, prefix, length) {
assertEnoughData(data, pos, prefix + length);
const buf = slice(data, pos + prefix, pos + prefix + length);
return new Token(Type.bytes, buf, prefix + length);
}
export function decodeBytesCompact(data, pos, minor, _options) {
return toToken(data, pos, 1, minor);
}
export function decodeBytes8(data, pos, _minor, options) {
return toToken(data, pos, 2, uint.readUint8(data, pos + 1, options));
}
export function decodeBytes16(data, pos, _minor, options) {
return toToken(data, pos, 3, uint.readUint16(data, pos + 1, options));
}
export function decodeBytes32(data, pos, _minor, options) {
return toToken(data, pos, 5, uint.readUint32(data, pos + 1, options));
}
export function decodeBytes64(data, pos, _minor, options) {
const l = uint.readUint64(data, pos + 1, options);
if (typeof l === 'bigint') {
throw new Error(`${ decodeErrPrefix } 64-bit integer bytes lengths not supported`);
}
return toToken(data, pos, 9, l);
}
function tokenBytes(token) {
if (token.encodedBytes === undefined) {
token.encodedBytes = token.type === Type.string ? fromString(token.value) : token.value;
}
return token.encodedBytes;
}
export function encodeBytes(buf, token) {
const bytes = tokenBytes(token);
uint.encodeUintValue(buf, token.type.majorEncoded, bytes.length);
buf.push(bytes);
}
encodeBytes.encodedSize = function encodedSize(token) {
const bytes = tokenBytes(token);
return uint.encodeUintValue.encodedSize(bytes.length) + bytes.length;
};
encodeBytes.compareTokens = function compareTokens(tok1, tok2) {
return compareBytes(tokenBytes(tok1), tokenBytes(tok2));
};
export function compareBytes(b1, b2) {
return b1.length < b2.length ? -1 : b1.length > b2.length ? 1 : compare(b1, b2);
}
+43
View File
@@ -0,0 +1,43 @@
import {
Token,
Type
} from './token.js';
import {
assertEnoughData,
decodeErrPrefix
} from './common.js';
import * as uint from './0uint.js';
import { encodeBytes } from './2bytes.js';
import {
toString,
slice
} from './byte-utils.js';
function toToken(data, pos, prefix, length, options) {
const totLength = prefix + length;
assertEnoughData(data, pos, totLength);
const tok = new Token(Type.string, toString(data, pos + prefix, pos + totLength), totLength);
if (options.retainStringBytes === true) {
tok.byteValue = slice(data, pos + prefix, pos + totLength);
}
return tok;
}
export function decodeStringCompact(data, pos, minor, options) {
return toToken(data, pos, 1, minor, options);
}
export function decodeString8(data, pos, _minor, options) {
return toToken(data, pos, 2, uint.readUint8(data, pos + 1, options), options);
}
export function decodeString16(data, pos, _minor, options) {
return toToken(data, pos, 3, uint.readUint16(data, pos + 1, options), options);
}
export function decodeString32(data, pos, _minor, options) {
return toToken(data, pos, 5, uint.readUint32(data, pos + 1, options), options);
}
export function decodeString64(data, pos, _minor, options) {
const l = uint.readUint64(data, pos + 1, options);
if (typeof l === 'bigint') {
throw new Error(`${ decodeErrPrefix } 64-bit integer string lengths not supported`);
}
return toToken(data, pos, 9, l, options);
}
export const encodeString = encodeBytes;
+41
View File
@@ -0,0 +1,41 @@
import {
Token,
Type
} from './token.js';
import * as uint from './0uint.js';
import { decodeErrPrefix } from './common.js';
function toToken(_data, _pos, prefix, length) {
return new Token(Type.array, length, prefix);
}
export function decodeArrayCompact(data, pos, minor, _options) {
return toToken(data, pos, 1, minor);
}
export function decodeArray8(data, pos, _minor, options) {
return toToken(data, pos, 2, uint.readUint8(data, pos + 1, options));
}
export function decodeArray16(data, pos, _minor, options) {
return toToken(data, pos, 3, uint.readUint16(data, pos + 1, options));
}
export function decodeArray32(data, pos, _minor, options) {
return toToken(data, pos, 5, uint.readUint32(data, pos + 1, options));
}
export function decodeArray64(data, pos, _minor, options) {
const l = uint.readUint64(data, pos + 1, options);
if (typeof l === 'bigint') {
throw new Error(`${ decodeErrPrefix } 64-bit integer array lengths not supported`);
}
return toToken(data, pos, 9, l);
}
export function decodeArrayIndefinite(data, pos, _minor, options) {
if (options.allowIndefinite === false) {
throw new Error(`${ decodeErrPrefix } indefinite length items not allowed`);
}
return toToken(data, pos, 1, Infinity);
}
export function encodeArray(buf, token) {
uint.encodeUintValue(buf, Type.array.majorEncoded, token.value);
}
encodeArray.compareTokens = uint.encodeUint.compareTokens;
encodeArray.encodedSize = function encodedSize(token) {
return uint.encodeUintValue.encodedSize(token.value);
};
+41
View File
@@ -0,0 +1,41 @@
import {
Token,
Type
} from './token.js';
import * as uint from './0uint.js';
import { decodeErrPrefix } from './common.js';
function toToken(_data, _pos, prefix, length) {
return new Token(Type.map, length, prefix);
}
export function decodeMapCompact(data, pos, minor, _options) {
return toToken(data, pos, 1, minor);
}
export function decodeMap8(data, pos, _minor, options) {
return toToken(data, pos, 2, uint.readUint8(data, pos + 1, options));
}
export function decodeMap16(data, pos, _minor, options) {
return toToken(data, pos, 3, uint.readUint16(data, pos + 1, options));
}
export function decodeMap32(data, pos, _minor, options) {
return toToken(data, pos, 5, uint.readUint32(data, pos + 1, options));
}
export function decodeMap64(data, pos, _minor, options) {
const l = uint.readUint64(data, pos + 1, options);
if (typeof l === 'bigint') {
throw new Error(`${ decodeErrPrefix } 64-bit integer map lengths not supported`);
}
return toToken(data, pos, 9, l);
}
export function decodeMapIndefinite(data, pos, _minor, options) {
if (options.allowIndefinite === false) {
throw new Error(`${ decodeErrPrefix } indefinite length items not allowed`);
}
return toToken(data, pos, 1, Infinity);
}
export function encodeMap(buf, token) {
uint.encodeUintValue(buf, Type.map.majorEncoded, token.value);
}
encodeMap.compareTokens = uint.encodeUint.compareTokens;
encodeMap.encodedSize = function encodedSize(token) {
return uint.encodeUintValue.encodedSize(token.value);
};
+27
View File
@@ -0,0 +1,27 @@
import {
Token,
Type
} from './token.js';
import * as uint from './0uint.js';
export function decodeTagCompact(_data, _pos, minor, _options) {
return new Token(Type.tag, minor, 1);
}
export function decodeTag8(data, pos, _minor, options) {
return new Token(Type.tag, uint.readUint8(data, pos + 1, options), 2);
}
export function decodeTag16(data, pos, _minor, options) {
return new Token(Type.tag, uint.readUint16(data, pos + 1, options), 3);
}
export function decodeTag32(data, pos, _minor, options) {
return new Token(Type.tag, uint.readUint32(data, pos + 1, options), 5);
}
export function decodeTag64(data, pos, _minor, options) {
return new Token(Type.tag, uint.readUint64(data, pos + 1, options), 9);
}
export function encodeTag(buf, token) {
uint.encodeUintValue(buf, Type.tag.majorEncoded, token.value);
}
encodeTag.compareTokens = uint.encodeUint.compareTokens;
encodeTag.encodedSize = function encodedSize(token) {
return uint.encodeUintValue.encodedSize(token.value);
};
+179
View File
@@ -0,0 +1,179 @@
import {
Token,
Type
} from './token.js';
import { decodeErrPrefix } from './common.js';
import { encodeUint } from './0uint.js';
const MINOR_FALSE = 20;
const MINOR_TRUE = 21;
const MINOR_NULL = 22;
const MINOR_UNDEFINED = 23;
export function decodeUndefined(_data, _pos, _minor, options) {
if (options.allowUndefined === false) {
throw new Error(`${ decodeErrPrefix } undefined values are not supported`);
} else if (options.coerceUndefinedToNull === true) {
return new Token(Type.null, null, 1);
}
return new Token(Type.undefined, undefined, 1);
}
export function decodeBreak(_data, _pos, _minor, options) {
if (options.allowIndefinite === false) {
throw new Error(`${ decodeErrPrefix } indefinite length items not allowed`);
}
return new Token(Type.break, undefined, 1);
}
function createToken(value, bytes, options) {
if (options) {
if (options.allowNaN === false && Number.isNaN(value)) {
throw new Error(`${ decodeErrPrefix } NaN values are not supported`);
}
if (options.allowInfinity === false && (value === Infinity || value === -Infinity)) {
throw new Error(`${ decodeErrPrefix } Infinity values are not supported`);
}
}
return new Token(Type.float, value, bytes);
}
export function decodeFloat16(data, pos, _minor, options) {
return createToken(readFloat16(data, pos + 1), 3, options);
}
export function decodeFloat32(data, pos, _minor, options) {
return createToken(readFloat32(data, pos + 1), 5, options);
}
export function decodeFloat64(data, pos, _minor, options) {
return createToken(readFloat64(data, pos + 1), 9, options);
}
export function encodeFloat(buf, token, options) {
const float = token.value;
if (float === false) {
buf.push([Type.float.majorEncoded | MINOR_FALSE]);
} else if (float === true) {
buf.push([Type.float.majorEncoded | MINOR_TRUE]);
} else if (float === null) {
buf.push([Type.float.majorEncoded | MINOR_NULL]);
} else if (float === undefined) {
buf.push([Type.float.majorEncoded | MINOR_UNDEFINED]);
} else {
let decoded;
let success = false;
if (!options || options.float64 !== true) {
encodeFloat16(float);
decoded = readFloat16(ui8a, 1);
if (float === decoded || Number.isNaN(float)) {
ui8a[0] = 249;
buf.push(ui8a.slice(0, 3));
success = true;
} else {
encodeFloat32(float);
decoded = readFloat32(ui8a, 1);
if (float === decoded) {
ui8a[0] = 250;
buf.push(ui8a.slice(0, 5));
success = true;
}
}
}
if (!success) {
encodeFloat64(float);
decoded = readFloat64(ui8a, 1);
ui8a[0] = 251;
buf.push(ui8a.slice(0, 9));
}
}
}
encodeFloat.encodedSize = function encodedSize(token, options) {
const float = token.value;
if (float === false || float === true || float === null || float === undefined) {
return 1;
}
if (!options || options.float64 !== true) {
encodeFloat16(float);
let decoded = readFloat16(ui8a, 1);
if (float === decoded || Number.isNaN(float)) {
return 3;
}
encodeFloat32(float);
decoded = readFloat32(ui8a, 1);
if (float === decoded) {
return 5;
}
}
return 9;
};
const buffer = new ArrayBuffer(9);
const dataView = new DataView(buffer, 1);
const ui8a = new Uint8Array(buffer, 0);
function encodeFloat16(inp) {
if (inp === Infinity) {
dataView.setUint16(0, 31744, false);
} else if (inp === -Infinity) {
dataView.setUint16(0, 64512, false);
} else if (Number.isNaN(inp)) {
dataView.setUint16(0, 32256, false);
} else {
dataView.setFloat32(0, inp);
const valu32 = dataView.getUint32(0);
const exponent = (valu32 & 2139095040) >> 23;
const mantissa = valu32 & 8388607;
if (exponent === 255) {
dataView.setUint16(0, 31744, false);
} else if (exponent === 0) {
dataView.setUint16(0, (inp & 2147483648) >> 16 | mantissa >> 13, false);
} else {
const logicalExponent = exponent - 127;
if (logicalExponent < -24) {
dataView.setUint16(0, 0);
} else if (logicalExponent < -14) {
dataView.setUint16(0, (valu32 & 2147483648) >> 16 | 1 << 24 + logicalExponent, false);
} else {
dataView.setUint16(0, (valu32 & 2147483648) >> 16 | logicalExponent + 15 << 10 | mantissa >> 13, false);
}
}
}
}
function readFloat16(ui8a, pos) {
if (ui8a.length - pos < 2) {
throw new Error(`${ decodeErrPrefix } not enough data for float16`);
}
const half = (ui8a[pos] << 8) + ui8a[pos + 1];
if (half === 31744) {
return Infinity;
}
if (half === 64512) {
return -Infinity;
}
if (half === 32256) {
return NaN;
}
const exp = half >> 10 & 31;
const mant = half & 1023;
let val;
if (exp === 0) {
val = mant * 2 ** -24;
} else if (exp !== 31) {
val = (mant + 1024) * 2 ** (exp - 25);
} else {
val = mant === 0 ? Infinity : NaN;
}
return half & 32768 ? -val : val;
}
function encodeFloat32(inp) {
dataView.setFloat32(0, inp, false);
}
function readFloat32(ui8a, pos) {
if (ui8a.length - pos < 4) {
throw new Error(`${ decodeErrPrefix } not enough data for float32`);
}
const offset = (ui8a.byteOffset || 0) + pos;
return new DataView(ui8a.buffer, offset, 4).getFloat32(0, false);
}
function encodeFloat64(inp) {
dataView.setFloat64(0, inp, false);
}
function readFloat64(ui8a, pos) {
if (ui8a.length - pos < 8) {
throw new Error(`${ decodeErrPrefix } not enough data for float64`);
}
const offset = (ui8a.byteOffset || 0) + pos;
return new DataView(ui8a.buffer, offset, 8).getFloat64(0, false);
}
encodeFloat.compareTokens = encodeUint.compareTokens;
+137
View File
@@ -0,0 +1,137 @@
import process from 'process';
import {
decode,
encode
} from '../cborg.js';
import {
tokensToDiagnostic,
fromDiag
} from './diagnostic.js';
import {
fromHex as _fromHex,
toHex
} from './byte-utils.js';
function usage(code) {
console.error('Usage: cborg <command> <args>');
console.error('Valid commands:');
console.error('\tbin2diag [binary input]');
console.error('\tbin2hex [binary input]');
console.error('\tbin2json [--pretty] [binary input]');
console.error('\tdiag2bin [diagnostic input]');
console.error('\tdiag2hex [diagnostic input]');
console.error('\tdiag2json [--pretty] [diagnostic input]');
console.error('\thex2bin [hex input]');
console.error('\thex2diag [hex input]');
console.error('\thex2json [--pretty] [hex input]');
console.error('\tjson2bin \'[json input]\'');
console.error('\tjson2diag \'[json input]\'');
console.error('\tjson2hex \'[json input]\'');
console.error('Input may either be supplied as an argument or piped via stdin');
process.exit(code || 0);
}
async function fromStdin() {
const chunks = [];
for await (const chunk of process.stdin) {
chunks.push(chunk);
}
return Buffer.concat(chunks);
}
function fromHex(str) {
str = str.replace(/\r?\n/g, '');
if (!/^([0-9a-f]{2})*$/i.test(str)) {
throw new Error('Input string is not hexadecimal format');
}
return _fromHex(str);
}
function argvPretty() {
const argv = process.argv.filter(s => s !== '--pretty');
const pretty = argv.length !== process.argv.length;
return {
argv,
pretty
};
}
async function run() {
const cmd = process.argv[2];
switch (cmd) {
case 'help': {
return usage(0);
}
case 'bin2diag': {
const bin = process.argv.length < 4 ? await fromStdin() : new TextEncoder().encode(process.argv[3]);
for (const line of tokensToDiagnostic(bin)) {
console.log(line);
}
return;
}
case 'bin2hex': {
const bin = process.argv.length < 4 ? await fromStdin() : new TextEncoder().encode(process.argv[3]);
return console.log(toHex(bin));
}
case 'bin2json': {
const {argv, pretty} = argvPretty();
const bin = argv.length < 4 ? await fromStdin() : new TextEncoder().encode(argv[3]);
return console.log(JSON.stringify(decode(bin), undefined, pretty ? 2 : undefined));
}
case 'diag2bin': {
const bin = fromDiag(process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3]);
return process.stdout.write(bin);
}
case 'diag2hex': {
const bin = fromDiag(process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3]);
return console.log(toHex(bin));
}
case 'diag2json': {
const {argv, pretty} = argvPretty();
const bin = fromDiag(argv.length < 4 ? (await fromStdin()).toString() : argv[3]);
return console.log(JSON.stringify(decode(bin), undefined, pretty ? 2 : undefined));
}
case 'hex2bin': {
const bin = fromHex(process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3]);
return process.stdout.write(bin);
}
case 'hex2diag': {
const bin = fromHex(process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3]);
for (const line of tokensToDiagnostic(bin)) {
console.log(line);
}
return;
}
case 'hex2json': {
const {argv, pretty} = argvPretty();
const bin = fromHex(argv.length < 4 ? (await fromStdin()).toString() : argv[3]);
return console.log(JSON.stringify(decode(bin), undefined, pretty ? 2 : undefined));
}
case 'json2bin': {
const inp = process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3];
const obj = JSON.parse(inp);
return process.stdout.write(encode(obj));
}
case 'json2diag': {
const inp = process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3];
const obj = JSON.parse(inp);
for (const line of tokensToDiagnostic(encode(obj))) {
console.log(line);
}
return;
}
case 'json2hex': {
const inp = process.argv.length < 4 ? (await fromStdin()).toString() : process.argv[3];
const obj = JSON.parse(inp);
return console.log(toHex(encode(obj)));
}
default: {
if (process.argv.findIndex(a => a.endsWith('mocha')) === -1) {
if (cmd) {
console.error(`Unknown command: '${ cmd }'`);
}
usage(1);
}
}
}
}
run().catch(err => {
console.error(err);
process.exit(1);
});
export default true;
+74
View File
@@ -0,0 +1,74 @@
import {
alloc,
concat,
slice
} from './byte-utils.js';
const defaultChunkSize = 256;
export class Bl {
constructor(chunkSize = defaultChunkSize) {
this.chunkSize = chunkSize;
this.cursor = 0;
this.maxCursor = -1;
this.chunks = [];
this._initReuseChunk = null;
}
reset() {
this.cursor = 0;
this.maxCursor = -1;
if (this.chunks.length) {
this.chunks = [];
}
if (this._initReuseChunk !== null) {
this.chunks.push(this._initReuseChunk);
this.maxCursor = this._initReuseChunk.length - 1;
}
}
push(bytes) {
let topChunk = this.chunks[this.chunks.length - 1];
const newMax = this.cursor + bytes.length;
if (newMax <= this.maxCursor + 1) {
const chunkPos = topChunk.length - (this.maxCursor - this.cursor) - 1;
topChunk.set(bytes, chunkPos);
} else {
if (topChunk) {
const chunkPos = topChunk.length - (this.maxCursor - this.cursor) - 1;
if (chunkPos < topChunk.length) {
this.chunks[this.chunks.length - 1] = topChunk.subarray(0, chunkPos);
this.maxCursor = this.cursor - 1;
}
}
if (bytes.length < 64 && bytes.length < this.chunkSize) {
topChunk = alloc(this.chunkSize);
this.chunks.push(topChunk);
this.maxCursor += topChunk.length;
if (this._initReuseChunk === null) {
this._initReuseChunk = topChunk;
}
topChunk.set(bytes, 0);
} else {
this.chunks.push(bytes);
this.maxCursor += bytes.length;
}
}
this.cursor += bytes.length;
}
toBytes(reset = false) {
let byts;
if (this.chunks.length === 1) {
const chunk = this.chunks[0];
if (reset && this.cursor > chunk.length / 2) {
byts = this.cursor === chunk.length ? chunk : chunk.subarray(0, this.cursor);
this._initReuseChunk = null;
this.chunks = [];
} else {
byts = slice(chunk, 0, this.cursor);
}
} else {
byts = concat(this.chunks, this.cursor);
}
if (reset) {
this.reset();
}
return byts;
}
}
+228
View File
@@ -0,0 +1,228 @@
export const useBuffer = globalThis.process && !globalThis.process.browser && globalThis.Buffer && typeof globalThis.Buffer.isBuffer === 'function';
const textDecoder = new TextDecoder();
const textEncoder = new TextEncoder();
function isBuffer(buf) {
return useBuffer && globalThis.Buffer.isBuffer(buf);
}
export function asU8A(buf) {
if (!(buf instanceof Uint8Array)) {
return Uint8Array.from(buf);
}
return isBuffer(buf) ? new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength) : buf;
}
export const toString = useBuffer ? (bytes, start, end) => {
return end - start > 64 ? globalThis.Buffer.from(bytes.subarray(start, end)).toString('utf8') : utf8Slice(bytes, start, end);
} : (bytes, start, end) => {
return end - start > 64 ? textDecoder.decode(bytes.subarray(start, end)) : utf8Slice(bytes, start, end);
};
export const fromString = useBuffer ? string => {
return string.length > 64 ? globalThis.Buffer.from(string) : utf8ToBytes(string);
} : string => {
return string.length > 64 ? textEncoder.encode(string) : utf8ToBytes(string);
};
export const fromArray = arr => {
return Uint8Array.from(arr);
};
export const slice = useBuffer ? (bytes, start, end) => {
if (isBuffer(bytes)) {
return new Uint8Array(bytes.subarray(start, end));
}
return bytes.slice(start, end);
} : (bytes, start, end) => {
return bytes.slice(start, end);
};
export const concat = useBuffer ? (chunks, length) => {
chunks = chunks.map(c => c instanceof Uint8Array ? c : globalThis.Buffer.from(c));
return asU8A(globalThis.Buffer.concat(chunks, length));
} : (chunks, length) => {
const out = new Uint8Array(length);
let off = 0;
for (let b of chunks) {
if (off + b.length > out.length) {
b = b.subarray(0, out.length - off);
}
out.set(b, off);
off += b.length;
}
return out;
};
export const alloc = useBuffer ? size => {
return globalThis.Buffer.allocUnsafe(size);
} : size => {
return new Uint8Array(size);
};
export const toHex = useBuffer ? d => {
if (typeof d === 'string') {
return d;
}
return globalThis.Buffer.from(toBytes(d)).toString('hex');
} : d => {
if (typeof d === 'string') {
return d;
}
return Array.prototype.reduce.call(toBytes(d), (p, c) => `${ p }${ c.toString(16).padStart(2, '0') }`, '');
};
export const fromHex = useBuffer ? hex => {
if (hex instanceof Uint8Array) {
return hex;
}
return globalThis.Buffer.from(hex, 'hex');
} : hex => {
if (hex instanceof Uint8Array) {
return hex;
}
if (!hex.length) {
return new Uint8Array(0);
}
return new Uint8Array(hex.split('').map((c, i, d) => i % 2 === 0 ? `0x${ c }${ d[i + 1] }` : '').filter(Boolean).map(e => parseInt(e, 16)));
};
function toBytes(obj) {
if (obj instanceof Uint8Array && obj.constructor.name === 'Uint8Array') {
return obj;
}
if (obj instanceof ArrayBuffer) {
return new Uint8Array(obj);
}
if (ArrayBuffer.isView(obj)) {
return new Uint8Array(obj.buffer, obj.byteOffset, obj.byteLength);
}
throw new Error('Unknown type, must be binary type');
}
export function compare(b1, b2) {
if (isBuffer(b1) && isBuffer(b2)) {
return b1.compare(b2);
}
for (let i = 0; i < b1.length; i++) {
if (b1[i] === b2[i]) {
continue;
}
return b1[i] < b2[i] ? -1 : 1;
}
return 0;
}
function utf8ToBytes(string, units = Infinity) {
let codePoint;
const length = string.length;
let leadSurrogate = null;
const bytes = [];
for (let i = 0; i < length; ++i) {
codePoint = string.charCodeAt(i);
if (codePoint > 55295 && codePoint < 57344) {
if (!leadSurrogate) {
if (codePoint > 56319) {
if ((units -= 3) > -1)
bytes.push(239, 191, 189);
continue;
} else if (i + 1 === length) {
if ((units -= 3) > -1)
bytes.push(239, 191, 189);
continue;
}
leadSurrogate = codePoint;
continue;
}
if (codePoint < 56320) {
if ((units -= 3) > -1)
bytes.push(239, 191, 189);
leadSurrogate = codePoint;
continue;
}
codePoint = (leadSurrogate - 55296 << 10 | codePoint - 56320) + 65536;
} else if (leadSurrogate) {
if ((units -= 3) > -1)
bytes.push(239, 191, 189);
}
leadSurrogate = null;
if (codePoint < 128) {
if ((units -= 1) < 0)
break;
bytes.push(codePoint);
} else if (codePoint < 2048) {
if ((units -= 2) < 0)
break;
bytes.push(codePoint >> 6 | 192, codePoint & 63 | 128);
} else if (codePoint < 65536) {
if ((units -= 3) < 0)
break;
bytes.push(codePoint >> 12 | 224, codePoint >> 6 & 63 | 128, codePoint & 63 | 128);
} else if (codePoint < 1114112) {
if ((units -= 4) < 0)
break;
bytes.push(codePoint >> 18 | 240, codePoint >> 12 & 63 | 128, codePoint >> 6 & 63 | 128, codePoint & 63 | 128);
} else {
throw new Error('Invalid code point');
}
}
return bytes;
}
function utf8Slice(buf, offset, end) {
const res = [];
while (offset < end) {
const firstByte = buf[offset];
let codePoint = null;
let bytesPerSequence = firstByte > 239 ? 4 : firstByte > 223 ? 3 : firstByte > 191 ? 2 : 1;
if (offset + bytesPerSequence <= end) {
let secondByte, thirdByte, fourthByte, tempCodePoint;
switch (bytesPerSequence) {
case 1:
if (firstByte < 128) {
codePoint = firstByte;
}
break;
case 2:
secondByte = buf[offset + 1];
if ((secondByte & 192) === 128) {
tempCodePoint = (firstByte & 31) << 6 | secondByte & 63;
if (tempCodePoint > 127) {
codePoint = tempCodePoint;
}
}
break;
case 3:
secondByte = buf[offset + 1];
thirdByte = buf[offset + 2];
if ((secondByte & 192) === 128 && (thirdByte & 192) === 128) {
tempCodePoint = (firstByte & 15) << 12 | (secondByte & 63) << 6 | thirdByte & 63;
if (tempCodePoint > 2047 && (tempCodePoint < 55296 || tempCodePoint > 57343)) {
codePoint = tempCodePoint;
}
}
break;
case 4:
secondByte = buf[offset + 1];
thirdByte = buf[offset + 2];
fourthByte = buf[offset + 3];
if ((secondByte & 192) === 128 && (thirdByte & 192) === 128 && (fourthByte & 192) === 128) {
tempCodePoint = (firstByte & 15) << 18 | (secondByte & 63) << 12 | (thirdByte & 63) << 6 | fourthByte & 63;
if (tempCodePoint > 65535 && tempCodePoint < 1114112) {
codePoint = tempCodePoint;
}
}
}
}
if (codePoint === null) {
codePoint = 65533;
bytesPerSequence = 1;
} else if (codePoint > 65535) {
codePoint -= 65536;
res.push(codePoint >>> 10 & 1023 | 55296);
codePoint = 56320 | codePoint & 1023;
}
res.push(codePoint);
offset += bytesPerSequence;
}
return decodeCodePointsArray(res);
}
const MAX_ARGUMENTS_LENGTH = 4096;
export function decodeCodePointsArray(codePoints) {
const len = codePoints.length;
if (len <= MAX_ARGUMENTS_LENGTH) {
return String.fromCharCode.apply(String, codePoints);
}
let res = '';
let i = 0;
while (i < len) {
res += String.fromCharCode.apply(String, codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH));
}
return res;
}
+19
View File
@@ -0,0 +1,19 @@
const decodeErrPrefix = 'CBOR decode error:';
const encodeErrPrefix = 'CBOR encode error:';
const uintMinorPrefixBytes = [];
uintMinorPrefixBytes[23] = 1;
uintMinorPrefixBytes[24] = 2;
uintMinorPrefixBytes[25] = 3;
uintMinorPrefixBytes[26] = 5;
uintMinorPrefixBytes[27] = 9;
function assertEnoughData(data, pos, need) {
if (data.length - pos < need) {
throw new Error(`${ decodeErrPrefix } not enough data for type`);
}
}
export {
decodeErrPrefix,
encodeErrPrefix,
uintMinorPrefixBytes,
assertEnoughData
};
+139
View File
@@ -0,0 +1,139 @@
import { decodeErrPrefix } from './common.js';
import { Type } from './token.js';
import {
jump,
quick
} from './jump.js';
const defaultDecodeOptions = {
strict: false,
allowIndefinite: true,
allowUndefined: true,
allowBigInt: true
};
class Tokeniser {
constructor(data, options = {}) {
this.pos = 0;
this.data = data;
this.options = options;
}
done() {
return this.pos >= this.data.length;
}
next() {
const byt = this.data[this.pos];
let token = quick[byt];
if (token === undefined) {
const decoder = jump[byt];
if (!decoder) {
throw new Error(`${ decodeErrPrefix } no decoder for major type ${ byt >>> 5 } (byte 0x${ byt.toString(16).padStart(2, '0') })`);
}
const minor = byt & 31;
token = decoder(this.data, this.pos, minor, this.options);
}
this.pos += token.encodedLength;
return token;
}
}
const DONE = Symbol.for('DONE');
const BREAK = Symbol.for('BREAK');
function tokenToArray(token, tokeniser, options) {
const arr = [];
for (let i = 0; i < token.value; i++) {
const value = tokensToObject(tokeniser, options);
if (value === BREAK) {
if (token.value === Infinity) {
break;
}
throw new Error(`${ decodeErrPrefix } got unexpected break to lengthed array`);
}
if (value === DONE) {
throw new Error(`${ decodeErrPrefix } found array but not enough entries (got ${ i }, expected ${ token.value })`);
}
arr[i] = value;
}
return arr;
}
function tokenToMap(token, tokeniser, options) {
const useMaps = options.useMaps === true;
const obj = useMaps ? undefined : {};
const m = useMaps ? new Map() : undefined;
for (let i = 0; i < token.value; i++) {
const key = tokensToObject(tokeniser, options);
if (key === BREAK) {
if (token.value === Infinity) {
break;
}
throw new Error(`${ decodeErrPrefix } got unexpected break to lengthed map`);
}
if (key === DONE) {
throw new Error(`${ decodeErrPrefix } found map but not enough entries (got ${ i } [no key], expected ${ token.value })`);
}
if (useMaps !== true && typeof key !== 'string') {
throw new Error(`${ decodeErrPrefix } non-string keys not supported (got ${ typeof key })`);
}
if (options.rejectDuplicateMapKeys === true) {
if (useMaps && m.has(key) || !useMaps && key in obj) {
throw new Error(`${ decodeErrPrefix } found repeat map key "${ key }"`);
}
}
const value = tokensToObject(tokeniser, options);
if (value === DONE) {
throw new Error(`${ decodeErrPrefix } found map but not enough entries (got ${ i } [no value], expected ${ token.value })`);
}
if (useMaps) {
m.set(key, value);
} else {
obj[key] = value;
}
}
return useMaps ? m : obj;
}
function tokensToObject(tokeniser, options) {
if (tokeniser.done()) {
return DONE;
}
const token = tokeniser.next();
if (token.type === Type.break) {
return BREAK;
}
if (token.type.terminal) {
return token.value;
}
if (token.type === Type.array) {
return tokenToArray(token, tokeniser, options);
}
if (token.type === Type.map) {
return tokenToMap(token, tokeniser, options);
}
if (token.type === Type.tag) {
if (options.tags && typeof options.tags[token.value] === 'function') {
const tagged = tokensToObject(tokeniser, options);
return options.tags[token.value](tagged);
}
throw new Error(`${ decodeErrPrefix } tag not supported (${ token.value })`);
}
throw new Error('unsupported');
}
function decode(data, options) {
if (!(data instanceof Uint8Array)) {
throw new Error(`${ decodeErrPrefix } data to decode must be a Uint8Array`);
}
options = Object.assign({}, defaultDecodeOptions, options);
const tokeniser = options.tokenizer || new Tokeniser(data, options);
const decoded = tokensToObject(tokeniser, options);
if (decoded === DONE) {
throw new Error(`${ decodeErrPrefix } did not find any content to decode`);
}
if (decoded === BREAK) {
throw new Error(`${ decodeErrPrefix } got unexpected break`);
}
if (!tokeniser.done()) {
throw new Error(`${ decodeErrPrefix } too many terminals, data makes no sense`);
}
return decoded;
}
export {
Tokeniser,
tokensToObject,
decode
};
+123
View File
@@ -0,0 +1,123 @@
import { Tokeniser } from './decode.js';
import {
toHex,
fromHex
} from './byte-utils.js';
import { uintBoundaries } from './0uint.js';
const utf8Encoder = new TextEncoder();
const utf8Decoder = new TextDecoder();
function* tokensToDiagnostic(inp, width = 100) {
const tokeniser = new Tokeniser(inp, {
retainStringBytes: true,
allowBigInt: true
});
let pos = 0;
const indent = [];
const slc = (start, length) => {
return toHex(inp.slice(pos + start, pos + start + length));
};
while (!tokeniser.done()) {
const token = tokeniser.next();
let margin = ''.padStart(indent.length * 2, ' ');
let vLength = token.encodedLength - 1;
let v = String(token.value);
let outp = `${ margin }${ slc(0, 1) }`;
const str = token.type.name === 'bytes' || token.type.name === 'string';
if (token.type.name === 'string') {
v = v.length;
vLength -= v;
} else if (token.type.name === 'bytes') {
v = token.value.length;
vLength -= v;
}
let multilen;
switch (token.type.name) {
case 'string':
case 'bytes':
case 'map':
case 'array':
multilen = token.type.name === 'string' ? utf8Encoder.encode(token.value).length : token.value.length;
if (multilen >= uintBoundaries[0]) {
if (multilen < uintBoundaries[1]) {
outp += ` ${ slc(1, 1) }`;
} else if (multilen < uintBoundaries[2]) {
outp += ` ${ slc(1, 2) }`;
} else if (multilen < uintBoundaries[3]) {
outp += ` ${ slc(1, 4) }`;
} else if (multilen < uintBoundaries[4]) {
outp += ` ${ slc(1, 8) }`;
}
}
break;
default:
outp += ` ${ slc(1, vLength) }`;
break;
}
outp = outp.padEnd(width / 2, ' ');
outp += `# ${ margin }${ token.type.name }`;
if (token.type.name !== v) {
outp += `(${ v })`;
}
yield outp;
if (str) {
let asString = token.type.name === 'string';
margin += ' ';
let repr = asString ? utf8Encoder.encode(token.value) : token.value;
if (asString && token.byteValue !== undefined) {
if (repr.length !== token.byteValue.length) {
repr = token.byteValue;
asString = false;
}
}
const wh = (width / 2 - margin.length - 1) / 2;
let snip = 0;
while (repr.length - snip > 0) {
const piece = repr.slice(snip, snip + wh);
snip += piece.length;
const st = asString ? utf8Decoder.decode(piece) : piece.reduce((p, c) => {
if (c < 32 || c >= 127 && c < 161 || c === 173) {
return `${ p }\\x${ c.toString(16).padStart(2, '0') }`;
}
return `${ p }${ String.fromCharCode(c) }`;
}, '');
yield `${ margin }${ toHex(piece) }`.padEnd(width / 2, ' ') + `# ${ margin }"${ st }"`;
}
}
if (indent.length) {
indent[indent.length - 1]--;
}
if (!token.type.terminal) {
switch (token.type.name) {
case 'map':
indent.push(token.value * 2);
break;
case 'array':
indent.push(token.value);
break;
case 'tag':
indent.push(1);
break;
default:
throw new Error(`Unknown token type '${ token.type.name }'`);
}
}
while (indent.length && indent[indent.length - 1] <= 0) {
indent.pop();
}
pos += token.encodedLength;
}
}
function fromDiag(input) {
if (typeof input !== 'string') {
throw new TypeError('Expected string input');
}
input = input.replace(/#.*?$/mg, '').replace(/[\s\r\n]+/mg, '');
if (/[^a-f0-9]/i.test(input)) {
throw new TypeError('Input string was not CBOR diagnostic format');
}
return fromHex(input);
}
export {
tokensToDiagnostic,
fromDiag
};
+246
View File
@@ -0,0 +1,246 @@
import { is } from './is.js';
import {
Token,
Type
} from './token.js';
import { Bl } from './bl.js';
import { encodeErrPrefix } from './common.js';
import { quickEncodeToken } from './jump.js';
import { asU8A } from './byte-utils.js';
import { encodeUint } from './0uint.js';
import { encodeNegint } from './1negint.js';
import { encodeBytes } from './2bytes.js';
import { encodeString } from './3string.js';
import { encodeArray } from './4array.js';
import { encodeMap } from './5map.js';
import { encodeTag } from './6tag.js';
import { encodeFloat } from './7float.js';
const defaultEncodeOptions = {
float64: false,
mapSorter,
quickEncodeToken
};
export function makeCborEncoders() {
const encoders = [];
encoders[Type.uint.major] = encodeUint;
encoders[Type.negint.major] = encodeNegint;
encoders[Type.bytes.major] = encodeBytes;
encoders[Type.string.major] = encodeString;
encoders[Type.array.major] = encodeArray;
encoders[Type.map.major] = encodeMap;
encoders[Type.tag.major] = encodeTag;
encoders[Type.float.major] = encodeFloat;
return encoders;
}
const cborEncoders = makeCborEncoders();
const buf = new Bl();
class Ref {
constructor(obj, parent) {
this.obj = obj;
this.parent = parent;
}
includes(obj) {
let p = this;
do {
if (p.obj === obj) {
return true;
}
} while (p = p.parent);
return false;
}
static createCheck(stack, obj) {
if (stack && stack.includes(obj)) {
throw new Error(`${ encodeErrPrefix } object contains circular references`);
}
return new Ref(obj, stack);
}
}
const simpleTokens = {
null: new Token(Type.null, null),
undefined: new Token(Type.undefined, undefined),
true: new Token(Type.true, true),
false: new Token(Type.false, false),
emptyArray: new Token(Type.array, 0),
emptyMap: new Token(Type.map, 0)
};
const typeEncoders = {
number(obj, _typ, _options, _refStack) {
if (!Number.isInteger(obj) || !Number.isSafeInteger(obj)) {
return new Token(Type.float, obj);
} else if (obj >= 0) {
return new Token(Type.uint, obj);
} else {
return new Token(Type.negint, obj);
}
},
bigint(obj, _typ, _options, _refStack) {
if (obj >= BigInt(0)) {
return new Token(Type.uint, obj);
} else {
return new Token(Type.negint, obj);
}
},
Uint8Array(obj, _typ, _options, _refStack) {
return new Token(Type.bytes, obj);
},
string(obj, _typ, _options, _refStack) {
return new Token(Type.string, obj);
},
boolean(obj, _typ, _options, _refStack) {
return obj ? simpleTokens.true : simpleTokens.false;
},
null(_obj, _typ, _options, _refStack) {
return simpleTokens.null;
},
undefined(_obj, _typ, _options, _refStack) {
return simpleTokens.undefined;
},
ArrayBuffer(obj, _typ, _options, _refStack) {
return new Token(Type.bytes, new Uint8Array(obj));
},
DataView(obj, _typ, _options, _refStack) {
return new Token(Type.bytes, new Uint8Array(obj.buffer, obj.byteOffset, obj.byteLength));
},
Array(obj, _typ, options, refStack) {
if (!obj.length) {
if (options.addBreakTokens === true) {
return [
simpleTokens.emptyArray,
new Token(Type.break)
];
}
return simpleTokens.emptyArray;
}
refStack = Ref.createCheck(refStack, obj);
const entries = [];
let i = 0;
for (const e of obj) {
entries[i++] = objectToTokens(e, options, refStack);
}
if (options.addBreakTokens) {
return [
new Token(Type.array, obj.length),
entries,
new Token(Type.break)
];
}
return [
new Token(Type.array, obj.length),
entries
];
},
Object(obj, typ, options, refStack) {
const isMap = typ !== 'Object';
const keys = isMap ? obj.keys() : Object.keys(obj);
const length = isMap ? obj.size : keys.length;
if (!length) {
if (options.addBreakTokens === true) {
return [
simpleTokens.emptyMap,
new Token(Type.break)
];
}
return simpleTokens.emptyMap;
}
refStack = Ref.createCheck(refStack, obj);
const entries = [];
let i = 0;
for (const key of keys) {
entries[i++] = [
objectToTokens(key, options, refStack),
objectToTokens(isMap ? obj.get(key) : obj[key], options, refStack)
];
}
sortMapEntries(entries, options);
if (options.addBreakTokens) {
return [
new Token(Type.map, length),
entries,
new Token(Type.break)
];
}
return [
new Token(Type.map, length),
entries
];
}
};
typeEncoders.Map = typeEncoders.Object;
typeEncoders.Buffer = typeEncoders.Uint8Array;
for (const typ of 'Uint8Clamped Uint16 Uint32 Int8 Int16 Int32 BigUint64 BigInt64 Float32 Float64'.split(' ')) {
typeEncoders[`${ typ }Array`] = typeEncoders.DataView;
}
function objectToTokens(obj, options = {}, refStack) {
const typ = is(obj);
const customTypeEncoder = options && options.typeEncoders && options.typeEncoders[typ] || typeEncoders[typ];
if (typeof customTypeEncoder === 'function') {
const tokens = customTypeEncoder(obj, typ, options, refStack);
if (tokens != null) {
return tokens;
}
}
const typeEncoder = typeEncoders[typ];
if (!typeEncoder) {
throw new Error(`${ encodeErrPrefix } unsupported type: ${ typ }`);
}
return typeEncoder(obj, typ, options, refStack);
}
function sortMapEntries(entries, options) {
if (options.mapSorter) {
entries.sort(options.mapSorter);
}
}
function mapSorter(e1, e2) {
const keyToken1 = Array.isArray(e1[0]) ? e1[0][0] : e1[0];
const keyToken2 = Array.isArray(e2[0]) ? e2[0][0] : e2[0];
if (keyToken1.type !== keyToken2.type) {
return keyToken1.type.compare(keyToken2.type);
}
const major = keyToken1.type.major;
const tcmp = cborEncoders[major].compareTokens(keyToken1, keyToken2);
if (tcmp === 0) {
console.warn('WARNING: complex key types used, CBOR key sorting guarantees are gone');
}
return tcmp;
}
function tokensToEncoded(buf, tokens, encoders, options) {
if (Array.isArray(tokens)) {
for (const token of tokens) {
tokensToEncoded(buf, token, encoders, options);
}
} else {
encoders[tokens.type.major](buf, tokens, options);
}
}
function encodeCustom(data, encoders, options) {
const tokens = objectToTokens(data, options);
if (!Array.isArray(tokens) && options.quickEncodeToken) {
const quickBytes = options.quickEncodeToken(tokens);
if (quickBytes) {
return quickBytes;
}
const encoder = encoders[tokens.type.major];
if (encoder.encodedSize) {
const size = encoder.encodedSize(tokens, options);
const buf = new Bl(size);
encoder(buf, tokens, options);
if (buf.chunks.length !== 1) {
throw new Error(`Unexpected error: pre-calculated length for ${ tokens } was wrong`);
}
return asU8A(buf.chunks[0]);
}
}
buf.reset();
tokensToEncoded(buf, tokens, encoders, options);
return buf.toBytes(true);
}
function encode(data, options) {
options = Object.assign({}, defaultEncodeOptions, options);
return encodeCustom(data, cborEncoders, options);
}
export {
objectToTokens,
encode,
encodeCustom,
Ref
};
+81
View File
@@ -0,0 +1,81 @@
const typeofs = [
'string',
'number',
'bigint',
'symbol'
];
const objectTypeNames = [
'Function',
'Generator',
'AsyncGenerator',
'GeneratorFunction',
'AsyncGeneratorFunction',
'AsyncFunction',
'Observable',
'Array',
'Buffer',
'Object',
'RegExp',
'Date',
'Error',
'Map',
'Set',
'WeakMap',
'WeakSet',
'ArrayBuffer',
'SharedArrayBuffer',
'DataView',
'Promise',
'URL',
'HTMLElement',
'Int8Array',
'Uint8Array',
'Uint8ClampedArray',
'Int16Array',
'Uint16Array',
'Int32Array',
'Uint32Array',
'Float32Array',
'Float64Array',
'BigInt64Array',
'BigUint64Array'
];
export function is(value) {
if (value === null) {
return 'null';
}
if (value === undefined) {
return 'undefined';
}
if (value === true || value === false) {
return 'boolean';
}
const typeOf = typeof value;
if (typeofs.includes(typeOf)) {
return typeOf;
}
if (typeOf === 'function') {
return 'Function';
}
if (Array.isArray(value)) {
return 'Array';
}
if (isBuffer(value)) {
return 'Buffer';
}
const objectType = getObjectType(value);
if (objectType) {
return objectType;
}
return 'Object';
}
function isBuffer(value) {
return value && value.constructor && value.constructor.isBuffer && value.constructor.isBuffer.call(null, value);
}
function getObjectType(value) {
const objectTypeName = Object.prototype.toString.call(value).slice(8, -1);
if (objectTypeNames.includes(objectTypeName)) {
return objectTypeName;
}
return undefined;
}
+413
View File
@@ -0,0 +1,413 @@
import { decode as _decode } from '../decode.js';
import {
Token,
Type
} from '../token.js';
import { decodeCodePointsArray } from '../byte-utils.js';
import { decodeErrPrefix } from '../common.js';
class Tokenizer {
constructor(data, options = {}) {
this.pos = 0;
this.data = data;
this.options = options;
this.modeStack = ['value'];
this.lastToken = '';
}
done() {
return this.pos >= this.data.length;
}
ch() {
return this.data[this.pos];
}
currentMode() {
return this.modeStack[this.modeStack.length - 1];
}
skipWhitespace() {
let c = this.ch();
while (c === 32 || c === 9 || c === 13 || c === 10) {
c = this.data[++this.pos];
}
}
expect(str) {
if (this.data.length - this.pos < str.length) {
throw new Error(`${ decodeErrPrefix } unexpected end of input at position ${ this.pos }`);
}
for (let i = 0; i < str.length; i++) {
if (this.data[this.pos++] !== str[i]) {
throw new Error(`${ decodeErrPrefix } unexpected token at position ${ this.pos }, expected to find '${ String.fromCharCode(...str) }'`);
}
}
}
parseNumber() {
const startPos = this.pos;
let negative = false;
let float = false;
const swallow = chars => {
while (!this.done()) {
const ch = this.ch();
if (chars.includes(ch)) {
this.pos++;
} else {
break;
}
}
};
if (this.ch() === 45) {
negative = true;
this.pos++;
}
if (this.ch() === 48) {
this.pos++;
if (this.ch() === 46) {
this.pos++;
float = true;
} else {
return new Token(Type.uint, 0, this.pos - startPos);
}
}
swallow([
48,
49,
50,
51,
52,
53,
54,
55,
56,
57
]);
if (negative && this.pos === startPos + 1) {
throw new Error(`${ decodeErrPrefix } unexpected token at position ${ this.pos }`);
}
if (!this.done() && this.ch() === 46) {
if (float) {
throw new Error(`${ decodeErrPrefix } unexpected token at position ${ this.pos }`);
}
float = true;
this.pos++;
swallow([
48,
49,
50,
51,
52,
53,
54,
55,
56,
57
]);
}
if (!this.done() && (this.ch() === 101 || this.ch() === 69)) {
float = true;
this.pos++;
if (!this.done() && (this.ch() === 43 || this.ch() === 45)) {
this.pos++;
}
swallow([
48,
49,
50,
51,
52,
53,
54,
55,
56,
57
]);
}
const numStr = String.fromCharCode.apply(null, this.data.subarray(startPos, this.pos));
const num = parseFloat(numStr);
if (float) {
return new Token(Type.float, num, this.pos - startPos);
}
if (this.options.allowBigInt !== true || Number.isSafeInteger(num)) {
return new Token(num >= 0 ? Type.uint : Type.negint, num, this.pos - startPos);
}
return new Token(num >= 0 ? Type.uint : Type.negint, BigInt(numStr), this.pos - startPos);
}
parseString() {
if (this.ch() !== 34) {
throw new Error(`${ decodeErrPrefix } unexpected character at position ${ this.pos }; this shouldn't happen`);
}
this.pos++;
for (let i = this.pos, l = 0; i < this.data.length && l < 65536; i++, l++) {
const ch = this.data[i];
if (ch === 92 || ch < 32 || ch >= 128) {
break;
}
if (ch === 34) {
const str = String.fromCharCode.apply(null, this.data.subarray(this.pos, i));
this.pos = i + 1;
return new Token(Type.string, str, l);
}
}
const startPos = this.pos;
const chars = [];
const readu4 = () => {
if (this.pos + 4 >= this.data.length) {
throw new Error(`${ decodeErrPrefix } unexpected end of unicode escape sequence at position ${ this.pos }`);
}
let u4 = 0;
for (let i = 0; i < 4; i++) {
let ch = this.ch();
if (ch >= 48 && ch <= 57) {
ch -= 48;
} else if (ch >= 97 && ch <= 102) {
ch = ch - 97 + 10;
} else if (ch >= 65 && ch <= 70) {
ch = ch - 65 + 10;
} else {
throw new Error(`${ decodeErrPrefix } unexpected unicode escape character at position ${ this.pos }`);
}
u4 = u4 * 16 + ch;
this.pos++;
}
return u4;
};
const readUtf8Char = () => {
const firstByte = this.ch();
let codePoint = null;
let bytesPerSequence = firstByte > 239 ? 4 : firstByte > 223 ? 3 : firstByte > 191 ? 2 : 1;
if (this.pos + bytesPerSequence > this.data.length) {
throw new Error(`${ decodeErrPrefix } unexpected unicode sequence at position ${ this.pos }`);
}
let secondByte, thirdByte, fourthByte, tempCodePoint;
switch (bytesPerSequence) {
case 1:
if (firstByte < 128) {
codePoint = firstByte;
}
break;
case 2:
secondByte = this.data[this.pos + 1];
if ((secondByte & 192) === 128) {
tempCodePoint = (firstByte & 31) << 6 | secondByte & 63;
if (tempCodePoint > 127) {
codePoint = tempCodePoint;
}
}
break;
case 3:
secondByte = this.data[this.pos + 1];
thirdByte = this.data[this.pos + 2];
if ((secondByte & 192) === 128 && (thirdByte & 192) === 128) {
tempCodePoint = (firstByte & 15) << 12 | (secondByte & 63) << 6 | thirdByte & 63;
if (tempCodePoint > 2047 && (tempCodePoint < 55296 || tempCodePoint > 57343)) {
codePoint = tempCodePoint;
}
}
break;
case 4:
secondByte = this.data[this.pos + 1];
thirdByte = this.data[this.pos + 2];
fourthByte = this.data[this.pos + 3];
if ((secondByte & 192) === 128 && (thirdByte & 192) === 128 && (fourthByte & 192) === 128) {
tempCodePoint = (firstByte & 15) << 18 | (secondByte & 63) << 12 | (thirdByte & 63) << 6 | fourthByte & 63;
if (tempCodePoint > 65535 && tempCodePoint < 1114112) {
codePoint = tempCodePoint;
}
}
}
if (codePoint === null) {
codePoint = 65533;
bytesPerSequence = 1;
} else if (codePoint > 65535) {
codePoint -= 65536;
chars.push(codePoint >>> 10 & 1023 | 55296);
codePoint = 56320 | codePoint & 1023;
}
chars.push(codePoint);
this.pos += bytesPerSequence;
};
while (!this.done()) {
const ch = this.ch();
let ch1;
switch (ch) {
case 92:
this.pos++;
if (this.done()) {
throw new Error(`${ decodeErrPrefix } unexpected string termination at position ${ this.pos }`);
}
ch1 = this.ch();
this.pos++;
switch (ch1) {
case 34:
case 39:
case 92:
case 47:
chars.push(ch1);
break;
case 98:
chars.push(8);
break;
case 116:
chars.push(9);
break;
case 110:
chars.push(10);
break;
case 102:
chars.push(12);
break;
case 114:
chars.push(13);
break;
case 117:
chars.push(readu4());
break;
default:
throw new Error(`${ decodeErrPrefix } unexpected string escape character at position ${ this.pos }`);
}
break;
case 34:
this.pos++;
return new Token(Type.string, decodeCodePointsArray(chars), this.pos - startPos);
default:
if (ch < 32) {
throw new Error(`${ decodeErrPrefix } invalid control character at position ${ this.pos }`);
} else if (ch < 128) {
chars.push(ch);
this.pos++;
} else {
readUtf8Char();
}
}
}
throw new Error(`${ decodeErrPrefix } unexpected end of string at position ${ this.pos }`);
}
parseValue() {
switch (this.ch()) {
case 123:
this.modeStack.push('obj-start');
this.pos++;
return new Token(Type.map, Infinity, 1);
case 91:
this.modeStack.push('array-start');
this.pos++;
return new Token(Type.array, Infinity, 1);
case 34: {
return this.parseString();
}
case 110:
this.expect([
110,
117,
108,
108
]);
return new Token(Type.null, null, 4);
case 102:
this.expect([
102,
97,
108,
115,
101
]);
return new Token(Type.false, false, 5);
case 116:
this.expect([
116,
114,
117,
101
]);
return new Token(Type.true, true, 4);
case 45:
case 48:
case 49:
case 50:
case 51:
case 52:
case 53:
case 54:
case 55:
case 56:
case 57:
return this.parseNumber();
default:
throw new Error(`${ decodeErrPrefix } unexpected character at position ${ this.pos }`);
}
}
next() {
this.skipWhitespace();
switch (this.currentMode()) {
case 'value':
this.modeStack.pop();
return this.parseValue();
case 'array-value': {
this.modeStack.pop();
if (this.ch() === 93) {
this.pos++;
this.skipWhitespace();
return new Token(Type.break, undefined, 1);
}
if (this.ch() !== 44) {
throw new Error(`${ decodeErrPrefix } unexpected character at position ${ this.pos }, was expecting array delimiter but found '${ String.fromCharCode(this.ch()) }'`);
}
this.pos++;
this.modeStack.push('array-value');
this.skipWhitespace();
return this.parseValue();
}
case 'array-start': {
this.modeStack.pop();
if (this.ch() === 93) {
this.pos++;
this.skipWhitespace();
return new Token(Type.break, undefined, 1);
}
this.modeStack.push('array-value');
this.skipWhitespace();
return this.parseValue();
}
case 'obj-key':
if (this.ch() === 125) {
this.modeStack.pop();
this.pos++;
this.skipWhitespace();
return new Token(Type.break, undefined, 1);
}
if (this.ch() !== 44) {
throw new Error(`${ decodeErrPrefix } unexpected character at position ${ this.pos }, was expecting object delimiter but found '${ String.fromCharCode(this.ch()) }'`);
}
this.pos++;
this.skipWhitespace();
case 'obj-start': {
this.modeStack.pop();
if (this.ch() === 125) {
this.pos++;
this.skipWhitespace();
return new Token(Type.break, undefined, 1);
}
const token = this.parseString();
this.skipWhitespace();
if (this.ch() !== 58) {
throw new Error(`${ decodeErrPrefix } unexpected character at position ${ this.pos }, was expecting key/value delimiter ':' but found '${ String.fromCharCode(this.ch()) }'`);
}
this.pos++;
this.modeStack.push('obj-value');
return token;
}
case 'obj-value': {
this.modeStack.pop();
this.modeStack.push('obj-key');
this.skipWhitespace();
return this.parseValue();
}
default:
throw new Error(`${ decodeErrPrefix } unexpected parse state at position ${ this.pos }; this shouldn't happen`);
}
}
}
function decode(data, options) {
options = Object.assign({ tokenizer: new Tokenizer(data, options) }, options);
return _decode(data, options);
}
export {
decode,
Tokenizer
};
+160
View File
@@ -0,0 +1,160 @@
import { Type } from '../token.js';
import { encodeCustom } from '../encode.js';
import { encodeErrPrefix } from '../common.js';
import {
asU8A,
fromString
} from '../byte-utils.js';
class JSONEncoder extends Array {
constructor() {
super();
this.inRecursive = [];
}
prefix(buf) {
const recurs = this.inRecursive[this.inRecursive.length - 1];
if (recurs) {
if (recurs.type === Type.array) {
recurs.elements++;
if (recurs.elements !== 1) {
buf.push([44]);
}
}
if (recurs.type === Type.map) {
recurs.elements++;
if (recurs.elements !== 1) {
if (recurs.elements % 2 === 1) {
buf.push([44]);
} else {
buf.push([58]);
}
}
}
}
}
[Type.uint.major](buf, token) {
this.prefix(buf);
const is = String(token.value);
const isa = [];
for (let i = 0; i < is.length; i++) {
isa[i] = is.charCodeAt(i);
}
buf.push(isa);
}
[Type.negint.major](buf, token) {
this[Type.uint.major](buf, token);
}
[Type.bytes.major](_buf, _token) {
throw new Error(`${ encodeErrPrefix } unsupported type: Uint8Array`);
}
[Type.string.major](buf, token) {
this.prefix(buf);
const byts = fromString(JSON.stringify(token.value));
buf.push(byts.length > 32 ? asU8A(byts) : byts);
}
[Type.array.major](buf, _token) {
this.prefix(buf);
this.inRecursive.push({
type: Type.array,
elements: 0
});
buf.push([91]);
}
[Type.map.major](buf, _token) {
this.prefix(buf);
this.inRecursive.push({
type: Type.map,
elements: 0
});
buf.push([123]);
}
[Type.tag.major](_buf, _token) {
}
[Type.float.major](buf, token) {
if (token.type.name === 'break') {
const recurs = this.inRecursive.pop();
if (recurs) {
if (recurs.type === Type.array) {
buf.push([93]);
} else if (recurs.type === Type.map) {
buf.push([125]);
} else {
throw new Error('Unexpected recursive type; this should not happen!');
}
return;
}
throw new Error('Unexpected break; this should not happen!');
}
if (token.value === undefined) {
throw new Error(`${ encodeErrPrefix } unsupported type: undefined`);
}
this.prefix(buf);
if (token.type.name === 'true') {
buf.push([
116,
114,
117,
101
]);
return;
} else if (token.type.name === 'false') {
buf.push([
102,
97,
108,
115,
101
]);
return;
} else if (token.type.name === 'null') {
buf.push([
110,
117,
108,
108
]);
return;
}
const is = String(token.value);
const isa = [];
let dp = false;
for (let i = 0; i < is.length; i++) {
isa[i] = is.charCodeAt(i);
if (!dp && (isa[i] === 46 || isa[i] === 101 || isa[i] === 69)) {
dp = true;
}
}
if (!dp) {
isa.push(46);
isa.push(48);
}
buf.push(isa);
}
}
function mapSorter(e1, e2) {
if (Array.isArray(e1[0]) || Array.isArray(e2[0])) {
throw new Error(`${ encodeErrPrefix } complex map keys are not supported`);
}
const keyToken1 = e1[0];
const keyToken2 = e2[0];
if (keyToken1.type !== Type.string || keyToken2.type !== Type.string) {
throw new Error(`${ encodeErrPrefix } non-string map keys are not supported`);
}
if (keyToken1 < keyToken2) {
return -1;
}
if (keyToken1 > keyToken2) {
return 1;
}
throw new Error(`${ encodeErrPrefix } unexpected duplicate map keys, this is not supported`);
}
const defaultEncodeOptions = {
addBreakTokens: true,
mapSorter
};
function encode(data, options) {
options = Object.assign({}, defaultEncodeOptions, options);
return encodeCustom(data, new JSONEncoder(), options);
}
export {
encode
};
+10
View File
@@ -0,0 +1,10 @@
import { encode } from './encode.js';
import {
decode,
Tokenizer
} from './decode.js';
export {
encode,
decode,
Tokenizer
};
+168
View File
@@ -0,0 +1,168 @@
import {
Token,
Type
} from './token.js';
import * as uint from './0uint.js';
import * as negint from './1negint.js';
import * as bytes from './2bytes.js';
import * as string from './3string.js';
import * as array from './4array.js';
import * as map from './5map.js';
import * as tag from './6tag.js';
import * as float from './7float.js';
import { decodeErrPrefix } from './common.js';
import { fromArray } from './byte-utils.js';
function invalidMinor(data, pos, minor) {
throw new Error(`${ decodeErrPrefix } encountered invalid minor (${ minor }) for major ${ data[pos] >>> 5 }`);
}
function errorer(msg) {
return () => {
throw new Error(`${ decodeErrPrefix } ${ msg }`);
};
}
export const jump = [];
for (let i = 0; i <= 23; i++) {
jump[i] = invalidMinor;
}
jump[24] = uint.decodeUint8;
jump[25] = uint.decodeUint16;
jump[26] = uint.decodeUint32;
jump[27] = uint.decodeUint64;
jump[28] = invalidMinor;
jump[29] = invalidMinor;
jump[30] = invalidMinor;
jump[31] = invalidMinor;
for (let i = 32; i <= 55; i++) {
jump[i] = invalidMinor;
}
jump[56] = negint.decodeNegint8;
jump[57] = negint.decodeNegint16;
jump[58] = negint.decodeNegint32;
jump[59] = negint.decodeNegint64;
jump[60] = invalidMinor;
jump[61] = invalidMinor;
jump[62] = invalidMinor;
jump[63] = invalidMinor;
for (let i = 64; i <= 87; i++) {
jump[i] = bytes.decodeBytesCompact;
}
jump[88] = bytes.decodeBytes8;
jump[89] = bytes.decodeBytes16;
jump[90] = bytes.decodeBytes32;
jump[91] = bytes.decodeBytes64;
jump[92] = invalidMinor;
jump[93] = invalidMinor;
jump[94] = invalidMinor;
jump[95] = errorer('indefinite length bytes/strings are not supported');
for (let i = 96; i <= 119; i++) {
jump[i] = string.decodeStringCompact;
}
jump[120] = string.decodeString8;
jump[121] = string.decodeString16;
jump[122] = string.decodeString32;
jump[123] = string.decodeString64;
jump[124] = invalidMinor;
jump[125] = invalidMinor;
jump[126] = invalidMinor;
jump[127] = errorer('indefinite length bytes/strings are not supported');
for (let i = 128; i <= 151; i++) {
jump[i] = array.decodeArrayCompact;
}
jump[152] = array.decodeArray8;
jump[153] = array.decodeArray16;
jump[154] = array.decodeArray32;
jump[155] = array.decodeArray64;
jump[156] = invalidMinor;
jump[157] = invalidMinor;
jump[158] = invalidMinor;
jump[159] = array.decodeArrayIndefinite;
for (let i = 160; i <= 183; i++) {
jump[i] = map.decodeMapCompact;
}
jump[184] = map.decodeMap8;
jump[185] = map.decodeMap16;
jump[186] = map.decodeMap32;
jump[187] = map.decodeMap64;
jump[188] = invalidMinor;
jump[189] = invalidMinor;
jump[190] = invalidMinor;
jump[191] = map.decodeMapIndefinite;
for (let i = 192; i <= 215; i++) {
jump[i] = tag.decodeTagCompact;
}
jump[216] = tag.decodeTag8;
jump[217] = tag.decodeTag16;
jump[218] = tag.decodeTag32;
jump[219] = tag.decodeTag64;
jump[220] = invalidMinor;
jump[221] = invalidMinor;
jump[222] = invalidMinor;
jump[223] = invalidMinor;
for (let i = 224; i <= 243; i++) {
jump[i] = errorer('simple values are not supported');
}
jump[244] = invalidMinor;
jump[245] = invalidMinor;
jump[246] = invalidMinor;
jump[247] = float.decodeUndefined;
jump[248] = errorer('simple values are not supported');
jump[249] = float.decodeFloat16;
jump[250] = float.decodeFloat32;
jump[251] = float.decodeFloat64;
jump[252] = invalidMinor;
jump[253] = invalidMinor;
jump[254] = invalidMinor;
jump[255] = float.decodeBreak;
export const quick = [];
for (let i = 0; i < 24; i++) {
quick[i] = new Token(Type.uint, i, 1);
}
for (let i = -1; i >= -24; i--) {
quick[31 - i] = new Token(Type.negint, i, 1);
}
quick[64] = new Token(Type.bytes, new Uint8Array(0), 1);
quick[96] = new Token(Type.string, '', 1);
quick[128] = new Token(Type.array, 0, 1);
quick[160] = new Token(Type.map, 0, 1);
quick[244] = new Token(Type.false, false, 1);
quick[245] = new Token(Type.true, true, 1);
quick[246] = new Token(Type.null, null, 1);
export function quickEncodeToken(token) {
switch (token.type) {
case Type.false:
return fromArray([244]);
case Type.true:
return fromArray([245]);
case Type.null:
return fromArray([246]);
case Type.bytes:
if (!token.value.length) {
return fromArray([64]);
}
return;
case Type.string:
if (token.value === '') {
return fromArray([96]);
}
return;
case Type.array:
if (token.value === 0) {
return fromArray([128]);
}
return;
case Type.map:
if (token.value === 0) {
return fromArray([160]);
}
return;
case Type.uint:
if (token.value < 24) {
return fromArray([Number(token.value)]);
}
return;
case Type.negint:
if (token.value >= -24) {
return fromArray([31 - Number(token.value)]);
}
}
}
+31
View File
@@ -0,0 +1,31 @@
import {
makeCborEncoders,
objectToTokens
} from './encode.js';
import { quickEncodeToken } from './jump.js';
const cborEncoders = makeCborEncoders();
const defaultEncodeOptions = {
float64: false,
quickEncodeToken
};
export function encodedLength(data, options) {
options = Object.assign({}, defaultEncodeOptions, options);
options.mapSorter = undefined;
const tokens = objectToTokens(data, options);
return tokensToLength(tokens, cborEncoders, options);
}
export function tokensToLength(tokens, encoders = cborEncoders, options = defaultEncodeOptions) {
if (Array.isArray(tokens)) {
let len = 0;
for (const token of tokens) {
len += tokensToLength(token, encoders, options);
}
return len;
} else {
const encoder = encoders[tokens.type.major];
if (encoder.encodedSize === undefined || typeof encoder.encodedSize !== 'function') {
throw new Error(`Encoder for ${ tokens.type.name } does not have an encodedSize()`);
}
return encoder.encodedSize(tokens, options);
}
}
+43
View File
@@ -0,0 +1,43 @@
class Type {
constructor(major, name, terminal) {
this.major = major;
this.majorEncoded = major << 5;
this.name = name;
this.terminal = terminal;
}
toString() {
return `Type[${ this.major }].${ this.name }`;
}
compare(typ) {
return this.major < typ.major ? -1 : this.major > typ.major ? 1 : 0;
}
}
Type.uint = new Type(0, 'uint', true);
Type.negint = new Type(1, 'negint', true);
Type.bytes = new Type(2, 'bytes', true);
Type.string = new Type(3, 'string', true);
Type.array = new Type(4, 'array', false);
Type.map = new Type(5, 'map', false);
Type.tag = new Type(6, 'tag', false);
Type.float = new Type(7, 'float', true);
Type.false = new Type(7, 'false', true);
Type.true = new Type(7, 'true', true);
Type.null = new Type(7, 'null', true);
Type.undefined = new Type(7, 'undefined', true);
Type.break = new Type(7, 'break', true);
class Token {
constructor(type, value, encodedLength) {
this.type = type;
this.value = value;
this.encodedLength = encodedLength;
this.encodedBytes = undefined;
this.byteValue = undefined;
}
toString() {
return `Token[${ this.type }].${ this.value }`;
}
}
export {
Type,
Token
};