blob: 9f084f517251c5ff3aa4e6ef3a97d14bcb54efa3 [file] [log] [blame] [edit]
/**
* @fileoverview This file contains helper code used by jspb.utils to
* handle 64-bit integer conversion to/from strings.
*
* @author [email protected] (Chris Fallin)
*
* TODO(haberman): move this to javascript/closure/math?
*/
goog.module('jspb.arith');
goog.module.declareLegacyNamespace();
const {getSplit64High, getSplit64Low, joinSignedDecimalString, joinUnsignedDecimalString, splitDecimalString} = goog.require('jspb.utils');
/**
* UInt64 implements some 64-bit arithmetic routines necessary for properly
* handling 64-bit integer fields from protobuf fields with decimal string
* conversions.
* @final
*/
class UInt64 {
/**
* @param {number} lo The low 32 bits.
* @param {number} hi The high 32 bits.
*/
constructor(lo, hi) {
/**
* The low 32 bits, always stored as uint32.
* @public @const {number}
*/
this.lo = lo >>> 0;
/**
* The high 32 bits, always stored as uint32.
* @public @const {number}
*/
this.hi = hi >>> 0;
}
/**
* Convert a 64-bit number to a string.
* @return {string}
*/
toDecimalString() {
return joinUnsignedDecimalString(this.lo, this.hi);
}
/**
* Negates this uint64 in twos-complement.
*
* @return {!UInt64}
*/
negateInTwosComplement() {
if (this.lo === 0) {
return new UInt64(0, 1 + ~this.hi);
}
return new UInt64(~this.lo + 1, ~this.hi);
}
/**
* Construct a Uint64 from a bigint
*
* @param {bigint} n
* @return {!UInt64}
*/
static fromBigInt(n) {
const asU64 = BigInt.asUintN(64, n);
return new UInt64(
/* lowBits = */ Number((asU64 & BigInt(0xFFFFFFFF))),
/* highBits = */ Number(asU64 >> BigInt(32)));
}
/**
* Parse a string into a 64-bit number. Returns `null` on a parse error.
*
* @param {string} s
* @return {?UInt64}
*/
static fromString(s) {
if (!s) return UInt64.getZero();
if (!/^\d+$/.test(s)) {
// TODO(web-protos-team): Throw an error rather than returning null.
return null;
}
splitDecimalString(s);
return new UInt64(getSplit64Low(), getSplit64High());
}
/**
* Construct a Uint64 from a JavaScript number.
* @param {number} n
* @return {!UInt64}
*/
static fromNumber(n) {
return new UInt64(n & ALL_32_BITS, n / TWO_PWR_32_DBL);
}
static getZero() {
return uint64Zero || (uint64Zero = new UInt64(0, 0));
}
}
let /** !UInt64|undefined */ uint64Zero;
/**
* Int64 is like UInt64, but modifies string conversions to interpret the stored
* 64-bit value as a twos-complement-signed integer with decimal string
* conversions.
* @final
*/
class Int64 {
/**
* @param {number} lo The low 32 bits.
* @param {number} hi The high 32 bits.
*/
constructor(lo, hi) {
/**
* The low 32 bits, always stored as uint32.
* @public @const {number}
*/
this.lo = lo >>> 0;
/**
* The high 32 bits, always stored as uint32.
* @public @const {number}
*/
this.hi = hi >>> 0;
}
/**
* Convert a 64-bit number to a string.
* @return {string}
*/
toDecimalString() {
return joinSignedDecimalString(this.lo, this.hi);
}
/**
* Construct a Int64 from a bigint
*
* @param {bigint} n
* @return {!Int64}
*/
static fromBigInt(n) {
const asUint64 = BigInt.asUintN(64, n);
return new Int64(
/* lowBits = */ Number((asUint64 & BigInt(0xFFFFFFFF))),
/* highBits = */ Number(asUint64 >> BigInt(32)));
}
/**
* Parse a string into a 64-bit number. Returns `null` on a parse error.
* @param {string} s
* @return {?Int64}
*/
static fromString(s) {
if (!s) return Int64.getZero();
if (!/^-?\d+$/.test(s)) {
// TODO: Throw an error rather than returning null.
return null;
}
splitDecimalString(s);
return new Int64(getSplit64Low(), getSplit64High());
}
/**
* Construct a Uint64 from a JavaScript number.
* @param {number} n
* @return {!Int64}
*/
static fromNumber(n) {
return new Int64(n & ALL_32_BITS, n / TWO_PWR_32_DBL);
}
static getZero() {
return int64Zero || (int64Zero = new Int64(0, 0));
}
}
let /** !Int64|undefined */ int64Zero;
/** @const {number} */
const ALL_32_BITS = 0xFFFFFFFF;
/** @const {number} */
const TWO_PWR_32_DBL = 0x100000000;
exports = {
UInt64,
Int64,
};