Skip to content
57 changes: 33 additions & 24 deletions src/readBig.ts
Original file line number Diff line number Diff line change
@@ -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')

Expand Down Expand Up @@ -28,30 +26,41 @@ 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
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)
)
}

// 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)
buffer = buffer.slice(offset)

const bufferLenght = Math.min(buffer.length, 8)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo in the name. also, i believe this code expects buffer to have 8 bytes storing the number, so you can get rid of the min here and also the Number.MAX_VALUE / maxIndex check below.


let tot: number = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this code loses precision -- this is a floating point number, which has fewer bits of precision than a 64 bit integer (since some of its 64 bits are allocated to the exponent). I think if you make this a BigInt right off the bat, it should be fine.

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 lo = first + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + buffer[++offset] * 2 ** 24
const addend = value * Math.pow(2, exponent)

tot += addend

const hi = buffer[++offset] + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + last * 2 ** 24
// console.log("index: " + index + ", buffer value: " + value + ", decimal value: " + addend + ", tot: " + tot)
}

return BigInt(lo) + (BigInt(hi) << BigInt(32)) // tslint:disable-line:no-bitwise
return BigInt(tot)
}

// This function works with react-native >= 0.66.1
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
}