]> arthur.barton.de Git - netdata.git/blob - node.d/node_modules/node-int64.js
Merge pull request #418 from ktsaou/master
[netdata.git] / node.d / node_modules / node-int64.js
1 //     Int64.js
2 //
3 //     Copyright (c) 2012 Robert Kieffer
4 //     MIT License - http://opensource.org/licenses/mit-license.php
5
6 /**
7  * Support for handling 64-bit int numbers in Javascript (node.js)
8  *
9  * JS Numbers are IEEE-754 binary double-precision floats, which limits the
10  * range of values that can be represented with integer precision to:
11  *
12  * 2^^53 <= N <= 2^53
13  *
14  * Int64 objects wrap a node Buffer that holds the 8-bytes of int64 data.  These
15  * objects operate directly on the buffer which means that if they are created
16  * using an existing buffer then setting the value will modify the Buffer, and
17  * vice-versa.
18  *
19  * Internal Representation
20  *
21  * The internal buffer format is Big Endian.  I.e. the most-significant byte is
22  * at buffer[0], the least-significant at buffer[7].  For the purposes of
23  * converting to/from JS native numbers, the value is assumed to be a signed
24  * integer stored in 2's complement form.
25  *
26  * For details about IEEE-754 see:
27  * http://en.wikipedia.org/wiki/Double_precision_floating-point_format
28  */
29
30 // Useful masks and values for bit twiddling
31 var MASK31 =  0x7fffffff, VAL31 = 0x80000000;
32 var MASK32 =  0xffffffff, VAL32 = 0x100000000;
33
34 // Map for converting hex octets to strings
35 var _HEX = [];
36 for (var i = 0; i < 256; i++) {
37   _HEX[i] = (i > 0xF ? '' : '0') + i.toString(16);
38 }
39
40 //
41 // Int64
42 //
43
44 /**
45  * Constructor accepts any of the following argument types:
46  *
47  * new Int64(buffer[, offset=0]) - Existing Buffer with byte offset
48  * new Int64(Uint8Array[, offset=0]) - Existing Uint8Array with a byte offset
49  * new Int64(string)             - Hex string (throws if n is outside int64 range)
50  * new Int64(number)             - Number (throws if n is outside int64 range)
51  * new Int64(hi, lo)             - Raw bits as two 32-bit values
52  */
53 var Int64 = module.exports = function(a1, a2) {
54   if (a1 instanceof Buffer) {
55     this.buffer = a1;
56     this.offset = a2 || 0;
57   } else if (Object.prototype.toString.call(a1) == '[object Uint8Array]') {
58     // Under Browserify, Buffers can extend Uint8Arrays rather than an
59     // instance of Buffer. We could assume the passed in Uint8Array is actually
60     // a buffer but that won't handle the case where a raw Uint8Array is passed
61     // in. We construct a new Buffer just in case.
62     this.buffer = new Buffer(a1);
63     this.offset = a2 || 0;
64   } else {
65     this.buffer = this.buffer || new Buffer(8);
66     this.offset = 0;
67     this.setValue.apply(this, arguments);
68   }
69 };
70
71
72 // Max integer value that JS can accurately represent
73 Int64.MAX_INT = Math.pow(2, 53);
74
75 // Min integer value that JS can accurately represent
76 Int64.MIN_INT = -Math.pow(2, 53);
77
78 Int64.prototype = {
79
80   constructor: Int64,
81
82   /**
83    * Do in-place 2's compliment.  See
84    * http://en.wikipedia.org/wiki/Two's_complement
85    */
86   _2scomp: function() {
87     var b = this.buffer, o = this.offset, carry = 1;
88     for (var i = o + 7; i >= o; i--) {
89       var v = (b[i] ^ 0xff) + carry;
90       b[i] = v & 0xff;
91       carry = v >> 8;
92     }
93   },
94
95   /**
96    * Set the value. Takes any of the following arguments:
97    *
98    * setValue(string) - A hexidecimal string
99    * setValue(number) - Number (throws if n is outside int64 range)
100    * setValue(hi, lo) - Raw bits as two 32-bit values
101    */
102   setValue: function(hi, lo) {
103     var negate = false;
104     if (arguments.length == 1) {
105       if (typeof(hi) == 'number') {
106         // Simplify bitfield retrieval by using abs() value.  We restore sign
107         // later
108         negate = hi < 0;
109         hi = Math.abs(hi);
110         lo = hi % VAL32;
111         hi = hi / VAL32;
112         if (hi > VAL32) throw new RangeError(hi  + ' is outside Int64 range');
113         hi = hi | 0;
114       } else if (typeof(hi) == 'string') {
115         hi = (hi + '').replace(/^0x/, '');
116         lo = hi.substr(-8);
117         hi = hi.length > 8 ? hi.substr(0, hi.length - 8) : '';
118         hi = parseInt(hi, 16);
119         lo = parseInt(lo, 16);
120       } else {
121         throw new Error(hi + ' must be a Number or String');
122       }
123     }
124
125     // Technically we should throw if hi or lo is outside int32 range here, but
126     // it's not worth the effort. Anything past the 32'nd bit is ignored.
127
128     // Copy bytes to buffer
129     var b = this.buffer, o = this.offset;
130     for (var i = 7; i >= 0; i--) {
131       b[o+i] = lo & 0xff;
132       lo = i == 4 ? hi : lo >>> 8;
133     }
134
135     // Restore sign of passed argument
136     if (negate) this._2scomp();
137   },
138
139   /**
140    * Convert to a native JS number.
141    *
142    * WARNING: Do not expect this value to be accurate to integer precision for
143    * large (positive or negative) numbers!
144    *
145    * @param allowImprecise If true, no check is performed to verify the
146    * returned value is accurate to integer precision.  If false, imprecise
147    * numbers (very large positive or negative numbers) will be forced to +/-
148    * Infinity.
149    */
150   toNumber: function(allowImprecise) {
151     var b = this.buffer, o = this.offset;
152
153     // Running sum of octets, doing a 2's complement
154     var negate = b[o] & 0x80, x = 0, carry = 1;
155     for (var i = 7, m = 1; i >= 0; i--, m *= 256) {
156       var v = b[o+i];
157
158       // 2's complement for negative numbers
159       if (negate) {
160         v = (v ^ 0xff) + carry;
161         carry = v >> 8;
162         v = v & 0xff;
163       }
164
165       x += v * m;
166     }
167
168     // Return Infinity if we've lost integer precision
169     if (!allowImprecise && x >= Int64.MAX_INT) {
170       return negate ? -Infinity : Infinity;
171     }
172
173     return negate ? -x : x;
174   },
175
176   /**
177    * Convert to a JS Number. Returns +/-Infinity for values that can't be
178    * represented to integer precision.
179    */
180   valueOf: function() {
181     return this.toNumber(false);
182   },
183
184   /**
185    * Return string value
186    *
187    * @param radix Just like Number#toString()'s radix
188    */
189   toString: function(radix) {
190     return this.valueOf().toString(radix || 10);
191   },
192
193   /**
194    * Return a string showing the buffer octets, with MSB on the left.
195    *
196    * @param sep separator string. default is '' (empty string)
197    */
198   toOctetString: function(sep) {
199     var out = new Array(8);
200     var b = this.buffer, o = this.offset;
201     for (var i = 0; i < 8; i++) {
202       out[i] = _HEX[b[o+i]];
203     }
204     return out.join(sep || '');
205   },
206
207   /**
208    * Returns the int64's 8 bytes in a buffer.
209    *
210    * @param {bool} [rawBuffer=false]  If no offset and this is true, return the internal buffer.  Should only be used if
211    *                                  you're discarding the Int64 afterwards, as it breaks encapsulation.
212    */
213   toBuffer: function(rawBuffer) {
214     if (rawBuffer && this.offset === 0) return this.buffer;
215
216     var out = new Buffer(8);
217     this.buffer.copy(out, 0, this.offset, this.offset + 8);
218     return out;
219   },
220
221   /**
222    * Copy 8 bytes of int64 into target buffer at target offset.
223    *
224    * @param {Buffer} targetBuffer       Buffer to copy into.
225    * @param {number} [targetOffset=0]   Offset into target buffer.
226    */
227   copy: function(targetBuffer, targetOffset) {
228     this.buffer.copy(targetBuffer, targetOffset || 0, this.offset, this.offset + 8);
229   },
230
231   /**
232    * Returns a number indicating whether this comes before or after or is the
233    * same as the other in sort order.
234    *
235    * @param {Int64} other  Other Int64 to compare.
236    */
237   compare: function(other) {
238
239     // If sign bits differ ...
240     if ((this.buffer[this.offset] & 0x80) != (other.buffer[other.offset] & 0x80)) {
241       return other.buffer[other.offset] - this.buffer[this.offset];
242     }
243
244     // otherwise, compare bytes lexicographically
245     for (var i = 0; i < 8; i++) {
246       if (this.buffer[this.offset+i] !== other.buffer[other.offset+i]) {
247         return this.buffer[this.offset+i] - other.buffer[other.offset+i];
248       }
249     }
250     return 0;
251   },
252
253   /**
254    * Returns a boolean indicating if this integer is equal to other.
255    *
256    * @param {Int64} other  Other Int64 to compare.
257    */
258   equals: function(other) {
259     return this.compare(other) === 0;
260   },
261
262   /**
263    * Pretty output in console.log
264    */
265   inspect: function() {
266     return '[Int64 value:' + this + ' octets:' + this.toOctetString(' ') + ']';
267   }
268 };