Comparing version
134
Int64.js
/** | ||
* 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. | ||
* | ||
* For details about IEEE-754 see: | ||
* http://en.wikipedia.org/wiki/Double_precision_floating-point_format | ||
*/ | ||
* 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 doing bit twiddling | ||
// Useful masks and values for bit twiddling | ||
var MASK31 = 0x7fffffff, VAL31 = 0x80000000; | ||
@@ -23,3 +30,3 @@ var MASK32 = 0xffffffff, VAL32 = 0x100000000; | ||
// Map for converting hex octets to strings | ||
var _HEX = [], _PADHEX = []; | ||
var _HEX = []; | ||
for (var i = 0; i < 256; i++) { | ||
@@ -34,9 +41,9 @@ _HEX[i] = (i > 0xF ? '' : '0') + i.toString(16); | ||
/** | ||
* Constructor accepts the following arguments: | ||
* | ||
* new Int64(buffer[, offset=0]) - Existing Buffer with 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 | ||
*/ | ||
* Constructor accepts any of the following argument types: | ||
* | ||
* new Int64(buffer[, offset=0]) - Existing Buffer with 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) { | ||
@@ -62,5 +69,5 @@ if (a1 instanceof Buffer) { | ||
/** | ||
* Do in-place 2's compliment. See | ||
* http://en.wikipedia.org/wiki/Two's_complement | ||
*/ | ||
* Do in-place 2's compliment. See | ||
* http://en.wikipedia.org/wiki/Two's_complement | ||
*/ | ||
_2scomp: function() { | ||
@@ -76,7 +83,8 @@ var b = this.buffer, o = this.offset, carry = 1; | ||
/** | ||
* Set the value: | ||
* 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 | ||
*/ | ||
* 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) { | ||
@@ -92,5 +100,5 @@ var negate = false; | ||
hi = hi / VAL32; | ||
if (hi > VAL32) throw RangeError(hi + ' is outside Int64 range'); | ||
if (hi > VAL32) throw new RangeError(hi + ' is outside Int64 range'); | ||
hi = hi | 0; | ||
} else if (typeof(hi) == 'string') { | ||
} else if (typeof(hi) == 'string') { | ||
hi = (hi + '').replace(/^0x/, ''); | ||
@@ -102,8 +110,8 @@ lo = hi.substr(-8); | ||
} else { | ||
throw Error(hi + ' must be a Number or String'); | ||
throw new Error(hi + ' must be a Number or String'); | ||
} | ||
} | ||
// Technically we should throw if hi/lo is outside int32 range here, but | ||
// it's not worth the effort. | ||
// 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. | ||
@@ -122,22 +130,32 @@ // Copy bytes to buffer | ||
/** | ||
* Convert to a JS Number. Returns +/-Infinity for values that can't be | ||
* represented to integer precision. | ||
*/ | ||
valueOf: function() { | ||
* 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[0] & 0x80, x = 0, carry = 1; | ||
for (var i = 0, ii = o + 7; i < 8; i++, ii--) { | ||
var v = b[ii]; | ||
// Do a running 2's complement for negative numbers | ||
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 & 0xff) * Math.pow(256, i); | ||
x += v * m; | ||
} | ||
// Return Infinity if we've lost integer precision | ||
if (x >= Int64.MAX_INT) { | ||
if (!allowImprecise && x >= Int64.MAX_INT) { | ||
return negate ? -Infinity : Infinity; | ||
@@ -150,4 +168,14 @@ } | ||
/** | ||
* Return string value | ||
*/ | ||
* 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) { | ||
@@ -158,4 +186,6 @@ return this.valueOf().toString(radix || 10); | ||
/** | ||
* Return a string showing the buffer octets, with MSB on the left. | ||
*/ | ||
* Return a string showing the buffer octets, with MSB on the left. | ||
* | ||
* @param sep separator string. default is '' (empty string) | ||
*/ | ||
toOctetString: function(sep) { | ||
@@ -171,4 +201,4 @@ var out = new Array(8); | ||
/** | ||
* Pretty output in console.log | ||
*/ | ||
* Pretty output in console.log | ||
*/ | ||
inspect: function() { | ||
@@ -175,0 +205,0 @@ return '[Int64 value:' + this + ' octets:' + this.toOctetString(' ') + ']'; |
@@ -11,3 +11,3 @@ { | ||
"main" : "./Int64.js", | ||
"version" : "0.2.0" | ||
"version" : "0.3.0" | ||
} |
35
test.js
@@ -0,16 +1,23 @@ | ||
var assert = require('assert'); | ||
var Int64 = require('./Int64'); | ||
var args = [ | ||
[0], '0000000000000000', | ||
[1], '0000000000000001', | ||
[-1], 'ffffffffffffffff', | ||
[1e18], '0de0b6b3a7640000', | ||
['0ff1234500654321'], '0ff1234500654321', | ||
[0xff12345, 0x654321], '0ff1234500654321', | ||
['0x0000123450654321'], '0000123450654321', | ||
['0xFFFFFFFFFFFFFFFF'], 'ffffffffffffffff' | ||
[0], '0000000000000000', 0, | ||
[1], '0000000000000001', 1, | ||
[-1], 'ffffffffffffffff', -1, | ||
[1e18], '0de0b6b3a7640000', 1e18, | ||
['0001234500654321'], '0001234500654321', 0x1234500654321, | ||
['0ff1234500654321'], '0ff1234500654321', 0xff1234500654300, // Imprecise! | ||
[0xff12345, 0x654321], '0ff1234500654321', 0xff1234500654300, // Imprecise! | ||
[0xfffaffff, 0xfffff700],'fffafffffffff700', -0x5000000000900, | ||
[0xafffffff, 0xfffff700],'affffffffffff700', -0x5000000000000800, // Imprecise! | ||
['0x0000123450654321'], '0000123450654321', 0x123450654321, | ||
['0xFFFFFFFFFFFFFFFF'], 'ffffffffffffffff', -1 | ||
]; | ||
for (var i = 0; i < args.length; i += 2) { | ||
var a = args[i], octets = args[i+1]; | ||
// Test constructor argments | ||
for (var i = 0; i < args.length; i += 3) { | ||
var a = args[i], octets = args[i+1], number = args[i+2]; | ||
console.log('Testing ' + a.join(', ')); | ||
// Create instance | ||
@@ -20,8 +27,6 @@ var x = new Int64(); | ||
console.log('new Int64(' + a + ')'); | ||
var pass = x.toOctetString() == octets; | ||
console.log((pass ? 'PASS' : 'FAIL') + ' - value:' + x + | ||
', octets: ' + x.toOctetString()); | ||
assert.equal(x.toOctetString(), octets, | ||
'Constuctor with ' + args.join(', ')); | ||
console.log('-----------'); | ||
assert.equal(x.toNumber(true), number); | ||
} |
9861
17.77%201
18.93%