@openproduct/web-sdk
Version:
OpenMask Web - TypeScript Browser SDK for TON blockchain
338 lines (337 loc) • 9 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BitString = void 0;
const bn_js_1 = __importDefault(require("bn.js"));
const utils_1 = require("../utils/utils");
class BitString {
array;
cursor;
length;
/**
* @param length {number} length of BitString in bits
*/
constructor(length) {
this.array = Uint8Array.from({ length: Math.ceil(length / 8) }, () => 0);
this.cursor = 0;
this.length = length;
}
static alloc(length) {
return new BitString(length);
}
/**
* @return {number}
*/
getFreeBits() {
return this.length - this.cursor;
}
/**
* @return {number}
*/
getUsedBits() {
return this.cursor;
}
/**
* @return {number}
*/
getUsedBytes() {
return Math.ceil(this.cursor / 8);
}
/**
* @param n {number}
* @return {boolean} bit value at position `n`
*/
get(n) {
return (this.array[(n / 8) | 0] & (1 << (7 - (n % 8)))) > 0;
}
/**
* @private
* @param n {number}
*/
checkRange(n) {
if (n > this.length) {
throw Error("BitString overflow");
}
}
/**
* Set bit value to 1 at position `n`
* @param n {number}
*/
on(n) {
this.checkRange(n);
this.array[(n / 8) | 0] |= 1 << (7 - (n % 8));
}
/**
* Set bit value to 0 at position `n`
* @param n {number}
*/
off(n) {
this.checkRange(n);
this.array[(n / 8) | 0] &= ~(1 << (7 - (n % 8)));
}
/**
* Toggle bit value at position `n`
* @param n {number}
*/
toggle(n) {
this.checkRange(n);
this.array[(n / 8) | 0] ^= 1 << (7 - (n % 8));
}
/**
* forEach every bit
* @param callback {function(boolean): void}
*/
forEach(callback) {
const max = this.cursor;
for (let x = 0; x < max; x++) {
callback(this.get(x));
}
}
/**
* Write bit and increase cursor
* @param b {boolean | number}
*/
writeBit(b) {
if (b && b > 0) {
this.on(this.cursor);
}
else {
this.off(this.cursor);
}
this.cursor = this.cursor + 1;
}
/**
* @param ba {Array<boolean | number>}
*/
writeBitArray(ba) {
for (let i = 0; i < ba.length; i++) {
this.writeBit(ba[i]);
}
}
/**
* Write unsigned int
* @param number {number | BN}
* @param bitLength {number} size of uint in bits
*/
writeUint(number, bitLength) {
number = new bn_js_1.default(number);
if (bitLength == 0 || number.toString(2).length > bitLength) {
if (number.toNumber() == 0)
return;
throw Error("bitLength is too small for number, got number=" +
number +
",bitLength=" +
bitLength);
}
const s = number.toString(2, bitLength);
for (let i = 0; i < bitLength; i++) {
this.writeBit(s[i] == "1");
}
}
/**
* Write signed int
* @param number {number | BN}
* @param bitLength {number} size of int in bits
*/
writeInt(number, bitLength) {
number = new bn_js_1.default(number);
if (bitLength == 1) {
if (number.toNumber() == -1) {
this.writeBit(true);
return;
}
if (number.toNumber() == 0) {
this.writeBit(false);
return;
}
throw Error("Bitlength is too small for number");
}
else {
if (number.isNeg()) {
this.writeBit(true);
const b = new bn_js_1.default(2);
const nb = b.pow(new bn_js_1.default(bitLength - 1));
this.writeUint(nb.add(number), bitLength - 1);
}
else {
this.writeBit(false);
this.writeUint(number, bitLength - 1);
}
}
}
/**
* Write unsigned 8-bit int
* @param ui8 {number}
*/
writeUint8(ui8) {
this.writeUint(ui8, 8);
}
/**
* Write array of unsigned 8-bit ints
* @param ui8 {Uint8Array}
*/
writeBytes(ui8) {
for (let i = 0; i < ui8.length; i++) {
this.writeUint8(ui8[i]);
}
}
/**
* Write UTF-8 string
*
* @param value {string}
*/
writeString(value) {
this.writeBytes(new TextEncoder().encode(value));
}
/**
* @param amount {number | BN} in nanograms
*/
writeGrams(amount) {
if (amount == 0) {
this.writeUint(0, 4);
}
else {
amount = new bn_js_1.default(amount);
const l = Math.ceil(amount.toString(16).length / 2);
this.writeUint(l, 4);
this.writeUint(amount, l * 8);
}
}
/**
* @param amount {number | BN} in nanotons
*/
writeCoins(amount) {
this.writeGrams(amount);
}
//addr_none$00 = MsgAddressExt;
//addr_std$10 anycast:(Maybe Anycast)
// workchain_id:int8 address:uint256 = MsgAddressInt;
/**
* @param address {Address | null}
*/
writeAddress(address) {
if (address == null) {
this.writeUint(0, 2);
}
else {
this.writeUint(2, 2);
this.writeUint(0, 1); // TODO split addresses (anycast)
this.writeInt(address.wc, 8);
this.writeBytes(address.hashPart);
}
}
/**
* write another BitString to this BitString
* @param anotherBitString {BitString}
*/
writeBitString(anotherBitString) {
anotherBitString.forEach((x) => {
this.writeBit(x);
});
}
writeVarUInt(value, headerBits) {
let v = new bn_js_1.default(value);
if (v.eq(new bn_js_1.default(0))) {
this.writeUint(0, headerBits);
}
else {
let h = v.toString("hex");
while (h.length % 2 !== 0) {
h = "0" + h;
}
const l = Math.ceil(h.length / 2);
this.writeUint(l, headerBits);
this.writeBuffer(Buffer.from(h, "hex"));
}
}
writeBuffer = (buffer) => {
for (let i = 0; i < buffer.length; i++) {
this.writeUint8(buffer[i]);
}
};
clone() {
const result = new BitString(0);
result.array = this.array.slice(0);
result.length = this.length;
result.cursor = this.cursor;
return result;
}
/**
* @return {string} hex
*/
toString() {
return this.toHex();
}
/**
* @return {Uint8Array}
*/
getTopUppedArray() {
const ret = this.clone();
let tu = Math.ceil(ret.cursor / 8) * 8 - ret.cursor;
if (tu > 0) {
tu = tu - 1;
ret.writeBit(true);
while (tu > 0) {
tu = tu - 1;
ret.writeBit(false);
}
}
ret.array = ret.array.slice(0, Math.ceil(ret.cursor / 8));
return ret.array;
}
/**
* like Fift
* @return {string}
*/
toHex() {
if (this.cursor % 4 === 0) {
const s = (0, utils_1.bytesToHex)(this.array.slice(0, Math.ceil(this.cursor / 8))).toUpperCase();
if (this.cursor % 8 === 0) {
return s;
}
else {
return s.substr(0, s.length - 1);
}
}
else {
const temp = this.clone();
temp.writeBit(1);
while (temp.cursor % 4 !== 0) {
temp.writeBit(0);
}
const hex = temp.toHex().toUpperCase();
return hex + "_";
}
}
/**
* set this cell data to match provided topUppedArray
* @param array {Uint8Array}
* @param fullfilledBytes {boolean}
*/
setTopUppedArray(array, fullfilledBytes = true) {
this.length = array.length * 8;
this.array = array;
this.cursor = this.length;
if (fullfilledBytes || !this.length) {
return;
}
else {
let foundEndBit = false;
for (let c = 0; c < 7; c++) {
this.cursor -= 1;
if (this.get(this.cursor)) {
foundEndBit = true;
this.off(this.cursor);
break;
}
}
if (!foundEndBit) {
console.log(array, fullfilledBytes);
throw new Error("Incorrect TopUppedArray");
}
}
}
}
exports.BitString = BitString;