19
19
const assert = require ( 'assert' ) ;
20
20
const isInt32 = require ( '../utils' ) . isInt32 ;
21
21
22
- const INT_RANGE = 0x100000000 ; // 2^32
23
-
24
- const HI_SAFE_INT_MAX = 0x1FFFFF ; //2^21-1
25
-
26
- const HI_SAFE_INT_MIN = - 0x200000 ; // -2^21
22
+ //Avoid ReferenceError(s) below in case bigint is not supported.
23
+ const BigIntCons = typeof BigInt === 'function' ? BigInt : Number ;
24
+
25
+ //We are not using bigint literals to avoid syntax errors if bigint is not
26
+ //supported.
27
+ const INT32_MIN = - 2147483648 ; // -2^31
28
+ const INT32_MAX = 2147483647 ; // 2^31 - 1
29
+ const INT32_RANGE = 0x100000000 ; // 2^32
30
+ const INT32_RANGE_BIGINT = BigIntCons ( 0x100000000 ) ; // 2^32n
31
+ const INT64_BIGINT_MIN = BigIntCons ( '-9223372036854775808' ) ; // -2^63
32
+ const INT64_BIGINT_MAX = BigIntCons ( '9223372036854775807' ) ; // 2^63 - 1
33
+
34
+ //Saving these instead of instantiating every time does improve performance
35
+ //according to benchmark.
36
+ const BIGINT_119 = BigIntCons ( 119 ) ;
37
+ const BIGINT_121 = BigIntCons ( 121 ) ;
27
38
28
39
/**
29
40
* In JavaScript, number can represent integer up to 53 bits long (not
@@ -42,50 +53,82 @@ const HI_SAFE_INT_MIN = -0x200000; // -2^21
42
53
* The results can be combined to represent resulting long integer as number
43
54
* (as long as it is within the safe integer range).
44
55
*/
45
- class SafeInt {
46
- static split ( value ) {
47
- if ( ! Number . isSafeInteger ( value ) ) {
48
- throw new RangeError ( `Value ${ value } out of range for ` +
49
- 'safe integer' ) ;
56
+ class Int64 {
57
+
58
+ static _to2sComplement ( valueL , valueR , isNegative ) {
59
+ if ( ! isNegative ) {
60
+ assert ( isInt32 ( valueL ) ) ;
61
+ valueR = ~ ~ valueR ;
62
+ } else {
63
+ valueL = ~ valueL ;
64
+ valueR = ~ valueR + 1 ;
65
+ if ( valueR === 0 ) {
66
+ //if ~valueR was -1 we have to carry 1 to the left side
67
+ valueL ++ ;
68
+ }
50
69
}
70
+ return { valueL, valueR } ;
71
+ }
51
72
52
- const negative = value < 0 ;
73
+ static _splitNumber ( value ) {
74
+ const isNegative = value < 0 ;
53
75
if ( isInt32 ( value ) ) {
54
- return { valueL : negative ? - 1 : 0 , valueR : value } ;
76
+ return {
77
+ valueL : isNegative ? - 1 : 0 ,
78
+ valueR : value
79
+ } ;
55
80
}
56
81
57
- if ( negative ) {
82
+ if ( isNegative ) {
58
83
value = - value ;
59
84
}
60
- let valueL = Math . floor ( value / INT_RANGE ) ;
61
- let valueR = value % INT_RANGE ;
62
- if ( ! negative ) {
63
- valueR = valueR & 0xFFFFFFFF ;
64
- } else {
65
- valueL = ( - valueL - 1 ) & 0xFFFFFFFF ;
66
- valueR = - ( valueR & 0xFFFFFFFF ) ;
67
- }
68
85
69
- return { valueL, valueR } ;
86
+ const valueL = Math . floor ( value / INT32_RANGE ) ;
87
+ const valueR = value % INT32_RANGE ;
88
+
89
+ return Int64 . _to2sComplement ( valueL , valueR , isNegative ) ;
70
90
}
71
91
72
- static combine ( valueL , valueR ) {
73
- if ( valueL < HI_SAFE_INT_MIN || valueL > HI_SAFE_INT_MAX ) {
74
- throw new RangeError ( `High order 32 bit part ${ valueL } is ` +
75
- 'out of range for safe integer' ) ;
92
+ static _splitBigInt ( value ) {
93
+ const isNegative = value < 0 ;
94
+ if ( value >= INT32_MIN && value <= INT32_MAX ) {
95
+ return {
96
+ valueL : isNegative ? - 1 : 0 ,
97
+ valueR : Number ( value )
98
+ } ;
76
99
}
100
+
101
+ if ( isNegative ) {
102
+ value = - value ;
103
+ }
104
+
105
+ const valueL = Number ( value / INT32_RANGE_BIGINT ) ;
106
+ const valueR = Number ( value % INT32_RANGE_BIGINT ) ;
107
+
108
+ return Int64 . _to2sComplement ( valueL , valueR , isNegative ) ;
109
+ }
110
+
111
+ static split ( value ) {
112
+ return typeof value === 'bigint' ?
113
+ Int64 . _splitBigInt ( value ) : Int64 . _splitNumber ( value ) ;
114
+ }
115
+
116
+ static combine ( valueL , valueR , toBigInt = false ) {
117
+ assert ( isInt32 ( valueL ) ) ;
77
118
assert ( isInt32 ( valueR ) ) ;
78
119
79
120
if ( valueL < 0 ) { //negative
80
121
valueL ++ ;
81
122
if ( valueR > 0 ) {
82
- valueR -= INT_RANGE ;
123
+ valueR -= INT32_RANGE ;
83
124
}
84
125
} else if ( valueR < 0 ) { //positive, adjust valueR to unsigned
85
- valueR += INT_RANGE ;
126
+ valueR += INT32_RANGE ;
86
127
}
87
128
88
- return valueL * INT_RANGE + valueR ;
129
+ return toBigInt ?
130
+ BigIntCons ( valueL ) * INT32_RANGE_BIGINT + BigIntCons ( valueR ) :
131
+ valueL * INT32_RANGE + valueR ;
89
132
}
90
133
}
91
134
@@ -138,7 +181,7 @@ class PackedInteger {
138
181
*/
139
182
let value ;
140
183
if ( negative ) {
141
- value = 0xFFFFFFFF ;
184
+ value = - 1 ;
142
185
} else {
143
186
value = 0 ;
144
187
}
@@ -176,7 +219,7 @@ class PackedInteger {
176
219
*
177
220
* @return tuple containing the resulting value and resulting offset
178
221
*/
179
- static readSortedLong ( buf , off ) {
222
+ static readSortedLong ( buf , off , toBigInt = false ) {
180
223
181
224
let byteLen ;
182
225
let negative ;
@@ -191,7 +234,10 @@ class PackedInteger {
191
234
byteLen = b1 - 0xf7 ;
192
235
negative = false ;
193
236
} else {
194
- return { value : b1 - 127 , off } ;
237
+ return {
238
+ value : toBigInt ? BigIntCons ( b1 - 127 ) : b1 - 127 ,
239
+ off
240
+ } ;
195
241
}
196
242
197
243
/*
@@ -211,7 +257,7 @@ class PackedInteger {
211
257
valueR = 0 ;
212
258
}
213
259
214
- //Safe integer overflow will be detected in SafeInt .combine()
260
+ //64 bit int overflow will be detected in Int64 .combine()
215
261
if ( byteLen > 7 ) {
216
262
valueL = ( valueL << 8 ) | buf . readUInt8 ( off ++ ) ;
217
263
}
@@ -235,16 +281,16 @@ class PackedInteger {
235
281
}
236
282
valueR = ( valueR << 8 ) | buf . readUInt8 ( off ++ ) ;
237
283
238
- let value = SafeInt . combine ( valueL , valueR ) ;
284
+ let value = Int64 . combine ( valueL , valueR , toBigInt ) ;
239
285
240
286
/*
241
287
* After obtaining the adjusted value, we have to adjust it back to the
242
288
* original value.
243
289
*/
244
290
if ( negative ) {
245
- value -= 119 ;
291
+ value -= toBigInt ? BIGINT_119 : 119 ;
246
292
} else {
247
- value += 121 ;
293
+ value += toBigInt ? BIGINT_121 : 121 ;
248
294
}
249
295
return { value, off } ;
250
296
}
@@ -268,6 +314,11 @@ class PackedInteger {
268
314
*/
269
315
static writeSortedInt ( buf , off , value ) {
270
316
317
+ if ( ! isInt32 ( value ) ) {
318
+ throw new RangeError (
319
+ `Value ${ value } out of range for type INTEGER` ) ;
320
+ }
321
+
271
322
/*
272
323
* Values in the inclusive range [-119,120] are stored in a single
273
324
* byte. For values outside that range, the first byte stores the
@@ -304,13 +355,13 @@ class PackedInteger {
304
355
* I haven't seen this stated explicitly in the doc, so adding
305
356
* (& 0xFF) just in case.
306
357
*/
307
- if ( ( value | 0x00FFFFFF ) != 0xFFFFFFFF ) {
358
+ if ( ( value | 0x00FFFFFF ) != - 1 ) {
308
359
buf . writeUInt8 ( ( value >> 24 ) & 0xFF , off ++ ) ;
309
360
}
310
- if ( ( value | 0x0000FFFF ) != 0xFFFFFFFF ) {
361
+ if ( ( value | 0x0000FFFF ) != - 1 ) {
311
362
buf . writeUInt8 ( ( value >> 16 ) & 0xFF , off ++ ) ;
312
363
}
313
- if ( ( value | 0x000000FF ) != 0xFFFFFFFF ) {
364
+ if ( ( value | 0x000000FF ) != - 1 ) {
314
365
buf . writeUInt8 ( ( value >> 8 ) & 0xFF , off ++ ) ;
315
366
}
316
367
buf . writeUInt8 ( value & 0xFF , off ++ ) ;
@@ -399,6 +450,16 @@ class PackedInteger {
399
450
* @return the offset past the bytes written.
400
451
*/
401
452
static writeSortedLong ( buf , off , value ) {
453
+ if ( typeof value === 'number' ) {
454
+ //See Protocol.writeFieldValue().
455
+ assert ( Number . isSafeInteger ( value ) ) ;
456
+ } else {
457
+ assert ( typeof value === 'bigint' ) ;
458
+ if ( value < INT64_BIGINT_MIN || value > INT64_BIGINT_MAX ) {
459
+ throw new RangeError (
460
+ `Value ${ value } out of range for type LONG` ) ;
461
+ }
462
+ }
402
463
403
464
/*
404
465
* Values in the inclusive range [-119,120] are stored in a single
@@ -416,9 +477,9 @@ class PackedInteger {
416
477
* Then the adjusted value is stored as an unsigned big endian
417
478
* integer.
418
479
*/
419
- value += 119 ;
480
+ value += typeof value === 'bigint' ? BIGINT_119 : 119 ;
420
481
421
- const { valueL, valueR } = SafeInt . split ( value ) ;
482
+ const { valueL, valueR } = Int64 . split ( value ) ;
422
483
423
484
/*
424
485
* Store the adjusted value as an unsigned big endian integer.
@@ -438,25 +499,25 @@ class PackedInteger {
438
499
* I haven't seen this stated explicitly in the doc, so adding
439
500
* (& 0xFF) just in case.
440
501
*/
441
- if ( ( valueL | 0x00FFFFFF ) != 0xFFFFFFFF ) {
502
+ if ( ( valueL | 0x00FFFFFF ) != - 1 ) {
442
503
buf . writeUInt8 ( ( valueL >> 24 ) & 0xFF , off ++ ) ;
443
504
}
444
- if ( ( valueL | 0x0000FFFF ) != 0xFFFFFFFF ) {
505
+ if ( ( valueL | 0x0000FFFF ) != - 1 ) {
445
506
buf . writeUInt8 ( ( valueL >> 16 ) & 0xFF , off ++ ) ;
446
507
}
447
- if ( ( valueL | 0x000000FF ) != 0xFFFFFFFF ) {
508
+ if ( ( valueL | 0x000000FF ) != - 1 ) {
448
509
buf . writeUInt8 ( ( valueL >> 8 ) & 0xFF , off ++ ) ;
449
510
}
450
- if ( valueL != 0xFFFFFFFFF ) {
511
+ if ( valueL != - 1 ) {
451
512
buf . writeUInt8 ( valueL & 0xFF , off ++ ) ;
452
513
}
453
- if ( ( valueR | 0x00FFFFFF ) != 0xFFFFFFFF ) {
514
+ if ( valueL != - 1 || ( valueR | 0x00FFFFFF ) != - 1 ) {
454
515
buf . writeUInt8 ( ( valueR >> 24 ) & 0xFF , off ++ ) ;
455
516
}
456
- if ( ( valueR | 0x0000FFFF ) != 0xFFFFFFFF ) {
517
+ if ( valueL != - 1 || ( valueR | 0x0000FFFF ) != - 1 ) {
457
518
buf . writeUInt8 ( ( valueR >> 16 ) & 0xFF , off ++ ) ;
458
519
}
459
- if ( ( valueR | 0x000000FF ) != 0xFFFFFFFF ) {
520
+ if ( valueL != - 1 || ( valueR | 0x000000FF ) != - 1 ) {
460
521
buf . writeUInt8 ( ( valueR >> 8 ) & 0xFF , off ++ ) ;
461
522
}
462
523
buf . writeUInt8 ( valueR & 0xFF , off ++ ) ;
@@ -482,9 +543,9 @@ class PackedInteger {
482
543
* 119. Then the adjusted value is stored as an unsigned big endian
483
544
* integer.
484
545
*/
485
- value -= 121 ;
546
+ value -= typeof value === 'bigint' ? BIGINT_121 : 121 ;
486
547
487
- const { valueL, valueR } = SafeInt . split ( value ) ;
548
+ const { valueL, valueR } = Int64 . split ( value ) ;
488
549
489
550
/*
490
551
* Store the adjusted value as an unsigned big endian integer.
@@ -509,13 +570,13 @@ class PackedInteger {
509
570
if ( valueL != 0 ) {
510
571
buf . writeUInt8 ( valueL & 0xFF , off ++ ) ;
511
572
}
512
- if ( ( valueR & 0xFF000000 ) != 0 ) {
573
+ if ( valueL || ( valueR & 0xFF000000 ) != 0 ) {
513
574
buf . writeUInt8 ( ( valueR >> 24 ) & 0xFF , off ++ ) ;
514
575
}
515
- if ( ( valueR & 0xFFFF0000 ) != 0 ) {
576
+ if ( valueL || ( valueR & 0xFFFF0000 ) != 0 ) {
516
577
buf . writeUInt8 ( ( valueR >> 16 ) & 0xFF , off ++ ) ;
517
578
}
518
- if ( ( valueR & 0xFFFFFF00 ) != 0 ) {
579
+ if ( valueL || ( valueR & 0xFFFFFF00 ) != 0 ) {
519
580
buf . writeUInt8 ( ( valueR >> 8 ) & 0xFF , off ++ ) ;
520
581
}
521
582
buf . writeUInt8 ( valueR & 0xFF , off ++ ) ;
@@ -535,7 +596,7 @@ class PackedInteger {
535
596
* If -119 <= value <= 120, only one byte is needed to store the
536
597
* value. The stored value is the original value adds 127.
537
598
*/
538
- buf . writeUInt8 ( value + 127 , byte1Off ) ;
599
+ buf . writeUInt8 ( Number ( value ) + 127 , byte1Off ) ;
539
600
}
540
601
return off ;
541
602
}
0 commit comments