/**
 * This implementation is largely taken a combination of two exiting libraries:
 * 1. node-int64
 * 2. apache thrift
 *
 * This takes elements of those two projects and re-implements the logic in TypeScript.
 * In regard to node-int64 we also remove the use of `new Buffer()` in favor of `Buffer.alloc()`.
 *
 * The original source can be found here:
 * https://github.com/broofa/node-int64
 * https://github.com/apache/thrift/blob/master/lib/nodejs/lib/thrift/int64_util.js
 *
 * Note that bit operators convert values to 32-bit integers.
 * Other than the >>> operators, the result is a signed 32-bit integer.
 * Operating on 64-bit values requires using arithmatic operators instead.
 */ const POW2_31 = Math . pow ( 2 , 31 ) ; const POW2_32 = Math . pow ( 2 , 32 ) ; const POW10_11 = Math . pow ( 10 , 11 ) ; /**
 * Support for handling 64-bit int numbers in Javascript (node.js)
 *
 * JS Numbers are IEEE-754 binary double-precision floats, which limits the
 * range of values that can be represented with integer precision to:
 *
 * 2^^53 <= N <= 2^53
 *
 * Int64 objects wrap a node Buffer that holds the 8-bytes of int64 data.  These
 * objects operate directly on the buffer which means that if they are created
 * using an existing buffer then setting the value will modify the Buffer, and
 * vice-versa.
 *
 * Internal Representation
 *
 * The internal buffer format is Big Endian.  I.e. the most-significant byte is
 * at buffer[0], the least-significant at buffer[7].  For the purposes of
 * converting to/from JS native numbers, the value is assumed to be a signed
 * integer stored in 2's complement form.
 *
 * For details about IEEE-754 see:
 * http://en.wikipedia.org/wiki/Double_precision_floating-point_format
 */ const _HEX = [ ] ; for ( let i = 0 ; i < 256 ; i ++ ) { _HEX [ i ] = ( i > 0xf ? '' : '0' ) + i . toString ( 16 ) ; } const HEX_REGEX = /^(?:0x)?([0-9a-fA-F]+)/ ; const BYTE_COUNT = 8 ; /**
 * 64-bit integer value as high and low 32-bit integers.
 *
 * This type just indicates the expectation of 32-bit integer values.
 * The number range is not enforced for performance.
 * The behavior is not defined if the values do not match expectations.
 *
 * The high and low values may be signed or unsigned.
 */ interface IHiLo { /** High 32 bits. */ hi : number ; /** Low 32 bits. */ lo : number ; } /**
 * 64-bit integer value as high and low signed 32-bit integers.
 *
 * This type just indicates the expectation of signed values.
 * The number range is not enforced for performance.
 * The behavior is not defined if the values do not match expectations.
 *
 * It would be possible to define distinct classes with validation for safety if this were to be made public but
 * for now it is limited to this module.
 */ interface IHiLoSigned extends IHiLo { /** Additional field for type checking so that IHiLoSigned is not the same as IHiLo. */ signed : true ; /** High signed 32 bits. */ hi : number ; /** Low signed 32 bits. */ lo : number ; } /**
 * 64-bit integer value as high and low unsigned 32-bit integers and a negative flag.
 *
 * This type just indicates the expectation of unsigned values.
 * The number range is not enforced for performance.
 * The behavior is not defined if the values do not match expectations.
 *
 * It would be possible to define distinct classes with validation for safety if this were to be made public but
 * for now it is limited to this module.
 */ interface IHiLoUnsigned extends IHiLo { /** True if the value is negative */ negative : boolean ; /** High signed 32 bits. */ hi : number ; /** Low signed 32 bits. */ lo : number ; } param i64 function hiLoNeg ( { hi , lo } ) { const nlo = ~ lo + 1 ; const carry = nlo === 0 ? 1 : 0 ; const nhi = ~ hi + carry ; return { hi : nhi , lo : nlo , } ; } param i64 function hiLoSignedToUnsigned ( i64 ) { const negative = i64 . hi < 0 ; const { hi , lo } = negative ? hiLoNeg ( i64 ) : i64 ; return { negative , hi : hi >>> 0 , lo : lo >>> 0 , } ; } see param hi param lo function hiLoSignedToNumber ( { hi , lo } ) { const carry = lo < 0 ? 1 : 0 ; return ( hi + carry ) * POW2_32 + lo ; } param i64 function hiLoSignedFromNumber ( i64 ) { i64 = Math . trunc ( i64 ) ; const lo = i64 >> 0 ; const carry = lo < 0 ? 1 : 0 ; const hi = ( i64 - lo ) / POW2_32 - carry ; return { signed : true , hi , lo } ; } param source param offset function prepBuffer ( source , offset ) { if ( offset === 0 && source . length === BYTE_COUNT ) { return source ; } const end = offset >= 0 ? offset + BYTE_COUNT : Math . max ( source . length + offset + BYTE_COUNT , 0 ) ; const slice = source . slice ( offset , end ) ; if ( slice . length > BYTE_COUNT ) { return slice . slice ( 0 , BYTE_COUNT ) ; } if ( slice . length === BYTE_COUNT ) { return slice ; } const out = Buffer . alloc ( BYTE_COUNT ) ; slice . copy ( out , BYTE_COUNT - slice . length ) ; return out ; } /**
 * Interface for a serializable 64-bit integer.
 *
 * The Int64 class implements a lot more functionality for using the 64-bit value.
 * This interface is for the types that can be written by TProtocol.
 */ export interface IInt64 { /**
     * 8-byte big-endian (network order) Buffer.
     */ readonly buffer : Buffer ; /**
     * Returns the value as a signed decimal integer.
     */ toDecimalString ( ) : string ; } /**
 * Constructor accepts any of the following argument types:
 *
 * new Int64(buffer[, offset=0]) - Existing Buffer with byte offset
 * new Int64(Uint8Array[, offset=0]) - Existing Uint8Array with a byte offset
 * new Int64(string)             - Hex string (throws if n is outside int64 range)
 * new Int64(number)             - Number (throws if n is outside int64 range)
 * new Int64(hi, lo)             - Raw bits as two 32-bit values
 */ export class Int64 implements IInt64 { public static MAX_INT : number = Math . pow ( 2 , 53 ) ; public static MIN_INT : number = - Math . pow ( 2 , 53 ) ; public static toDecimalString ( i64 ) { if ( typeof i64 === 'number' ) { return `${ i64 }` ; } else { return i64 . toDecimalString ( ) ; } } public static fromDecimalString ( text ) { const negative = text . charAt ( 0 ) === '-' ; if ( text . length < ( negative ? 17 : 16 ) ) { return new Int64 ( + text ) ; } else if ( text . length > ( negative ? 20 : 19 ) ) { throw new RangeError ( `Too many digits for Int64: ${ text }` ) ; } else { const high5 = + text . slice ( negative ? 1 : 0 , - 15 ) ; const remainder = + text . slice ( - 15 ) + high5 * 2764472320 ; const hi = Math . floor ( remainder / POW2_32 ) + high5 * 232830 ; const lo = remainder % POW2_32 ; if ( hi >= POW2_31 && ! ( negative && hi === POW2_31 && lo === 0 ) ) { throw new RangeError ( 'The magnitude is too large for Int64.' ) ; } if ( negative ) { const neg = hiLoNeg ( { hi , lo } ) ; return new Int64 ( neg . hi , neg . lo ) ; } return new Int64 ( hi , lo ) ; } } inheritDoc public readonly buffer : Buffer ; constructor ( buf , offset ? ) ; constructor ( str ) ; constructor ( ... args ) { if ( args [ 0 ] instanceof Buffer ) { const source = args [ 0 ] ; const offset = args [ 1 ] || 0 ; this . buffer = prepBuffer ( source , offset ) ; } else if ( Object . prototype . toString . call ( args [ 0 ] ) === '[object Uint8Array]' ) { const source = Buffer . from ( args [ 0 ] ) ; const offset = args [ 1 ] || 0 ; this . buffer = prepBuffer ( source , offset ) ; } else { this . buffer = Buffer . alloc ( BYTE_COUNT ) ; this . setValue ( args [ 0 ] , args [ 1 ] ) ; } } /**
     * Set the value. Takes any of the following arguments:
     *
     * setValue(string) - A hexidecimal string
     * setValue(number) - Number (throws if n is outside int64 range)
     * setValue(hi, lo) - Raw bits as two 32-bit values
     */ public setValue ( str ) ; public setValue ( hi , lo ? ) ; public setValue ( hi , lo ? ) { if ( lo === undefined ) { if ( typeof hi === 'number' ) { this . setNumber ( hi ) ; return ; } if ( typeof hi === 'string' ) { this . setHexString ( hi ) ; return ; } throw new Error ( hi + ' must be a Number or String' ) ; } if ( typeof hi !== 'number' || typeof lo !== 'number' ) { throw new Error ( `${ hi } and ${ lo } must be Numbers` ) ; } this . setHiLo ( { hi , lo } ) ; } inheritDoc public toDecimalString ( ) { const i64 = this . read ( ) ; const value = hiLoSignedToNumber ( i64 ) ; if ( Number . isSafeInteger ( value ) ) { return value . toString ( ) ; } const { negative , hi , lo } = hiLoSignedToUnsigned ( i64 ) ; const high2 = hi >>> 16 ; const low = lo + ( hi & 0x0000ffff ) * POW2_32 + high2 * 74976710656 ; const high = Math . floor ( low / POW10_11 ) + high2 * 2814 ; const lowStr = ( '00000000000' + String ( low % POW10_11 ) ) . slice ( - 11 ) ; return ( negative ? '-' : '' ) + String ( high ) + lowStr ; } param allowImprecise public toNumber ( allowImprecise = true ) { const x = hiLoSignedToNumber ( this . read ( ) ) ; if ( ! allowImprecise && ! Number . isSafeInteger ( x ) ) { return x < 0 ? Number . NEGATIVE_INFINITY : Number . POSITIVE_INFINITY ; } return x ; } /**
     * Convert to a JS Number. Returns +/-Infinity for values that can't be
     * represented to integer precision.
     */ public valueOf ( ) { return this . toNumber ( false ) ; } param radix public toString ( radix = 10 ) { return this . valueOf ( ) . toString ( radix ) ; } param sep public toOctetString ( sep = '' ) { if ( ! sep ) { return this . buffer . toString ( 'hex' ) ; } return Array . from ( this . buffer , ( num ) => _HEX [ num ] ) . join ( sep ) ; } param { boolean } rawBuffer public toBuffer ( rawBuffer = false ) { if ( rawBuffer ) { return this . buffer ; } const out = Buffer . alloc ( BYTE_COUNT ) ; this . buffer . copy ( out ) ; return out ; } param { Buffer } targetBuffer param { number } targetOffset public copy ( targetBuffer , targetOffset = 0 ) { this . buffer . copy ( targetBuffer , targetOffset ) ; } param { Int64 } other public compare ( other ) { if ( ( this . buffer [ 0 ] & 0x80 ) !== ( other . buffer [ 0 ] & 0x80 ) ) { return other . buffer [ 0 ] - this . buffer [ 0 ] ; } return this . buffer . compare ( other . buffer ) ; } param { Int64 } other public equals ( other ) { return this . compare ( other ) === 0 ; } /**
     * Pretty output for console.log
     */ public inspect ( ) { return ( '[Int64 value:' + this + ' octets:' + this . toOctetString ( ' ' ) + ']' ) ; } param i64 private setNumber ( i64 ) { if ( ! Number . isFinite ( i64 ) ) { throw new RangeError ( `Value is not finite: ${ i64 }` ) ; } const { hi , lo } = hiLoSignedFromNumber ( i64 ) ; try { this . buffer . writeInt32BE ( hi , 0 ) ; this . buffer . writeInt32BE ( lo , 4 ) ; } catch { throw new RangeError ( `Value is outside the Int64 range: ${ i64 }` ) ; } } param i64 private setHiLo ( i64 ) { const { hi , lo } = i64 ; if ( ! Number . isInteger ( hi ) ) { throw new TypeError ( `Hi is not an integer: ${ hi }` ) ; } if ( ! Number . isInteger ( lo ) ) { throw new TypeError ( `Lo is not an integer: ${ lo }` ) ; } try { if ( hi < 0 ) { this . buffer . writeInt32BE ( hi , 0 ) ; } else { this . buffer . writeUInt32BE ( hi , 0 ) ; } } catch { throw new RangeError ( `Hi is outside the Int64 range: ${ hi }` ) ; } try { if ( lo < 0 ) { this . buffer . writeInt32BE ( lo , 4 ) ; } else { this . buffer . writeUInt32BE ( lo , 4 ) ; } } catch { throw new RangeError ( `Lo is outside the Int64 range: ${ lo }` ) ; } } param source private setHexString ( source ) { const matches = source . match ( HEX_REGEX ) ; const match = ( matches && matches [ 1 ] ) || '' ; const hex = ( match . length % 2 === 0 ? '' : '0' ) + match ; const length = Buffer . byteLength ( hex , 'hex' ) ; if ( length > BYTE_COUNT ) { throw new RangeError ( source + ' is outside Int64 range' ) ; } const offset = BYTE_COUNT - length ; this . buffer . fill ( 0 , 0 , offset ) ; this . buffer . write ( hex , offset , length , 'hex' ) ; } /**
     * Get the high and low signed 32-bit values.
     */ private read ( ) { return { signed : true , hi : this . buffer . readInt32BE ( 0 ) , lo : this . buffer . readInt32BE ( 4 ) , } ; } } param i64 export function isInt64 ( i64 ) { return ( i64 instanceof Int64 || ( typeof i64 === 'object' && i64 . buffer instanceof Buffer && i64 . buffer . length === BYTE_COUNT && typeof i64 . toDecimalString === 'function' ) ) ; }