UNPKG

@chainsafe/ssz

Version:

Simple Serialize

103 lines 4.08 kB
import { getNodesAtDepth, packedNodeRootsToBytes, packedRootsBytesToNode } from "@chainsafe/persistent-merkle-tree"; import { maxChunksToDepth } from "../util/merkleize.js"; import { namedClass } from "../util/named.js"; import { BitArray } from "../value/bitArray.js"; import { BitArrayType } from "./bitArray.js"; /** * BitVector: ordered fixed-length collection of boolean values, with N bits * - Notation: `Bitvector[N]` * - Value: `BitArray`, @see BitArray for a justification of its memory efficiency and performance * - View: `BitArrayTreeView` * - ViewDU: `BitArrayTreeViewDU` */ export class BitVectorType extends BitArrayType { lengthBits; typeName; chunkCount; depth; fixedSize; minSize; maxSize; maxChunkCount; isList = false; /** * Mask to check if trailing bits are zero'ed. Mask returns bits that must be zero'ed * ``` * lengthBits % 8 | zeroBitsMask * 0 | 0 * 1 | 11111110 * 2 | 11111100 * 7 | 10000000 * ``` */ zeroBitsMask; constructor(lengthBits, opts) { super(); this.lengthBits = lengthBits; if (lengthBits === 0) throw Error("Vector length must be > 0"); this.typeName = opts?.typeName ?? `BitVector[${lengthBits}]`; this.chunkCount = Math.ceil(this.lengthBits / 8 / 32); this.maxChunkCount = this.chunkCount; this.depth = maxChunksToDepth(this.chunkCount); this.fixedSize = Math.ceil(this.lengthBits / 8); this.minSize = this.fixedSize; this.maxSize = this.fixedSize; // To cache mask for trailing zero bits validation this.zeroBitsMask = lengthBits % 8 === 0 ? 0 : 0xff & (0xff << (lengthBits % 8)); } static named(limitBits, opts) { return new (namedClass(BitVectorType, opts.typeName))(limitBits, opts); } defaultValue() { return BitArray.fromBitLen(this.lengthBits); } // Views: inherited from BitArrayType // Serialization + deserialization value_serializedSize() { return this.fixedSize; } value_serializeToBytes(output, offset, value) { output.uint8Array.set(value.uint8Array, offset); return offset + this.fixedSize; } value_deserializeFromBytes(data, start, end) { this.assertValidLength(data.uint8Array, start, end); // Buffer.prototype.slice does not copy memory, Enforce Uint8Array usage https://github.com/nodejs/node/issues/28087 return new BitArray(Uint8Array.prototype.slice.call(data.uint8Array, start, end), this.lengthBits); } tree_serializedSize() { return this.fixedSize; } tree_serializeToBytes(output, offset, node) { const nodes = getNodesAtDepth(node, this.depth, 0, this.chunkCount); packedNodeRootsToBytes(output.dataView, offset, this.fixedSize, nodes); return offset + this.fixedSize; } tree_deserializeFromBytes(data, start, end) { this.assertValidLength(data.uint8Array, start, end); return packedRootsBytesToNode(this.depth, data.dataView, start, end); } tree_getByteLen() { return this.fixedSize; } // Merkleization: inherited from BitArrayType // Proofs: inherited from BitArrayType // JSON: inherited from BitArrayType // Deserializer helpers assertValidLength(data, start, end) { const size = end - start; if (end - start !== this.fixedSize) { throw Error(`Invalid BitVector size ${size} != ${this.fixedSize}`); } // If lengthBits is not aligned to bytes, ensure trailing bits are zeroed if ( // If zeroBitsMask == 0, then the BitVector uses full bytes only this.zeroBitsMask > 0 && // if the last byte is partial, retrieve it and use the cached mask to check if trailing bits are zeroed (data[end - 1] & this.zeroBitsMask) > 0) { throw Error("BitVector: nonzero bits past length"); } } } //# sourceMappingURL=bitVector.js.map