123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268 |
- // Int64.js
- //
- // Copyright (c) 2012 Robert Kieffer
- // MIT License - http://opensource.org/licenses/mit-license.php
- /**
- * 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
- */
- // Useful masks and values for bit twiddling
- var MASK31 = 0x7fffffff, VAL31 = 0x80000000;
- var MASK32 = 0xffffffff, VAL32 = 0x100000000;
- // Map for converting hex octets to strings
- var _HEX = [];
- for (var i = 0; i < 256; i++) {
- _HEX[i] = (i > 0xF ? '' : '0') + i.toString(16);
- }
- //
- // Int64
- //
- /**
- * 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
- */
- var Int64 = module.exports = function(a1, a2) {
- if (a1 instanceof Buffer) {
- this.buffer = a1;
- this.offset = a2 || 0;
- } else if (Object.prototype.toString.call(a1) == '[object Uint8Array]') {
- // Under Browserify, Buffers can extend Uint8Arrays rather than an
- // instance of Buffer. We could assume the passed in Uint8Array is actually
- // a buffer but that won't handle the case where a raw Uint8Array is passed
- // in. We construct a new Buffer just in case.
- this.buffer = new Buffer(a1);
- this.offset = a2 || 0;
- } else {
- this.buffer = this.buffer || new Buffer(8);
- this.offset = 0;
- this.setValue.apply(this, arguments);
- }
- };
- // Max integer value that JS can accurately represent
- Int64.MAX_INT = Math.pow(2, 53);
- // Min integer value that JS can accurately represent
- Int64.MIN_INT = -Math.pow(2, 53);
- Int64.prototype = {
- constructor: Int64,
- /**
- * Do in-place 2's compliment. See
- * http://en.wikipedia.org/wiki/Two's_complement
- */
- _2scomp: function() {
- var b = this.buffer, o = this.offset, carry = 1;
- for (var i = o + 7; i >= o; i--) {
- var v = (b[i] ^ 0xff) + carry;
- b[i] = v & 0xff;
- carry = v >> 8;
- }
- },
- /**
- * 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
- */
- setValue: function(hi, lo) {
- var negate = false;
- if (arguments.length == 1) {
- if (typeof(hi) == 'number') {
- // Simplify bitfield retrieval by using abs() value. We restore sign
- // later
- negate = hi < 0;
- hi = Math.abs(hi);
- lo = hi % VAL32;
- hi = hi / VAL32;
- if (hi > VAL32) throw new RangeError(hi + ' is outside Int64 range');
- hi = hi | 0;
- } else if (typeof(hi) == 'string') {
- hi = (hi + '').replace(/^0x/, '');
- lo = hi.substr(-8);
- hi = hi.length > 8 ? hi.substr(0, hi.length - 8) : '';
- hi = parseInt(hi, 16);
- lo = parseInt(lo, 16);
- } else {
- throw new Error(hi + ' must be a Number or String');
- }
- }
- // Technically we should throw if hi or lo is outside int32 range here, but
- // it's not worth the effort. Anything past the 32'nd bit is ignored.
- // Copy bytes to buffer
- var b = this.buffer, o = this.offset;
- for (var i = 7; i >= 0; i--) {
- b[o+i] = lo & 0xff;
- lo = i == 4 ? hi : lo >>> 8;
- }
- // Restore sign of passed argument
- if (negate) this._2scomp();
- },
- /**
- * Convert to a native JS number.
- *
- * WARNING: Do not expect this value to be accurate to integer precision for
- * large (positive or negative) numbers!
- *
- * @param allowImprecise If true, no check is performed to verify the
- * returned value is accurate to integer precision. If false, imprecise
- * numbers (very large positive or negative numbers) will be forced to +/-
- * Infinity.
- */
- toNumber: function(allowImprecise) {
- var b = this.buffer, o = this.offset;
- // Running sum of octets, doing a 2's complement
- var negate = b[o] & 0x80, x = 0, carry = 1;
- for (var i = 7, m = 1; i >= 0; i--, m *= 256) {
- var v = b[o+i];
- // 2's complement for negative numbers
- if (negate) {
- v = (v ^ 0xff) + carry;
- carry = v >> 8;
- v = v & 0xff;
- }
- x += v * m;
- }
- // Return Infinity if we've lost integer precision
- if (!allowImprecise && x >= Int64.MAX_INT) {
- return negate ? -Infinity : Infinity;
- }
- return negate ? -x : x;
- },
- /**
- * Convert to a JS Number. Returns +/-Infinity for values that can't be
- * represented to integer precision.
- */
- valueOf: function() {
- return this.toNumber(false);
- },
- /**
- * Return string value
- *
- * @param radix Just like Number#toString()'s radix
- */
- toString: function(radix) {
- return this.valueOf().toString(radix || 10);
- },
- /**
- * Return a string showing the buffer octets, with MSB on the left.
- *
- * @param sep separator string. default is '' (empty string)
- */
- toOctetString: function(sep) {
- var out = new Array(8);
- var b = this.buffer, o = this.offset;
- for (var i = 0; i < 8; i++) {
- out[i] = _HEX[b[o+i]];
- }
- return out.join(sep || '');
- },
- /**
- * Returns the int64's 8 bytes in a buffer.
- *
- * @param {bool} [rawBuffer=false] If no offset and this is true, return the internal buffer. Should only be used if
- * you're discarding the Int64 afterwards, as it breaks encapsulation.
- */
- toBuffer: function(rawBuffer) {
- if (rawBuffer && this.offset === 0) return this.buffer;
- var out = new Buffer(8);
- this.buffer.copy(out, 0, this.offset, this.offset + 8);
- return out;
- },
- /**
- * Copy 8 bytes of int64 into target buffer at target offset.
- *
- * @param {Buffer} targetBuffer Buffer to copy into.
- * @param {number} [targetOffset=0] Offset into target buffer.
- */
- copy: function(targetBuffer, targetOffset) {
- this.buffer.copy(targetBuffer, targetOffset || 0, this.offset, this.offset + 8);
- },
- /**
- * Returns a number indicating whether this comes before or after or is the
- * same as the other in sort order.
- *
- * @param {Int64} other Other Int64 to compare.
- */
- compare: function(other) {
- // If sign bits differ ...
- if ((this.buffer[this.offset] & 0x80) != (other.buffer[other.offset] & 0x80)) {
- return other.buffer[other.offset] - this.buffer[this.offset];
- }
- // otherwise, compare bytes lexicographically
- for (var i = 0; i < 8; i++) {
- if (this.buffer[this.offset+i] !== other.buffer[other.offset+i]) {
- return this.buffer[this.offset+i] - other.buffer[other.offset+i];
- }
- }
- return 0;
- },
- /**
- * Returns a boolean indicating if this integer is equal to other.
- *
- * @param {Int64} other Other Int64 to compare.
- */
- equals: function(other) {
- return this.compare(other) === 0;
- },
- /**
- * Pretty output in console.log
- */
- inspect: function() {
- return '[Int64 value:' + this + ' octets:' + this.toOctetString(' ') + ']';
- }
- };
|