From 89928fbd1651640c04c989ba595b29d295aa1e03 Mon Sep 17 00:00:00 2001 From: Alfredo Di Maria Date: Mon, 29 Nov 2021 11:56:34 +0100 Subject: [PATCH 01/10] React-native compatible big int deserialization --- src/readBig.ts | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/src/readBig.ts b/src/readBig.ts index 5ec754f..25cf07a 100644 --- a/src/readBig.ts +++ b/src/readBig.ts @@ -1,5 +1,3 @@ -import { Buffer } from 'buffer' - // https://github.com/nodejs/node/blob/v14.17.0/lib/internal/errors.js#L758 const ERR_BUFFER_OUT_OF_BOUNDS = () => new Error('Attempt to access memory outside buffer bounds') @@ -28,30 +26,12 @@ function boundsError(value: number, length: number) { throw ERR_OUT_OF_RANGE('offset', `>= 0 and <= ${length}`, value) } -// https://github.com/nodejs/node/blob/v14.17.0/lib/internal/buffer.js#L129-L145 +// This function works with react-native >= 0.66.1 export function readBigInt64LE(buffer: Buffer, offset = 0): bigint { - validateNumber(offset, 'offset') - const first = buffer[offset] - const last = buffer[offset + 7] - if (first === undefined || last === undefined) boundsError(offset, buffer.length - 8) - // tslint:disable-next-line:no-bitwise - const val = buffer[offset + 4] + buffer[offset + 5] * 2 ** 8 + buffer[offset + 6] * 2 ** 16 + (last << 24) // Overflow - return ( - (BigInt(val) << BigInt(32)) + // tslint:disable-line:no-bitwise - BigInt(first + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + buffer[++offset] * 2 ** 24) - ) + return BigInt(buffer.readIntLE(offset, 8)) } -// https://github.com/nodejs/node/blob/v14.17.0/lib/internal/buffer.js#L89-L107 +// This function works with react-native >= 0.66.1 export function readBigUInt64LE(buffer: Buffer, offset = 0): bigint { - validateNumber(offset, 'offset') - const first = buffer[offset] - const last = buffer[offset + 7] - if (first === undefined || last === undefined) boundsError(offset, buffer.length - 8) - - const lo = first + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + buffer[++offset] * 2 ** 24 - - const hi = buffer[++offset] + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + last * 2 ** 24 - - return BigInt(lo) + (BigInt(hi) << BigInt(32)) // tslint:disable-line:no-bitwise + return BigInt(buffer.readUIntLE(offset, 8)) } From 5377e05d01fc53abe0495cfc08d9e59cfd779bd9 Mon Sep 17 00:00:00 2001 From: Alfredo Di Maria Date: Mon, 29 Nov 2021 12:56:05 +0100 Subject: [PATCH 02/10] byte lenght bugfix --- src/readBig.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/readBig.ts b/src/readBig.ts index 25cf07a..d316616 100644 --- a/src/readBig.ts +++ b/src/readBig.ts @@ -28,10 +28,10 @@ function boundsError(value: number, length: number) { // This function works with react-native >= 0.66.1 export function readBigInt64LE(buffer: Buffer, offset = 0): bigint { - return BigInt(buffer.readIntLE(offset, 8)) + return BigInt(buffer.readIntLE(offset, 6)) } // This function works with react-native >= 0.66.1 export function readBigUInt64LE(buffer: Buffer, offset = 0): bigint { - return BigInt(buffer.readUIntLE(offset, 8)) + return BigInt(buffer.readUIntLE(offset, 6)) } From a0f706d4aec881b89e5436a5417a1458f073aace Mon Sep 17 00:00:00 2001 From: Alfredo Di Maria Date: Mon, 29 Nov 2021 13:12:27 +0100 Subject: [PATCH 03/10] Custom deserialization function for 8 bit buffers --- src/readBig.ts | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/src/readBig.ts b/src/readBig.ts index d316616..c1d1670 100644 --- a/src/readBig.ts +++ b/src/readBig.ts @@ -27,11 +27,40 @@ function boundsError(value: number, length: number) { } // This function works with react-native >= 0.66.1 -export function readBigInt64LE(buffer: Buffer, offset = 0): bigint { - return BigInt(buffer.readIntLE(offset, 6)) +export function readBigUInt64LE(buffer: Buffer, offset = 0): bigint { + buffer = buffer.slice(offset) + + const bufferLenght = Math.min(buffer.length, 8) + + let tot: number = 0 + const maxIndex = Math.log2(Number.MAX_VALUE) + for(let index = 0; index < bufferLenght; index++) { + const value = buffer[index] + const exponent = 8*index + + if(exponent > maxIndex) + throw new Error("out of range") + + const addend = value * Math.pow(2, exponent) + + tot += addend + + //console.log("index: " + index + ", buffer value: " + value + ", decimal value: " + addend + ", tot: " + tot) + } + + return BigInt(tot) } // This function works with react-native >= 0.66.1 -export function readBigUInt64LE(buffer: Buffer, offset = 0): bigint { - return BigInt(buffer.readUIntLE(offset, 6)) -} +export function readBigInt64LE(buffer: Buffer, offset = 0): bigint { + const resultUnsigned = readBigUInt64LE(buffer, offset) + + const FFFFFFFFFFFFFFFF = BigInt(2**64 - 1); + + if(buffer.length >= 8) { + if(buffer[7] >= 128) + return resultUnsigned - FFFFFFFFFFFFFFFF; + } + + return resultUnsigned +} \ No newline at end of file From 372cef9bd41bd1f5d03ed6f5ccc0caa19a974a0d Mon Sep 17 00:00:00 2001 From: Alfredo Di Maria Date: Mon, 29 Nov 2021 17:01:20 +0100 Subject: [PATCH 04/10] lint and comment fix --- src/readBig.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/readBig.ts b/src/readBig.ts index c1d1670..2e1d8ba 100644 --- a/src/readBig.ts +++ b/src/readBig.ts @@ -45,7 +45,7 @@ export function readBigUInt64LE(buffer: Buffer, offset = 0): bigint { tot += addend - //console.log("index: " + index + ", buffer value: " + value + ", decimal value: " + addend + ", tot: " + tot) + // console.log("index: " + index + ", buffer value: " + value + ", decimal value: " + addend + ", tot: " + tot) } return BigInt(tot) From 78b1e2b1ce26e3e987bf81b057994ef59fa1eb38 Mon Sep 17 00:00:00 2001 From: Alfredo Di Maria Date: Mon, 29 Nov 2021 17:35:19 +0100 Subject: [PATCH 05/10] number to bigint refactoring and small changes --- src/readBig.ts | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/readBig.ts b/src/readBig.ts index 2e1d8ba..7e9ef7d 100644 --- a/src/readBig.ts +++ b/src/readBig.ts @@ -30,25 +30,18 @@ function boundsError(value: number, length: number) { export function readBigUInt64LE(buffer: Buffer, offset = 0): bigint { buffer = buffer.slice(offset) - const bufferLenght = Math.min(buffer.length, 8) - - let tot: number = 0 - const maxIndex = Math.log2(Number.MAX_VALUE) - for(let index = 0; index < bufferLenght; index++) { + let tot: bigint = BigInt(0) + for(let index = 0; index < buffer.length; index++) { const value = buffer[index] const exponent = 8*index - if(exponent > maxIndex) - throw new Error("out of range") - - const addend = value * Math.pow(2, exponent) - + const addend = BigInt(value * Math.pow(2, exponent)) tot += addend // console.log("index: " + index + ", buffer value: " + value + ", decimal value: " + addend + ", tot: " + tot) } - return BigInt(tot) + return tot } // This function works with react-native >= 0.66.1 From 3e53b691e7d864edd56367d295b2d5b664b7006f Mon Sep 17 00:00:00 2001 From: Alfredo Di Maria Date: Mon, 29 Nov 2021 18:22:16 +0100 Subject: [PATCH 06/10] fix test failure: The number NaN cannot be converted to a BigInt --- src/readBig.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/readBig.ts b/src/readBig.ts index 7e9ef7d..f24e27a 100644 --- a/src/readBig.ts +++ b/src/readBig.ts @@ -33,6 +33,9 @@ export function readBigUInt64LE(buffer: Buffer, offset = 0): bigint { let tot: bigint = BigInt(0) for(let index = 0; index < buffer.length; index++) { const value = buffer[index] + if(value === NaN) + break + const exponent = 8*index const addend = BigInt(value * Math.pow(2, exponent)) From 0f9e287d1c90318b9cd086be354a86263207085f Mon Sep 17 00:00:00 2001 From: Alfredo Di Maria Date: Mon, 29 Nov 2021 18:31:02 +0100 Subject: [PATCH 07/10] Bugfix for long buffers --- src/readBig.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/readBig.ts b/src/readBig.ts index f24e27a..880bfa1 100644 --- a/src/readBig.ts +++ b/src/readBig.ts @@ -28,19 +28,15 @@ function boundsError(value: number, length: number) { // This function works with react-native >= 0.66.1 export function readBigUInt64LE(buffer: Buffer, offset = 0): bigint { - buffer = buffer.slice(offset) + // Some functions call this one with a buffer longer than 8 bytes + buffer = buffer.slice(offset, 8) let tot: bigint = BigInt(0) for(let index = 0; index < buffer.length; index++) { const value = buffer[index] - if(value === NaN) - break - const exponent = 8*index - const addend = BigInt(value * Math.pow(2, exponent)) tot += addend - // console.log("index: " + index + ", buffer value: " + value + ", decimal value: " + addend + ", tot: " + tot) } From a2dce1150ff40c6fee7c9f71d1adcaf96db0d12d Mon Sep 17 00:00:00 2001 From: Alfredo Di Maria Date: Mon, 29 Nov 2021 18:39:58 +0100 Subject: [PATCH 08/10] Minimal changes to work with react-native --- src/readBig.ts | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/src/readBig.ts b/src/readBig.ts index 880bfa1..39460cd 100644 --- a/src/readBig.ts +++ b/src/readBig.ts @@ -1,3 +1,5 @@ +import { Buffer } from 'buffer' + // https://github.com/nodejs/node/blob/v14.17.0/lib/internal/errors.js#L758 const ERR_BUFFER_OUT_OF_BOUNDS = () => new Error('Attempt to access memory outside buffer bounds') @@ -26,33 +28,30 @@ function boundsError(value: number, length: number) { throw ERR_OUT_OF_RANGE('offset', `>= 0 and <= ${length}`, value) } -// This function works with react-native >= 0.66.1 -export function readBigUInt64LE(buffer: Buffer, offset = 0): bigint { - // Some functions call this one with a buffer longer than 8 bytes - buffer = buffer.slice(offset, 8) - - let tot: bigint = BigInt(0) - for(let index = 0; index < buffer.length; index++) { - const value = buffer[index] - const exponent = 8*index - const addend = BigInt(value * Math.pow(2, exponent)) - tot += addend - // console.log("index: " + index + ", buffer value: " + value + ", decimal value: " + addend + ", tot: " + tot) - } - - return tot +// https://github.com/nodejs/node/blob/v14.17.0/lib/internal/buffer.js#L129-L145 +export function readBigInt64LE(buffer: Buffer, offset = 0): bigint { + validateNumber(offset, 'offset') + const first = buffer[offset] + const last = buffer[offset + 7] + if (first === undefined || last === undefined) boundsError(offset, buffer.length - 8) + // tslint:disable-next-line:no-bitwise + const val = buffer[offset + 4] + buffer[offset + 5] * 2 ** 8 + buffer[offset + 6] * 2 ** 16 + (last * 2 ** 24) // Overflow + return ( + (BigInt(val) * BigInt(2 ** 32)) + // tslint:disable-line:no-bitwise + BigInt(first + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + buffer[++offset] * 2 ** 24) + ) } -// This function works with react-native >= 0.66.1 -export function readBigInt64LE(buffer: Buffer, offset = 0): bigint { - const resultUnsigned = readBigUInt64LE(buffer, offset) +// https://github.com/nodejs/node/blob/v14.17.0/lib/internal/buffer.js#L89-L107 +export function readBigUInt64LE(buffer: Buffer, offset = 0): bigint { + validateNumber(offset, 'offset') + const first = buffer[offset] + const last = buffer[offset + 7] + if (first === undefined || last === undefined) boundsError(offset, buffer.length - 8) - const FFFFFFFFFFFFFFFF = BigInt(2**64 - 1); + const lo = first + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + buffer[++offset] * 2 ** 24 - if(buffer.length >= 8) { - if(buffer[7] >= 128) - return resultUnsigned - FFFFFFFFFFFFFFFF; - } + const hi = buffer[++offset] + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + last * 2 ** 24 - return resultUnsigned -} \ No newline at end of file + return BigInt(lo) + (BigInt(hi) * BigInt(2 ** 32)) // tslint:disable-line:no-bitwise +} From e7374a8f007f23e49162f22757ca11d743bec2d8 Mon Sep 17 00:00:00 2001 From: Alfredo Di Maria Date: Mon, 29 Nov 2021 19:44:32 +0100 Subject: [PATCH 09/10] Fix signed numbers without << operator --- src/readBig.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/readBig.ts b/src/readBig.ts index 39460cd..7e4b01a 100644 --- a/src/readBig.ts +++ b/src/readBig.ts @@ -30,16 +30,16 @@ function boundsError(value: number, length: number) { // https://github.com/nodejs/node/blob/v14.17.0/lib/internal/buffer.js#L129-L145 export function readBigInt64LE(buffer: Buffer, offset = 0): bigint { - validateNumber(offset, 'offset') - const first = buffer[offset] - const last = buffer[offset + 7] - if (first === undefined || last === undefined) boundsError(offset, buffer.length - 8) - // tslint:disable-next-line:no-bitwise - const val = buffer[offset + 4] + buffer[offset + 5] * 2 ** 8 + buffer[offset + 6] * 2 ** 16 + (last * 2 ** 24) // Overflow - return ( - (BigInt(val) * BigInt(2 ** 32)) + // tslint:disable-line:no-bitwise - BigInt(first + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + buffer[++offset] * 2 ** 24) - ) + const resultUnsigned = readBigUInt64LE(buffer, offset) + + const FFFFFFFFFFFFFFFF = BigInt(2**64 - 1); + + if(buffer.length >= 8) { + if(buffer[7] >= 128) + return resultUnsigned - FFFFFFFFFFFFFFFF; + } + + return resultUnsigned } // https://github.com/nodejs/node/blob/v14.17.0/lib/internal/buffer.js#L89-L107 From 1eba710aa096a41c25675290fd71f474bbe62469 Mon Sep 17 00:00:00 2001 From: Alfredo Di Maria Date: Thu, 9 Dec 2021 08:04:01 +0100 Subject: [PATCH 10/10] Problem with biginteger and BigInteger --- package-lock.json | 34 ++++++++++++++++++++++++++++++++++ package.json | 2 ++ src/readBig.ts | 21 ++++++++++----------- tsconfig.json | 1 + 4 files changed, 47 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3d6c4c7..6f250d4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,9 +11,11 @@ "dependencies": { "@solana/web3.js": "^1.30.2", "assert": "^2.0.0", + "big-integer": "^1.6.51", "buffer": "^6.0.1" }, "devDependencies": { + "@types/big-integer": "^0.0.31", "@types/jest": "^26.0.23", "jest": "^27.3.1", "prettier": "^2.3.0", @@ -1264,6 +1266,16 @@ "@babel/types": "^7.3.0" } }, + "node_modules/@types/big-integer": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/@types/big-integer/-/big-integer-0.0.31.tgz", + "integrity": "sha512-nYrYenHwC07vTBXoQ8jUUi6sednNYHGQxh0ecvfWm46n3djgxxbe7AZIJVaGjzQaEQVEcH6KmB6VMt//vAP0AA==", + "deprecated": "This is a stub types definition for BigInteger.js (https://github.com/peterolson/BigInteger.js). BigInteger.js provides its own type definitions, so you don't need @types/big-integer installed!", + "dev": true, + "dependencies": { + "big-integer": "*" + } + }, "node_modules/@types/bn.js": { "version": "4.11.6", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", @@ -1696,6 +1708,14 @@ } ] }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/bn.js": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", @@ -7336,6 +7356,15 @@ "@babel/types": "^7.3.0" } }, + "@types/big-integer": { + "version": "0.0.31", + "resolved": "https://registry.npmjs.org/@types/big-integer/-/big-integer-0.0.31.tgz", + "integrity": "sha512-nYrYenHwC07vTBXoQ8jUUi6sednNYHGQxh0ecvfWm46n3djgxxbe7AZIJVaGjzQaEQVEcH6KmB6VMt//vAP0AA==", + "dev": true, + "requires": { + "big-integer": "*" + } + }, "@types/bn.js": { "version": "4.11.6", "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", @@ -7691,6 +7720,11 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" + }, "bn.js": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", diff --git a/package.json b/package.json index 4c36fd5..4acd6d6 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ ], "license": "Apache-2.0", "devDependencies": { + "@types/big-integer": "^0.0.31", "@types/jest": "^26.0.23", "jest": "^27.3.1", "prettier": "^2.3.0", @@ -39,6 +40,7 @@ "dependencies": { "@solana/web3.js": "^1.30.2", "assert": "^2.0.0", + "big-integer": "^1.6.51", "buffer": "^6.0.1" } } diff --git a/src/readBig.ts b/src/readBig.ts index 7e4b01a..581a9b6 100644 --- a/src/readBig.ts +++ b/src/readBig.ts @@ -1,4 +1,5 @@ import { Buffer } from 'buffer' +import BigInt from 'big-integer' // https://github.com/nodejs/node/blob/v14.17.0/lib/internal/errors.js#L758 const ERR_BUFFER_OUT_OF_BOUNDS = () => new Error('Attempt to access memory outside buffer bounds') @@ -30,16 +31,14 @@ function boundsError(value: number, length: number) { // https://github.com/nodejs/node/blob/v14.17.0/lib/internal/buffer.js#L129-L145 export function readBigInt64LE(buffer: Buffer, offset = 0): bigint { - const resultUnsigned = readBigUInt64LE(buffer, offset) - - const FFFFFFFFFFFFFFFF = BigInt(2**64 - 1); - - if(buffer.length >= 8) { - if(buffer[7] >= 128) - return resultUnsigned - FFFFFFFFFFFFFFFF; - } - - return resultUnsigned + validateNumber(offset, 'offset') + const first = buffer[offset] + const last = buffer[offset + 7] + if (first === undefined || last === undefined) boundsError(offset, buffer.length - 8) + // tslint:disable-next-line:no-bitwise + const val = buffer[offset + 4] + buffer[offset + 5] * 2 ** 8 + buffer[offset + 6] * 2 ** 16 + (last << 24) // Overflow + const result = BigInt(val).shiftLeft(32).add(BigInt(first + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + buffer[++offset] * 2 ** 24)) + return result; } // https://github.com/nodejs/node/blob/v14.17.0/lib/internal/buffer.js#L89-L107 @@ -53,5 +52,5 @@ export function readBigUInt64LE(buffer: Buffer, offset = 0): bigint { const hi = buffer[++offset] + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + last * 2 ** 24 - return BigInt(lo) + (BigInt(hi) * BigInt(2 ** 32)) // tslint:disable-line:no-bitwise + return BigInt(hi).shiftLeft(32).add(lo) // tslint:disable-line:no-bitwise } diff --git a/tsconfig.json b/tsconfig.json index e305280..e5b2280 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "module": "commonjs", "declaration": true, "outDir": "./lib", + "esModuleInterop": true, "strict": true }, "include": ["src"],