toBigInt utilities (#1191)
* toBigInt utilities * Handle negatives? * two's complement adjustments * Adjust * Compliment util extract * dedupe * Ensure > u32 tests
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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)
|
||||
);
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user