From 7341c32df5c71990e58f81b473fbfc7bf5fe4033 Mon Sep 17 00:00:00 2001 From: Jaco Date: Sat, 7 May 2022 10:54:08 +0200 Subject: [PATCH] Optimize `u8aToHex` with direct (non-hex) conversion (#1477) * Optimize `u8aToHex` with direct (non-hex) conversion * Adjust compactFromU8a tests --- CHANGELOG.md | 7 +++++++ packages/util/src/compact/fromU8a.spec.ts | 11 +++++----- packages/util/src/compact/fromU8a.ts | 14 ++++++------- packages/util/src/hex/toBn.ts | 9 ++++---- packages/util/src/u8a/toBigInt.spec.ts | 3 +++ packages/util/src/u8a/toBigInt.ts | 2 +- packages/util/src/u8a/toBn.spec.ts | 3 +++ packages/util/src/u8a/toBn.ts | 25 +++++++++++++++-------- 8 files changed, 46 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 942c4e7c1..355b79907 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # CHANGELOG +## master + +Changes: + +- Optimize `u8aToHex` with direct (non-hex) conversion + + ## 9.1.1 Apr 30, 2022 Contributed: diff --git a/packages/util/src/compact/fromU8a.spec.ts b/packages/util/src/compact/fromU8a.spec.ts index 915017c7a..4be95136f 100644 --- a/packages/util/src/compact/fromU8a.spec.ts +++ b/packages/util/src/compact/fromU8a.spec.ts @@ -14,16 +14,17 @@ describe('compactFromU8a', (): void => { it('decodes from same u16 encoded value', (): void => { expect( - compactFromU8a(new Uint8Array([0b11111101, 0b00000111])) - ).toEqual([2, new BN(511)]); + compactFromU8a(new Uint8Array([0b11111101, 0b00000111])).toString() + ).toEqual( + [2, new BN(511)].toString() + ); }); it('decodes from same u32 encoded value (short)', (): void => { expect( - compactFromU8a(new Uint8Array([254, 255, 3, 0])) + compactFromU8a(new Uint8Array([254, 255, 3, 0])).toString() ).toEqual( - // since we use in-place, the words are different... HACK it - [4, new BN(0xffff).ishln(16).ishrn(16)] + [4, new BN(0xffff)].toString() ); }); diff --git a/packages/util/src/compact/fromU8a.ts b/packages/util/src/compact/fromU8a.ts index f0913fa17..4ad99fbcf 100644 --- a/packages/util/src/compact/fromU8a.ts +++ b/packages/util/src/compact/fromU8a.ts @@ -3,12 +3,12 @@ import type { U8aLike } from '../types'; -import { BN, BN_FOUR } from '../bn'; +import { BN, BN_FIVE } from '../bn'; import { u8aToBn, u8aToU8a } from '../u8a'; /** * @name compactFromU8a - * @description Retrievs the offset and encoded length from a compact-prefixed value + * @description Retrives the offset and encoded length from a compact-prefixed value * @example *
* @@ -32,12 +32,10 @@ export function compactFromU8a (input: U8aLike): [number, BN] { return [4, u8aToBn(u8a.subarray(0, 4), true).ishrn(2)]; } - const offset = 1 + ( - new BN(u8a[0]) - .ishrn(2) // clear flag - .iadd(BN_FOUR) // add 4 for base length - .toNumber() - ); + const offset = new BN(u8a[0]) + .ishrn(2) // clear flag + .iadd(BN_FIVE) // add 4 for base length, 1 for this byte + .toNumber(); return [offset, u8aToBn(u8a.subarray(1, offset), true)]; } diff --git a/packages/util/src/hex/toBn.ts b/packages/util/src/hex/toBn.ts index 94dde73e8..0d6eaa86e 100644 --- a/packages/util/src/hex/toBn.ts +++ b/packages/util/src/hex/toBn.ts @@ -8,8 +8,6 @@ import { isBoolean } from '../is/boolean'; import { objectSpread } from '../object/spread'; import { hexStripPrefix } from './stripPrefix'; -const DEFAULT_OPTS: ToBnOptions = { isLe: false, isNegative: false }; - /** * @name hexToBn * @summary Creates a BN.js object from a hex string. @@ -28,12 +26,13 @@ const DEFAULT_OPTS: ToBnOptions = { isLe: false, isNegative: false }; * hexToBn('0x123480001f'); // => BN(0x123480001f) * ``` */ -export function hexToBn (value?: string | null, options: ToBnOptions | boolean = DEFAULT_OPTS): BN { +export function hexToBn (value?: string | null, options: ToBnOptions | boolean = {}): BN { if (!value || value === '0x') { return new BN(0); } - const { isLe, isNegative }: ToBnOptions = objectSpread( + // For hex, default to BE + const { isLe, isNegative } = objectSpread( { isLe: false, isNegative: false }, isBoolean(options) ? { isLe: options } @@ -43,7 +42,7 @@ export function hexToBn (value?: string | null, options: ToBnOptions | boolean = 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. + // multiplied by 4 (2 bytes being 8 bits) return isNegative ? bn.fromTwos(stripped.length * 4) : bn; diff --git a/packages/util/src/u8a/toBigInt.spec.ts b/packages/util/src/u8a/toBigInt.spec.ts index d15497121..6be9ad183 100644 --- a/packages/util/src/u8a/toBigInt.spec.ts +++ b/packages/util/src/u8a/toBigInt.spec.ts @@ -1,6 +1,7 @@ // Copyright 2017-2022 @polkadot/util authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { performance } from '../test/performance'; import { u8aToBigInt } from '.'; describe('u8aToBigInt', (): void => { @@ -56,4 +57,6 @@ describe('u8aToBigInt', (): void => { ) ).toEqual(-1234n); }); + + performance('u8aToBigInt', 500000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], u8aToBigInt); }); diff --git a/packages/util/src/u8a/toBigInt.ts b/packages/util/src/u8a/toBigInt.ts index 5d135708b..68a52e1bc 100644 --- a/packages/util/src/u8a/toBigInt.ts +++ b/packages/util/src/u8a/toBigInt.ts @@ -55,7 +55,7 @@ export function u8aToBigInt (value: Uint8Array, options: ToBnOptions = {}): bigi return BigInt(0); } - const { isLe, isNegative }: ToBnOptions = objectSpread({ isLe: true, isNegative: false }, options); + const { isLe, isNegative } = objectSpread({ isLe: true, isNegative: false }, options); const u8a = isLe ? value.reverse() : value; diff --git a/packages/util/src/u8a/toBn.spec.ts b/packages/util/src/u8a/toBn.spec.ts index 2565efe2a..5d8e06e53 100644 --- a/packages/util/src/u8a/toBn.spec.ts +++ b/packages/util/src/u8a/toBn.spec.ts @@ -1,6 +1,7 @@ // Copyright 2017-2022 @polkadot/util authors & contributors // SPDX-License-Identifier: Apache-2.0 +import { performance } from '../test/performance'; import { u8aToBn } from '.'; describe('u8aToBn', (): void => { @@ -89,4 +90,6 @@ describe('u8aToBn', (): void => { ) ).toBe(true); }); + + performance('u8aToBn', 500000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], u8aToBn); }); diff --git a/packages/util/src/u8a/toBn.ts b/packages/util/src/u8a/toBn.ts index 8cbf3b811..095b431cc 100644 --- a/packages/util/src/u8a/toBn.ts +++ b/packages/util/src/u8a/toBn.ts @@ -1,13 +1,11 @@ // Copyright 2017-2022 @polkadot/util authors & contributors // SPDX-License-Identifier: Apache-2.0 -import type { BN } from '../bn/bn'; import type { ToBnOptions } from '../types'; -import { hexToBn } from '../hex/toBn'; -import { u8aToHex } from './toHex'; - -const DEFAULT_OPTS: ToBnOptions = { isLe: true, isNegative: false }; +import { BN } from '../bn/bn'; +import { isBoolean } from '../is/boolean'; +import { objectSpread } from '../object/spread'; /** * @name u8aToBn @@ -27,9 +25,18 @@ const DEFAULT_OPTS: ToBnOptions = { isLe: true, isNegative: false }; * u8aToHex(new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0xf])); // 0x68656c0f * ``` */ -export function u8aToBn (value: Uint8Array, options: ToBnOptions | boolean = DEFAULT_OPTS): BN { - return hexToBn( - u8aToHex(value), - options +export function u8aToBn (value: Uint8Array, options: ToBnOptions | boolean = {}): BN { + // NOTE: This is the same process as followed in the hex conversion + // For Uint8Array, default to LE + const { isLe, isNegative } = objectSpread( + { isLe: true, isNegative: false }, + isBoolean(options) + ? { isLe: options } + : options ); + const bn = new BN(value, isLe ? 'le' : 'be'); + + return isNegative + ? bn.fromTwos(value.length * 8) + : bn; }