@ckb-lumos/bi
Version:
Bigint support in lumos
264 lines (259 loc) • 7.88 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ckbDecimals = exports.BI = void 0;
exports.formatUnit = formatUnit;
exports.isBIish = isBIish;
exports.parseUnit = parseUnit;
exports.toJSBI = toJSBI;
var _jsbi = _interopRequireDefault(require("jsbi"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function isBIish(value) {
return value !== null && (typeof value === "number" && value % 1 === 0 || typeof value === "string" && (!!value.match(/^0x(0|[0-9a-fA-F]+)$/) || !!value.match(/^-?[0-9]+$/)) || typeof value === "bigint" || BI.isBI(value));
}
class BI {
constructor(value) {
this.jsbi = value;
this._isBI = true;
}
add(other) {
return toBI(_jsbi.default.add(this.jsbi, toJSBI(other)));
}
sub(other) {
return toBI(_jsbi.default.subtract(this.jsbi, toJSBI(other)));
}
div(other) {
return toBI(_jsbi.default.divide(this.jsbi, toJSBI(other)));
}
mul(other) {
return toBI(_jsbi.default.multiply(this.jsbi, toJSBI(other)));
}
mod(other) {
return toBI(_jsbi.default.remainder(this.jsbi, toJSBI(other)));
}
abs() {
if (_jsbi.default.greaterThanOrEqual(this.jsbi, toJSBI(0))) {
return toBI(this.jsbi);
} else {
return toBI(_jsbi.default.unaryMinus(this.jsbi));
}
}
pow(other) {
return toBI(_jsbi.default.exponentiate(this.jsbi, toJSBI(other)));
}
and(other) {
return toBI(_jsbi.default.bitwiseAnd(this.jsbi, toJSBI(other)));
}
or(other) {
return toBI(_jsbi.default.bitwiseOr(this.jsbi, toJSBI(other)));
}
xor(other) {
return toBI(_jsbi.default.bitwiseXor(this.jsbi, toJSBI(other)));
}
not() {
return toBI(_jsbi.default.bitwiseNot(this.jsbi));
}
mask(other) {
const jsbiOther = toJSBI(other);
if (_jsbi.default.lessThan(jsbiOther, toJSBI(0)) || _jsbi.default.lessThan(this.jsbi, toJSBI(0))) {
throw new Error("mask works only with positive numbers");
}
const length = toJSBI(this.jsbi.toString(2).length);
if (_jsbi.default.lessThanOrEqual(length, jsbiOther)) {
return toBI(this.jsbi);
} else {
const maskNum = _jsbi.default.leftShift(_jsbi.default.signedRightShift(this.jsbi, jsbiOther), jsbiOther);
return toBI(_jsbi.default.bitwiseXor(this.jsbi, maskNum));
}
}
shl(other) {
return toBI(_jsbi.default.leftShift(this.jsbi, toJSBI(other)));
}
shr(other) {
return toBI(_jsbi.default.signedRightShift(this.jsbi, toJSBI(other)));
}
eq(other) {
return _jsbi.default.equal(this.jsbi, toJSBI(other));
}
lt(other) {
return _jsbi.default.lessThan(this.jsbi, toJSBI(other));
}
lte(other) {
return _jsbi.default.lessThanOrEqual(this.jsbi, toJSBI(other));
}
gt(other) {
return _jsbi.default.greaterThan(this.jsbi, toJSBI(other));
}
gte(other) {
return _jsbi.default.greaterThanOrEqual(this.jsbi, toJSBI(other));
}
isNegative() {
return _jsbi.default.lessThan(this.jsbi, toJSBI(0));
}
isZero() {
return _jsbi.default.equal(this.jsbi, toJSBI(0));
}
toNumber() {
return _jsbi.default.toNumber(this.jsbi);
}
toBigInt() {
try {
return BigInt(this.jsbi.toString(10));
} catch (e) {
throw new Error("this platform does not support BigInt");
}
}
toString(radix) {
radix = radix || 10;
return this.jsbi.toString(radix);
}
toHexString() {
if (_jsbi.default.lessThan(this.jsbi, toJSBI(0))) {
return "-0x" + _jsbi.default.unaryMinus(this.jsbi).toString(16);
} else {
return "0x" + this.jsbi.toString(16);
}
}
static from(value) {
if (value instanceof BI) {
return value;
} else if (isBIish(value)) {
return toBI(toJSBI(value));
} else if (value instanceof _jsbi.default) {
return toBI(toJSBI(value.toString()));
} else {
throw new Error(`invalid type: ${value} can't be converted into BI`);
}
}
static isBI(value) {
return isBILike(value) && !!value._isBI;
}
}
exports.BI = BI;
function isBILike(value) {
if (value == null) return false;
return typeof value === "object";
}
function toBI(value) {
return new BI(value);
}
function toJSBI(value) {
if (typeof value === "number" || typeof value === "string") {
return _jsbi.default.BigInt(value);
} else {
return _jsbi.default.BigInt(value.toString());
}
}
const validUnitNames = ["shannon", "ckb"];
const ckbDecimals = exports.ckbDecimals = 8;
const negativeOne = BI.from(-1);
function formatUnit(value, unit) {
const decimals = parseDecimals(unit);
return formatFixed(value, decimals);
}
function parseUnit(value, unit) {
const decimals = parseDecimals(unit);
return parseFixed(value, decimals);
}
function formatFixed(value, decimals) {
if (!isValidDecimalSize(decimals)) {
throw new Error(`decimal size must be a non-negative integer`);
}
const multiplier = "1" + Array(decimals).fill("0").join("");
value = BI.from(value);
const isNegative = value.isNegative();
if (isNegative) {
value = value.mul(negativeOne);
}
const wholePart = value.div(multiplier).toString();
let result = wholePart;
if (multiplier.length > 1) {
let decimalPart = value.mod(multiplier).toString();
while (decimalPart.length < multiplier.length - 1) {
decimalPart = "0" + decimalPart;
}
// remove trailing zeros
decimalPart = decimalPart.match(/^([0-9]*[1-9]|0)(0*)/)[1];
result += "." + decimalPart;
}
if (isNegative) {
result = "-" + result;
}
return result;
}
function parseFixed(value, decimals) {
if (!isValidDecimalSize(decimals)) {
throw new Error(`decimal size must be a non-negative integer`);
}
// check if value represents a valid decimal number
if (!value.match(/^-?\d+(\.\d{1,})?$/)) {
throw new Error("invalid decimal string");
}
const multiplier = "1" + Array(decimals).fill("0").join("");
const isNegative = value.substring(0, 1) === "-";
if (isNegative) {
value = value.substring(1);
}
let wholePart, decimalPart;
const valueParts = value.split(".");
if (valueParts.length === 1) {
wholePart = valueParts[0];
decimalPart = "0";
} else if (valueParts.length === 2) {
wholePart = valueParts[0];
decimalPart = valueParts[1];
} else {
throw new Error("too many decimal points (should not happen)");
}
// remove leading zeros of whole part
while (wholePart.length > 0 && wholePart[0] === "0") {
wholePart = wholePart.substring(1);
}
if (wholePart === "") {
wholePart = "0";
}
// remove trailing zeros of decimal part
while (decimalPart.length > 0 && decimalPart[decimalPart.length - 1] === "0") {
decimalPart = decimalPart.substring(0, decimalPart.length - 1);
}
if (decimalPart.length > multiplier.length - 1) {
throw new Error("decimal part exceeds max decimals");
}
if (decimalPart === "") {
decimalPart = "0";
}
// pad decimal part with zeros to get to shannon
while (decimalPart.length < multiplier.length - 1) {
decimalPart += "0";
}
const wholeValue = BI.from(wholePart);
const decimalValue = BI.from(decimalPart);
let shannons = wholeValue.mul(multiplier).add(decimalValue);
if (isNegative) {
shannons = shannons.mul(negativeOne);
}
return shannons;
}
function parseDecimals(unit) {
let decimals = 0;
if (typeof unit === "string") {
if (validUnitNames.indexOf(unit) === -1) {
throw new Error(`invalid unit name, supported names are ${validUnitNames.join(", ")}`);
}
if (unit === "ckb") {
decimals = ckbDecimals;
}
} else {
if (isValidDecimalSize(unit)) {
decimals = unit;
} else {
throw new Error(`unit of integer must be a non-negative integer`);
}
}
return decimals;
}
function isValidDecimalSize(decimals) {
return Number.isInteger(decimals) && decimals >= 0;
}
//# sourceMappingURL=index.js.map
;