import BN from "bn.js"; import Address from "../utils/address"; import { bytesToHex } from "../utils/utils"; import { BitString } from "./bitString"; import { Cell } from "./cell"; import { parseDict } from "./dict/parseDict"; /** * A partial view of a TVM cell, used for parsing data from Cells. */ export class Slice { array: Uint8Array; length: number; readCursor: number; refs: Slice[]; refCursor: number; /** * @param array {Uint8Array} * @param length {number} length in bits * @param refs {Slice[]} child cells */ constructor(array: Uint8Array, length: number, refs: Slice[]) { this.array = array; this.length = length; this.readCursor = 0; this.refs = refs; this.refCursor = 0; } /** * @return {number} */ getFreeBits() { return this.length - this.readCursor; } getFreeRefs() { return this.refs.length - this.refCursor; } /** * @private * @param n {number} */ checkRange(n: number) { if (n > this.length) { throw Error("BitString overflow"); } } /** * @private * @param n {number} * @return {boolean} bit value at position `n` */ get(n: number) { this.checkRange(n); return (this.array[(n / 8) | 0] & (1 << (7 - (n % 8)))) > 0; } readUnaryLength() { let res = 0; while (this.loadBit()) { res++; } return res; } readRemaining() { let res = BitString.alloc(1023); while (this.readCursor < this.length) { res.writeBit(this.loadBit()); } return res; } /** * @return {boolean} read bit */ loadBit() { const result = this.get(this.readCursor); this.readCursor++; return result; } /** * @param bitLength {number} * @return {Uint8Array} */ loadBits(bitLength: number) { const result = new BitString(bitLength); for (let i = 0; i < bitLength; i++) { result.writeBit(this.loadBit()); } return result.array; } /** * Reads unsigned int * * @param {number} bitLength Size of uint in bits * @returns {BN} number */ loadUint(bitLength: number) { if (bitLength < 1) { throw "Incorrect bitLength"; } let s = ""; for (let i = 0; i < bitLength; i++) { s += this.loadBit() ? "1" : "0"; } return new BN(s, 2); } /** * Reads signed int * * @param {number} bitLength Size of uint in bits * @returns {BN} number */ loadInt(bitLength: number) { if (bitLength < 1) { throw "Incorrect bitLength"; } const sign = this.loadBit(); if (bitLength === 1) { return sign ? new BN(-1) : new BN(0); } let number = this.loadUint(bitLength - 1); if (sign) { const b = new BN(2); const nb = b.pow(new BN(bitLength - 1)); number = number.sub(nb); } return number; } /** * @param bitLength {number} * @return {BN} */ loadVarUint(bitLength: number) { const len = this.loadUint(new BN(bitLength).toString(2).length - 1); if (len.toNumber() === 0) { return new BN(0); } else { return this.loadUint(len.toNumber() * 8); } } /** * @return {BN} */ loadCoins() { return this.loadVarUint(16); } loadAddress() { const b = this.loadUint(2); if (b.toNumber() === 0) return null; // null address if (b.toNumber() !== 2) throw new Error("unsupported address type"); if (this.loadBit()) throw new Error("unsupported address type"); const wc = this.loadInt(8).toNumber(); const hashPart = this.loadBits(256); return new Address(wc + ":" + bytesToHex(hashPart)); } /** * @return {Slice} */ loadRef() { if (this.refCursor >= 4) throw new Error("refs overflow"); const result = this.refs[this.refCursor]; this.refCursor++; return result; } readCell = () => { let first = this.loadRef(); if (first) { return first.toCell(); } else { throw Error("No ref"); } }; readOptDict = (keySize: number, extractor: (slice: Slice) => T) => { if (this.loadBit()) { return this.readDict(keySize, extractor); } else { return null; } }; readDict = (keySize: number, extractor: (slice: Slice) => T) => { let first = this.loadRef(); if (first) { return parseDict(first, keySize, extractor); } else { throw Error("No ref"); } }; toCell(): Cell { const free = this.getFreeBits(); const bits = this.loadBits(free); const freeRefs = this.getFreeRefs(); const cell = new Cell(); cell.bits.writeBytes(bits); for (let i = 0; i < freeRefs; i++) { const ref = this.loadRef(); cell.refs.push(ref.toCell()); } return cell; } }