From f26616cadab124d79365ff34262cb1220c00d6ba Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 12 Sep 2024 03:09:55 +0100 Subject: [PATCH 1/5] Create BigDecimalParser4694Test.java (#1332) --- .../failing/BigDecimalParser4694Test.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 src/test/java/com/fasterxml/jackson/failing/BigDecimalParser4694Test.java diff --git a/src/test/java/com/fasterxml/jackson/failing/BigDecimalParser4694Test.java b/src/test/java/com/fasterxml/jackson/failing/BigDecimalParser4694Test.java new file mode 100644 index 0000000000..5515fdd747 --- /dev/null +++ b/src/test/java/com/fasterxml/jackson/failing/BigDecimalParser4694Test.java @@ -0,0 +1,25 @@ +package com.fasterxml.jackson.failing; + +import ch.randelshofer.fastdoubleparser.JavaBigDecimalParser; +import com.fasterxml.jackson.core.io.BigDecimalParser; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class BigDecimalParser4694Test extends com.fasterxml.jackson.core.JUnit5TestBase +{ + // https://github.com/FasterXML/jackson-databind/issues/4694 + @Test + void issueDatabind4694() { + final String str = "-11000.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + final BigDecimal expected = new BigDecimal(str); + assertEquals(expected, JavaBigDecimalParser.parseBigDecimal(str)); + assertEquals(expected, BigDecimalParser.parse(str)); + assertEquals(expected, BigDecimalParser.parseWithFastParser(str)); + final char[] arr = str.toCharArray(); + assertEquals(expected, BigDecimalParser.parse(arr, 0, arr.length)); + assertEquals(expected, BigDecimalParser.parseWithFastParser(arr, 0, arr.length)); + } +} From a808f5e8e22c2c820df51ee1821a0f630c8f52e2 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 13 Sep 2024 23:17:19 +0100 Subject: [PATCH 2/5] fix grammar issue in javadoc (#1336) --- src/main/java/com/fasterxml/jackson/core/JsonGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java index f8ee301e2b..91451a0ea0 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java @@ -1774,7 +1774,7 @@ public abstract int writeBinary(Base64Variant bv, * If implementation does not implement this method, * it needs to throw {@link UnsupportedOperationException}. * - * @param encodedValue Textual (possibly format) number representation to write + * @param encodedValue Textual (possibly formatted) number representation to write * * @throws IOException if there is either an underlying I/O problem or encoding * issue at format layer From 7902535c7f1733d5953b63e1914f303076a32a18 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Fri, 13 Sep 2024 23:18:24 +0100 Subject: [PATCH 3/5] uptake FastDoubleParser v1.0.1 (#1331) --- pom.xml | 2 +- .../jackson/core/io/BigDecimalParser.java | 16 +++++++- .../jackson/core/io/BigDecimalParserTest.java | 13 +++++++ .../jackson/core/read/NumberParsingTest.java | 38 +++++++++++++++++++ .../failing/BigDecimalParser4694Test.java | 25 ------------ .../failing/NumberParsing4694Test.java | 29 -------------- 6 files changed, 66 insertions(+), 57 deletions(-) delete mode 100644 src/test/java/com/fasterxml/jackson/failing/BigDecimalParser4694Test.java delete mode 100644 src/test/java/com/fasterxml/jackson/failing/NumberParsing4694Test.java diff --git a/pom.xml b/pom.xml index d1b05737a1..49496c3d4b 100644 --- a/pom.xml +++ b/pom.xml @@ -299,7 +299,7 @@ com.fasterxml.jackson.core.*;version=${project.version} ch.randelshofer fastdoubleparser - 1.0.0 + 1.0.1 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 eddbb9347d..2be42744c8 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java +++ b/src/main/java/com/fasterxml/jackson/core/io/BigDecimalParser.java @@ -25,6 +25,7 @@ public final class BigDecimalParser { final static int MAX_CHARS_TO_REPORT = 1000; + private final static int SIZE_FOR_SWITCH_TO_FASTDOUBLEPARSER = 500; private BigDecimalParser() {} @@ -40,7 +41,17 @@ private BigDecimalParser() {} * @throws NumberFormatException */ public static BigDecimal parse(String valueStr) { - return parse(valueStr.toCharArray()); + try { + if (valueStr.length() < SIZE_FOR_SWITCH_TO_FASTDOUBLEPARSER) { + return new BigDecimal(valueStr); + } + return JavaBigDecimalParser.parseBigDecimal(valueStr); + + // 20-Aug-2022, tatu: Although "new BigDecimal(...)" only throws NumberFormatException + // operations by "parseBigDecimal()" can throw "ArithmeticException", so handle both: + } catch (ArithmeticException | NumberFormatException e) { + throw _parseFailure(e, valueStr); + } } /** @@ -55,7 +66,7 @@ public static BigDecimal parse(String valueStr) { */ public static BigDecimal parse(final char[] chars, final int off, final int len) { try { - if (len < 500) { + if (len < SIZE_FOR_SWITCH_TO_FASTDOUBLEPARSER) { return new BigDecimal(chars, off, len); } return JavaBigDecimalParser.parseBigDecimal(chars, off, len); @@ -165,4 +176,5 @@ private static String _generateExceptionMessage(final String valueToReport, fina return String.format("Value %s can not be deserialized as `java.math.BigDecimal`, reason: %s" , valueToReport, desc); } + } 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 bdcdea4ba1..9ec4cc6cda 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/BigDecimalParserTest.java @@ -2,6 +2,7 @@ import java.math.BigDecimal; +import ch.randelshofer.fastdoubleparser.JavaBigDecimalParser; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -51,6 +52,18 @@ void longValidStringFastParse() { assertEquals(EXP, BigDecimalParser.parseWithFastParser(num.toCharArray(), 0, num.length())); } + @Test + void issueDatabind4694() { + final String str = "-11000.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + final BigDecimal expected = new BigDecimal(str); + assertEquals(expected, JavaBigDecimalParser.parseBigDecimal(str)); + assertEquals(expected, BigDecimalParser.parse(str)); + assertEquals(expected, BigDecimalParser.parseWithFastParser(str)); + final char[] arr = str.toCharArray(); + assertEquals(expected, BigDecimalParser.parse(arr, 0, arr.length)); + assertEquals(expected, BigDecimalParser.parseWithFastParser(arr, 0, arr.length)); + } + static String genLongInvalidString() { final int len = 1500; final StringBuilder sb = new StringBuilder(len); diff --git a/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java b/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java index 676228d89a..c64a9f732f 100644 --- a/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java +++ b/src/test/java/com/fasterxml/jackson/core/read/NumberParsingTest.java @@ -28,6 +28,9 @@ protected JsonFactory jsonFactory() { return sharedStreamFactory(); } + private final String ISSUE_4694_VALUE = + "-11000.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + /* /********************************************************************** /* Tests, Boolean @@ -996,6 +999,41 @@ void negativeMaxNumberLength() { } } + // https://github.com/FasterXML/jackson-databind/issues/4694 + @Test + void databind4694() throws Exception { + final BigDecimal expected = new BigDecimal(ISSUE_4694_VALUE); + for (int mode : ALL_MODES) { + try (JsonParser p = createParser(mode, String.format(" %s ", ISSUE_4694_VALUE))) { + assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); + assertEquals(expected, p.getDecimalValue()); + assertFalse(p.isNaN()); + } + } + } + + void databind4694Double() throws Exception { + final Double expected = new Double(ISSUE_4694_VALUE); + for (int mode : ALL_MODES) { + try (JsonParser p = createParser(mode, String.format(" %s ", ISSUE_4694_VALUE))) { + assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); + assertEquals(expected, p.getDoubleValue()); + assertFalse(p.isNaN()); + } + } + } + + void databind4694Float() throws Exception { + final Float expected = new Float(ISSUE_4694_VALUE); + for (int mode : ALL_MODES) { + try (JsonParser p = createParser(mode, String.format(" %s ", ISSUE_4694_VALUE))) { + assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); + assertEquals(expected, p.getFloatValue()); + assertFalse(p.isNaN()); + } + } + } + /* /********************************************************** /* Helper methods diff --git a/src/test/java/com/fasterxml/jackson/failing/BigDecimalParser4694Test.java b/src/test/java/com/fasterxml/jackson/failing/BigDecimalParser4694Test.java deleted file mode 100644 index 5515fdd747..0000000000 --- a/src/test/java/com/fasterxml/jackson/failing/BigDecimalParser4694Test.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.fasterxml.jackson.failing; - -import ch.randelshofer.fastdoubleparser.JavaBigDecimalParser; -import com.fasterxml.jackson.core.io.BigDecimalParser; -import org.junit.jupiter.api.Test; - -import java.math.BigDecimal; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -class BigDecimalParser4694Test extends com.fasterxml.jackson.core.JUnit5TestBase -{ - // https://github.com/FasterXML/jackson-databind/issues/4694 - @Test - void issueDatabind4694() { - final String str = "-11000.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - final BigDecimal expected = new BigDecimal(str); - assertEquals(expected, JavaBigDecimalParser.parseBigDecimal(str)); - assertEquals(expected, BigDecimalParser.parse(str)); - assertEquals(expected, BigDecimalParser.parseWithFastParser(str)); - final char[] arr = str.toCharArray(); - assertEquals(expected, BigDecimalParser.parse(arr, 0, arr.length)); - assertEquals(expected, BigDecimalParser.parseWithFastParser(arr, 0, arr.length)); - } -} diff --git a/src/test/java/com/fasterxml/jackson/failing/NumberParsing4694Test.java b/src/test/java/com/fasterxml/jackson/failing/NumberParsing4694Test.java deleted file mode 100644 index 1dae2ae406..0000000000 --- a/src/test/java/com/fasterxml/jackson/failing/NumberParsing4694Test.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.fasterxml.jackson.failing; - -import com.fasterxml.jackson.core.JUnit5TestBase; -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonToken; -import org.junit.jupiter.api.Test; - -import java.math.BigDecimal; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; - -class NumberParsing4694Test - extends JUnit5TestBase -{ - // https://github.com/FasterXML/jackson-databind/issues/4694 - @Test - void databind4694() throws Exception { - final String str = "-11000.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - final BigDecimal expected = new BigDecimal(str); - for (int mode : ALL_MODES) { - try (JsonParser p = createParser(mode, String.format(" %s ", str))) { - assertEquals(JsonToken.VALUE_NUMBER_FLOAT, p.nextToken()); - assertEquals(expected, p.getDecimalValue()); - assertFalse(p.isNaN()); - } - } - } -} From 2b0ea4ff0e42e3c2041d3ff8cde00e2a7c8544f8 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 13 Sep 2024 15:21:09 -0700 Subject: [PATCH 4/5] Update release notes --- release-notes/VERSION-2.x | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 8bd265452a..0ba6e5336e 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -38,8 +38,9 @@ a pure JSON library. #1305: Make helper methods of `WriterBasedJsonGenerator` non-final to allow overriding (contributed by @zhangOranges) #1310: Add new `StreamReadConstraints` (`maxTokenCount`) to limit maximum number - of Tokens allowed per document - (implemented by @pjfanning) + of Tokens allowed per document# +#1331: Update to FastDoubleParser v1.0.1 to fix `BigDecimal` decoding proble + (fixed by @pjfanning) 2.17.2 (05-Jul-2024) From af8e0a2d7602e889d8cec1a4f25d3ff1a1ecc2a3 Mon Sep 17 00:00:00 2001 From: Tatu Saloranta Date: Fri, 13 Sep 2024 15:23:05 -0700 Subject: [PATCH 5/5] Backport #1331 library upgrade --- pom.xml | 2 +- release-notes/VERSION-2.x | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d917cfce0b..1e207c0bee 100644 --- a/pom.xml +++ b/pom.xml @@ -293,7 +293,7 @@ com.fasterxml.jackson.core.*;version=${project.version} ch.randelshofer fastdoubleparser - 1.0.0 + 1.0.1 diff --git a/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index f947c7eb24..8c01d90cac 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -14,6 +14,11 @@ a pure JSON library. === Releases === ------------------------------------------------------------------------ +2.17.3 (not yet released): + +#1331: Update to FastDoubleParser v1.0.1 to fix `BigDecimal` decoding problem + (contributed by @pjfanning) + 2.17.2 (05-Jul-2024) #1308: Relax validation by `NumberInput.looksLikeValidNumber()` to allow