From 9291485007ddf57936a830ab1c3e05af0b1e21ac Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Wed, 30 Nov 2022 14:42:26 +0100 Subject: [PATCH 01/10] add USE_FAST_BIG_DECIMAL_PARSER --- .../fasterxml/jackson/core/JsonParser.java | 11 +++++ .../jackson/core/StreamReadFeature.java | 13 +++++- .../jackson/core/base/ParserBase.java | 6 ++- .../jackson/core/io/NumberInput.java | 44 +++++++++++++++++-- .../jackson/core/util/TextBuffer.java | 15 ++++--- 5 files changed, 76 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParser.java b/src/main/java/com/fasterxml/jackson/core/JsonParser.java index 50c6b0e1a7..75ffbd1f5b 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonParser.java @@ -348,6 +348,17 @@ public enum Feature { */ USE_FAST_DOUBLE_PARSER(false), + /** + * Feature that determines whether we use the built-in {@link new BigDecimal(String)} code to parse + * BigDecimals or if we use {@link com.fasterxml.jackson.core.io.doubleparser} + * instead. + *

+ * This setting is disabled by default for backwards compatibility. + * + * @since 2.15 + */ + USE_FAST_BIG_DECIMAL_PARSER(false) + ; /** diff --git a/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java b/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java index fb1fca0581..f24e385505 100644 --- a/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java +++ b/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java @@ -100,7 +100,18 @@ public enum StreamReadFeature * * @since 2.14 */ - USE_FAST_DOUBLE_PARSER(JsonParser.Feature.USE_FAST_DOUBLE_PARSER) + USE_FAST_DOUBLE_PARSER(JsonParser.Feature.USE_FAST_DOUBLE_PARSER), + + /** + * Feature that determines whether we use the built-in {@link new BigDecimal(String)} code to parse + * BigDecimals or if we use {@link com.fasterxml.jackson.core.io.doubleparser} + * instead. + *

+ * This setting is disabled by default. + * + * @since 2.15 + */ + USE_FAST_BIG_DECIMAL_PARSER(JsonParser.Feature.USE_FAST_BIG_DECIMAL_PARSER) ; 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 79c1f3f8ee..a156dcbc87 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -1148,7 +1148,8 @@ protected BigInteger _getBigInteger() { } else if (_numberString == null) { throw new IllegalStateException("cannot get BigInteger from current parser state"); } - _numberBigInt = NumberInput.parseBigInteger(_numberString); + _numberBigInt = NumberInput.parseBigInteger( + _numberString, isEnabled(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER)); _numberString = null; return _numberBigInt; } @@ -1165,7 +1166,8 @@ protected BigDecimal _getBigDecimal() { } else if (_numberString == null) { throw new IllegalStateException("cannot get BigDecimal from current parser state"); } - _numberBigDecimal = NumberInput.parseBigDecimal(_numberString); + _numberBigDecimal = NumberInput.parseBigDecimal( + _numberString, isEnabled(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER)); _numberString = null; return _numberBigDecimal; } 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 8ac3e90b44..4b356e18f0 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java +++ b/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java @@ -1,5 +1,7 @@ package com.fasterxml.jackson.core.io; +import ch.randelshofer.fastdoubleparser.JavaBigDecimalParser; +import ch.randelshofer.fastdoubleparser.JavaBigIntegerParser; import ch.randelshofer.fastdoubleparser.JavaDoubleParser; import ch.randelshofer.fastdoubleparser.JavaFloatParser; @@ -380,28 +382,62 @@ public static float parseFloat(final String s, final boolean useFastParser) thro return useFastParser ? JavaFloatParser.parseFloat(s) : Float.parseFloat(s); } - public static BigDecimal parseBigDecimal(String s) throws NumberFormatException { + public static BigDecimal parseBigDecimal(final String s) throws NumberFormatException { return BigDecimalParser.parse(s); } - public static BigDecimal parseBigDecimal(char[] ch, int off, int len) throws NumberFormatException { + public static BigDecimal parseBigDecimal(final String s, final boolean useFastParser) throws NumberFormatException { + return useFastParser ? + JavaBigDecimalParser.parseBigDecimal(s) : + BigDecimalParser.parse(s); + } + + public static BigDecimal parseBigDecimal(final char[] ch, final int off, final int len) throws NumberFormatException { return BigDecimalParser.parse(ch, off, len); } - public static BigDecimal parseBigDecimal(char[] ch) throws NumberFormatException { + public static BigDecimal parseBigDecimal(final char[] ch, final int off, final int len, final boolean useFastParser) + throws NumberFormatException { + return useFastParser ? + JavaBigDecimalParser.parseBigDecimal(ch, off, len) : + BigDecimalParser.parse(ch, off, len); + } + + public static BigDecimal parseBigDecimal(final char[] ch) throws NumberFormatException { return BigDecimalParser.parse(ch); } + public static BigDecimal parseBigDecimal(final char[] ch, final boolean useFastParser) throws NumberFormatException { + return useFastParser ? + JavaBigDecimalParser.parseBigDecimal(ch) : + BigDecimalParser.parse(ch); + } + /** * @param s a string representing a number to parse * @return a BigInteger * @throws NumberFormatException if string cannot be represented by a BigInteger * @since v2.14 */ - public static BigInteger parseBigInteger(String s) throws NumberFormatException { + public static BigInteger parseBigInteger(final String s) throws NumberFormatException { if (s.length() > LARGE_INT_SIZE) { return BigDecimalParser.parse(s).toBigInteger(); } return new BigInteger(s); } + + /** + * @param s a string representing a number to parse + * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} + * @return a BigInteger + * @throws NumberFormatException if string cannot be represented by a BigInteger + * @since v2.15 + */ + public static BigInteger parseBigInteger(final String s, final boolean useFastParser) throws NumberFormatException { + if (useFastParser) { + return JavaBigIntegerParser.parseBigInteger(s); + } else { + return parseBigInteger(s); + } + } } 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 6093a6306d..910d31dd3b 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java +++ b/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java @@ -484,38 +484,41 @@ public char[] contentsAsArray() { * Convenience method for converting contents of the buffer * into a {@link BigDecimal}. * + * @param constraints constraints for stream reading + * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} * @return Buffered text value parsed as a {@link BigDecimal}, if possible * * @throws NumberFormatException if contents are not a valid Java number * * @since 2.15 */ - public BigDecimal contentsAsDecimal(StreamReadConstraints constraints) throws NumberFormatException + public BigDecimal contentsAsDecimal(final StreamReadConstraints constraints, + final boolean useFastParser) throws NumberFormatException { // Already got a pre-cut array? if (_resultArray != null) { constraints.validateFPLength(_resultArray.length); - return NumberInput.parseBigDecimal(_resultArray); + return NumberInput.parseBigDecimal(_resultArray, useFastParser); } // Or a shared buffer? if ((_inputStart >= 0) && (_inputBuffer != null)) { constraints.validateFPLength(_inputLen); - return NumberInput.parseBigDecimal(_inputBuffer, _inputStart, _inputLen); + return NumberInput.parseBigDecimal(_inputBuffer, _inputStart, _inputLen, useFastParser); } // Or if not, just a single buffer (the usual case) if ((_segmentSize == 0) && (_currentSegment != null)) { constraints.validateFPLength(_currentSize); - return NumberInput.parseBigDecimal(_currentSegment, 0, _currentSize); + return NumberInput.parseBigDecimal(_currentSegment, 0, _currentSize, useFastParser); } // If not, let's just get it aggregated... final char[] numArray = contentsAsArray(); constraints.validateFPLength(numArray.length); - return NumberInput.parseBigDecimal(numArray); + return NumberInput.parseBigDecimal(numArray, useFastParser); } @Deprecated // @since 2.15 public BigDecimal contentsAsDecimal() throws NumberFormatException { - return contentsAsDecimal(StreamReadConstraints.defaults()); + return contentsAsDecimal(StreamReadConstraints.defaults(), false); } /** From 7b5a92ce7ac4665fdade5d73960fd497f76cc895 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Wed, 30 Nov 2022 14:53:23 +0100 Subject: [PATCH 02/10] ensure large numbers are not printed in entirety (error messages) --- .../jackson/core/io/BigDecimalParser.java | 38 +++++++++++++++++++ .../jackson/core/io/NumberInput.java | 10 ++--- 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java b/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java index 173af8300e..55472fd407 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java +++ b/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java @@ -1,6 +1,10 @@ package com.fasterxml.jackson.core.io; +import ch.randelshofer.fastdoubleparser.JavaBigDecimalParser; +import ch.randelshofer.fastdoubleparser.JavaBigIntegerParser; + import java.math.BigDecimal; +import java.math.BigInteger; import java.util.Arrays; // Based on a great idea of Eric Obermühlner to use a tree of smaller BigDecimals for parsing @@ -60,6 +64,40 @@ public static BigDecimal parse(char[] chars) { return parse(chars, 0, chars.length); } + public static BigDecimal parseWithFastParser(final String valueStr) { + try { + return JavaBigDecimalParser.parseBigDecimal(valueStr); + } catch (NumberFormatException nfe) { + final String reportNum = valueStr.length() <= MAX_CHARS_TO_REPORT ? + valueStr : valueStr.substring(0, MAX_CHARS_TO_REPORT) + " [truncated]"; + throw new NumberFormatException("Value \"" + reportNum + + "\" can not be represented as `java.math.BigDecimal`, reason: " + nfe.getMessage()); + } + } + + public static BigDecimal parseWithFastParser(final char[] ch, final int off, final int len) { + try { + return JavaBigDecimalParser.parseBigDecimal(ch, off, len); + } catch (NumberFormatException nfe) { + final String reportNum = len <= MAX_CHARS_TO_REPORT ? + new String(ch, off, len) : new String(ch, off, MAX_CHARS_TO_REPORT) + " [truncated]"; + final int reportLen = Math.min(len, MAX_CHARS_TO_REPORT); + throw new NumberFormatException("Value \"" + new String(ch, off, reportLen) + + "\" can not be represented as `java.math.BigDecimal`, reason: " + nfe.getMessage()); + } + } + + public static BigInteger parseBigIntegerWithFastParser(final String valueStr) { + try { + return JavaBigIntegerParser.parseBigInteger(valueStr); + } catch (NumberFormatException nfe) { + final String reportNum = valueStr.length() <= MAX_CHARS_TO_REPORT ? + valueStr : valueStr.substring(0, MAX_CHARS_TO_REPORT) + " [truncated]"; + throw new NumberFormatException("Value \"" + reportNum + + "\" can not be represented as `java.math.BigDecimal`, reason: " + nfe.getMessage()); + } + } + private static BigDecimal parseBigDecimal(final char[] chars, final int off, final int len, final int splitLen) { boolean numHasSign = false; boolean expHasSign = false; 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 4b356e18f0..7f76af9887 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java +++ b/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java @@ -1,7 +1,5 @@ package com.fasterxml.jackson.core.io; -import ch.randelshofer.fastdoubleparser.JavaBigDecimalParser; -import ch.randelshofer.fastdoubleparser.JavaBigIntegerParser; import ch.randelshofer.fastdoubleparser.JavaDoubleParser; import ch.randelshofer.fastdoubleparser.JavaFloatParser; @@ -388,7 +386,7 @@ public static BigDecimal parseBigDecimal(final String s) throws NumberFormatExce public static BigDecimal parseBigDecimal(final String s, final boolean useFastParser) throws NumberFormatException { return useFastParser ? - JavaBigDecimalParser.parseBigDecimal(s) : + BigDecimalParser.parseWithFastParser(s) : BigDecimalParser.parse(s); } @@ -399,7 +397,7 @@ public static BigDecimal parseBigDecimal(final char[] ch, final int off, final i public static BigDecimal parseBigDecimal(final char[] ch, final int off, final int len, final boolean useFastParser) throws NumberFormatException { return useFastParser ? - JavaBigDecimalParser.parseBigDecimal(ch, off, len) : + BigDecimalParser.parseWithFastParser(ch, off, len) : BigDecimalParser.parse(ch, off, len); } @@ -409,7 +407,7 @@ public static BigDecimal parseBigDecimal(final char[] ch) throws NumberFormatExc public static BigDecimal parseBigDecimal(final char[] ch, final boolean useFastParser) throws NumberFormatException { return useFastParser ? - JavaBigDecimalParser.parseBigDecimal(ch) : + BigDecimalParser.parseWithFastParser(ch, 0, ch.length) : BigDecimalParser.parse(ch); } @@ -435,7 +433,7 @@ public static BigInteger parseBigInteger(final String s) throws NumberFormatExce */ public static BigInteger parseBigInteger(final String s, final boolean useFastParser) throws NumberFormatException { if (useFastParser) { - return JavaBigIntegerParser.parseBigInteger(s); + return BigDecimalParser.parseBigIntegerWithFastParser(s); } else { return parseBigInteger(s); } From f7e3f3ee7db86d080faf58801333bb1c89d149c9 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 1 Dec 2022 01:02:51 +0100 Subject: [PATCH 03/10] add javadoc --- .../jackson/core/io/NumberInput.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) 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 7f76af9887..02bd6914c5 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java +++ b/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java @@ -380,20 +380,48 @@ public static float parseFloat(final String s, final boolean useFastParser) thro return useFastParser ? JavaFloatParser.parseFloat(s) : Float.parseFloat(s); } + /** + * @param s a string representing a number to parse + * @return a BigDecimal + * @throws NumberFormatException if the char array cannot be represented by a BigDecimal + */ public static BigDecimal parseBigDecimal(final String s) throws NumberFormatException { return BigDecimalParser.parse(s); } + /** + * @param s a string representing a number to parse + * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} + * @return a BigDecimal + * @throws NumberFormatException if the char array cannot be represented by a BigDecimal + * @since v2.15 + */ public static BigDecimal parseBigDecimal(final String s, final boolean useFastParser) throws NumberFormatException { return useFastParser ? BigDecimalParser.parseWithFastParser(s) : BigDecimalParser.parse(s); } + /** + * @param ch a char array with text that makes up a number + * @param off the offset to apply when parsing the number in the char array + * @param len the length of the number in the char array + * @return a BigDecimal + * @throws NumberFormatException if the char array cannot be represented by a BigDecimal + */ public static BigDecimal parseBigDecimal(final char[] ch, final int off, final int len) throws NumberFormatException { return BigDecimalParser.parse(ch, off, len); } + /** + * @param ch a char array with text that makes up a number + * @param off the offset to apply when parsing the number in the char array + * @param len the length of the number in the char array + * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} + * @return a BigDecimal + * @throws NumberFormatException if the char array cannot be represented by a BigDecimal + * @since v2.15 + */ public static BigDecimal parseBigDecimal(final char[] ch, final int off, final int len, final boolean useFastParser) throws NumberFormatException { return useFastParser ? @@ -401,10 +429,22 @@ public static BigDecimal parseBigDecimal(final char[] ch, final int off, final i BigDecimalParser.parse(ch, off, len); } + /** + * @param ch a char array with text that makes up a number + * @return a BigDecimal + * @throws NumberFormatException if the char array cannot be represented by a BigDecimal + */ public static BigDecimal parseBigDecimal(final char[] ch) throws NumberFormatException { return BigDecimalParser.parse(ch); } + /** + * @param ch a char array with text that makes up a number + * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} + * @return a BigDecimal + * @throws NumberFormatException if the char array cannot be represented by a BigDecimal + * @since v2.15 + */ public static BigDecimal parseBigDecimal(final char[] ch, final boolean useFastParser) throws NumberFormatException { return useFastParser ? BigDecimalParser.parseWithFastParser(ch, 0, ch.length) : From b47ed3a663fc1a57b1de790776a3479cb4e8bff4 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 1 Dec 2022 01:19:27 +0100 Subject: [PATCH 04/10] add some tests --- .../jackson/core/io/BigDecimalParserTest.java | 36 ++++++++++++++++--- .../jackson/core/io/TestNumberInput.java | 2 ++ 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java b/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java index 436d74cd1d..d02f68a4c8 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java @@ -2,17 +2,43 @@ public class BigDecimalParserTest extends com.fasterxml.jackson.core.BaseTest { public void testLongStringParse() { - final int len = 1500; - final StringBuilder sb = new StringBuilder(len); - for (int i = 0; i < len; i++) { - sb.append("A"); + try { + BigDecimalParser.parse(genLongString()); + fail("expected NumberFormatException"); + } catch (NumberFormatException nfe) { + assertTrue("exception message starts as expected?", nfe.getMessage().startsWith("Value \"AAAAA")); + assertTrue("exception message value contains truncated", nfe.getMessage().contains("truncated")); + } + } + + public void testLongStringFastParse() { + try { + BigDecimalParser.parseWithFastParser(genLongString()); + fail("expected NumberFormatException"); + } catch (NumberFormatException nfe) { + assertTrue("exception message starts as expected?", nfe.getMessage().startsWith("Value \"AAAAA")); + assertTrue("exception message value contains truncated", nfe.getMessage().contains("truncated")); } + } + + /* there is an open issue at https://github.com/wrandelshofer/FastDoubleParser/issues/24 about this + public void testLongStringFastParseBigInteger() { try { - BigDecimalParser.parse(sb.toString()); + BigDecimalParser.parseBigIntegerWithFastParser(genLongString()); fail("expected NumberFormatException"); } catch (NumberFormatException nfe) { assertTrue("exception message starts as expected?", nfe.getMessage().startsWith("Value \"AAAAA")); assertTrue("exception message value contains truncated", nfe.getMessage().contains("truncated")); } } + */ + + private String genLongString() { + final int len = 1500; + final StringBuilder sb = new StringBuilder(len); + for (int i = 0; i < len; i++) { + sb.append("A"); + } + return sb.toString(); + } } diff --git a/src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java b/src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java index 544aa9c85e..e1411b532e 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java +++ b/src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java @@ -35,11 +35,13 @@ public void testParseLongBigInteger() } String test1000 = stringBuilder.toString(); assertEquals(new BigInteger(test1000), NumberInput.parseBigInteger(test1000)); + assertEquals(new BigInteger(test1000), NumberInput.parseBigInteger(test1000, true)); for (int i = 0; i < 1000; i++) { stringBuilder.append(7); } String test2000 = stringBuilder.toString(); assertEquals(new BigInteger(test2000), NumberInput.parseBigInteger(test2000)); + assertEquals(new BigInteger(test2000), NumberInput.parseBigInteger(test2000, true)); } } From a46ebb63393549c5ac6dc4742e432608c7d81145 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 1 Dec 2022 01:38:54 +0100 Subject: [PATCH 05/10] support parallel parsing --- .../fasterxml/jackson/core/JsonParser.java | 12 +++++++++- .../jackson/core/StreamReadFeature.java | 12 +++++++++- .../jackson/core/base/ParserBase.java | 8 +++++-- .../jackson/core/io/BigDecimalParser.java | 19 ++++++++++----- .../jackson/core/io/NumberInput.java | 24 ++++++++++++------- .../jackson/core/util/TextBuffer.java | 13 +++++----- .../jackson/core/io/BigDecimalParserTest.java | 12 +++++++++- .../jackson/core/io/TestNumberInput.java | 6 +++-- 8 files changed, 79 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParser.java b/src/main/java/com/fasterxml/jackson/core/JsonParser.java index 75ffbd1f5b..8d6d7be934 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonParser.java @@ -357,7 +357,17 @@ public enum Feature { * * @since 2.15 */ - USE_FAST_BIG_DECIMAL_PARSER(false) + USE_FAST_BIG_DECIMAL_PARSER(false), + + /** + * If USE_FAST_BIG_DECIMAL_PARSER is enabled, then enabling this feature can + * further speed up parsing by using multiple threads. + *

+ * This setting is disabled by default for backwards compatibility. + * + * @since 2.15 + */ + USE_PARALLEL_FAST_BIG_DECIMAL_PARSER(false) ; diff --git a/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java b/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java index f24e385505..2c5fe1b996 100644 --- a/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java +++ b/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java @@ -111,7 +111,17 @@ public enum StreamReadFeature * * @since 2.15 */ - USE_FAST_BIG_DECIMAL_PARSER(JsonParser.Feature.USE_FAST_BIG_DECIMAL_PARSER) + USE_FAST_BIG_DECIMAL_PARSER(JsonParser.Feature.USE_FAST_BIG_DECIMAL_PARSER), + + /** + * If USE_FAST_BIG_DECIMAL_PARSER is enabled, then enabling this feature can + * further speed up parsing by using multiple threads. + *

+ * This setting is disabled by default for backwards compatibility. + * + * @since 2.15 + */ + USE_PARALLEL_FAST_BIG_DECIMAL_PARSER(JsonParser.Feature.USE_PARALLEL_FAST_BIG_DECIMAL_PARSER) ; 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 a156dcbc87..01bcf6f7cc 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -1149,7 +1149,9 @@ protected BigInteger _getBigInteger() { throw new IllegalStateException("cannot get BigInteger from current parser state"); } _numberBigInt = NumberInput.parseBigInteger( - _numberString, isEnabled(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER)); + _numberString, + isEnabled(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER), + isEnabled(StreamReadFeature.USE_PARALLEL_FAST_BIG_DECIMAL_PARSER)); _numberString = null; return _numberBigInt; } @@ -1167,7 +1169,9 @@ protected BigDecimal _getBigDecimal() { throw new IllegalStateException("cannot get BigDecimal from current parser state"); } _numberBigDecimal = NumberInput.parseBigDecimal( - _numberString, isEnabled(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER)); + _numberString, + isEnabled(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER), + isEnabled(StreamReadFeature.USE_PARALLEL_FAST_BIG_DECIMAL_PARSER)); _numberString = null; return _numberBigDecimal; } diff --git a/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java b/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java index 55472fd407..ae71dba93c 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java +++ b/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java @@ -64,9 +64,11 @@ public static BigDecimal parse(char[] chars) { return parse(chars, 0, chars.length); } - public static BigDecimal parseWithFastParser(final String valueStr) { + public static BigDecimal parseWithFastParser(final String valueStr, final boolean allowParallelParsing) { try { - return JavaBigDecimalParser.parseBigDecimal(valueStr); + return allowParallelParsing ? + JavaBigDecimalParser.parallelParseBigDecimal(valueStr) : + JavaBigDecimalParser.parseBigDecimal(valueStr); } catch (NumberFormatException nfe) { final String reportNum = valueStr.length() <= MAX_CHARS_TO_REPORT ? valueStr : valueStr.substring(0, MAX_CHARS_TO_REPORT) + " [truncated]"; @@ -75,9 +77,12 @@ public static BigDecimal parseWithFastParser(final String valueStr) { } } - public static BigDecimal parseWithFastParser(final char[] ch, final int off, final int len) { + public static BigDecimal parseWithFastParser(final char[] ch, final int off, final int len, + final boolean allowParallelParsing) { try { - return JavaBigDecimalParser.parseBigDecimal(ch, off, len); + return allowParallelParsing ? + JavaBigDecimalParser.parallelParseBigDecimal(ch, off, len) : + JavaBigDecimalParser.parseBigDecimal(ch, off, len); } catch (NumberFormatException nfe) { final String reportNum = len <= MAX_CHARS_TO_REPORT ? new String(ch, off, len) : new String(ch, off, MAX_CHARS_TO_REPORT) + " [truncated]"; @@ -87,9 +92,11 @@ public static BigDecimal parseWithFastParser(final char[] ch, final int off, fin } } - public static BigInteger parseBigIntegerWithFastParser(final String valueStr) { + public static BigInteger parseBigIntegerWithFastParser(final String valueStr, final boolean allowParallelParsing) { try { - return JavaBigIntegerParser.parseBigInteger(valueStr); + return allowParallelParsing ? + JavaBigIntegerParser.parallelParseBigInteger(valueStr) : + JavaBigIntegerParser.parseBigInteger(valueStr); } catch (NumberFormatException nfe) { final String reportNum = valueStr.length() <= MAX_CHARS_TO_REPORT ? valueStr : valueStr.substring(0, MAX_CHARS_TO_REPORT) + " [truncated]"; 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 02bd6914c5..0cf27c0c17 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java +++ b/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java @@ -392,13 +392,15 @@ public static BigDecimal parseBigDecimal(final String s) throws NumberFormatExce /** * @param s a string representing a number to parse * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} + * @param allowParallelParsing whether to use multiple threads when using fast parser * @return a BigDecimal * @throws NumberFormatException if the char array cannot be represented by a BigDecimal * @since v2.15 */ - public static BigDecimal parseBigDecimal(final String s, final boolean useFastParser) throws NumberFormatException { + public static BigDecimal parseBigDecimal(final String s, final boolean useFastParser, + final boolean allowParallelParsing) throws NumberFormatException { return useFastParser ? - BigDecimalParser.parseWithFastParser(s) : + BigDecimalParser.parseWithFastParser(s, allowParallelParsing) : BigDecimalParser.parse(s); } @@ -418,14 +420,16 @@ public static BigDecimal parseBigDecimal(final char[] ch, final int off, final i * @param off the offset to apply when parsing the number in the char array * @param len the length of the number in the char array * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} + * @param allowParallelParsing whether to use multiple threads when using fast parser * @return a BigDecimal * @throws NumberFormatException if the char array cannot be represented by a BigDecimal * @since v2.15 */ - public static BigDecimal parseBigDecimal(final char[] ch, final int off, final int len, final boolean useFastParser) + public static BigDecimal parseBigDecimal(final char[] ch, final int off, final int len, + final boolean useFastParser, final boolean allowParallelParsing) throws NumberFormatException { return useFastParser ? - BigDecimalParser.parseWithFastParser(ch, off, len) : + BigDecimalParser.parseWithFastParser(ch, off, len, allowParallelParsing) : BigDecimalParser.parse(ch, off, len); } @@ -441,13 +445,15 @@ public static BigDecimal parseBigDecimal(final char[] ch) throws NumberFormatExc /** * @param ch a char array with text that makes up a number * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} + * @param allowParallelParsing whether to use multiple threads when using fast parser * @return a BigDecimal * @throws NumberFormatException if the char array cannot be represented by a BigDecimal * @since v2.15 */ - public static BigDecimal parseBigDecimal(final char[] ch, final boolean useFastParser) throws NumberFormatException { + public static BigDecimal parseBigDecimal(final char[] ch, final boolean useFastParser, + final boolean allowParallelParsing) throws NumberFormatException { return useFastParser ? - BigDecimalParser.parseWithFastParser(ch, 0, ch.length) : + BigDecimalParser.parseWithFastParser(ch, 0, ch.length, allowParallelParsing) : BigDecimalParser.parse(ch); } @@ -467,13 +473,15 @@ public static BigInteger parseBigInteger(final String s) throws NumberFormatExce /** * @param s a string representing a number to parse * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} + * @param allowParallelParsing whether to use multiple threads when using fast parser * @return a BigInteger * @throws NumberFormatException if string cannot be represented by a BigInteger * @since v2.15 */ - public static BigInteger parseBigInteger(final String s, final boolean useFastParser) throws NumberFormatException { + public static BigInteger parseBigInteger(final String s, final boolean useFastParser, + final boolean allowParallelParsing) throws NumberFormatException { if (useFastParser) { - return BigDecimalParser.parseBigIntegerWithFastParser(s); + return BigDecimalParser.parseBigIntegerWithFastParser(s, allowParallelParsing); } else { return parseBigInteger(s); } 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 910d31dd3b..e14d92ddf7 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java +++ b/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java @@ -493,32 +493,33 @@ public char[] contentsAsArray() { * @since 2.15 */ public BigDecimal contentsAsDecimal(final StreamReadConstraints constraints, - final boolean useFastParser) throws NumberFormatException + final boolean useFastParser, + final boolean allowParallelParsing) throws NumberFormatException { // Already got a pre-cut array? if (_resultArray != null) { constraints.validateFPLength(_resultArray.length); - return NumberInput.parseBigDecimal(_resultArray, useFastParser); + return NumberInput.parseBigDecimal(_resultArray, useFastParser, allowParallelParsing); } // Or a shared buffer? if ((_inputStart >= 0) && (_inputBuffer != null)) { constraints.validateFPLength(_inputLen); - return NumberInput.parseBigDecimal(_inputBuffer, _inputStart, _inputLen, useFastParser); + return NumberInput.parseBigDecimal(_inputBuffer, _inputStart, _inputLen, useFastParser, allowParallelParsing); } // Or if not, just a single buffer (the usual case) if ((_segmentSize == 0) && (_currentSegment != null)) { constraints.validateFPLength(_currentSize); - return NumberInput.parseBigDecimal(_currentSegment, 0, _currentSize, useFastParser); + return NumberInput.parseBigDecimal(_currentSegment, 0, _currentSize, useFastParser, allowParallelParsing); } // If not, let's just get it aggregated... final char[] numArray = contentsAsArray(); constraints.validateFPLength(numArray.length); - return NumberInput.parseBigDecimal(numArray, useFastParser); + return NumberInput.parseBigDecimal(numArray, useFastParser, allowParallelParsing); } @Deprecated // @since 2.15 public BigDecimal contentsAsDecimal() throws NumberFormatException { - return contentsAsDecimal(StreamReadConstraints.defaults(), false); + return contentsAsDecimal(StreamReadConstraints.defaults(), false, false); } /** diff --git a/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java b/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java index d02f68a4c8..27ea8c2ac0 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java @@ -13,7 +13,17 @@ public void testLongStringParse() { public void testLongStringFastParse() { try { - BigDecimalParser.parseWithFastParser(genLongString()); + BigDecimalParser.parseWithFastParser(genLongString(), false); + fail("expected NumberFormatException"); + } catch (NumberFormatException nfe) { + assertTrue("exception message starts as expected?", nfe.getMessage().startsWith("Value \"AAAAA")); + assertTrue("exception message value contains truncated", nfe.getMessage().contains("truncated")); + } + } + + public void testLongStringFastParseParallel() { + try { + BigDecimalParser.parseWithFastParser(genLongString(), true); fail("expected NumberFormatException"); } catch (NumberFormatException nfe) { assertTrue("exception message starts as expected?", nfe.getMessage().startsWith("Value \"AAAAA")); diff --git a/src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java b/src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java index e1411b532e..3b746235ed 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java +++ b/src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java @@ -35,13 +35,15 @@ public void testParseLongBigInteger() } String test1000 = stringBuilder.toString(); assertEquals(new BigInteger(test1000), NumberInput.parseBigInteger(test1000)); - assertEquals(new BigInteger(test1000), NumberInput.parseBigInteger(test1000, true)); + assertEquals(new BigInteger(test1000), NumberInput.parseBigInteger(test1000, true, false)); + assertEquals(new BigInteger(test1000), NumberInput.parseBigInteger(test1000, true, true)); for (int i = 0; i < 1000; i++) { stringBuilder.append(7); } String test2000 = stringBuilder.toString(); assertEquals(new BigInteger(test2000), NumberInput.parseBigInteger(test2000)); - assertEquals(new BigInteger(test2000), NumberInput.parseBigInteger(test2000, true)); + assertEquals(new BigInteger(test2000), NumberInput.parseBigInteger(test2000, true, false)); + assertEquals(new BigInteger(test2000), NumberInput.parseBigInteger(test2000, true, true)); } } From 748803480f9ab4583a6e6ae339161b2c9221a678 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 1 Dec 2022 01:42:35 +0100 Subject: [PATCH 06/10] don't refer to threads --- src/main/java/com/fasterxml/jackson/core/JsonParser.java | 2 +- .../com/fasterxml/jackson/core/StreamReadFeature.java | 2 +- .../java/com/fasterxml/jackson/core/io/NumberInput.java | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParser.java b/src/main/java/com/fasterxml/jackson/core/JsonParser.java index 8d6d7be934..299c410ca5 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonParser.java @@ -361,7 +361,7 @@ public enum Feature { /** * If USE_FAST_BIG_DECIMAL_PARSER is enabled, then enabling this feature can - * further speed up parsing by using multiple threads. + * further speed up parsing by using parallel processing. *

* This setting is disabled by default for backwards compatibility. * diff --git a/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java b/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java index 2c5fe1b996..74c095333a 100644 --- a/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java +++ b/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java @@ -115,7 +115,7 @@ public enum StreamReadFeature /** * If USE_FAST_BIG_DECIMAL_PARSER is enabled, then enabling this feature can - * further speed up parsing by using multiple threads. + * further speed up parsing by using parallel processing. *

* This setting is disabled by default for backwards compatibility. * 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 0cf27c0c17..064dd8d318 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java +++ b/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java @@ -392,7 +392,7 @@ public static BigDecimal parseBigDecimal(final String s) throws NumberFormatExce /** * @param s a string representing a number to parse * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} - * @param allowParallelParsing whether to use multiple threads when using fast parser + * @param allowParallelParsing whether to use parallel processing when using fast parser * @return a BigDecimal * @throws NumberFormatException if the char array cannot be represented by a BigDecimal * @since v2.15 @@ -420,7 +420,7 @@ public static BigDecimal parseBigDecimal(final char[] ch, final int off, final i * @param off the offset to apply when parsing the number in the char array * @param len the length of the number in the char array * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} - * @param allowParallelParsing whether to use multiple threads when using fast parser + * @param allowParallelParsing whether to use parallel processing when using fast parser * @return a BigDecimal * @throws NumberFormatException if the char array cannot be represented by a BigDecimal * @since v2.15 @@ -445,7 +445,7 @@ public static BigDecimal parseBigDecimal(final char[] ch) throws NumberFormatExc /** * @param ch a char array with text that makes up a number * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} - * @param allowParallelParsing whether to use multiple threads when using fast parser + * @param allowParallelParsing whether to use parallel processing when using fast parser * @return a BigDecimal * @throws NumberFormatException if the char array cannot be represented by a BigDecimal * @since v2.15 @@ -473,7 +473,7 @@ public static BigInteger parseBigInteger(final String s) throws NumberFormatExce /** * @param s a string representing a number to parse * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} - * @param allowParallelParsing whether to use multiple threads when using fast parser + * @param allowParallelParsing whether to use parallel processing when using fast parser * @return a BigInteger * @throws NumberFormatException if string cannot be represented by a BigInteger * @since v2.15 From c11efa2a7e058c06b8a5d5774fe3f8ab6f7b598d Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 1 Dec 2022 12:22:07 +0100 Subject: [PATCH 07/10] remove parallel parsing feature --- .../fasterxml/jackson/core/JsonParser.java | 12 +--------- .../jackson/core/StreamReadFeature.java | 12 +--------- .../jackson/core/base/ParserBase.java | 6 ++--- .../jackson/core/io/BigDecimalParser.java | 19 +++++---------- .../jackson/core/io/NumberInput.java | 23 +++++++------------ .../jackson/core/util/TextBuffer.java | 13 +++++------ .../jackson/core/io/BigDecimalParserTest.java | 12 +--------- .../jackson/core/io/TestNumberInput.java | 6 ++--- 8 files changed, 27 insertions(+), 76 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParser.java b/src/main/java/com/fasterxml/jackson/core/JsonParser.java index 299c410ca5..75ffbd1f5b 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonParser.java @@ -357,17 +357,7 @@ public enum Feature { * * @since 2.15 */ - USE_FAST_BIG_DECIMAL_PARSER(false), - - /** - * If USE_FAST_BIG_DECIMAL_PARSER is enabled, then enabling this feature can - * further speed up parsing by using parallel processing. - *

- * This setting is disabled by default for backwards compatibility. - * - * @since 2.15 - */ - USE_PARALLEL_FAST_BIG_DECIMAL_PARSER(false) + USE_FAST_BIG_DECIMAL_PARSER(false) ; diff --git a/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java b/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java index 74c095333a..f24e385505 100644 --- a/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java +++ b/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java @@ -111,17 +111,7 @@ public enum StreamReadFeature * * @since 2.15 */ - USE_FAST_BIG_DECIMAL_PARSER(JsonParser.Feature.USE_FAST_BIG_DECIMAL_PARSER), - - /** - * If USE_FAST_BIG_DECIMAL_PARSER is enabled, then enabling this feature can - * further speed up parsing by using parallel processing. - *

- * This setting is disabled by default for backwards compatibility. - * - * @since 2.15 - */ - USE_PARALLEL_FAST_BIG_DECIMAL_PARSER(JsonParser.Feature.USE_PARALLEL_FAST_BIG_DECIMAL_PARSER) + USE_FAST_BIG_DECIMAL_PARSER(JsonParser.Feature.USE_FAST_BIG_DECIMAL_PARSER) ; 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 01bcf6f7cc..81b9d8c339 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -1150,8 +1150,7 @@ protected BigInteger _getBigInteger() { } _numberBigInt = NumberInput.parseBigInteger( _numberString, - isEnabled(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER), - isEnabled(StreamReadFeature.USE_PARALLEL_FAST_BIG_DECIMAL_PARSER)); + isEnabled(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER)); _numberString = null; return _numberBigInt; } @@ -1170,8 +1169,7 @@ protected BigDecimal _getBigDecimal() { } _numberBigDecimal = NumberInput.parseBigDecimal( _numberString, - isEnabled(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER), - isEnabled(StreamReadFeature.USE_PARALLEL_FAST_BIG_DECIMAL_PARSER)); + isEnabled(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER)); _numberString = null; return _numberBigDecimal; } diff --git a/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java b/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java index ae71dba93c..55472fd407 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java +++ b/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java @@ -64,11 +64,9 @@ public static BigDecimal parse(char[] chars) { return parse(chars, 0, chars.length); } - public static BigDecimal parseWithFastParser(final String valueStr, final boolean allowParallelParsing) { + public static BigDecimal parseWithFastParser(final String valueStr) { try { - return allowParallelParsing ? - JavaBigDecimalParser.parallelParseBigDecimal(valueStr) : - JavaBigDecimalParser.parseBigDecimal(valueStr); + return JavaBigDecimalParser.parseBigDecimal(valueStr); } catch (NumberFormatException nfe) { final String reportNum = valueStr.length() <= MAX_CHARS_TO_REPORT ? valueStr : valueStr.substring(0, MAX_CHARS_TO_REPORT) + " [truncated]"; @@ -77,12 +75,9 @@ public static BigDecimal parseWithFastParser(final String valueStr, final boolea } } - public static BigDecimal parseWithFastParser(final char[] ch, final int off, final int len, - final boolean allowParallelParsing) { + public static BigDecimal parseWithFastParser(final char[] ch, final int off, final int len) { try { - return allowParallelParsing ? - JavaBigDecimalParser.parallelParseBigDecimal(ch, off, len) : - JavaBigDecimalParser.parseBigDecimal(ch, off, len); + return JavaBigDecimalParser.parseBigDecimal(ch, off, len); } catch (NumberFormatException nfe) { final String reportNum = len <= MAX_CHARS_TO_REPORT ? new String(ch, off, len) : new String(ch, off, MAX_CHARS_TO_REPORT) + " [truncated]"; @@ -92,11 +87,9 @@ public static BigDecimal parseWithFastParser(final char[] ch, final int off, fin } } - public static BigInteger parseBigIntegerWithFastParser(final String valueStr, final boolean allowParallelParsing) { + public static BigInteger parseBigIntegerWithFastParser(final String valueStr) { try { - return allowParallelParsing ? - JavaBigIntegerParser.parallelParseBigInteger(valueStr) : - JavaBigIntegerParser.parseBigInteger(valueStr); + return JavaBigIntegerParser.parseBigInteger(valueStr); } catch (NumberFormatException nfe) { final String reportNum = valueStr.length() <= MAX_CHARS_TO_REPORT ? valueStr : valueStr.substring(0, MAX_CHARS_TO_REPORT) + " [truncated]"; 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 064dd8d318..e661c87d1b 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java +++ b/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java @@ -392,15 +392,13 @@ public static BigDecimal parseBigDecimal(final String s) throws NumberFormatExce /** * @param s a string representing a number to parse * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} - * @param allowParallelParsing whether to use parallel processing when using fast parser * @return a BigDecimal * @throws NumberFormatException if the char array cannot be represented by a BigDecimal * @since v2.15 */ - public static BigDecimal parseBigDecimal(final String s, final boolean useFastParser, - final boolean allowParallelParsing) throws NumberFormatException { + public static BigDecimal parseBigDecimal(final String s, final boolean useFastParser) throws NumberFormatException { return useFastParser ? - BigDecimalParser.parseWithFastParser(s, allowParallelParsing) : + BigDecimalParser.parseWithFastParser(s) : BigDecimalParser.parse(s); } @@ -420,16 +418,15 @@ public static BigDecimal parseBigDecimal(final char[] ch, final int off, final i * @param off the offset to apply when parsing the number in the char array * @param len the length of the number in the char array * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} - * @param allowParallelParsing whether to use parallel processing when using fast parser * @return a BigDecimal * @throws NumberFormatException if the char array cannot be represented by a BigDecimal * @since v2.15 */ public static BigDecimal parseBigDecimal(final char[] ch, final int off, final int len, - final boolean useFastParser, final boolean allowParallelParsing) + final boolean useFastParser) throws NumberFormatException { return useFastParser ? - BigDecimalParser.parseWithFastParser(ch, off, len, allowParallelParsing) : + BigDecimalParser.parseWithFastParser(ch, off, len) : BigDecimalParser.parse(ch, off, len); } @@ -445,15 +442,13 @@ public static BigDecimal parseBigDecimal(final char[] ch) throws NumberFormatExc /** * @param ch a char array with text that makes up a number * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} - * @param allowParallelParsing whether to use parallel processing when using fast parser * @return a BigDecimal * @throws NumberFormatException if the char array cannot be represented by a BigDecimal * @since v2.15 */ - public static BigDecimal parseBigDecimal(final char[] ch, final boolean useFastParser, - final boolean allowParallelParsing) throws NumberFormatException { + public static BigDecimal parseBigDecimal(final char[] ch, final boolean useFastParser) throws NumberFormatException { return useFastParser ? - BigDecimalParser.parseWithFastParser(ch, 0, ch.length, allowParallelParsing) : + BigDecimalParser.parseWithFastParser(ch, 0, ch.length) : BigDecimalParser.parse(ch); } @@ -473,15 +468,13 @@ public static BigInteger parseBigInteger(final String s) throws NumberFormatExce /** * @param s a string representing a number to parse * @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser} - * @param allowParallelParsing whether to use parallel processing when using fast parser * @return a BigInteger * @throws NumberFormatException if string cannot be represented by a BigInteger * @since v2.15 */ - public static BigInteger parseBigInteger(final String s, final boolean useFastParser, - final boolean allowParallelParsing) throws NumberFormatException { + public static BigInteger parseBigInteger(final String s, final boolean useFastParser) throws NumberFormatException { if (useFastParser) { - return BigDecimalParser.parseBigIntegerWithFastParser(s, allowParallelParsing); + return BigDecimalParser.parseBigIntegerWithFastParser(s); } else { return parseBigInteger(s); } 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 e14d92ddf7..910d31dd3b 100644 --- a/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java +++ b/src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java @@ -493,33 +493,32 @@ public char[] contentsAsArray() { * @since 2.15 */ public BigDecimal contentsAsDecimal(final StreamReadConstraints constraints, - final boolean useFastParser, - final boolean allowParallelParsing) throws NumberFormatException + final boolean useFastParser) throws NumberFormatException { // Already got a pre-cut array? if (_resultArray != null) { constraints.validateFPLength(_resultArray.length); - return NumberInput.parseBigDecimal(_resultArray, useFastParser, allowParallelParsing); + return NumberInput.parseBigDecimal(_resultArray, useFastParser); } // Or a shared buffer? if ((_inputStart >= 0) && (_inputBuffer != null)) { constraints.validateFPLength(_inputLen); - return NumberInput.parseBigDecimal(_inputBuffer, _inputStart, _inputLen, useFastParser, allowParallelParsing); + return NumberInput.parseBigDecimal(_inputBuffer, _inputStart, _inputLen, useFastParser); } // Or if not, just a single buffer (the usual case) if ((_segmentSize == 0) && (_currentSegment != null)) { constraints.validateFPLength(_currentSize); - return NumberInput.parseBigDecimal(_currentSegment, 0, _currentSize, useFastParser, allowParallelParsing); + return NumberInput.parseBigDecimal(_currentSegment, 0, _currentSize, useFastParser); } // If not, let's just get it aggregated... final char[] numArray = contentsAsArray(); constraints.validateFPLength(numArray.length); - return NumberInput.parseBigDecimal(numArray, useFastParser, allowParallelParsing); + return NumberInput.parseBigDecimal(numArray, useFastParser); } @Deprecated // @since 2.15 public BigDecimal contentsAsDecimal() throws NumberFormatException { - return contentsAsDecimal(StreamReadConstraints.defaults(), false, false); + return contentsAsDecimal(StreamReadConstraints.defaults(), false); } /** diff --git a/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java b/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java index 27ea8c2ac0..d02f68a4c8 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java @@ -13,17 +13,7 @@ public void testLongStringParse() { public void testLongStringFastParse() { try { - BigDecimalParser.parseWithFastParser(genLongString(), false); - fail("expected NumberFormatException"); - } catch (NumberFormatException nfe) { - assertTrue("exception message starts as expected?", nfe.getMessage().startsWith("Value \"AAAAA")); - assertTrue("exception message value contains truncated", nfe.getMessage().contains("truncated")); - } - } - - public void testLongStringFastParseParallel() { - try { - BigDecimalParser.parseWithFastParser(genLongString(), true); + BigDecimalParser.parseWithFastParser(genLongString()); fail("expected NumberFormatException"); } catch (NumberFormatException nfe) { assertTrue("exception message starts as expected?", nfe.getMessage().startsWith("Value \"AAAAA")); diff --git a/src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java b/src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java index 3b746235ed..e1411b532e 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java +++ b/src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java @@ -35,15 +35,13 @@ public void testParseLongBigInteger() } String test1000 = stringBuilder.toString(); assertEquals(new BigInteger(test1000), NumberInput.parseBigInteger(test1000)); - assertEquals(new BigInteger(test1000), NumberInput.parseBigInteger(test1000, true, false)); - assertEquals(new BigInteger(test1000), NumberInput.parseBigInteger(test1000, true, true)); + assertEquals(new BigInteger(test1000), NumberInput.parseBigInteger(test1000, true)); for (int i = 0; i < 1000; i++) { stringBuilder.append(7); } String test2000 = stringBuilder.toString(); assertEquals(new BigInteger(test2000), NumberInput.parseBigInteger(test2000)); - assertEquals(new BigInteger(test2000), NumberInput.parseBigInteger(test2000, true, false)); - assertEquals(new BigInteger(test2000), NumberInput.parseBigInteger(test2000, true, true)); + assertEquals(new BigInteger(test2000), NumberInput.parseBigInteger(test2000, true)); } } From f8e541d3bd0ab231da73e795234fade35987dfbb Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 1 Dec 2022 22:06:53 +0100 Subject: [PATCH 08/10] update tests --- .../core/read/FastParserNonStandardNumberParsingTest.java | 1 + .../fasterxml/jackson/core/read/FastParserNumberParsingTest.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/test/java/com/fasterxml/jackson/core/read/FastParserNonStandardNumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/FastParserNonStandardNumberParsingTest.java index ab4a64f41f..618fea856c 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/FastParserNonStandardNumberParsingTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/FastParserNonStandardNumberParsingTest.java @@ -13,6 +13,7 @@ public class FastParserNonStandardNumberParsingTest .enable(JsonReadFeature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS) .enable(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS) .enable(StreamReadFeature.USE_FAST_DOUBLE_PARSER) + .enable(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER) .build(); @Override diff --git a/src/test/java/com/fasterxml/jackson/core/read/FastParserNumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/FastParserNumberParsingTest.java index fae6dda24c..81ebf28813 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/FastParserNumberParsingTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/FastParserNumberParsingTest.java @@ -7,6 +7,7 @@ public class FastParserNumberParsingTest extends NumberParsingTest { private final JsonFactory fastFactory = JsonFactory.builder() .enable(StreamReadFeature.USE_FAST_DOUBLE_PARSER) + .enable(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER) .build(); @Override From 96bbc601873e1a4c4e6445fcfa13d5f57145030a Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 2 Dec 2022 21:02:35 +0100 Subject: [PATCH 09/10] javadoc --- src/main/java/com/fasterxml/jackson/core/JsonParser.java | 2 +- src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParser.java b/src/main/java/com/fasterxml/jackson/core/JsonParser.java index 75ffbd1f5b..427ce54deb 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonParser.java @@ -351,7 +351,7 @@ public enum Feature { /** * Feature that determines whether we use the built-in {@link new BigDecimal(String)} code to parse * BigDecimals or if we use {@link com.fasterxml.jackson.core.io.doubleparser} - * instead. + * instead. This feature has a similar effect on BigInteger parsing. *

* This setting is disabled by default for backwards compatibility. * diff --git a/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java b/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java index f24e385505..4c892365d4 100644 --- a/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java +++ b/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java @@ -105,7 +105,7 @@ public enum StreamReadFeature /** * Feature that determines whether we use the built-in {@link new BigDecimal(String)} code to parse * BigDecimals or if we use {@link com.fasterxml.jackson.core.io.doubleparser} - * instead. + * instead. This feature has a similar effect on BigInteger parsing. *

* This setting is disabled by default. * From ceb325bf29688858dc537b7dd23e08411e9e9703 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 3 Dec 2022 00:29:40 +0100 Subject: [PATCH 10/10] apply review changes --- .../fasterxml/jackson/core/JsonParser.java | 8 ++--- .../jackson/core/StreamReadFeature.java | 8 ++--- .../jackson/core/base/ParserBase.java | 4 +-- .../jackson/core/io/BigDecimalParser.java | 15 +--------- .../jackson/core/io/BigIntegerParser.java | 29 +++++++++++++++++++ .../jackson/core/io/NumberInput.java | 2 +- ...astParserNonStandardNumberParsingTest.java | 2 +- .../read/FastParserNumberParsingTest.java | 2 +- 8 files changed, 43 insertions(+), 27 deletions(-) create mode 100644 src/main/java/com/fasterxml/jackson/core/io/BigIntegerParser.java diff --git a/src/main/java/com/fasterxml/jackson/core/JsonParser.java b/src/main/java/com/fasterxml/jackson/core/JsonParser.java index 427ce54deb..f1a9692aff 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonParser.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonParser.java @@ -349,15 +349,15 @@ public enum Feature { USE_FAST_DOUBLE_PARSER(false), /** - * Feature that determines whether we use the built-in {@link new BigDecimal(String)} code to parse - * BigDecimals or if we use {@link com.fasterxml.jackson.core.io.doubleparser} - * instead. This feature has a similar effect on BigInteger parsing. + * Feature that determines whether to use the built-in Java code for parsing + * BigDecimals and BigIntegerss or to use + * {@link com.fasterxml.jackson.core.io.doubleparser} instead. *

* This setting is disabled by default for backwards compatibility. * * @since 2.15 */ - USE_FAST_BIG_DECIMAL_PARSER(false) + USE_FAST_BIG_NUMBER_PARSER(false) ; diff --git a/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java b/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java index 4c892365d4..69b2dda8f1 100644 --- a/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java +++ b/src/main/java/com/fasterxml/jackson/core/StreamReadFeature.java @@ -103,15 +103,15 @@ public enum StreamReadFeature USE_FAST_DOUBLE_PARSER(JsonParser.Feature.USE_FAST_DOUBLE_PARSER), /** - * Feature that determines whether we use the built-in {@link new BigDecimal(String)} code to parse - * BigDecimals or if we use {@link com.fasterxml.jackson.core.io.doubleparser} - * instead. This feature has a similar effect on BigInteger parsing. + * Feature that determines whether to use the built-in Java code for parsing + * BigDecimals and BigIntegerss or to use + * {@link com.fasterxml.jackson.core.io.doubleparser} instead. *

* This setting is disabled by default. * * @since 2.15 */ - USE_FAST_BIG_DECIMAL_PARSER(JsonParser.Feature.USE_FAST_BIG_DECIMAL_PARSER) + USE_FAST_BIG_NUMBER_PARSER(JsonParser.Feature.USE_FAST_BIG_NUMBER_PARSER) ; 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 81b9d8c339..a713f1b003 100644 --- a/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java +++ b/src/main/java/com/fasterxml/jackson/core/base/ParserBase.java @@ -1150,7 +1150,7 @@ protected BigInteger _getBigInteger() { } _numberBigInt = NumberInput.parseBigInteger( _numberString, - isEnabled(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER)); + isEnabled(StreamReadFeature.USE_FAST_BIG_NUMBER_PARSER)); _numberString = null; return _numberBigInt; } @@ -1169,7 +1169,7 @@ protected BigDecimal _getBigDecimal() { } _numberBigDecimal = NumberInput.parseBigDecimal( _numberString, - isEnabled(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER)); + isEnabled(StreamReadFeature.USE_FAST_BIG_NUMBER_PARSER)); _numberString = null; return _numberBigDecimal; } diff --git a/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java b/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java index 55472fd407..b86502aaf2 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java +++ b/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java @@ -1,10 +1,8 @@ package com.fasterxml.jackson.core.io; import ch.randelshofer.fastdoubleparser.JavaBigDecimalParser; -import ch.randelshofer.fastdoubleparser.JavaBigIntegerParser; import java.math.BigDecimal; -import java.math.BigInteger; import java.util.Arrays; // Based on a great idea of Eric Obermühlner to use a tree of smaller BigDecimals for parsing @@ -25,7 +23,7 @@ */ public final class BigDecimalParser { - private final static int MAX_CHARS_TO_REPORT = 1000; + final static int MAX_CHARS_TO_REPORT = 1000; private BigDecimalParser() {} @@ -87,17 +85,6 @@ public static BigDecimal parseWithFastParser(final char[] ch, final int off, fin } } - public static BigInteger parseBigIntegerWithFastParser(final String valueStr) { - try { - return JavaBigIntegerParser.parseBigInteger(valueStr); - } catch (NumberFormatException nfe) { - final String reportNum = valueStr.length() <= MAX_CHARS_TO_REPORT ? - valueStr : valueStr.substring(0, MAX_CHARS_TO_REPORT) + " [truncated]"; - throw new NumberFormatException("Value \"" + reportNum - + "\" can not be represented as `java.math.BigDecimal`, reason: " + nfe.getMessage()); - } - } - private static BigDecimal parseBigDecimal(final char[] chars, final int off, final int len, final int splitLen) { boolean numHasSign = false; boolean expHasSign = false; diff --git a/src/main/java/com/fasterxml/jackson/core/io/BigIntegerParser.java b/src/main/java/com/fasterxml/jackson/core/io/BigIntegerParser.java new file mode 100644 index 0000000000..c7b2e1adc7 --- /dev/null +++ b/src/main/java/com/fasterxml/jackson/core/io/BigIntegerParser.java @@ -0,0 +1,29 @@ +package com.fasterxml.jackson.core.io; + +import ch.randelshofer.fastdoubleparser.JavaBigIntegerParser; + +import java.math.BigInteger; + +import static com.fasterxml.jackson.core.io.BigDecimalParser.MAX_CHARS_TO_REPORT; + +/** + * Helper class used to implement more optimized parsing of {@link BigInteger} for REALLY + * big values (over 500 characters). + * + * @since 2.15 + */ +public final class BigIntegerParser +{ + private BigIntegerParser() {} + + public static BigInteger parseWithFastParser(final String valueStr) { + try { + return JavaBigIntegerParser.parseBigInteger(valueStr); + } catch (NumberFormatException nfe) { + final String reportNum = valueStr.length() <= MAX_CHARS_TO_REPORT ? + valueStr : valueStr.substring(0, MAX_CHARS_TO_REPORT) + " [truncated]"; + throw new NumberFormatException("Value \"" + reportNum + + "\" can not be represented as `java.math.BigDecimal`, reason: " + nfe.getMessage()); + } + } +} 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 e661c87d1b..4620c15b69 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java +++ b/src/main/java/com/fasterxml/jackson/core/io/NumberInput.java @@ -474,7 +474,7 @@ public static BigInteger parseBigInteger(final String s) throws NumberFormatExce */ public static BigInteger parseBigInteger(final String s, final boolean useFastParser) throws NumberFormatException { if (useFastParser) { - return BigDecimalParser.parseBigIntegerWithFastParser(s); + return BigIntegerParser.parseWithFastParser(s); } else { return parseBigInteger(s); } diff --git a/src/test/java/com/fasterxml/jackson/core/read/FastParserNonStandardNumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/FastParserNonStandardNumberParsingTest.java index 618fea856c..11ef1bc18e 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/FastParserNonStandardNumberParsingTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/FastParserNonStandardNumberParsingTest.java @@ -13,7 +13,7 @@ public class FastParserNonStandardNumberParsingTest .enable(JsonReadFeature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS) .enable(JsonReadFeature.ALLOW_TRAILING_DECIMAL_POINT_FOR_NUMBERS) .enable(StreamReadFeature.USE_FAST_DOUBLE_PARSER) - .enable(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER) + .enable(StreamReadFeature.USE_FAST_BIG_NUMBER_PARSER) .build(); @Override diff --git a/src/test/java/com/fasterxml/jackson/core/read/FastParserNumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/FastParserNumberParsingTest.java index 81ebf28813..ce02dd79f1 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/FastParserNumberParsingTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/FastParserNumberParsingTest.java @@ -7,7 +7,7 @@ public class FastParserNumberParsingTest extends NumberParsingTest { private final JsonFactory fastFactory = JsonFactory.builder() .enable(StreamReadFeature.USE_FAST_DOUBLE_PARSER) - .enable(StreamReadFeature.USE_FAST_BIG_DECIMAL_PARSER) + .enable(StreamReadFeature.USE_FAST_BIG_NUMBER_PARSER) .build(); @Override