UNPKG

@aggris2/ssz

Version:

Simple Serialize

176 lines 7.36 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.assertValidArrayLength = exports.value_defaultValueArray = exports.value_equals = exports.value_cloneArray = exports.value_toJsonArray = exports.value_fromJsonArray = exports.tree_deserializeFromBytesArrayBasic = exports.tree_serializeToBytesArrayBasic = exports.value_deserializeFromBytesArrayBasic = exports.value_serializeToBytesArrayBasic = exports.setChunksNode = exports.addLengthNode = exports.getChunksNodeFromRootNode = exports.getLengthFromRootNode = void 0; const persistent_merkle_tree_1 = require("@chainsafe/persistent-merkle-tree"); // There's a matrix of Array-ish types that require a combination of this functions. // Regular class extends syntax doesn't work because it can only extend a single class. // // Type of array: List, Vector. Changes length property // Type of element: Basic, Composite. Changes merkelization if packing or not. // If Composite: Fixed len, Variable len. Changes the serialization requiring offsets. /** * SSZ Lists (variable-length arrays) include the length of the list in the tree * This length is always in the same index in the tree * ``` * 1 * / \ * 2 3 // <-here * ``` */ function getLengthFromRootNode(node) { // Length is represented as a Uint32 at the start of the chunk: // 4 = 4 bytes in Uint32 // 0 = 0 offset bytes in Node's data return node.right.getUint(4, 0); } exports.getLengthFromRootNode = getLengthFromRootNode; function getChunksNodeFromRootNode(node) { return node.left; } exports.getChunksNodeFromRootNode = getChunksNodeFromRootNode; function addLengthNode(chunksNode, length) { return new persistent_merkle_tree_1.BranchNode(chunksNode, persistent_merkle_tree_1.LeafNode.fromUint32(length)); } exports.addLengthNode = addLengthNode; function setChunksNode(rootNode, chunksNode, newLength) { const lengthNode = newLength !== undefined ? // If newLength is set, create a new node for length persistent_merkle_tree_1.LeafNode.fromUint32(newLength) : // else re-use existing node rootNode.right; return new persistent_merkle_tree_1.BranchNode(chunksNode, lengthNode); } exports.setChunksNode = setChunksNode; /** * @param length In List length = value.length, Vector length = fixed value */ function value_serializeToBytesArrayBasic(elementType, length, output, offset, value) { const elSize = elementType.byteLength; for (let i = 0; i < length; i++) { elementType.value_serializeToBytes(output, offset + i * elSize, value[i]); } return offset + length * elSize; } exports.value_serializeToBytesArrayBasic = value_serializeToBytesArrayBasic; function value_deserializeFromBytesArrayBasic(elementType, data, start, end, arrayProps) { const elSize = elementType.byteLength; // Vector + List length validation const length = (end - start) / elSize; assertValidArrayLength(length, arrayProps, true); const values = new Array(length); for (let i = 0; i < length; i++) { // TODO: If faster, consider skipping size check for uint types values[i] = elementType.value_deserializeFromBytes(data, start + i * elSize, start + (i + 1) * elSize); } return values; } exports.value_deserializeFromBytesArrayBasic = value_deserializeFromBytesArrayBasic; /** * @param length In List length = value.length, Vector length = fixed value */ function tree_serializeToBytesArrayBasic(elementType, length, depth, output, offset, node) { const size = elementType.byteLength * length; const chunkCount = Math.ceil(size / 32); const nodes = persistent_merkle_tree_1.getNodesAtDepth(node, depth, 0, chunkCount); persistent_merkle_tree_1.packedNodeRootsToBytes(output.dataView, offset, size, nodes); return offset + size; } exports.tree_serializeToBytesArrayBasic = tree_serializeToBytesArrayBasic; // List of basic elements will pack them in merkelized form function tree_deserializeFromBytesArrayBasic(elementType, chunkDepth, data, start, end, arrayProps) { // Vector + List length validation const length = (end - start) / elementType.byteLength; assertValidArrayLength(length, arrayProps, true); // Abstract converting data to LeafNode to allow for custom data representation, such as the hashObject const chunksNode = persistent_merkle_tree_1.packedRootsBytesToNode(chunkDepth, data.dataView, start, end); if (arrayProps.isList) { return addLengthNode(chunksNode, length); } else { return chunksNode; } } exports.tree_deserializeFromBytesArrayBasic = tree_deserializeFromBytesArrayBasic; /** * @param length In List length = undefined, Vector length = fixed value */ function value_fromJsonArray(elementType, json, arrayProps) { if (!Array.isArray(json)) { throw Error("JSON is not an array"); } assertValidArrayLength(json.length, arrayProps); const value = new Array(json.length); for (let i = 0; i < json.length; i++) { value[i] = elementType.fromJson(json[i]); } return value; } exports.value_fromJsonArray = value_fromJsonArray; /** * @param length In List length = undefined, Vector length = fixed value */ function value_toJsonArray(elementType, value, arrayProps) { const length = arrayProps.isList ? value.length : arrayProps.length; const json = new Array(length); for (let i = 0; i < length; i++) { json[i] = elementType.toJson(value[i]); } return json; } exports.value_toJsonArray = value_toJsonArray; /** * Clone recursively an array of basic or composite types */ function value_cloneArray(elementType, value) { const newValue = new Array(value.length); for (let i = 0; i < value.length; i++) { newValue[i] = elementType.clone(value[i]); } return newValue; } exports.value_cloneArray = value_cloneArray; /** * Check recursively if a type is structuraly equal. Returns early */ function value_equals(elementType, a, b) { if (a.length !== b.length) { return false; } for (let i = 0; i < a.length; i++) { if (!elementType.equals(a[i], b[i])) { return false; } } return true; } exports.value_equals = value_equals; function value_defaultValueArray(elementType, length) { const values = new Array(length); for (let i = 0; i < length; i++) { values[i] = elementType.defaultValue(); } return values; } exports.value_defaultValueArray = value_defaultValueArray; /** * @param checkNonDecimalLength Check that length is a multiple of element size. * Optional since it's not necessary in getOffsetsArrayComposite() fn. */ function assertValidArrayLength(length, arrayProps, checkNonDecimalLength) { if (checkNonDecimalLength && length % 1 !== 0) { throw Error("size not multiple of element fixedSize"); } // Vector + List length validation if (arrayProps.isList) { if (length > arrayProps.limit) { throw new Error(`Invalid list length ${length} over limit ${arrayProps.limit}`); } } else { if (length !== arrayProps.length) { throw new Error(`Incorrect vector length ${length} expected ${arrayProps.length}`); } } } exports.assertValidArrayLength = assertValidArrayLength; //# sourceMappingURL=arrayBasic.js.map