Skip to content

Commit 518e1b7

Browse files
committed
fix of overflow detection for numeric primitive types
1 parent f5dfab1 commit 518e1b7

File tree

5 files changed

+115
-80
lines changed

5 files changed

+115
-80
lines changed

src/main/java/com/jsoniter/IterImpl.java

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -323,123 +323,136 @@ public static int updateStringCopyBound(final JsonIterator iter, final int bound
323323
return bound;
324324
}
325325

326-
static final int readPositiveInt(final JsonIterator iter, byte c) throws IOException {
326+
static final int readInt(final JsonIterator iter, final byte c, final boolean negative) throws IOException {
327327
int ind = IterImplNumber.intDigits[c];
328328
if (ind == 0) {
329329
IterImplForStreaming.assertNotLeadingZero(iter);
330330
return 0;
331331
}
332332
if (ind == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
333-
throw iter.reportError("readPositiveInt", "expect 0~9");
333+
throw iter.reportError("readInt", "expect 0~9");
334334
}
335335
if (iter.tail - iter.head > 9) {
336336
int i = iter.head;
337337
int ind2 = IterImplNumber.intDigits[iter.buf[i]];
338338
if (ind2 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
339339
iter.head = i;
340-
return ind;
340+
return negative ? -ind : ind;
341341
}
342342
int ind3 = IterImplNumber.intDigits[iter.buf[++i]];
343343
if (ind3 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
344344
iter.head = i;
345-
return ind * 10 + ind2;
345+
ind = ind * 10 + ind2;
346+
return negative ? -ind : ind;
346347
}
347348
int ind4 = IterImplNumber.intDigits[iter.buf[++i]];
348349
if (ind4 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
349350
iter.head = i;
350-
return ind * 100 + ind2 * 10 + ind3;
351+
ind = ind * 100 + ind2 * 10 + ind3;
352+
return negative ? -ind : ind;
351353
}
352354
int ind5 = IterImplNumber.intDigits[iter.buf[++i]];
353355
if (ind5 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
354356
iter.head = i;
355-
return ind * 1000 + ind2 * 100 + ind3 * 10 + ind4;
357+
ind = ind * 1000 + ind2 * 100 + ind3 * 10 + ind4;
358+
return negative ? -ind : ind;
356359
}
357360
int ind6 = IterImplNumber.intDigits[iter.buf[++i]];
358361
if (ind6 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
359362
iter.head = i;
360-
return ind * 10000 + ind2 * 1000 + ind3 * 100 + ind4 * 10 + ind5;
363+
ind = ind * 10000 + ind2 * 1000 + ind3 * 100 + ind4 * 10 + ind5;
364+
return negative ? -ind : ind;
361365
}
362366
int ind7 = IterImplNumber.intDigits[iter.buf[++i]];
363367
if (ind7 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
364368
iter.head = i;
365-
return ind * 100000 + ind2 * 10000 + ind3 * 1000 + ind4 * 100 + ind5 * 10 + ind6;
369+
ind = ind * 100000 + ind2 * 10000 + ind3 * 1000 + ind4 * 100 + ind5 * 10 + ind6;
370+
return negative ? -ind : ind;
366371
}
367372
int ind8 = IterImplNumber.intDigits[iter.buf[++i]];
368373
if (ind8 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
369374
iter.head = i;
370-
return ind * 1000000 + ind2 * 100000 + ind3 * 10000 + ind4 * 1000 + ind5 * 100 + ind6 * 10 + ind7;
375+
ind = ind * 1000000 + ind2 * 100000 + ind3 * 10000 + ind4 * 1000 + ind5 * 100 + ind6 * 10 + ind7;
376+
return negative ? -ind : ind;
371377
}
372378
int ind9 = IterImplNumber.intDigits[iter.buf[++i]];
373379
ind = ind * 10000000 + ind2 * 1000000 + ind3 * 100000 + ind4 * 10000 + ind5 * 1000 + ind6 * 100 + ind7 * 10 + ind8;
374380
iter.head = i;
375381
if (ind9 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
376-
return ind;
382+
return negative ? -ind : ind;
377383
}
378384
}
379-
return IterImplForStreaming.readIntSlowPath(iter, ind);
385+
return IterImplForStreaming.readIntSlowPath(iter, ind, negative);
380386
}
381387

382-
static final long readPositiveLong(final JsonIterator iter, byte c) throws IOException {
388+
static final long readLong(final JsonIterator iter, final byte c, final boolean negative) throws IOException {
383389
long ind = IterImplNumber.intDigits[c];
384390
if (ind == 0) {
385391
IterImplForStreaming.assertNotLeadingZero(iter);
386392
return 0;
387393
}
388394
if (ind == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
389-
throw iter.reportError("readPositiveLong", "expect 0~9");
395+
throw iter.reportError("readLong", "expect 0~9");
390396
}
391397
if (iter.tail - iter.head > 9) {
392398
int i = iter.head;
393399
int ind2 = IterImplNumber.intDigits[iter.buf[i]];
394400
if (ind2 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
395401
iter.head = i;
396-
return ind;
402+
return negative ? -ind : ind;
397403
}
398404
int ind3 = IterImplNumber.intDigits[iter.buf[++i]];
399405
if (ind3 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
400406
iter.head = i;
401-
return ind * 10 + ind2;
407+
ind = ind * 10 + ind2;
408+
return negative ? -ind : ind;
402409
}
403410
int ind4 = IterImplNumber.intDigits[iter.buf[++i]];
404411
if (ind4 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
405412
iter.head = i;
406-
return ind * 100 + ind2 * 10 + ind3;
413+
ind = ind * 100 + ind2 * 10 + ind3;
414+
return negative ? -ind : ind;
407415
}
408416
int ind5 = IterImplNumber.intDigits[iter.buf[++i]];
409417
if (ind5 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
410418
iter.head = i;
411-
return ind * 1000 + ind2 * 100 + ind3 * 10 + ind4;
419+
ind = ind * 1000 + ind2 * 100 + ind3 * 10 + ind4;
420+
return negative ? -ind : ind;
412421
}
413422
int ind6 = IterImplNumber.intDigits[iter.buf[++i]];
414423
if (ind6 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
415424
iter.head = i;
416-
return ind * 10000 + ind2 * 1000 + ind3 * 100 + ind4 * 10 + ind5;
425+
ind = ind * 10000 + ind2 * 1000 + ind3 * 100 + ind4 * 10 + ind5;
426+
return negative ? -ind : ind;
417427
}
418428
int ind7 = IterImplNumber.intDigits[iter.buf[++i]];
419429
if (ind7 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
420430
iter.head = i;
421-
return ind * 100000 + ind2 * 10000 + ind3 * 1000 + ind4 * 100 + ind5 * 10 + ind6;
431+
ind = ind * 100000 + ind2 * 10000 + ind3 * 1000 + ind4 * 100 + ind5 * 10 + ind6;
432+
return negative ? -ind : ind;
422433
}
423434
int ind8 = IterImplNumber.intDigits[iter.buf[++i]];
424435
if (ind8 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
425436
iter.head = i;
426-
return ind * 1000000 + ind2 * 100000 + ind3 * 10000 + ind4 * 1000 + ind5 * 100 + ind6 * 10 + ind7;
437+
ind = ind * 1000000 + ind2 * 100000 + ind3 * 10000 + ind4 * 1000 + ind5 * 100 + ind6 * 10 + ind7;
438+
return negative ? -ind : ind;
427439
}
428440
int ind9 = IterImplNumber.intDigits[iter.buf[++i]];
429441
ind = ind * 10000000 + ind2 * 1000000 + ind3 * 100000 + ind4 * 10000 + ind5 * 1000 + ind6 * 100 + ind7 * 10 + ind8;
430442
iter.head = i;
431443
if (ind9 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
432-
return ind;
444+
return negative ? -ind : ind;
433445
}
434446
}
435-
return IterImplForStreaming.readLongSlowPath(iter, ind);
447+
return IterImplForStreaming.readLongSlowPath(iter, ind, negative);
436448
}
437449

438-
static final double readPositiveDouble(final JsonIterator iter) throws IOException {
450+
static final double readDouble(final JsonIterator iter, final boolean negative) throws IOException {
439451
int oldHead = iter.head;
440452
try {
441453
try {
442-
long value = IterImplNumber.readLong(iter); // without the dot
454+
long value = IterImplNumber.readLong(iter); // without the dot & sign
455+
value = negative ? -value : value;
443456
if (iter.head == iter.tail) {
444457
return value;
445458
}
@@ -448,27 +461,29 @@ static final double readPositiveDouble(final JsonIterator iter) throws IOExcepti
448461
iter.head++;
449462
int start = iter.head;
450463
c = iter.buf[iter.head++];
451-
long decimalPart = readPositiveLong(iter, c);
464+
long decimalPart = readLong(iter, c, negative);
452465
int decimalPlaces = iter.head - start;
453466
if (decimalPlaces > 0 && decimalPlaces < IterImplNumber.POW10.length && (iter.head - oldHead) < 10) {
454-
value = value * IterImplNumber.POW10[decimalPlaces] + decimalPart;
455-
return value / (double) IterImplNumber.POW10[decimalPlaces];
467+
return value + (decimalPart / (double) IterImplNumber.POW10[decimalPlaces]);
456468
} else {
457469
iter.head = oldHead;
458-
return IterImplForStreaming.readDoubleSlowPath(iter);
470+
double result = IterImplForStreaming.readDoubleSlowPath(iter);
471+
return negative ? -result : result;
459472
}
460473
} else {
461474
return value;
462475
}
463476
} finally {
464477
if (iter.head < iter.tail && (iter.buf[iter.head] == 'e' || iter.buf[iter.head] == 'E')) {
465478
iter.head = oldHead;
466-
return IterImplForStreaming.readDoubleSlowPath(iter);
479+
double result = IterImplForStreaming.readDoubleSlowPath(iter);
480+
return negative ? -result : result;
467481
}
468482
}
469483
} catch (JsonException e) {
470484
iter.head = oldHead;
471-
return IterImplForStreaming.readDoubleSlowPath(iter);
485+
double result = IterImplForStreaming.readDoubleSlowPath(iter);
486+
return negative ? -result : result;
472487
}
473488
}
474489
}

src/main/java/com/jsoniter/IterImplForStreaming.java

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -487,48 +487,56 @@ public final static int readStringSlowPath(JsonIterator iter, int j) throws IOEx
487487
}
488488
}
489489

