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/release-notes/VERSION-2.x b/release-notes/VERSION-2.x index 2dbe9ea39f..eaa678f1da 100644 --- a/release-notes/VERSION-2.x +++ b/release-notes/VERSION-2.x @@ -42,8 +42,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) diff --git a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java index 147d221d57..42ff2a9d82 100644 --- a/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java +++ b/src/main/java/com/fasterxml/jackson/core/JsonGenerator.java @@ -1781,7 +1781,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 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()); - } - } - } -}