Unroll u8aToBigInt loops (#1821)
* Unroll u8aToBigInt loops * Bump dev * Rolling tests
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
Changes:
|
||||
|
||||
- Align `u8aToBigInt` tests with `u8aToBn`
|
||||
- Unroll loops on most-frequently used paths for `u8aToBigInt`
|
||||
|
||||
|
||||
## 12.1.2 May 1, 2023
|
||||
|
||||
@@ -54,6 +54,15 @@ describe('u8aToBigInt', (): void => {
|
||||
).toBe('78563412');
|
||||
});
|
||||
|
||||
it('converts values (i32)', (): void => {
|
||||
expect(
|
||||
u8aToBigInt(
|
||||
new Uint8Array([0xf2, 0x34, 0x56, 0x78]),
|
||||
{ isLe: false, isNegative: true }
|
||||
).toString(16)
|
||||
).toBe('-dcba988');
|
||||
});
|
||||
|
||||
it('converts values (u40)', (): void => {
|
||||
expect(
|
||||
u8aToBigInt(
|
||||
@@ -71,6 +80,29 @@ describe('u8aToBigInt', (): void => {
|
||||
).toString(16)
|
||||
).toBe('bc9a78563412');
|
||||
});
|
||||
|
||||
it('converts values (u128)', (): void => {
|
||||
expect(
|
||||
u8aToBigInt(
|
||||
new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78]),
|
||||
{ isLe: true }
|
||||
).toString(16)
|
||||
).toBe('78563412785634127856341278563412');
|
||||
});
|
||||
|
||||
for (let i = 1; i < 32; i++) {
|
||||
const tu8a = [0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78];
|
||||
const tstr = tu8a.map((n) => n.toString(16));
|
||||
|
||||
it(`converts values with length ${i}`, (): void => {
|
||||
expect(
|
||||
u8aToBigInt(
|
||||
new Uint8Array(tu8a.slice(0, i)),
|
||||
{ isLe: true }
|
||||
).toString(16)
|
||||
).toBe(tstr.slice(0, i).reverse().join(''));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('signed', (): void => {
|
||||
@@ -193,6 +225,29 @@ describe('u8aToBigInt', (): void => {
|
||||
).toString(16)
|
||||
).toBe('123456789abc');
|
||||
});
|
||||
|
||||
it('converts values (u128)', (): void => {
|
||||
expect(
|
||||
u8aToBigInt(
|
||||
new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78]),
|
||||
{ isLe: false }
|
||||
).toString(16)
|
||||
).toBe('12345678123456781234567812345678');
|
||||
});
|
||||
|
||||
for (let i = 1; i < 32; i++) {
|
||||
const tu8a = [0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78];
|
||||
const tstr = tu8a.map((n) => n.toString(16));
|
||||
|
||||
it(`converts values with length ${i}`, (): void => {
|
||||
expect(
|
||||
u8aToBigInt(
|
||||
new Uint8Array(tu8a.slice(0, i)),
|
||||
{ isLe: false }
|
||||
).toString(16)
|
||||
).toBe(tstr.slice(0, i).join(''));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('empty creation', (): void => {
|
||||
@@ -262,9 +317,11 @@ describe('u8aToBigInt', (): void => {
|
||||
).toBe(256n);
|
||||
});
|
||||
|
||||
perf('u8aToBigInt (u32)', 1_000_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], u8aToBigInt);
|
||||
perf('u8aToBigInt (i32)', 1_000_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], (v: Uint8Array) => u8aToBigInt(v, { isNegative: true }));
|
||||
perf('u8aToBigInt (u64)', 500_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x68, 0x65, 0x6c, 0x6c])]], u8aToBigInt);
|
||||
perf('u8aToBigInt (i32)', 750_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], (v: Uint8Array) => u8aToBigInt(v, { isNegative: true }));
|
||||
|
||||
perf('u8aToBigInt (u32)', 750_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], u8aToBigInt);
|
||||
perf('u8aToBigInt (u64)', 750_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x68, 0x65, 0x6c, 0x6c])]], u8aToBigInt);
|
||||
perf('u8aToBigInt (u128)', 750_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x68, 0x65, 0x6c, 0x6c, 0x68, 0x65, 0x6c, 0x6c, 0x68, 0x65, 0x6c, 0x6c])]], u8aToBigInt);
|
||||
|
||||
// perf('BigInt (constructor)', 1_000_000, [[12345678]], (v: number) => BigInt(v).toString());
|
||||
// perf('BigInt (constructor -> string)', 1_000_000, [[12345678]], (v: number) => BigInt(v).toString());
|
||||
|
||||
@@ -9,27 +9,43 @@ import { _1n } from '../bi/consts.js';
|
||||
|
||||
const U8_MAX = BigInt(256);
|
||||
const U16_MAX = BigInt(256 * 256);
|
||||
const U64_MAX = BigInt('0x10000000000000000');
|
||||
|
||||
/**
|
||||
* @name u8aToBigInt
|
||||
* @summary Creates a BigInt from a Uint8Array object.
|
||||
*/
|
||||
export function u8aToBigInt (value: Uint8Array, { isLe = true, isNegative = false }: ToBnOptions = {}): bigint {
|
||||
if (!value || !value.length) {
|
||||
return BigInt(0);
|
||||
}
|
||||
|
||||
// BE is not the optimal path - LE is the SCALE default
|
||||
const u8a = isLe
|
||||
? value
|
||||
: value.reverse();
|
||||
const dvI = new DataView(u8a.buffer, u8a.byteOffset);
|
||||
: value.slice().reverse();
|
||||
const count = u8a.length;
|
||||
const mod = count % 2;
|
||||
let result = BigInt(0);
|
||||
|
||||
// This is mostly written for readability (with the single isNegative shortcut),
|
||||
// as opposed to performance, e.g. `u8aToBn` does loop unrolling, etc.
|
||||
if (isNegative) {
|
||||
switch (count) {
|
||||
case 0:
|
||||
return BigInt(0);
|
||||
|
||||
case 1:
|
||||
return BigInt(((u8a[0] ^ 0x0000_00ff) * -1) - 1);
|
||||
|
||||
case 2:
|
||||
return BigInt((((u8a[0] + (u8a[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);
|
||||
}
|
||||
|
||||
const dvI = new DataView(u8a.buffer, u8a.byteOffset);
|
||||
|
||||
if (count === 8) {
|
||||
return dvI.getBigInt64(0, true);
|
||||
}
|
||||
|
||||
let result = BigInt(0);
|
||||
const mod = count % 2;
|
||||
|
||||
for (let i = count - 2; i >= mod; i -= 2) {
|
||||
result = (result * U16_MAX) + BigInt(dvI.getUint16(i, true) ^ 0xffff);
|
||||
}
|
||||
@@ -37,17 +53,46 @@ export function u8aToBigInt (value: Uint8Array, { isLe = true, isNegative = fals
|
||||
if (mod) {
|
||||
result = (result * U8_MAX) + BigInt(u8a[0] ^ 0xff);
|
||||
}
|
||||
} else {
|
||||
for (let i = count - 2; i >= mod; i -= 2) {
|
||||
result = (result * U16_MAX) + BigInt(dvI.getUint16(i, true));
|
||||
}
|
||||
|
||||
if (mod) {
|
||||
result = (result * U8_MAX) + BigInt(u8a[0]);
|
||||
}
|
||||
return (result * -_1n) - _1n;
|
||||
}
|
||||
|
||||
return isNegative
|
||||
? ((result * -_1n) - _1n)
|
||||
: result;
|
||||
switch (count) {
|
||||
case 0:
|
||||
return BigInt(0);
|
||||
|
||||
case 1:
|
||||
return BigInt(u8a[0]);
|
||||
|
||||
case 2:
|
||||
return BigInt(u8a[0] + (u8a[1] << 8));
|
||||
|
||||
case 4:
|
||||
return BigInt(u8a[0] + (u8a[1] << 8) + (u8a[2] << 16) + (u8a[3] * 0x1_00_00_00));
|
||||
}
|
||||
|
||||
const dvI = new DataView(u8a.buffer, u8a.byteOffset);
|
||||
|
||||
switch (count) {
|
||||
case 8:
|
||||
return dvI.getBigUint64(0, true);
|
||||
|
||||
case 16:
|
||||
return (dvI.getBigUint64(8, true) * U64_MAX) + dvI.getBigUint64(0, true);
|
||||
|
||||
default: {
|
||||
let result = BigInt(0);
|
||||
const mod = count % 2;
|
||||
|
||||
for (let i = count - 2; i >= mod; i -= 2) {
|
||||
result = (result * U16_MAX) + BigInt(dvI.getUint16(i, true));
|
||||
}
|
||||
|
||||
if (mod) {
|
||||
result = (result * U8_MAX) + BigInt(u8a[0]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,29 @@ describe('u8aToBn', (): void => {
|
||||
).toString(16)
|
||||
).toBe('bc9a78563412');
|
||||
});
|
||||
|
||||
it('converts values (u128)', (): void => {
|
||||
expect(
|
||||
u8aToBn(
|
||||
new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78]),
|
||||
{ isLe: true }
|
||||
).toString(16)
|
||||
).toBe('78563412785634127856341278563412');
|
||||
});
|
||||
|
||||
for (let i = 1; i < 32; i++) {
|
||||
const tu8a = [0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78];
|
||||
const tstr = tu8a.map((n) => n.toString(16));
|
||||
|
||||
it(`converts values with length ${i}`, (): void => {
|
||||
expect(
|
||||
u8aToBn(
|
||||
new Uint8Array(tu8a.slice(0, i)),
|
||||
{ isLe: true }
|
||||
).toString(16)
|
||||
).toBe(tstr.slice(0, i).reverse().join(''));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('signed', (): void => {
|
||||
@@ -177,6 +200,15 @@ describe('u8aToBn', (): void => {
|
||||
).toBe('12345678');
|
||||
});
|
||||
|
||||
it('converts values (i32)', (): void => {
|
||||
expect(
|
||||
u8aToBn(
|
||||
new Uint8Array([0xf2, 0x34, 0x56, 0x78]),
|
||||
{ isLe: false, isNegative: true }
|
||||
).toString(16)
|
||||
).toBe('-dcba988');
|
||||
});
|
||||
|
||||
it('converts values (u40)', (): void => {
|
||||
expect(
|
||||
u8aToBn(
|
||||
@@ -194,6 +226,29 @@ describe('u8aToBn', (): void => {
|
||||
).toString(16)
|
||||
).toBe('123456789abc');
|
||||
});
|
||||
|
||||
it('converts values (u128)', (): void => {
|
||||
expect(
|
||||
u8aToBn(
|
||||
new Uint8Array([0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78]),
|
||||
{ isLe: false }
|
||||
).toString(16)
|
||||
).toBe('12345678123456781234567812345678');
|
||||
});
|
||||
|
||||
for (let i = 1; i < 32; i++) {
|
||||
const tu8a = [0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78];
|
||||
const tstr = tu8a.map((n) => n.toString(16));
|
||||
|
||||
it(`converts values with length ${i}`, (): void => {
|
||||
expect(
|
||||
u8aToBn(
|
||||
new Uint8Array(tu8a.slice(0, i)),
|
||||
{ isLe: false }
|
||||
).toString(16)
|
||||
).toBe(tstr.slice(0, i).join(''));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
describe('empty creation', (): void => {
|
||||
@@ -263,9 +318,11 @@ describe('u8aToBn', (): void => {
|
||||
).toBe(256);
|
||||
});
|
||||
|
||||
perf('u8aToBn (u32)', 1_000_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], u8aToBn);
|
||||
perf('u8aToBn (i32)', 1_000_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], (v: Uint8Array) => u8aToBn(v, { isNegative: true }));
|
||||
perf('u8aToBn (u64)', 500_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x68, 0x65, 0x6c, 0x6c])]], u8aToBn);
|
||||
perf('u8aToBn (i32)', 750_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], (v: Uint8Array) => u8aToBn(v, { isNegative: true }));
|
||||
|
||||
perf('u8aToBn (u32)', 750_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], u8aToBn);
|
||||
perf('u8aToBn (u64)', 750_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x68, 0x65, 0x6c, 0x6c])]], u8aToBn);
|
||||
perf('u8aToBn (u128)', 750_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c, 0x68, 0x65, 0x6c, 0x6c, 0x68, 0x65, 0x6c, 0x6c, 0x68, 0x65, 0x6c, 0x6c])]], u8aToBn);
|
||||
|
||||
// perf('BN (constructor)', 1_000_000, [[12345678]], (v: number) => new BN(v));
|
||||
// perf('BN (constructor -> string)', 1_000_000, [[12345678]], (v: number) => new BN(v).toString());
|
||||
|
||||
@@ -28,95 +28,100 @@ export function u8aToBn (value: Uint8Array, { isLe = true, isNegative = false }:
|
||||
|
||||
// shortcut for <= u48 values - in this case the manual conversion
|
||||
// here seems to be more efficient than passing the full array
|
||||
if (count <= 6) {
|
||||
if (isNegative) {
|
||||
let result = 0;
|
||||
|
||||
if (isLe) {
|
||||
// Most common case i{8, 16, 32} default LE SCALE-encoded
|
||||
// For <= 32, we also optimize the xor to a single op
|
||||
// (see the comments around unrolling in the next section)
|
||||
switch (count) {
|
||||
case 0:
|
||||
return new BN(0);
|
||||
|
||||
case 1:
|
||||
result = value[0] ^ 0x0000_00ff;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
result = (value[0] + (value[1] << 8)) ^ 0x0000_ffff;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
result = (value[0] + (value[1] << 8) + (value[2] << 16)) ^ 0x00ff_ffff;
|
||||
break;
|
||||
|
||||
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
|
||||
result = (value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)) ^ 0xffff_ffff;
|
||||
break;
|
||||
|
||||
case 5:
|
||||
result = ((value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)) ^ 0xffff_ffff) + ((value[4] ^ 0xff) * 0x1_00_00_00_00);
|
||||
break;
|
||||
|
||||
default: // 6
|
||||
result = ((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);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < count; i++) {
|
||||
result = (result * 0x1_00) + (value[i] ^ 0xff);
|
||||
}
|
||||
}
|
||||
|
||||
return count
|
||||
? new BN((result * -1) - 1)
|
||||
: new BN(0);
|
||||
} else if (isLe) {
|
||||
// Most common case - u{8, 16, 32} default LE SCALE-encoded
|
||||
//
|
||||
// There are some slight benefits in unrolling this specific loop,
|
||||
// however it comes with diminishing returns since here the actual
|
||||
// `new BN` does seem to take up the bulk of the time
|
||||
if (isNegative) {
|
||||
if (isLe) {
|
||||
// Most common case i{8, 16, 32} default LE SCALE-encoded
|
||||
// For <= 32, we also optimize the xor to a single op
|
||||
switch (count) {
|
||||
case 0:
|
||||
return new BN(0);
|
||||
|
||||
case 1:
|
||||
return new BN(value[0]);
|
||||
return new BN(((value[0] ^ 0x0000_00ff) * -1) - 1);
|
||||
|
||||
case 2:
|
||||
return new BN(value[0] + (value[1] << 8));
|
||||
return new BN((((value[0] + (value[1] << 8)) ^ 0x0000_ffff) * -1) - 1);
|
||||
|
||||
case 3:
|
||||
return new BN(value[0] + (value[1] << 8) + (value[2] << 16));
|
||||
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(value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00));
|
||||
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(value[0] + (value[1] << 8) + (value[2] << 16) + ((value[3] + (value[4] << 8)) * 0x1_00_00_00));
|
||||
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);
|
||||
|
||||
default: // 6
|
||||
return new BN(value[0] + (value[1] << 8) + (value[2] << 16) + ((value[3] + (value[4] << 8) + (value[5] << 16)) * 0x1_00_00_00));
|
||||
case 6:
|
||||
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(value, 'le').fromTwos(value.length * 8);
|
||||
}
|
||||
} else {
|
||||
let result = 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
result = (result * 0x1_00) + value[i];
|
||||
}
|
||||
if (count === 0) {
|
||||
return new BN(0);
|
||||
} else if (count > 6) {
|
||||
return new BN(value, 'be').fromTwos(value.length * 8);
|
||||
}
|
||||
|
||||
return new BN(result);
|
||||
let result = 0;
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
result = (result * 0x1_00) + (value[i] ^ 0xff);
|
||||
}
|
||||
|
||||
return new BN((result * -1) - 1);
|
||||
}
|
||||
|
||||
if (isLe) {
|
||||
// Most common case - u{8, 16, 32} default LE SCALE-encoded
|
||||
//
|
||||
// There are some slight benefits in unrolling this specific loop,
|
||||
// however it comes with diminishing returns since here the actual
|
||||
// `new BN` does seem to take up the bulk of the time
|
||||
switch (count) {
|
||||
case 0:
|
||||
return new BN(0);
|
||||
|
||||
case 1:
|
||||
return new BN(value[0]);
|
||||
|
||||
case 2:
|
||||
return new BN(value[0] + (value[1] << 8));
|
||||
|
||||
case 3:
|
||||
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(value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00));
|
||||
|
||||
case 5:
|
||||
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(value[0] + (value[1] << 8) + (value[2] << 16) + ((value[3] + (value[4] << 8) + (value[5] << 16)) * 0x1_00_00_00));
|
||||
|
||||
default:
|
||||
return new BN(value, 'le');
|
||||
}
|
||||
}
|
||||
|
||||
return isNegative
|
||||
? new BN(value, isLe ? 'le' : 'be').fromTwos(value.length * 8)
|
||||
: new BN(value, isLe ? 'le' : 'be');
|
||||
if (count === 0) {
|
||||
return new BN(0);
|
||||
} else if (count > 6) {
|
||||
return new BN(value, 'be');
|
||||
}
|
||||
|
||||
let result = 0;
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
result = (result * 0x1_00) + value[i];
|
||||
}
|
||||
|
||||
return new BN(result);
|
||||
}
|
||||
|
||||
@@ -132,6 +132,6 @@ describe('u8aToNumber', (): void => {
|
||||
});
|
||||
});
|
||||
|
||||
perf('u8aToNumber (u32)', 1_000_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], u8aToNumber);
|
||||
perf('u8aToNumber (i32)', 1_000_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], (v: Uint8Array) => u8aToNumber(v, { isNegative: true }));
|
||||
perf('u8aToNumber (i32)', 750_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], (v: Uint8Array) => u8aToNumber(v, { isNegative: true }));
|
||||
perf('u8aToNumber (u32)', 750_000, [[new Uint8Array([0x68, 0x65, 0x6c, 0x6c])]], u8aToNumber);
|
||||
});
|
||||
|
||||
@@ -10,49 +10,39 @@ interface ToNumberOptions {
|
||||
|
||||
/**
|
||||
* @name u8aToNumber
|
||||
* @summary Creates a number from a Uint8Array object.
|
||||
* @summary Creates a number from a Uint8Array object. This only operates on LE values as used in SCALE.
|
||||
*/
|
||||
export function u8aToNumber (value: Uint8Array, { isNegative = false }: ToNumberOptions = {}): number {
|
||||
const count = value.length;
|
||||
|
||||
if (isNegative) {
|
||||
let result = 0;
|
||||
|
||||
switch (count) {
|
||||
case 0:
|
||||
return 0;
|
||||
|
||||
case 1:
|
||||
result = value[0] ^ 0x0000_00ff;
|
||||
break;
|
||||
return (((value[0] ^ 0x0000_00ff) * -1) - 1);
|
||||
|
||||
case 2:
|
||||
result = (value[0] + (value[1] << 8)) ^ 0x0000_ffff;
|
||||
break;
|
||||
return ((((value[0] + (value[1] << 8)) ^ 0x0000_ffff) * -1) - 1);
|
||||
|
||||
case 3:
|
||||
result = (value[0] + (value[1] << 8) + (value[2] << 16)) ^ 0x00ff_ffff;
|
||||
break;
|
||||
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
|
||||
result = (value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)) ^ 0xffff_ffff;
|
||||
break;
|
||||
return ((((value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)) ^ 0xffff_ffff) * -1) - 1);
|
||||
|
||||
case 5:
|
||||
result = ((value[0] + (value[1] << 8) + (value[2] << 16) + (value[3] * 0x1_00_00_00)) ^ 0xffff_ffff) + ((value[4] ^ 0xff) * 0x1_00_00_00_00);
|
||||
break;
|
||||
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:
|
||||
result = ((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);
|
||||
break;
|
||||
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');
|
||||
}
|
||||
|
||||
return (result * -1) - 1;
|
||||
}
|
||||
|
||||
switch (count) {
|
||||
|
||||
Reference in New Issue
Block a user