490-
static long readLongSlowPath(JsonIterator iter, long value) throws IOException {
490+
static long readLongSlowPath(final JsonIterator iter, long value, final boolean negative) throws IOException {
491+
value = -value; // add negatives to avoid redundant checks for Long.MIN_VALUE on each iteration
492+
long limit = negative ? Long.MIN_VALUE : -Long.MAX_VALUE;
493+
long multmin = -922337203685477580L; // limit / 10
491494
for (; ; ) {
492495
for (int i = iter.head; i < iter.tail; i++) {
493496
int ind = IterImplNumber.intDigits[iter.buf[i]];
494497
if (ind == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
495498
iter.head = i;
496-
return value;
499+
return negative ? value : -value;
497500
}
498-
if (value == Long.MIN_VALUE) {
499-
throw iter.reportError("readLongSlowPath", "value is too large for long");
501+
if (value < multmin) {
502+
throw iter.reportError("readIntSlowPath", "value is too large for int");
500503
}
501-
value = (value << 3) + (value << 1) + ind;
502-
if (value < 0 && value != Long.MIN_VALUE) {
504+
value = (value << 3) + (value << 1);
505+
if (value < limit + ind) {
503506
throw iter.reportError("readLongSlowPath", "value is too large for long");
504507
}
508+
value -= ind;
505509
}
506510
if (!IterImpl.loadMore(iter)) {
507511
iter.head = iter.tail;
508-
return value;
512+
return negative ? value : -value;
509513
}
510514
}
511515
}
512516

513-
static int readIntSlowPath(JsonIterator iter, int value) throws IOException {
517+
static int readIntSlowPath(final JsonIterator iter, int value, final boolean negative) throws IOException {
518+
value = -value; // add negatives to avoid redundant checks for Integer.MIN_VALUE on each iteration
519+
int limit = negative ? Integer.MIN_VALUE : -Integer.MAX_VALUE;
520+
int multmin = -214748364; // limit / 10
514521
for (; ; ) {
515522
for (int i = iter.head; i < iter.tail; i++) {
516523
int ind = IterImplNumber.intDigits[iter.buf[i]];
517524
if (ind == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
518525
iter.head = i;
519-
return value;
526+
return negative ? value : -value;
520527
}
521-
if (value == Integer.MIN_VALUE) {
528+
if (value < multmin) {
522529
throw iter.reportError("readIntSlowPath", "value is too large for int");
523530
}
524-
value = (value << 3) + (value << 1) + ind;
525-
if (value < 0 && value != Integer.MIN_VALUE) {
531+
value = (value << 3) + (value << 1);
532+
if (value < limit + ind) {
526533
throw iter.reportError("readIntSlowPath", "value is too large for int");
527534
}
535+
value -= ind;
528536
}
529537
if (!IterImpl.loadMore(iter)) {
530538
iter.head = iter.tail;
531-
return value;
539+
return negative ? value : -value;
532540
}
533541
}
534542
}
@@ -582,34 +590,33 @@ public static final String readNumber(final JsonIterator iter) throws IOExceptio
582590
}
583591
}
584592

585-
586-
static final double readPositiveDouble(final JsonIterator iter) throws IOException {
587-
return readDoubleSlowPath(iter);
593+
static final double readDouble(final JsonIterator iter, final boolean negative) throws IOException {
594+
double result = readDoubleSlowPath(iter);
595+
return negative ? -result : result;
588596
}
589597

590-
591-
static final long readPositiveLong(final JsonIterator iter, byte c) throws IOException {
598+
static final long readLong(final JsonIterator iter, final byte c, final boolean negative) throws IOException {
592599
long ind = IterImplNumber.intDigits[c];
593600
if (ind == 0) {
594601
assertNotLeadingZero(iter);
595602
return 0;
596603
}
597604
if (ind == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
598-
throw iter.reportError("readPositiveLong", "expect 0~9");
605+
throw iter.reportError("readLong", "expect 0~9");
599606
}
600-
return IterImplForStreaming.readLongSlowPath(iter, ind);
607+
return IterImplForStreaming.readLongSlowPath(iter, ind, negative);
601608
}
602609

603-
static final int readPositiveInt(final JsonIterator iter, byte c) throws IOException {
610+
static final int readInt(final JsonIterator iter, final byte c, final boolean negative) throws IOException {
604611
int ind = IterImplNumber.intDigits[c];
605612
if (ind == 0) {
606613
assertNotLeadingZero(iter);
607614
return 0;
608615
}
609616
if (ind == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
610-
throw iter.reportError("readPositiveInt", "expect 0~9");
617+
throw iter.reportError("readInt", "expect 0~9");
611618
}
612-
return IterImplForStreaming.readIntSlowPath(iter, ind);
619+
return IterImplForStreaming.readIntSlowPath(iter, ind, negative);
613620
}
614621

615622
static void assertNotLeadingZero(JsonIterator iter) throws IOException {
@@ -620,7 +627,7 @@ static void assertNotLeadingZero(JsonIterator iter) throws IOException {
620627
if (ind2 == IterImplNumber.INVALID_CHAR_FOR_NUMBER) {
621628
return;
622629
}
623-
throw iter.reportError("readPositiveInt", "leading zero is invalid");
630+
throw iter.reportError("assertNotLeadingZero", "leading zero is invalid");
624631
} catch (ArrayIndexOutOfBoundsException e) {
625632
iter.head = iter.tail;
626633
return;

src/main/java/com/jsoniter/IterImplNumber.java

Lines changed: 8 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
3131
*/
3232
package com.jsoniter;
3333

34-
import com.jsoniter.spi.JsonException;
35-
3634
import java.io.IOException;
3735

3836
class IterImplNumber {
@@ -65,40 +63,26 @@ class IterImplNumber {
6563

6664
public static final double readDouble(final JsonIterator iter) throws IOException {
6765
final byte c = IterImpl.nextToken(iter);
68-
if (c == '-') {
69-
return -IterImpl.readPositiveDouble(iter);
70-
} else {
66+
boolean negative = c == '-';
67+
if (!negative) {
7168
iter.unreadByte();
72-
return IterImpl.readPositiveDouble(iter);
7369
}
70+
return IterImpl.readDouble(iter, negative);
7471
}
7572

7673
public static final float readFloat(final JsonIterator iter) throws IOException {
77-
final byte c = IterImpl.nextToken(iter);
78-
if (c == '-') {
79-
return (float)-IterImpl.readPositiveDouble(iter);
80-
} else {
81-
iter.unreadByte();
82-
return (float) IterImpl.readPositiveDouble(iter);
83-
}
74+
return (float) IterImplNumber.readDouble(iter);
8475
}
8576

8677
public static final int readInt(final JsonIterator iter) throws IOException {
8778
byte c = IterImpl.nextToken(iter);
88-
if (c == '-') {
89-
return -IterImpl.readPositiveInt(iter, IterImpl.readByte(iter));
90-
} else {
91-
return IterImpl.readPositiveInt(iter, c);
92-
}
79+
boolean negative = c == '-';
80+
return IterImpl.readInt(iter, negative ? IterImpl.readByte(iter) : c, negative);
9381
}
9482

9583
public static final long readLong(JsonIterator iter) throws IOException {
9684
byte c = IterImpl.nextToken(iter);
97-
if (c == '-') {
98-
return -IterImpl.readPositiveLong(iter, IterImpl.readByte(iter));
99-
} else {
100-
return IterImpl.readPositiveLong(iter, c);
101-
}
85+
boolean negative = c == '-';
86+
return IterImpl.readLong(iter, negative ? IterImpl.readByte(iter) : c, negative);
10287
}
103-
10488
}

src/test/java/com/jsoniter/TestFloat.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,15 @@ public void test_ieee_754() throws IOException {
3434
public void test_decimal_places() throws IOException {
3535
assertEquals(Long.MAX_VALUE, parseFloat("9223372036854775807,"), 0.01f);
3636
assertEquals(Long.MAX_VALUE, parseDouble("9223372036854775807,"), 0.01f);
37+
assertEquals(Long.MIN_VALUE, parseDouble("-9223372036854775808,"), 0.01f);
3738
assertEquals(9923372036854775807f, parseFloat("9923372036854775807,"), 0.01f);
39+
assertEquals(-9923372036854775808f, parseFloat("-9923372036854775808,"), 0.01f);
3840
assertEquals(9923372036854775807d, parseDouble("9923372036854775807,"), 0.01f);
41+
assertEquals(-9923372036854775808d, parseDouble("-9923372036854775808,"), 0.01f);
3942
assertEquals(720368.54775807f, parseFloat("720368.54775807,"), 0.01f);
43+
assertEquals(-720368.54775807f, parseFloat("-720368.54775807,"), 0.01f);
4044
assertEquals(720368.54775807d, parseDouble("720368.54775807,"), 0.01f);
45+
assertEquals(-720368.54775807d, parseDouble("-720368.54775807,"), 0.01f);
4146
assertEquals(72036.854775807f, parseFloat("72036.854775807,"), 0.01f);
4247
assertEquals(72036.854775807d, parseDouble("72036.854775807,"), 0.01f);
4348
assertEquals(720368.54775807f, parseFloat("720368.547758075,"), 0.01f);

0 commit comments

Comments
 (0)