bigint-lib v1.1.0
bigint-lib
Use BigInt in library code, whether native or polyfilled.
Installation
npm install --save bigint-libUsage
If polyfilling, polyfill BigInt first.
Then,
import {bigIntLib} from "bigint-lib";
/**
* `true` if natively supported; `false` otherwise
*/
bigIntLib.isNativelySupported();
/**
* `true` if considered a `BigInt`
*/
bigIntLib.isBigInt(x);
/**
* Creates a `BigInt` instance
*/
bigIntLib.BigInt(0);See API for more details
Since this library is meant for library authors, reading the source code to understand how it works internally is recommended.
Motivation
Not all browsers support BigInt natively.
https://caniuse.com/#feat=mdn-javascript_builtins_bigint
Application code can use polyfills for BigInt easily.
https://github.com/GoogleChromeLabs/jsbi
However, libraries cannot assume that BigInt is natively supported, or polyfilled.
A library has to check.
If it is natively supported, we can use BigInt and all built-in operations directly.
If it is polyfilled, we cannot assume the shape of the exposed API for built-in operations.
Is addition done with x.add(y)? Or Polyfill.add(x, y)? Or x.plus(y)? Is it even implemented at all?
What about every other operation?
This library provides a unified API for all built-in BigInt operations,
regardless of whether BigInt is natively supported, or polyfilled.
How it works
This library's only dependency is jsbi.
We have three cases to handle,
BigIntis natively supportedWe use the built-in operations directly.
This is the most efficient.
See src/native.ts for more details.
BigIntis polyfilled usingjsbiWe use
jsbifor built-in operations.This is still pretty efficient.
See src/jsbi-polyfill.ts for more details.
BigIntis polyfilled using some other libraryWe convert the other library's
BigInttoJSBI, usejsbifor built-in operations, and convert the result back to the other libary'sBigInt.This is the least efficient, but saves us from having to worry about the exposed API of the other library.
See src/non-jsbi-polyfill.ts for more details.
Expected Polyfill
This library assumes polyfills conform to some minimal API.
Polyfill with JSBI
To polyfill using jsbi,
import JSBI from "jsbi";
global.BigInt = JSBI.BigInt;At the moment, using jsbi+TypeScript with esModuleInterop:false may cause the import to fail during run-time.
You may set esModuleInterop:true to fix the problem.
However, setting esModuleInterop:true may cause other imports to fail.
You may choose to keep esModuleInterop:false and change the import to,
import {JSBI} from "bigint-lib/dist/jsbi";
global.BigInt = JSBI.BigInt;Polyfill with Other Library
If polyfilling with some other library, the library must, minimally, have the following API,
class MyPolyfill {
constructor (mixed : any) {
//snip https://tc39.es/ecma262/#sec-bigint-constructor
}
toString () {
return //snip base-10 string
}
}
global.BigInt = (mixed : any) => new MyPolyfill(mixed);The polyfill must also satisfy the following properties,
BigInt(0) instanceof MyPolyfill
> true
Object.getPrototypeOf(BigInt(0)).constructor === MyPolyfill
> trueAPI
The following are exported by this library,
BigIntLibis the unified API, for native and polyfilledBigIntoperations.getBigIntLib()returns an instanceofBigIntLib, depending on whetherBigIntis native or polyfilled. See How it works for more details.JSBIis a re-export ofjsbibigIntLibis of typeBigIntLiband is initialized withgetBigIntLib()whenbigint-libis first imported.biLibis a synonym forbigIntLib; in casebigIntLibis too verbosenativeBigIntLibis aBigIntLibinstance that assumesBigIntis natively supported.jsbiPolyfillBigIntLibis aBigIntLibinstance that assumesBigIntis polyfilled withjsbi.getNonJsbiPolyfillBigIntLib()returns aBigIntLibinstance that assumesBigIntis polyfilled with some other library.getNativeOrJsbiPolyfillBigIntLib()returns anativeBigIntLiborjsbiPolyfillBigIntLib, depending on whetherBigIntis natively supported or polyfilled.nativeOrJsbiLibis aBigIntLibinstance and is initialized withgetNativeOrJsbiPolyfillBigIntLib()whenbigint-libis first imported.
BigIntLib
The interface is mostly the same as jsbi's
| Operation | native BigInt | bigint-lib | Note |
|---|---|---|---|
| Creation from String | a = BigInt("456") | a = biLib.BigInt("456") | |
| Creation from Number | a = BigInt(789) | a = biLib.BigInt(789) | |
| Creation from BigInt | a = BigInt(a) | a = biLib.BigInt(a) | |
| Conversion to String | a.toString(radix) | biLib.toString(a, radix) | radix defaults to 10; must be in 2, 36 |
| Conversion to Number | Number(a) | biLib.toNumber(a) | May result in precision loss |
| Truncation | BigInt.asIntN(width, a) | biLib.asIntN(width, a) | Throws if width is negative |
BigInt.asUintN(width, a) | biLib.asUintN(width, a) | Throws if width is negative | |
| Type check | typeof a === "bigint" | biLib.isBigInt(a) | |
Native BigInt check | typeof BigInt(0) === "bigint" | biLib.isNativelySupported() |
| Operation | native BigInt | bigint-lib | Note |
|---|---|---|---|
| Arithmetic | |||
| Unary minus | b = -a | b = biLib.unaryMinus(a) | |
| Addition | c = a + b | c = biLib.add(a, b) | |
| Subtraction | c = a - b | c = biLib.subtract(a, b) | |
| Multiplication | c = a * b | c = biLib.multiply(a, b) | |
| Division | c = a / b | c = biLib.divide(a, b) | Throws if b is zero |
| Remainder | c = a % b | c = biLib.remainder(a, b) | Throws if b is zero |
| Exponentiation | c = a ** b | c = biLib.exponentiate(a, b) | Throws if b is negative |
| Bitwise | |||
| Left-shift | c = a << b | c = biLib.leftShift(a, b) | Allows negative shift |
| Signed right-shift | c = a >> b | c = biLib.signedRightShift(a, b) | Allows negative shift |
| Bitwise NOT | b = ~a | c = biLib.bitwiseNot(a) | |
| Bitwise AND | c = a & b | c = biLib.bitwiseAnd(a, b) | |
| Bitwise OR | c = a \| b | c = biLib.bitwiseOr(a, b) | |
| Bitwise XOR | c = a ^ b | c = biLib.bitwiseXor(a, b) | |
| Comparison | a == b | biLib.equal(a, b) | |
a != b | biLib.notEqual(a, b) | ||
a < b | biLib.lessThan(a, b) | ||
a <= b | biLib.lessThanOrEqual(a, b) | ||
a > b | biLib.greaterThan(a, b) | ||
a >= b | biLib.greaterThanOrEqual(a, b) | ||
| Unsupported | |||
| Literals | a = 123n | N/A | |
| Increment | a++/++a | N/A | |
a + 1n | biLib.add(a, biLib.BigInt(1)) | ||
| Decrement | a--/--a | N/A | |
a - 1n | biLib.subtract(a, biLib.BigInt(1)) |
nativeOrJsbiLib
If global.BigInt is natively supported, returns nativeBigIntLib.
Otherwise, returns jsbiPolyfillBigIntLib.
This is useful for libraries that need to perform many complex
BigInt operations before returning a result.
import {bigIntLib, nativeOrJsbiLib} from "bigint-lib";
const my1 = nativeOrJsbiLib.BigInt(1);
//Takes native or JSBI polyfilled bigints
//Will return a native or JSBI polyfilled bigint
function myComplexFunctionImpl (m : bigint, n : bigint) : bigint {
if (nativeOrJsbiLib.equal(m, 0)) {
return nativeOrJsbiLib.add(n, my1);
}
if (nativeOrJsbiLib.equal(n, 0)) {
return myComplexFunctionImpl(
nativeOrJsbiLib.subtract(m, my1),
my1
);
}
return myComplexFunctionImpl(
nativeOrJsbiLib.subtract(m, my1),
myComplexFunctionImpl(m, nativeOrJsbiLib.subtract(n, 1))
);
}
//Takes native, JSBI polyfilled, or other polyfilled bigints
//Must return a `bigint` that may be native, polyfilled with JSBI, or polyfilled with other libraries
function myComplexFunction (m : bigint, n : bigint) : bigint {
//Will be a native or JSBI polyfilled `bigint`
const myM = nativeOrJsbiLib.BigInt(m.toString());
const myN = nativeOrJsbiLib.BigInt(n.toString());
const myResult = myComplexFunctionImpl(myM, myN);
//Convert the result to a `bigint` type the same as its input
return bigIntLib.BigInt(myResult.toString());
}Using nativeOrJsbiLib saves the time needed to convert between JSBI and other polyfill libraries,
if other polyfill libraries are used.
Polyfilling with node
- Have your polyfill code in a
.jsfile. node -r my-polyfill.js my-entry-point.js
Polyfilling with ts-node
- Have your polyfill code in a
.tsfile. ts-node -r my-polyfill.ts my-entry-point.ts
Development
git clone https://github.com/AnyhowStep/bigint-lib.gitnpm installnpm run sanity-checkto build and run tests.npm runto see a list of commands.