/
Int64.js
177 lines (159 loc) · 4.68 KB
/
Int64.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
/**
* node-int64
* From https://github.com/broofa/node-int64
*
* Support for handling 64-bit int numbers in Javascript (node.js)
*
* JS Numbers are IEEE-754 double-precision floats, which limits the range of
* integer values that can be accurately represented to +/- 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, setting the value will modify the Buffer and
* vice-versa.
*/
// Useful masks and values for doing 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 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
*/
var Int64 = module.exports = function Int64(a1, a2) {
if (!(this instanceof Int64)) {
if (arguments.length == 1) {
return new Int64(a1)
}
return new Int64(a1, a2)
}
if (a1 instanceof Buffer) {
this.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 = {
/**
* 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:
* 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 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 Error(hi + ' must be a Number or String');
}
}
// TODO: Do we want to throw if hi/lo is outside int32 range here?
// Copy bytes to buffer
b = this.buffer;
var 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();
},
/**
* Return the approximate error involved in converting the current value to a
* native JS number. If > 0, the value is outside the range JS can represent
* to integer precision.
*/
error: function() {
return Math.ceil(Math.abs(this.valueOf()) / Int64.MAX_INT) - 1;
},
/**
* Convert to a JS Number.
*
* Be aware that if the returned value is outside the range ...
*
* Int64.MIN_INT <= x <= Int64.MAX_INT
*
* ... it is unlikely to exactly represent the underlying 64-bit value.
*/
valueOf: function() {
var b = this.buffer, o = this.offset;
var negate = b[0] & 0x80, x = 0, xx = 1;
if (negate) this._2scomp();
for (var i = o + 7; i >= o; i--) {
var v = b[i] & (i == 0 ? 0x7f : 0xff);
x += v*xx;
xx *= 0x100;
}
if (negate) {
x = -x;
this._2scomp();
}
return x;
},
/**
* Get value as a string of hex octets
*
* @param sep (String) string to join() with. Default=''
*/
toString: function(sep) {
var b = this.buffer, o = this.offset, s = ['0x'];
for (var i = 0; i < 8; i++) {
s.push(_HEX[this.buffer[o+i]]);
}
return s.join('');
},
/**
* Used by console.log and util.inspect.
*/
inspect: function() {
var value = this.valueOf()
if (value < Int64.MAX_INT && value > Int64.MIN_INT) {
return "<Int64 " + value + ">";
} else {
return "<Int64 " + this.toString() + ">";
}
}
};