diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 728102e847..7bfb448282 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -33,6 +33,8 @@ a pure JSON library. #1274: `NUL`-corrupted keys, values on JSON serialization (reported, fix contributed by Jared S) #1277: Add back Java 22 optimisation in FastDoubleParser +#1284: Optimize `JsonParser.getDoubleValue()/getFloatValue()/getDecimalValue()` + to avoid String allocation #1305: Make helper methods of `WriterBasedJsonGenerator` non-final to allow overriding (contributed by @zhangOranges) #1310: Add new `StreamReadConstraints` (`maxTokenCount`) to limit maximum number 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 b987d84623..65f169badc 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -847,6 +847,7 @@ public double getDoubleValue() throws IOException if (_numTypesValid == NR_UNKNOWN) { _parseNumericValue(NR_DOUBLE); } + // if underlying type not FP, need conversion: if ((_numTypesValid & NR_DOUBLE) == 0) { convertNumberToDouble(); } @@ -987,17 +988,19 @@ private void _parseSlowFloat(int expType) throws IOException if (expType == NR_BIGDECIMAL) { // 04-Dec-2022, tatu: Let's defer actual decoding until it is certain // value is actually needed. - _numberBigDecimal = null; - _numberString = _textBuffer.contentsAsString(); + // 24-Jun-2024, tatu: No; we shouldn't have to defer unless specifically + // request w/ `getNumberValueDeferred()` or so + _numberBigDecimal = _textBuffer.contentsAsDecimal(isEnabled(StreamReadFeature.USE_FAST_BIG_NUMBER_PARSER)); _numTypesValid = NR_BIGDECIMAL; + } else if (expType == NR_DOUBLE) { + _numberDouble = _textBuffer.contentsAsDouble(isEnabled(StreamReadFeature.USE_FAST_DOUBLE_PARSER)); + _numTypesValid = NR_DOUBLE; } else if (expType == NR_FLOAT) { - _numberFloat = 0.0f; - _numberString = _textBuffer.contentsAsString(); + _numberFloat = _textBuffer.contentsAsFloat(isEnabled(StreamReadFeature.USE_FAST_DOUBLE_PARSER)); _numTypesValid = NR_FLOAT; - } else { - // Otherwise double has to do - // 04-Dec-2022, tatu: We can get all kinds of values here, NR_DOUBLE - // but also NR_INT or even NR_UNKNOWN. Shouldn't we try further + } else { // NR_UNKOWN, or one of int types + // 04-Dec-2022, tatu: We can get all kinds of values here + // (NR_INT, NR_LONG or even NR_UNKNOWN). Should we try further // deferring some typing? _numberDouble = 0.0; _numberString = _textBuffer.contentsAsString(); @@ -1248,7 +1251,8 @@ protected BigInteger _convertBigDecimalToBigInteger(BigDecimal bigDec) throws IO protected BigInteger _getBigInteger() throws JsonParseException { if (_numberBigInt != null) { return _numberBigInt; - } else if (_numberString == null) { + } + if (_numberString == null) { throw new IllegalStateException("cannot get BigInteger from current parser state"); } try { @@ -1276,7 +1280,8 @@ protected BigInteger _getBigInteger() throws JsonParseException { protected BigDecimal _getBigDecimal() throws JsonParseException { if (_numberBigDecimal != null) { return _numberBigDecimal; - } else if (_numberString == null) { + } + if (_numberString == null) { throw new IllegalStateException("cannot get BigDecimal from current parser state"); } try { diff --git a/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java b/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java index 585a8dd78b..5cef8dbb33 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java +++ b/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java @@ -569,9 +569,10 @@ public static BigDecimal parseBigDecimal(final char[] ch) throws NumberFormatExc * @since v2.15 */ public static BigDecimal parseBigDecimal(final char[] ch, final boolean useFastParser) throws NumberFormatException { - return useFastParser ? - BigDecimalParser.parseWithFastParser(ch, 0, ch.length) : - BigDecimalParser.parse(ch); + if (useFastParser) { + return BigDecimalParser.parseWithFastParser(ch, 0, ch.length); + } + return BigDecimalParser.parse(ch); } /** diff --git a/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java b/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java index 1c49aa8380..06c96fe48b 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java +++ b/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java @@ -551,7 +551,7 @@ public double contentsAsDouble(final boolean useFastParser) throws NumberFormatE if (_inputStart >= 0) { // shared? return NumberInput.parseDouble(_inputBuffer, _inputStart, _inputLen, useFastParser); } - if (_currentSize == 0) { // all content in current segment! + if (!_hasSegments) { // all content in current segment! return NumberInput.parseDouble(_currentSegment, 0, _currentSize, useFastParser); } if (_resultArray != null) { @@ -626,7 +626,7 @@ public float contentsAsFloat(final boolean useFastParser) throws NumberFormatExc if (_inputStart >= 0) { // shared? return NumberInput.parseFloat(_inputBuffer, _inputStart, _inputLen, useFastParser); } - if (_currentSize == 0) { // all content in current segment! + if (!_hasSegments) { // all content in current segment! return NumberInput.parseFloat(_currentSegment, 0, _currentSize, useFastParser); } if (_resultArray != null) { @@ -643,18 +643,38 @@ public float contentsAsFloat(final boolean useFastParser) throws NumberFormatExc } /** - * @return Buffered text value parsed as a {@link BigDecimal}, if possible - * @throws NumberFormatException if contents are not a valid Java number - * - * @deprecated Since 2.15 just access String contents if necessary, call - * {@link NumberInput#parseBigDecimal(String, boolean)} (or other overloads) - * directly instead + * @deprecated Since 2.15 use {@link #contentsAsDecimal(boolean)} instead. */ @Deprecated public BigDecimal contentsAsDecimal() throws NumberFormatException { - // Was more optimized earlier, removing special handling due to deprecation + return contentsAsDecimal(false); + } + + /** + * @since 2.18 + */ + public BigDecimal contentsAsDecimal(final boolean useFastParser) throws NumberFormatException + { + // Order in which check is somewhat arbitrary... try likeliest ones + // that do not require allocation first + + // except _resultString first since it works best with JDK (non-fast parser) + if (_resultString != null) { + return NumberInput.parseBigDecimal(_resultString, useFastParser); + } + if (_inputStart >= 0) { // shared? + return NumberInput.parseBigDecimal(_inputBuffer, _inputStart, _inputLen, useFastParser); + } + if (!_hasSegments) { // all content in current segment! + return NumberInput.parseBigDecimal(_currentSegment, 0, _currentSize, useFastParser); + } + if (_resultArray != null) { + return NumberInput.parseBigDecimal(_resultArray, useFastParser); + } + + // Otherwise, segmented so need to use slow path try { - return NumberInput.parseBigDecimal(contentsAsArray()); + return NumberInput.parseBigDecimal(contentsAsArray(), useFastParser); } catch (IOException e) { // JsonParseException is used to denote a string that is too long throw new NumberFormatException(e.getMessage());