toBigInt utilities (#1191)

* toBigInt utilities

* Handle negatives?

* two's complement adjustments

* Adjust

* Compliment util extract

* dedupe

* Ensure > u32 tests
This commit is contained in:
Jaco
2021-11-15 20:46:48 +02:00
committed by GitHub
parent 82f5fec822
commit 06c06a8b73
8 changed files with 223 additions and 2 deletions
+2
View File
@@ -6,10 +6,12 @@ Changes:
- Add `stringPascalCase` utility
- Add strongly typed `object{Entries, Keys, Values}` utilities
- Add `{hex, u8a}ToBigInt` utilities
- Expand tests for uncovered functions
- Sync with upstream Substrate ss58 registry
- Optimize for `hexToU8a` and `u8aToHex` functions
- Optimize `u8aEq` to use `DataView` for compares
- Space optimization for bip39 wordlists
## 7.8.2 Nov 7, 2021
+1
View File
@@ -9,6 +9,7 @@ export { hexAddPrefix } from './addPrefix';
export { hexFixLength } from './fixLength';
export { hexHasPrefix } from './hasPrefix';
export { hexStripPrefix } from './stripPrefix';
export { hexToBigInt } from './toBigInt';
export { hexToBn } from './toBn';
export { hexToNumber } from './toNumber';
export { hexToString } from './toString';
+72
View File
@@ -0,0 +1,72 @@
// Copyright 2017-2021 @polkadot/util authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { hexToBigInt } from '.';
describe('hexToBigInt', (): void => {
it('converts prefixed hex values to BN', (): void => {
expect(
hexToBigInt('0x81')
).toEqual(0x81n);
});
it('converts null values to BN(0)', (): void => {
expect(
hexToBigInt(null)
).toEqual(0n);
});
it('converts 0x values to BN(0)', (): void => {
expect(
hexToBigInt('0x')
).toEqual(0n);
});
it('should convert with Big Endian by default', (): void => {
expect(
hexToBigInt('0x0100123456')
).toEqual(0x0100123456n);
});
it('converts 0x values to BN(0) (LE)', (): void => {
expect(
hexToBigInt('0x', { isLe: true })
).toEqual(0n);
});
it('converts little-endian', (): void => {
expect(
hexToBigInt('0x4500000000000000', { isLe: true })
).toEqual(69n);
});
it('handles negative numbers (LE)', (): void => {
expect(
hexToBigInt('0x2efb', { isLe: true, isNegative: true })
).toEqual(-1234n);
});
it('handles negative numbers (BE)', (): void => {
expect(
hexToBigInt('0xfb2e', { isLe: false, isNegative: true })
).toEqual(-1234n);
});
it('handles negative numbers (LE, 128)', (): void => {
expect(
hexToBigInt('0x00009c584c491ff2ffffffffffffffff', { isLe: true, isNegative: true })
).toEqual(-1000000000000000000n);
});
it('handles starting zeros correctly (BE)', (): void => {
expect(
hexToBigInt('0x0000000000000100', { isLe: false })
).toEqual(256n);
});
it('handles starting zeros correctly (LE)', (): void => {
expect(
hexToBigInt('0x0001000000000000', { isLe: true })
).toEqual(256n);
});
});
+23
View File
@@ -0,0 +1,23 @@
// Copyright 2017-2021 @polkadot/util authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ToBnOptions } from '../types';
import { objectSpread } from '../object/spread';
import { u8aToBigInt } from '../u8a/toBigInt';
import { hexToU8a } from './toU8a';
/**
* @name hexToBigInt
* @summary Creates a BigInt instance object from a hex string.
*/
export function hexToBigInt (value?: string | null, options: ToBnOptions = {}): bigint {
if (!value || value === '0x') {
return BigInt(0);
}
return u8aToBigInt(
hexToU8a(value),
objectSpread({ isLe: false, isNegative: false }, options)
);
}
+2 -2
View File
@@ -29,7 +29,7 @@ const DEFAULT_OPTS: ToBnOptions = { isLe: false, isNegative: false };
* ```
*/
export function hexToBn (value?: string | null, options: ToBnOptions | boolean = DEFAULT_OPTS): BN {
if (!value) {
if (!value || value === '0x') {
return new BN(0);
}
@@ -40,7 +40,7 @@ export function hexToBn (value?: string | null, options: ToBnOptions | boolean =
: options
);
const stripped = hexStripPrefix(value);
const bn = new BN(stripped || '00', 16, isLe ? 'le' : 'be');
const bn = new BN(stripped, 16, isLe ? 'le' : 'be');
// fromTwos takes as parameter the number of bits, which is the hex length
// multiplied by 4.
+1
View File
@@ -11,6 +11,7 @@ export { u8aEmpty } from './empty';
export { u8aEq } from './eq';
export { u8aFixLength } from './fixLength';
export { u8aSorted } from './sorted';
export { u8aToBigInt } from './toBigInt';
export { u8aToBn } from './toBn';
export { u8aToBuffer } from './toBuffer';
export { u8aToHex } from './toHex';
+59
View File
@@ -0,0 +1,59 @@
// Copyright 2017-2021 @polkadot/util authors & contributors
// SPDX-License-Identifier: Apache-2.0
import { u8aToBigInt } from '.';
describe('u8aToBigInt', (): void => {
it('converts little-endian by default', (): void => {
expect(
u8aToBigInt(
new Uint8Array([0x12, 0x34])
)
).toEqual(0x3412n);
});
it('converts values (big-endian)', (): void => {
expect(
u8aToBigInt(
new Uint8Array([0x12, 0x34]),
{ isLe: false }
)
).toEqual(0x1234n);
});
it('converts values (little-endian)', (): void => {
expect(
u8aToBigInt(
new Uint8Array([0x12, 0x34, 0x56]),
{ isLe: true }
)
).toEqual(0x563412n);
});
it('converts empty', (): void => {
expect(
u8aToBigInt(
new Uint8Array(),
{ isLe: true }
)
).toEqual(0n);
});
it('handles negative numbers (little-endian)', (): void => {
expect(
u8aToBigInt(
new Uint8Array([46, 251]),
{ isLe: true, isNegative: true }
)
).toEqual(-1234n);
});
it('handles negative numbers (big-endian)', (): void => {
expect(
u8aToBigInt(
new Uint8Array([251, 46]),
{ isLe: false, isNegative: true }
)
).toEqual(-1234n);
});
});
+63
View File
@@ -0,0 +1,63 @@
// Copyright 2017-2021 @polkadot/util authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { ToBnOptions } from '../types';
import { objectSpread } from '../object/spread';
const U8_MULT = 256n;
const U16_MUL = U8_MULT * U8_MULT;
function xor (input: Uint8Array): Uint8Array {
const result = new Uint8Array(input.length);
const dvI = new DataView(input.buffer, input.byteOffset);
const dvO = new DataView(result.buffer);
const mod = input.length % 2;
const length = input.length - mod;
for (let i = 0; i < length; i += 2) {
dvO.setUint16(i, dvI.getUint16(i) ^ 0xffff);
}
if (mod) {
dvO.setUint8(length, dvI.getUint8(length) ^ 0xff);
}
return result;
}
function toBigInt (input: Uint8Array): bigint {
const dvI = new DataView(input.buffer, input.byteOffset);
const mod = input.length % 2;
const length = input.length - mod;
let result = BigInt(0);
for (let i = 0; i < length; i += 2) {
result = (result * U16_MUL) + BigInt(dvI.getUint16(i));
}
if (mod) {
result = (result * U8_MULT) + BigInt(dvI.getUint8(length));
}
return result;
}
/**
* @name u8aToBigInt
* @summary Creates a BigInt from a Uint8Array object.
*/
export function u8aToBigInt (value: Uint8Array, options: ToBnOptions = {}): bigint {
if (!value || !value.length) {
return BigInt(0);
}
const { isLe, isNegative }: ToBnOptions = objectSpread({ isLe: true, isNegative: false }, options);
const u8a = isLe
? value.reverse()
: value;
return isNegative
? ((toBigInt(xor(u8a)) * -1n) - 1n)
: toBigInt(u8a);
}