UNPKG

@aggris2/ssz

Version:

Simple Serialize

236 lines 10.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.value_getRootsArrayComposite = exports.tree_deserializeFromBytesArrayComposite = exports.tree_serializeToBytesArrayComposite = exports.tree_serializedSizeArrayComposite = exports.value_deserializeFromBytesArrayComposite = exports.value_serializeToBytesArrayComposite = exports.value_serializedSizeArrayComposite = exports.maxSizeArrayComposite = exports.minSizeArrayComposite = void 0; const persistent_merkle_tree_1 = require("@chainsafe/persistent-merkle-tree"); const arrayBasic_1 = require("./arrayBasic"); function minSizeArrayComposite(elementType, minCount) { // Variable Length if (elementType.fixedSize === null) { return minCount * (4 + elementType.minSize); } // Fixed length else { return minCount * elementType.minSize; } } exports.minSizeArrayComposite = minSizeArrayComposite; function maxSizeArrayComposite(elementType, maxCount) { // Variable Length if (elementType.fixedSize === null) { return maxCount * (4 + elementType.maxSize); } // Fixed length else { return maxCount * elementType.maxSize; } } exports.maxSizeArrayComposite = maxSizeArrayComposite; function value_serializedSizeArrayComposite(elementType, length, value) { // Variable Length if (elementType.fixedSize === null) { let size = 0; for (let i = 0; i < length; i++) { size += 4 + elementType.value_serializedSize(value[i]); } return size; } // Fixed length else { return length * elementType.fixedSize; } } exports.value_serializedSizeArrayComposite = value_serializedSizeArrayComposite; /** * @param length In List length = value.length, Vector length = fixed value */ function value_serializeToBytesArrayComposite(elementType, length, output, offset, value) { // Variable length if (elementType.fixedSize === null) { let variableIndex = offset + length * 4; for (let i = 0; i < length; i++) { // write offset output.dataView.setUint32(offset + i * 4, variableIndex - offset, true); // write serialized element to variable section variableIndex = elementType.value_serializeToBytes(output, variableIndex, value[i]); } return variableIndex; } // Fixed length else { for (let i = 0; i < length; i++) { elementType.value_serializeToBytes(output, offset + i * elementType.fixedSize, value[i]); } return offset + length * elementType.fixedSize; } } exports.value_serializeToBytesArrayComposite = value_serializeToBytesArrayComposite; function value_deserializeFromBytesArrayComposite(elementType, data, start, end, arrayProps) { const offsets = readOffsetsArrayComposite(elementType.fixedSize, data.dataView, start, end, arrayProps); const length = offsets.length; // Capture length before pushing end offset const values = new Array(length); // offests include the last element end for (let i = 0; i < length; i++) { // The offsets are relative to the start const startEl = start + offsets[i]; const endEl = i === length - 1 ? end : start + offsets[i + 1]; values[i] = elementType.value_deserializeFromBytes(data, startEl, endEl); } return values; } exports.value_deserializeFromBytesArrayComposite = value_deserializeFromBytesArrayComposite; /** * @param length In List length = value.length, Vector length = fixed value */ function tree_serializedSizeArrayComposite(elementType, length, depth, node) { // Variable Length if (elementType.fixedSize === null) { const nodes = persistent_merkle_tree_1.getNodesAtDepth(node, depth, 0, length); let size = 0; for (let i = 0; i < nodes.length; i++) { size += 4 + elementType.tree_serializedSize(nodes[i]); } return size; } // Fixed length else { return length * elementType.fixedSize; } } exports.tree_serializedSizeArrayComposite = tree_serializedSizeArrayComposite; /** * @param length In List length = value.length, Vector length = fixed value */ function tree_serializeToBytesArrayComposite(elementType, length, depth, node, output, offset) { const nodes = persistent_merkle_tree_1.getNodesAtDepth(node, depth, 0, length); // Variable Length // Indices contain offsets, which are indices deeper in the byte array if (elementType.fixedSize === null) { let variableIndex = offset + length * 4; const { dataView } = output; for (let i = 0; i < nodes.length; i++) { // write offset dataView.setUint32(offset + i * 4, variableIndex - offset, true); // write serialized element to variable section variableIndex = elementType.tree_serializeToBytes(output, variableIndex, nodes[i]); } return variableIndex; } // Fixed length else { for (let i = 0; i < nodes.length; i++) { offset = elementType.tree_serializeToBytes(output, offset, nodes[i]); } return offset; } } exports.tree_serializeToBytesArrayComposite = tree_serializeToBytesArrayComposite; function tree_deserializeFromBytesArrayComposite(elementType, chunkDepth, data, start, end, arrayProps) { const offsets = readOffsetsArrayComposite(elementType.fixedSize, data.dataView, start, end, arrayProps); const length = offsets.length; // Capture length before pushing end offset const nodes = new Array(length); // offests include the last element end for (let i = 0; i < length; i++) { // The offsets are relative to the start const startEl = start + offsets[i]; const endEl = i === length - 1 ? end : start + offsets[i + 1]; nodes[i] = elementType.tree_deserializeFromBytes(data, startEl, endEl); } // Abstract converting data to LeafNode to allow for custom data representation, such as the hashObject const chunksNode = persistent_merkle_tree_1.subtreeFillToContents(nodes, chunkDepth); // TODO: Add LeafNode.fromUint() if (arrayProps.isList) { return arrayBasic_1.addLengthNode(chunksNode, length); } else { return chunksNode; } } exports.tree_deserializeFromBytesArrayComposite = tree_deserializeFromBytesArrayComposite; /** * @param length In List length = value.length, Vector length = fixed value */ function value_getRootsArrayComposite(elementType, length, value) { const roots = new Array(length); for (let i = 0; i < length; i++) { roots[i] = elementType.hashTreeRoot(value[i]); } return roots; } exports.value_getRootsArrayComposite = value_getRootsArrayComposite; function readOffsetsArrayComposite(elementFixedSize, data, start, end, arrayProps) { const size = end - start; let offsets; // Variable Length // Indices contain offsets, which are indices deeper in the byte array if (elementFixedSize === null) { offsets = readVariableOffsetsArrayComposite(data, start, size); } // Fixed length else { // There's no valid CompositeType with fixed size 0, it's un-rechable code. But prevents diving by zero /* istanbul ignore if */ if (elementFixedSize === 0) { throw Error("element fixed length is 0"); } if (size % elementFixedSize !== 0) { throw Error(`size ${size} is not multiple of element fixedSize ${elementFixedSize}`); } const length = size / elementFixedSize; offsets = new Uint32Array(length); for (let i = 0; i < length; i++) { offsets[i] = i * elementFixedSize; } } // Vector + List length validation arrayBasic_1.assertValidArrayLength(offsets.length, arrayProps); return offsets; } /** * Reads the values of contiguous variable offsets. Provided buffer includes offsets that point to position * within `size`. This function also validates that all offsets are in range. */ function readVariableOffsetsArrayComposite(dataView, start, size) { if (size === 0) { return new Uint32Array(0); } // all elements are variable-sized // indices contain offsets, which are indices deeper in the byte array // The serialized data will start with offsets of all the serialized objects (BYTES_PER_LENGTH_OFFSET bytes each) const firstOffset = dataView.getUint32(start, true); // Using the first offset, we can compute the length of the list (divide by BYTES_PER_LENGTH_OFFSET), as it gives // us the total number of bytes in the offset data const offsetDataLength = firstOffset; if (firstOffset === 0) { throw Error("First offset must be > 0"); } if (offsetDataLength % 4 !== 0) { throw Error("Offset data length not multiple of 4"); } const offsetCount = offsetDataLength / 4; const offsets = new Uint32Array(offsetCount); offsets[0] = firstOffset; // ArrayComposite has a contiguous section of offsets, then the data // // [offset 1] [offset 2] [data 1 ..........] [data 2 ..] // 0x 08000000 0e000000 010002000300 01000200 // // Ensure that: // - Offsets point to regions of > 0 bytes, i.e. are increasing // - Offsets don't point to bytes outside of the array's size // // In the example above the first offset is 8, so 8 / 4 = 2 offsets. // Then, read the rest of offsets to get offsets = [8, 14] for (let offsetIdx = 1; offsetIdx < offsetCount; offsetIdx++) { const offset = dataView.getUint32(start + offsetIdx * 4, true); offsets[offsetIdx] = offset; // Offsets must point to data within the Array bytes section if (offset > size) { throw new Error(`Offset out of bounds ${offset} > ${size}`); } if (offset < offsets[offsetIdx - 1]) { throw new Error(`Offsets must be increasing ${offset} < ${offsets[offsetIdx - 1]}`); } } return offsets; } //# sourceMappingURL=arrayComposite.js.map