From cf9082e03af7caabb5c43eae9328dad0969ca0c0 Mon Sep 17 00:00:00 2001 From: Jaco Date: Tue, 22 Aug 2023 20:50:50 +0300 Subject: [PATCH] Adjust BE u8aTo{Bn, BigInt, Number} check (#1868) * Adjust BE u8aTo{Bn, BigInt, Numer} check * Adjust function names (liberal find-and-replace) --- packages/util/src/u8a/toBigInt.ts | 31 ++++++++++++------------ packages/util/src/u8a/toBn.ts | 39 ++++++++++++++++--------------- packages/util/src/u8a/toNumber.ts | 35 +++++++++++++-------------- 3 files changed, 54 insertions(+), 51 deletions(-) diff --git a/packages/util/src/u8a/toBigInt.ts b/packages/util/src/u8a/toBigInt.ts index fcccd5549..00a47c2d1 100644 --- a/packages/util/src/u8a/toBigInt.ts +++ b/packages/util/src/u8a/toBigInt.ts @@ -18,27 +18,28 @@ const U64_MAX = BigInt('0x10000000000000000'); export function u8aToBigInt (value: Uint8Array, { isLe = true, isNegative = false }: ToBnOptions = {}): bigint { // slice + reverse is expensive, however SCALE is LE by default so this is the path // we are most interested in (the BE is added for the sake of being comprehensive) - const u8a = isLe - ? value - : value.slice().reverse(); - const count = u8a.length; + if (!isLe) { + value = value.slice().reverse(); + } - if (isNegative && count && (u8a[count - 1] & 0x80)) { + const count = value.length; + + if (isNegative && count && (value[count - 1] & 0x80)) { switch (count) { case 0: return BigInt(0); case 1: - return BigInt(((u8a[0] ^ 0x0000_00ff) * -1) - 1); + return BigInt(((value[0] ^ 0x0000_00ff) * -1) - 1); case 2: - return BigInt((((u8a[0] + (u8a[1] << 8)) ^ 0x0000_ffff) * -1) - 1); + return BigInt((((value[0] + (value[1] << 8)) ^ 0x0000_ffff) * -1) - 1); case 4: - return BigInt((((u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + (u8a[3] * 0x1_00_00_00)) ^ 0xffff_ffff) * -1) - 1); + return BigInt((((value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)) ^ 0xffff_ffff) * -1) - 1); } - const dvI = new DataView(u8a.buffer, u8a.byteOffset); + const dvI = new DataView(value.buffer, value.byteOffset); if (count === 8) { return dvI.getBigInt64(0, true); @@ -52,7 +53,7 @@ export function u8aToBigInt (value: Uint8Array, { isLe = true, isNegative = fals } if (mod) { - result = (result * U8_MAX) + BigInt(u8a[0] ^ 0xff); + result = (result * U8_MAX) + BigInt(value[0] ^ 0xff); } return (result * -_1n) - _1n; @@ -63,16 +64,16 @@ export function u8aToBigInt (value: Uint8Array, { isLe = true, isNegative = fals return BigInt(0); case 1: - return BigInt(u8a[0]); + return BigInt(value[0]); case 2: - return BigInt(u8a[0] + (u8a[1] << 8)); + return BigInt(value[0] + (value[1] << 8)); case 4: - return BigInt(u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + (u8a[3] * 0x1_00_00_00)); + return BigInt(value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)); } - const dvI = new DataView(u8a.buffer, u8a.byteOffset); + const dvI = new DataView(value.buffer, value.byteOffset); switch (count) { case 8: @@ -90,7 +91,7 @@ export function u8aToBigInt (value: Uint8Array, { isLe = true, isNegative = fals } if (mod) { - result = (result * U8_MAX) + BigInt(u8a[0]); + result = (result * U8_MAX) + BigInt(value[0]); } return result; diff --git a/packages/util/src/u8a/toBn.ts b/packages/util/src/u8a/toBn.ts index 518d80fff..6f00e7126 100644 --- a/packages/util/src/u8a/toBn.ts +++ b/packages/util/src/u8a/toBn.ts @@ -26,14 +26,15 @@ import { BN } from '../bn/bn.js'; export function u8aToBn (value: Uint8Array, { isLe = true, isNegative = false }: ToBnOptions = {}): BN { // slice + reverse is expensive, however SCALE is LE by default so this is the path // we are most interested in (the BE is added for the sake of being comprehensive) - const u8a = isLe - ? value - : value.slice().reverse(); - const count = u8a.length; + if (!isLe) { + value = value.slice().reverse(); + } + + const count = value.length; // shortcut for <= u48 values - in this case the manual conversion // here seems to be more efficient than passing the full array - if (isNegative && count && (u8a[count - 1] & 0x80)) { + if (isNegative && count && (value[count - 1] & 0x80)) { // Most common case i{8, 16, 32} default LE SCALE-encoded // For <= 32, we also optimize the xor to a single op switch (count) { @@ -41,27 +42,27 @@ export function u8aToBn (value: Uint8Array, { isLe = true, isNegative = false }: return new BN(0); case 1: - return new BN(((u8a[0] ^ 0x0000_00ff) * -1) - 1); + return new BN(((value[0] ^ 0x0000_00ff) * -1) - 1); case 2: - return new BN((((u8a[0] + (u8a[1] << 8)) ^ 0x0000_ffff) * -1) - 1); + return new BN((((value[0] + (value[1] << 8)) ^ 0x0000_ffff) * -1) - 1); case 3: - return new BN((((u8a[0] + (u8a[1] << 8) + (u8a[2] << 16)) ^ 0x00ff_ffff) * -1) - 1); + return new BN((((value[0] + (value[1] << 8) + (value[2] << 16)) ^ 0x00ff_ffff) * -1) - 1); case 4: // for the 3rd byte, we don't << 24 - since JS converts all bitwise operators to // 32-bit, in the case where the top-most bit is set this yields a negative value - return new BN((((u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + (u8a[3] * 0x1_00_00_00)) ^ 0xffff_ffff) * -1) - 1); + return new BN((((value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)) ^ 0xffff_ffff) * -1) - 1); case 5: - return new BN(((((u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + (u8a[3] * 0x1_00_00_00)) ^ 0xffff_ffff) + ((u8a[4] ^ 0xff) * 0x1_00_00_00_00)) * -1) - 1); + return new BN(((((value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)) ^ 0xffff_ffff) + ((value[4] ^ 0xff) * 0x1_00_00_00_00)) * -1) - 1); case 6: - return new BN(((((u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + (u8a[3] * 0x1_00_00_00)) ^ 0xffff_ffff) + (((u8a[4] + (u8a[5] << 8)) ^ 0x0000_ffff) * 0x1_00_00_00_00)) * -1) - 1); + return new BN(((((value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)) ^ 0xffff_ffff) + (((value[4] + (value[5] << 8)) ^ 0x0000_ffff) * 0x1_00_00_00_00)) * -1) - 1); default: - return new BN(u8a, 'le').fromTwos(count * 8); + return new BN(value, 'le').fromTwos(count * 8); } } @@ -75,26 +76,26 @@ export function u8aToBn (value: Uint8Array, { isLe = true, isNegative = false }: return new BN(0); case 1: - return new BN(u8a[0]); + return new BN(value[0]); case 2: - return new BN(u8a[0] + (u8a[1] << 8)); + return new BN(value[0] + (value[1] << 8)); case 3: - return new BN(u8a[0] + (u8a[1] << 8) + (u8a[2] << 16)); + return new BN(value[0] + (value[1] << 8) + (value[2] << 16)); case 4: // for the 3rd byte, we don't << 24 - since JS converts all bitwise operators to // 32-bit, in the case where the top-most bit is set this yields a negative value - return new BN(u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + (u8a[3] * 0x1_00_00_00)); + return new BN(value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)); case 5: - return new BN(u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + ((u8a[3] + (u8a[4] << 8)) * 0x1_00_00_00)); + return new BN(value[0] + (value[1] << 8) + (value[2] << 16) + ((value[3] + (value[4] << 8)) * 0x1_00_00_00)); case 6: - return new BN(u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + ((u8a[3] + (u8a[4] << 8) + (u8a[5] << 16)) * 0x1_00_00_00)); + return new BN(value[0] + (value[1] << 8) + (value[2] << 16) + ((value[3] + (value[4] << 8) + (value[5] << 16)) * 0x1_00_00_00)); default: - return new BN(u8a, 'le'); + return new BN(value, 'le'); } } diff --git a/packages/util/src/u8a/toNumber.ts b/packages/util/src/u8a/toNumber.ts index b70f314b6..273e495b2 100644 --- a/packages/util/src/u8a/toNumber.ts +++ b/packages/util/src/u8a/toNumber.ts @@ -10,39 +10,40 @@ import type { ToBnOptions } from '../types.js'; export function u8aToNumber (value: Uint8Array, { isLe = true, isNegative = false }: ToBnOptions = {}): number { // slice + reverse is expensive, however SCALE is LE by default so this is the path // we are most interested in (the BE is added for the sake of being comprehensive) - const u8a = isLe - ? value - : value.slice().reverse(); - const count = u8a.length; + if (!isLe) { + value = value.slice().reverse(); + } + + const count = value.length; // When the value is a i{8, 16, 24, 32, 40, 40} values and the top-most bit // indicates a signed value, we use a two's complement conversion. If one of these // flags are not set, we just do a normal unsigned conversion (the same shortcut // applies in both the u8aTo{BigInt, Bn} conversions as well) - if (isNegative && count && (u8a[count - 1] & 0x80)) { + if (isNegative && count && (value[count - 1] & 0x80)) { switch (count) { case 0: return 0; case 1: - return (((u8a[0] ^ 0x0000_00ff) * -1) - 1); + return (((value[0] ^ 0x0000_00ff) * -1) - 1); case 2: - return ((((u8a[0] + (u8a[1] << 8)) ^ 0x0000_ffff) * -1) - 1); + return ((((value[0] + (value[1] << 8)) ^ 0x0000_ffff) * -1) - 1); case 3: - return ((((u8a[0] + (u8a[1] << 8) + (u8a[2] << 16)) ^ 0x00ff_ffff) * -1) - 1); + return ((((value[0] + (value[1] << 8) + (value[2] << 16)) ^ 0x00ff_ffff) * -1) - 1); case 4: // for the 3rd byte, we don't << 24 - since JS converts all bitwise operators to // 32-bit, in the case where the top-most bit is set this yields a negative value - return ((((u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + (u8a[3] * 0x1_00_00_00)) ^ 0xffff_ffff) * -1) - 1); + return ((((value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)) ^ 0xffff_ffff) * -1) - 1); case 5: - return (((((u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + (u8a[3] * 0x1_00_00_00)) ^ 0xffff_ffff) + ((u8a[4] ^ 0xff) * 0x1_00_00_00_00)) * -1) - 1); + return (((((value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)) ^ 0xffff_ffff) + ((value[4] ^ 0xff) * 0x1_00_00_00_00)) * -1) - 1); case 6: - return (((((u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + (u8a[3] * 0x1_00_00_00)) ^ 0xffff_ffff) + (((u8a[4] + (u8a[5] << 8)) ^ 0x0000_ffff) * 0x1_00_00_00_00)) * -1) - 1); + return (((((value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)) ^ 0xffff_ffff) + (((value[4] + (value[5] << 8)) ^ 0x0000_ffff) * 0x1_00_00_00_00)) * -1) - 1); default: throw new Error('Value more than 48-bits cannot be reliably converted'); @@ -54,24 +55,24 @@ export function u8aToNumber (value: Uint8Array, { isLe = true, isNegative = fals return 0; case 1: - return u8a[0]; + return value[0]; case 2: - return u8a[0] + (u8a[1] << 8); + return value[0] + (value[1] << 8); case 3: - return u8a[0] + (u8a[1] << 8) + (u8a[2] << 16); + return value[0] + (value[1] << 8) + (value[2] << 16); case 4: // for the 3rd byte, we don't << 24 - since JS converts all bitwise operators to // 32-bit, in the case where the top-most bit is set this yields a negative value - return u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + (u8a[3] * 0x1_00_00_00); + return value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00); case 5: - return u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + ((u8a[3] + (u8a[4] << 8)) * 0x1_00_00_00); + return value[0] + (value[1] << 8) + (value[2] << 16) + ((value[3] + (value[4] << 8)) * 0x1_00_00_00); case 6: - return u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + ((u8a[3] + (u8a[4] << 8) + (u8a[5] << 16)) * 0x1_00_00_00); + return value[0] + (value[1] << 8) + (value[2] << 16) + ((value[3] + (value[4] << 8) + (value[5] << 16)) * 0x1_00_00_00); default: throw new Error('Value more than 48-bits cannot be reliably converted');