From aa57816edec6754041a2bc28672ca9f1855c5a8e Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 23 Oct 2022 16:49:28 +0100 Subject: [PATCH 1/4] make BigInteger parsing lazy --- .../jackson/core/base/ParserBase.java | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java index 7b666e3526..3ffc7896a3 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -203,6 +203,8 @@ public abstract class ParserBase extends ParserMinimalBase protected BigDecimal _numberBigDecimal; + protected String _numberString; + // And then other information about value itself /** @@ -607,7 +609,7 @@ public Number getNumberValue() throws IOException return _numberLong; } if ((_numTypesValid & NR_BIGINT) != 0) { - return _numberBigInt; + return getBigInteger(); } _throwInternal(); } @@ -641,7 +643,7 @@ public Number getNumberValueExact() throws IOException return _numberLong; } if ((_numTypesValid & NR_BIGINT) != 0) { - return _numberBigInt; + return getBigInteger(); } _throwInternal(); } @@ -731,7 +733,7 @@ public BigInteger getBigIntegerValue() throws IOException convertNumberToBigInteger(); } } - return _numberBigInt; + return getBigInteger(); } @Override @@ -930,8 +932,9 @@ private void _parseSlowInt(int expType) throws IOException _numberDouble = NumberInput.parseDouble(numStr, isEnabled(Feature.USE_FAST_DOUBLE_PARSER)); _numTypesValid = NR_DOUBLE; } else { - // nope, need the heavy guns... (rare case) - _numberBigInt = NumberInput.parseBigInteger(numStr); + // nope, need the heavy guns... (rare case) - since Jackson v2.14, BigInteger parsing is lazy + _numberBigInt = null; + _numberString = numStr; _numTypesValid = NR_BIGINT; } } @@ -968,11 +971,12 @@ protected void convertNumberToInt() throws IOException } _numberInt = result; } else if ((_numTypesValid & NR_BIGINT) != 0) { - if (BI_MIN_INT.compareTo(_numberBigInt) > 0 - || BI_MAX_INT.compareTo(_numberBigInt) < 0) { + final BigInteger bigInteger = getBigInteger(); + if (BI_MIN_INT.compareTo(bigInteger) > 0 + || BI_MAX_INT.compareTo(bigInteger) < 0) { reportOverflowInt(); } - _numberInt = _numberBigInt.intValue(); + _numberInt = bigInteger.intValue(); } else if ((_numTypesValid & NR_DOUBLE) != 0) { // Need to check boundaries if (_numberDouble < MIN_INT_D || _numberDouble > MAX_INT_D) { @@ -996,11 +1000,12 @@ protected void convertNumberToLong() throws IOException if ((_numTypesValid & NR_INT) != 0) { _numberLong = (long) _numberInt; } else if ((_numTypesValid & NR_BIGINT) != 0) { - if (BI_MIN_LONG.compareTo(_numberBigInt) > 0 - || BI_MAX_LONG.compareTo(_numberBigInt) < 0) { + final BigInteger bigInteger = getBigInteger(); + if (BI_MIN_LONG.compareTo(bigInteger) > 0 + || BI_MAX_LONG.compareTo(bigInteger) < 0) { reportOverflowLong(); } - _numberLong = _numberBigInt.longValue(); + _numberLong = bigInteger.longValue(); } else if ((_numTypesValid & NR_DOUBLE) != 0) { // Need to check boundaries if (_numberDouble < MIN_LONG_D || _numberDouble > MAX_LONG_D) { @@ -1047,7 +1052,7 @@ protected void convertNumberToDouble() throws IOException if ((_numTypesValid & NR_BIGDECIMAL) != 0) { _numberDouble = _numberBigDecimal.doubleValue(); } else if ((_numTypesValid & NR_BIGINT) != 0) { - _numberDouble = _numberBigInt.doubleValue(); + _numberDouble = getBigInteger().doubleValue(); } else if ((_numTypesValid & NR_LONG) != 0) { _numberDouble = (double) _numberLong; } else if ((_numTypesValid & NR_INT) != 0) { @@ -1071,7 +1076,7 @@ protected void convertNumberToFloat() throws IOException if ((_numTypesValid & NR_BIGDECIMAL) != 0) { _numberFloat = _numberBigDecimal.floatValue(); } else if ((_numTypesValid & NR_BIGINT) != 0) { - _numberFloat = _numberBigInt.floatValue(); + _numberFloat = getBigInteger().floatValue(); } else if ((_numTypesValid & NR_LONG) != 0) { _numberFloat = (float) _numberLong; } else if ((_numTypesValid & NR_INT) != 0) { @@ -1098,7 +1103,7 @@ protected void convertNumberToBigDecimal() throws IOException */ _numberBigDecimal = NumberInput.parseBigDecimal(getText()); } else if ((_numTypesValid & NR_BIGINT) != 0) { - _numberBigDecimal = new BigDecimal(_numberBigInt); + _numberBigDecimal = new BigDecimal(getBigInteger()); } else if ((_numTypesValid & NR_LONG) != 0) { _numberBigDecimal = BigDecimal.valueOf(_numberLong); } else if ((_numTypesValid & NR_INT) != 0) { @@ -1345,4 +1350,15 @@ protected void loadMoreGuaranteed() throws IOException { // Can't declare as deprecated, for now, but shouldn't be needed protected void _finishString() throws IOException { } + + private BigInteger getBigInteger() { + if (_numberBigInt != null) { + return _numberBigInt; + } else if (_numberString == null) { + throw new IllegalStateException("cannot get BigInteger from current parser state"); + } + _numberBigInt = NumberInput.parseBigInteger(_numberString); + _numberString = null; + return _numberBigInt; + } } From f46c1c635e2306b947f12de763c5c68650884f27 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 23 Oct 2022 17:11:46 +0100 Subject: [PATCH 2/4] make big decimal parsing lazy --- .../jackson/core/base/ParserBase.java | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java index 3ffc7896a3..db1451007e 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -617,7 +617,7 @@ public Number getNumberValue() throws IOException // And then floating point types. But here optimal type // needs to be big decimal, to avoid losing any data? if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - return _numberBigDecimal; + return getBigDecimal(); } if ((_numTypesValid & NR_FLOAT) != 0) { return _numberFloat; @@ -652,7 +652,7 @@ public Number getNumberValueExact() throws IOException _parseNumericValue(NR_BIGDECIMAL); } if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - return _numberBigDecimal; + return getBigDecimal(); } if ((_numTypesValid & NR_FLOAT) != 0) { return _numberFloat; @@ -783,7 +783,7 @@ public BigDecimal getDecimalValue() throws IOException convertNumberToBigDecimal(); } } - return _numberBigDecimal; + return getBigDecimal(); } /* @@ -892,7 +892,8 @@ private void _parseSlowFloat(int expType) throws IOException */ try { if (expType == NR_BIGDECIMAL) { - _numberBigDecimal = _textBuffer.contentsAsDecimal(); + _numberBigDecimal = null; + _numberString = _textBuffer.contentsAsString(); _numTypesValid = NR_BIGDECIMAL; } else if (expType == NR_FLOAT) { _numberFloat = _textBuffer.contentsAsFloat(isEnabled(Feature.USE_FAST_DOUBLE_PARSER)); @@ -984,11 +985,12 @@ protected void convertNumberToInt() throws IOException } _numberInt = (int) _numberDouble; } else if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - if (BD_MIN_INT.compareTo(_numberBigDecimal) > 0 - || BD_MAX_INT.compareTo(_numberBigDecimal) < 0) { + final BigDecimal bigDecimal = getBigDecimal(); + if (BD_MIN_INT.compareTo(bigDecimal) > 0 + || BD_MAX_INT.compareTo(bigDecimal) < 0) { reportOverflowInt(); } - _numberInt = _numberBigDecimal.intValue(); + _numberInt = bigDecimal.intValue(); } else { _throwInternal(); } @@ -1013,11 +1015,12 @@ protected void convertNumberToLong() throws IOException } _numberLong = (long) _numberDouble; } else if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - if (BD_MIN_LONG.compareTo(_numberBigDecimal) > 0 - || BD_MAX_LONG.compareTo(_numberBigDecimal) < 0) { + final BigDecimal bigDecimal = getBigDecimal(); + if (BD_MIN_LONG.compareTo(bigDecimal) > 0 + || BD_MAX_LONG.compareTo(bigDecimal) < 0) { reportOverflowLong(); } - _numberLong = _numberBigDecimal.longValue(); + _numberLong = bigDecimal.longValue(); } else { _throwInternal(); } @@ -1028,7 +1031,7 @@ protected void convertNumberToBigInteger() throws IOException { if ((_numTypesValid & NR_BIGDECIMAL) != 0) { // here it'll just get truncated, no exceptions thrown - _numberBigInt = _numberBigDecimal.toBigInteger(); + _numberBigInt = getBigDecimal().toBigInteger(); } else if ((_numTypesValid & NR_LONG) != 0) { _numberBigInt = BigInteger.valueOf(_numberLong); } else if ((_numTypesValid & NR_INT) != 0) { @@ -1050,7 +1053,7 @@ protected void convertNumberToDouble() throws IOException */ if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - _numberDouble = _numberBigDecimal.doubleValue(); + _numberDouble = getBigDecimal().doubleValue(); } else if ((_numTypesValid & NR_BIGINT) != 0) { _numberDouble = getBigInteger().doubleValue(); } else if ((_numTypesValid & NR_LONG) != 0) { @@ -1074,7 +1077,7 @@ protected void convertNumberToFloat() throws IOException */ if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - _numberFloat = _numberBigDecimal.floatValue(); + _numberFloat = getBigDecimal().floatValue(); } else if ((_numTypesValid & NR_BIGINT) != 0) { _numberFloat = getBigInteger().floatValue(); } else if ((_numTypesValid & NR_LONG) != 0) { @@ -1361,4 +1364,15 @@ private BigInteger getBigInteger() { _numberString = null; return _numberBigInt; } + + private BigDecimal getBigDecimal() { + if (_numberBigDecimal != null) { + return _numberBigDecimal; + } else if (_numberString == null) { + throw new IllegalStateException("cannot get BigDecimal from current parser state"); + } + _numberBigDecimal = NumberInput.parseBigDecimal(_numberString); + _numberString = null; + return _numberBigDecimal; + } } From b5a5d48d615b91df6c5743a523a1954e96b5c75e Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 23 Oct 2022 19:39:52 +0100 Subject: [PATCH 3/4] rename methods --- .../jackson/core/base/ParserBase.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java index db1451007e..0e0c2c8485 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -609,7 +609,7 @@ public Number getNumberValue() throws IOException return _numberLong; } if ((_numTypesValid & NR_BIGINT) != 0) { - return getBigInteger(); + return _getBigInteger(); } _throwInternal(); } @@ -617,7 +617,7 @@ public Number getNumberValue() throws IOException // And then floating point types. But here optimal type // needs to be big decimal, to avoid losing any data? if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - return getBigDecimal(); + return _getBigDecimal(); } if ((_numTypesValid & NR_FLOAT) != 0) { return _numberFloat; @@ -643,7 +643,7 @@ public Number getNumberValueExact() throws IOException return _numberLong; } if ((_numTypesValid & NR_BIGINT) != 0) { - return getBigInteger(); + return _getBigInteger(); } _throwInternal(); } @@ -652,7 +652,7 @@ public Number getNumberValueExact() throws IOException _parseNumericValue(NR_BIGDECIMAL); } if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - return getBigDecimal(); + return _getBigDecimal(); } if ((_numTypesValid & NR_FLOAT) != 0) { return _numberFloat; @@ -733,7 +733,7 @@ public BigInteger getBigIntegerValue() throws IOException convertNumberToBigInteger(); } } - return getBigInteger(); + return _getBigInteger(); } @Override @@ -783,7 +783,7 @@ public BigDecimal getDecimalValue() throws IOException convertNumberToBigDecimal(); } } - return getBigDecimal(); + return _getBigDecimal(); } /* @@ -972,7 +972,7 @@ protected void convertNumberToInt() throws IOException } _numberInt = result; } else if ((_numTypesValid & NR_BIGINT) != 0) { - final BigInteger bigInteger = getBigInteger(); + final BigInteger bigInteger = _getBigInteger(); if (BI_MIN_INT.compareTo(bigInteger) > 0 || BI_MAX_INT.compareTo(bigInteger) < 0) { reportOverflowInt(); @@ -985,7 +985,7 @@ protected void convertNumberToInt() throws IOException } _numberInt = (int) _numberDouble; } else if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - final BigDecimal bigDecimal = getBigDecimal(); + final BigDecimal bigDecimal = _getBigDecimal(); if (BD_MIN_INT.compareTo(bigDecimal) > 0 || BD_MAX_INT.compareTo(bigDecimal) < 0) { reportOverflowInt(); @@ -1002,7 +1002,7 @@ protected void convertNumberToLong() throws IOException if ((_numTypesValid & NR_INT) != 0) { _numberLong = (long) _numberInt; } else if ((_numTypesValid & NR_BIGINT) != 0) { - final BigInteger bigInteger = getBigInteger(); + final BigInteger bigInteger = _getBigInteger(); if (BI_MIN_LONG.compareTo(bigInteger) > 0 || BI_MAX_LONG.compareTo(bigInteger) < 0) { reportOverflowLong(); @@ -1015,7 +1015,7 @@ protected void convertNumberToLong() throws IOException } _numberLong = (long) _numberDouble; } else if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - final BigDecimal bigDecimal = getBigDecimal(); + final BigDecimal bigDecimal = _getBigDecimal(); if (BD_MIN_LONG.compareTo(bigDecimal) > 0 || BD_MAX_LONG.compareTo(bigDecimal) < 0) { reportOverflowLong(); @@ -1031,7 +1031,7 @@ protected void convertNumberToBigInteger() throws IOException { if ((_numTypesValid & NR_BIGDECIMAL) != 0) { // here it'll just get truncated, no exceptions thrown - _numberBigInt = getBigDecimal().toBigInteger(); + _numberBigInt = _getBigDecimal().toBigInteger(); } else if ((_numTypesValid & NR_LONG) != 0) { _numberBigInt = BigInteger.valueOf(_numberLong); } else if ((_numTypesValid & NR_INT) != 0) { @@ -1053,9 +1053,9 @@ protected void convertNumberToDouble() throws IOException */ if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - _numberDouble = getBigDecimal().doubleValue(); + _numberDouble = _getBigDecimal().doubleValue(); } else if ((_numTypesValid & NR_BIGINT) != 0) { - _numberDouble = getBigInteger().doubleValue(); + _numberDouble = _getBigInteger().doubleValue(); } else if ((_numTypesValid & NR_LONG) != 0) { _numberDouble = (double) _numberLong; } else if ((_numTypesValid & NR_INT) != 0) { @@ -1077,9 +1077,9 @@ protected void convertNumberToFloat() throws IOException */ if ((_numTypesValid & NR_BIGDECIMAL) != 0) { - _numberFloat = getBigDecimal().floatValue(); + _numberFloat = _getBigDecimal().floatValue(); } else if ((_numTypesValid & NR_BIGINT) != 0) { - _numberFloat = getBigInteger().floatValue(); + _numberFloat = _getBigInteger().floatValue(); } else if ((_numTypesValid & NR_LONG) != 0) { _numberFloat = (float) _numberLong; } else if ((_numTypesValid & NR_INT) != 0) { @@ -1106,7 +1106,7 @@ protected void convertNumberToBigDecimal() throws IOException */ _numberBigDecimal = NumberInput.parseBigDecimal(getText()); } else if ((_numTypesValid & NR_BIGINT) != 0) { - _numberBigDecimal = new BigDecimal(getBigInteger()); + _numberBigDecimal = new BigDecimal(_getBigInteger()); } else if ((_numTypesValid & NR_LONG) != 0) { _numberBigDecimal = BigDecimal.valueOf(_numberLong); } else if ((_numTypesValid & NR_INT) != 0) { @@ -1354,7 +1354,7 @@ protected void loadMoreGuaranteed() throws IOException { // Can't declare as deprecated, for now, but shouldn't be needed protected void _finishString() throws IOException { } - private BigInteger getBigInteger() { + private BigInteger _getBigInteger() { if (_numberBigInt != null) { return _numberBigInt; } else if (_numberString == null) { @@ -1365,7 +1365,7 @@ private BigInteger getBigInteger() { return _numberBigInt; } - private BigDecimal getBigDecimal() { + private BigDecimal _getBigDecimal() { if (_numberBigDecimal != null) { return _numberBigDecimal; } else if (_numberString == null) { From d475c5dc511ca004a4fe77217134f5fbe86e13b1 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 24 Oct 2022 00:02:53 +0100 Subject: [PATCH 4/4] Update ParserBase.java --- .../jackson/core/base/ParserBase.java | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java index 7ca966e05d..76b824265a 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -1140,6 +1140,23 @@ protected BigInteger _getBigInteger() { _numberString = null; return _numberBigInt; } + + /** + * Internal accessor that needs to be used for accessing number value of type + * {@link BigDecimal} which -- as of 2.14 -- is typically lazily parsed. + * + * @since 2.14 + */ + protected BigDecimal _getBigDecimal() { + if (_numberBigDecimal != null) { + return _numberBigDecimal; + } else if (_numberString == null) { + throw new IllegalStateException("cannot get BigDecimal from current parser state"); + } + _numberBigDecimal = NumberInput.parseBigDecimal(_numberString); + _numberString = null; + return _numberBigDecimal; + } /* /********************************************************** @@ -1378,25 +1395,4 @@ protected void loadMoreGuaranteed() throws IOException { // Can't declare as deprecated, for now, but shouldn't be needed protected void _finishString() throws IOException { } - private BigInteger _getBigInteger() { - if (_numberBigInt != null) { - return _numberBigInt; - } else if (_numberString == null) { - throw new IllegalStateException("cannot get BigInteger from current parser state"); - } - _numberBigInt = NumberInput.parseBigInteger(_numberString); - _numberString = null; - return _numberBigInt; - } - - private BigDecimal _getBigDecimal() { - if (_numberBigDecimal != null) { - return _numberBigDecimal; - } else if (_numberString == null) { - throw new IllegalStateException("cannot get BigDecimal from current parser state"); - } - _numberBigDecimal = NumberInput.parseBigDecimal(_numberString); - _numberString = null; - return _numberBigDecimal; - } }