UNPKG

@chainsafe/ssz

Version:

Simple Serialize

125 lines 5.07 kB
import { subtreeFillToContents, toSnapshot, treeZeroAfterIndex, zeroNode } from "@chainsafe/persistent-merkle-tree"; import { tree_serializeToBytesArrayComposite } from "../type/arrayComposite.js"; import { zeroSnapshot } from "../util/snapshot.js"; import { ArrayCompositeTreeViewDU } from "./arrayComposite.js"; export class ListCompositeTreeViewDU extends ArrayCompositeTreeViewDU { type; _rootNode; constructor(type, _rootNode, cache) { super(type, _rootNode, cache); this.type = type; this._rootNode = _rootNode; } /** * Adds one value element at the end of the array and adds 1 to the un-commited ViewDU length */ push(view) { if (this._length >= this.type.limit) { throw Error("Error pushing over limit"); } this.dirtyLength = true; const index = this._length++; // No need for pre-initialization like in ListBasic.push since ArrayCompositeTreeViewDU.set() doesn't do a get node this.set(index, view); } /** * Returns a new ListCompositeTreeViewDU instance with the values from 0 to `index`. * The new list is equivalent to (pseudo-code): * * ```ts * const nodes = getChunkNodes() * return listFromChunkNodes(nodes.slice(0, index + 1)) * ``` * * To achieve it, rebinds the underlying tree zero-ing all nodes right of `index`. * * Note: Using index = -1, returns an empty list of length 0. */ sliceTo(index) { // Commit before getting rootNode to ensure all pending data is in the rootNode this.commit(); const rootNode = this._rootNode; const length = this._length; // All nodes beyond length are already zero // Array of length 2: [X,X,0,0], for index >= 1 no action needed if (index >= length - 1) { return this; } // Since this is a List, do the treeZeroAfterIndex operation on the chunks tree const chunksNode = this.type.tree_getChunksNode(rootNode); const newChunksNode = treeZeroAfterIndex(chunksNode, this.type.chunkDepth, index); // Must set new length and commit to tree to restore the same tree at that index const newLength = index + 1; const newRootNode = this.type.tree_setChunksNode(rootNode, newChunksNode, newLength); return this.rootNodeToViewDU(newRootNode); } /** * Returns a new ListCompositeTreeViewDU instance with the values from `index` to the end of list * * ```ts * const nodes = getChunkNodes() * return listFromChunkNodes(node.slice(index)) * ``` * * Note: If index === n, returns an empty list of length 0 * */ sliceFrom(index) { // Commit before getting rootNode to ensure all pending data is in the rootNode this.commit(); // populate to `this.nodes` to ensure all nodes are loaded this.populateAllNodes(); // If negative index, try to make it positive long as |index| < length if (index < 0) { index += this.nodes.length; } // If slicing from 0 or neg index, no slicing is necesary if (index <= 0) { return this; } let newChunksNode; let newLength; if (index >= this.nodes.length) { newChunksNode = zeroNode(this.type.chunkDepth); newLength = 0; } else { const nodes = this.nodes.slice(index); newChunksNode = subtreeFillToContents(nodes, this.type.chunkDepth); newLength = nodes.length; } const newRootNode = this.type.tree_setChunksNode(this._rootNode, newChunksNode, newLength); return this.rootNodeToViewDU(newRootNode); } /** * Create snapshot from the first `count` elements of the list. */ toSnapshot(count) { // Commit before getting rootNode to ensure all pending data is in the rootNode this.commit(); if (count < 0 || count > this._length) { throw Error(`Invalid count ${count}, length is ${this._length}`); } if (count === 0) { return zeroSnapshot(this.type.chunkDepth); } // sliceTo is inclusive const rootNode = this.sliceTo(count - 1)._rootNode; const chunksNode = this.type.tree_getChunksNode(rootNode); const snapshot = toSnapshot(chunksNode, this.type.chunkDepth, count); return { ...snapshot, root: rootNode.root }; } /** * Same method to `type/listComposite.ts` leveraging cached nodes. */ serializeToBytes(output, offset) { this.commit(); this.populateAllNodes(); const chunksNode = this.type.tree_getChunksNode(this._rootNode); return tree_serializeToBytesArrayComposite(this.type.elementType, this._length, this.type.chunkDepth, chunksNode, output, offset, this.nodes); } rootNodeToViewDU(rootNode) { return this.type.getViewDU(rootNode); } } //# sourceMappingURL=listComposite.js.map