Files
common/packages/trie-codec/src/decode.ts
T

102 lines
3.0 KiB
TypeScript

// Copyright 2017-2019 @polkadot/trie-codec authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { compactStripLength, logger } from '@polkadot/util';
import NodeHeader, { BranchHeader, NibbleHeader } from './NodeHeader';
import { NODE_TYPE_NULL, NODE_TYPE_BRANCH, NODE_TYPE_EXT, NODE_TYPE_LEAF } from './constants';
import { addNibblesTerminator, encodeNibbles } from './nibbles';
import { toNibbles } from './util';
const EMPTY_BRANCH: Array<Uint8Array | null> = [
null, null, null, null,
null, null, null, null,
null, null, null, null,
null, null, null, null
];
const l = logger('trie/codec');
l.noop();
function _decodeBranch (header: NodeHeader, input: Uint8Array): Array<null | Uint8Array | [Uint8Array, Uint8Array]> {
let offset = header.encodedLength;
const branch = header.value as BranchHeader;
const bitmap = input[offset] + (input[offset + 1] * 256);
let value: null | Uint8Array = null;
offset += 2;
if (branch.valueOf() === true) {
const [length, bytes] = compactStripLength(input.subarray(offset));
value = bytes;
offset += length;
}
let cursor = 1;
return EMPTY_BRANCH.concat(value).map((value, index) => {
let result: null | Uint8Array | [Uint8Array, Uint8Array] = value;
if ((index < 16) && (bitmap & cursor)) {
const [length, bytes] = compactStripLength(input.subarray(offset));
result = bytes.length === 32
? bytes
: decode(bytes) as any;
offset += length;
}
cursor = cursor << 1;
return result;
});
}
function _decodeKv (header: NodeHeader, input: Uint8Array): Array<null | Uint8Array | [Uint8Array, Uint8Array]> {
let offset = header.encodedLength;
const nibbleCount = (header.value as NibbleHeader).toNumber();
const nibbleLength = Math.floor((nibbleCount + 1) / 2);
const nibbleData = input.subarray(offset, offset + nibbleLength);
// for odd, ignore the first nibble, data starts at offset 1
const nibbles = toNibbles(nibbleData).subarray(nibbleCount % 2);
offset += nibbleData.length;
const [, value] = compactStripLength(input.subarray(offset));
return [
encodeNibbles(
header.nodeType === NODE_TYPE_LEAF
? addNibblesTerminator(nibbles)
: nibbles
),
value
];
}
function _decode (input: null | Uint8Array): Uint8Array | null | Array<null | Uint8Array | [Uint8Array, Uint8Array]> {
const header = new NodeHeader(input);
const nodeType = header.nodeType;
if (!input || nodeType === NODE_TYPE_NULL) {
return input;
} else if (nodeType === NODE_TYPE_BRANCH) {
return _decodeBranch(header, input);
} else if (nodeType === NODE_TYPE_EXT || nodeType === NODE_TYPE_LEAF) {
return _decodeKv(header, input);
}
throw new Error('Unreachable');
}
export default function decode (input: null | Uint8Array): Uint8Array | null | Array<null | Uint8Array | Array<null | Uint8Array>> {
const decoded = _decode(input);
// l.debug(() => ['decode', { input, decoded }]);
return decoded;
}