Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
4850dc7
init work on inlining fastdoubleparser
pjfanning Mar 31, 2022
03bcd19
add one doubleparser test case (as a start)
pjfanning Mar 31, 2022
0780285
uptake latest code
pjfanning Apr 20, 2022
0c90aeb
update float parse
pjfanning Apr 21, 2022
cfaf818
don't parse floats as doubles first
pjfanning Apr 25, 2022
ead5eb6
add feature to control double parser impl
pjfanning Apr 26, 2022
09029de
refactor params
pjfanning Apr 27, 2022
fa5c456
remove some deprecations (will be hard to change all calls in jackson…
pjfanning Apr 27, 2022
94242e4
add fast-double-parser tests
pjfanning Apr 27, 2022
660bd33
add fast float support
pjfanning Apr 28, 2022
4890fd4
Update NumberInput.java
pjfanning Apr 28, 2022
b3704c8
update tests
pjfanning Apr 28, 2022
96ba118
Create ReaderTest.java
pjfanning Apr 28, 2022
0018637
remove char array support
pjfanning Apr 28, 2022
e7ea012
add JsonReadFeature.USE_FAST_DOUBLE_PARSER
pjfanning May 3, 2022
d3c5544
Update FloatParsingTest.java
pjfanning May 3, 2022
5071aeb
float support
pjfanning May 9, 2022
71fadd3
Update TextBuffer.java
pjfanning May 9, 2022
dcb5169
add tests
pjfanning May 17, 2022
9f0bb0a
Delete AbstractFloatValueFromCharArray.java
pjfanning May 17, 2022
0ef1c27
latest code from fastdoubleparser
pjfanning Jun 4, 2022
866f41d
update tests
pjfanning Jun 4, 2022
e2817a4
latest Werner Randelshofer code
pjfanning Jun 5, 2022
0d7133f
update licenses
pjfanning Jun 11, 2022
37fae99
compile issues
pjfanning Jun 13, 2022
45235af
undo format changes
pjfanning Jun 13, 2022
2e9af39
Update JsonParserBase.java
pjfanning Jun 13, 2022
40a0270
Update TextBuffer.java
pjfanning Jun 13, 2022
02f96ca
Revert "Update TextBuffer.java"
pjfanning Jun 13, 2022
c31eef8
Update FloatParsingTest.java
pjfanning Jun 14, 2022
8b882c2
Merge branch 'master' into double-parser-master
pjfanning Jun 17, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/main/java/com/fasterxml/jackson/core/JsonParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -1634,3 +1634,4 @@ protected final StreamReadException _constructReadException(String msg, Throwabl
return new StreamReadException(this, msg, t);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -959,3 +959,4 @@ protected ContentReference _contentReference() {
return ContentReference.unknown();
}
}

72 changes: 63 additions & 9 deletions src/main/java/com/fasterxml/jackson/core/io/NumberInput.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.fasterxml.jackson.core.io;

import com.fasterxml.jackson.core.io.doubleparser.FastDoubleParser;
import com.fasterxml.jackson.core.io.doubleparser.FastFloatParser;

import java.math.BigDecimal;

public final class NumberInput
Expand Down Expand Up @@ -226,7 +229,9 @@ public static int parseAsInt(String s, int def)
// if other symbols, parse as Double, coerce
if (c > '9' || c < '0') {
try {
return (int) parseDouble(s);
//useFastParser=true is used because there is a lot less risk that small changes in result will have an affect
//and performance benefit is useful
return (int) parseDouble(s, true);
} catch (NumberFormatException e) {
return def;
}
Expand Down Expand Up @@ -263,7 +268,9 @@ public static long parseAsLong(String s, long def)
// if other symbols, parse as Double, coerce
if (c > '9' || c < '0') {
try {
return (long) parseDouble(s);
//useFastParser=true is used because there is a lot less risk that small changes in result will have an affect
//and performance benefit is useful
return (long) parseDouble(s, true);
} catch (NumberFormatException e) {
return def;
}
Expand All @@ -274,8 +281,26 @@ public static long parseAsLong(String s, long def)
} catch (NumberFormatException e) { }
return def;
}

public static double parseAsDouble(String s, double def)

/**
* @param s a string representing a number to parse
* @param def the default to return if `s` is not a parseable number
* @return closest matching double (or `def` if there is an issue with `s`) where useFastParser=false
* @see #parseAsDouble(String, double, boolean)
*/
public static double parseAsDouble(final String s, final double def)
{
return parseAsDouble(s, def, false);
}

/**
* @param s a string representing a number to parse
* @param def the default to return if `s` is not a parseable number
* @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser}
* @return closest matching double (or `def` if there is an issue with `s`)
* @since 2.14
*/
public static double parseAsDouble(String s, final double def, final boolean useFastParser)
{
if (s == null) { return def; }
s = s.trim();
Expand All @@ -284,23 +309,52 @@ public static double parseAsDouble(String s, double def)
return def;
}
try {
return parseDouble(s);
return parseDouble(s, useFastParser);
} catch (NumberFormatException e) { }
return def;
}

public static double parseDouble(String s) throws NumberFormatException {
return Double.parseDouble(s);
/**
* @param s a string representing a number to parse
* @return closest matching double
* @throws NumberFormatException if string cannot be represented by a double where useFastParser=false
* @see #parseDouble(String, boolean)
*/
public static double parseDouble(final String s) throws NumberFormatException {
return parseDouble(s, false);
}

/**
* @param s a string representing a number to parse
* @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser}
* @return closest matching double
* @throws NumberFormatException if string cannot be represented by a double
* @since v2.14
*/
public static double parseDouble(final String s, final boolean useFastParser) throws NumberFormatException {
return useFastParser ? FastDoubleParser.parseDouble(s) : Double.parseDouble(s);
}

/**
* @param s a string representing a number to parse
* @return closest matching float
* @throws NumberFormatException if string cannot be represented by a float where useFastParser=false
* @see #parseFloat(String, boolean)
* @since v2.14
*/
public static float parseFloat(final String s) throws NumberFormatException {
return parseFloat(s, false);
}

/**
* @param s a string representing a number to parse
* @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser}
* @return closest matching float
* @throws NumberFormatException if string cannot be represented by a float
* @since v2.14
*/
public static float parseFloat(String s) throws NumberFormatException {
return Float.parseFloat(s);
public static float parseFloat(final String s, final boolean useFastParser) throws NumberFormatException {
return useFastParser ? FastFloatParser.parseFloat(s) : Float.parseFloat(s);
}

public static BigDecimal parseBigDecimal(String s) throws NumberFormatException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,11 @@ private void _parseSlowFloat(int expType) throws JacksonException
_numberBigDecimal = _textBuffer.contentsAsDecimal();
_numTypesValid = NR_BIGDECIMAL;
} else if (expType == NR_FLOAT) {
_numberFloat = _textBuffer.contentsAsFloat();
_numberFloat = _textBuffer.contentsAsFloat(isEnabled(JsonReadFeature.USE_FAST_DOUBLE_PARSER));
_numTypesValid = NR_FLOAT;
} else {
// Otherwise double has to do
_numberDouble = _textBuffer.contentsAsDouble();
_numberDouble = _textBuffer.contentsAsDouble(isEnabled(JsonReadFeature.USE_FAST_DOUBLE_PARSER));
_numTypesValid = NR_DOUBLE;
}
} catch (NumberFormatException nex) {
Expand Down Expand Up @@ -276,7 +276,7 @@ private void _parseSlowInt(int expType) throws JacksonException
_reportTooLongIntegral(expType, numStr);
}
if ((expType == NR_DOUBLE) || (expType == NR_FLOAT)) {
_numberDouble = NumberInput.parseDouble(numStr);
_numberDouble = NumberInput.parseDouble(numStr, isEnabled(JsonReadFeature.USE_FAST_DOUBLE_PARSER));
_numTypesValid = NR_DOUBLE;
} else {
// nope, need the heavy guns... (rare case)
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/json/JsonReadFeature.java
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,17 @@ public enum JsonReadFeature
* feature, and as such disabled by default.
*/
ALLOW_TRAILING_COMMA(false),

/**
* Feature that determines whether we use the built-in {@link Double#parseDouble(String)} code to parse
* doubles or if we use {@link com.fasterxml.jackson.core.io.doubleparser}
* instead.
*<p>
* This setting is disabled by default.
*
* @since 2.14
*/
USE_FAST_DOUBLE_PARSER(false)
;

final private boolean _defaultState;
Expand Down
36 changes: 34 additions & 2 deletions src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -504,9 +504,25 @@ public BigDecimal contentsAsDecimal() throws NumberFormatException
* @return Buffered text value parsed as a {@link Double}, if possible
*
* @throws NumberFormatException if contents are not a valid Java number
* @deprecated use {@link #contentsAsDouble(boolean)}
*/
@Deprecated
public double contentsAsDouble() throws NumberFormatException {
return NumberInput.parseDouble(contentsAsString());
return contentsAsDouble(false);
}

/**
* Convenience method for converting contents of the buffer
* into a Double value.
*
* @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser}
* @return Buffered text value parsed as a {@link Double}, if possible
*
* @throws NumberFormatException if contents are not a valid Java number
* @since 2.14
*/
public double contentsAsDouble(final boolean useFastParser) throws NumberFormatException {
return NumberInput.parseDouble(contentsAsString(), useFastParser);
}

/**
Expand All @@ -517,9 +533,25 @@ public double contentsAsDouble() throws NumberFormatException {
*
* @throws NumberFormatException if contents are not a valid Java number
* @since 2.14
* @deprecated use {@link #contentsAsFloat(boolean)}
*/
@Deprecated
public float contentsAsFloat() throws NumberFormatException {
return NumberInput.parseFloat(contentsAsString());
return contentsAsFloat(false);
}

/**
* Convenience method for converting contents of the buffer
* into a Float value.
*
* @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser}
* @return Buffered text value parsed as a {@link Float}, if possible
*
* @throws NumberFormatException if contents are not a valid Java number
* @since 2.14
*/
public float contentsAsFloat(final boolean useFastParser) throws NumberFormatException {
return NumberInput.parseFloat(contentsAsString(), useFastParser);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,20 @@ public void testNastySmallDouble()
//prior to jackson v2.14, this value used to be returned as Double.MIN_VALUE
final String nastySmallDouble = "2.2250738585072012e-308";
assertEquals(Double.parseDouble(nastySmallDouble), NumberInput.parseDouble(nastySmallDouble));
assertEquals(Double.parseDouble(nastySmallDouble), NumberInput.parseDouble(nastySmallDouble, true));
}

public void testParseFloat()
{
final String exampleFloat = "1.199999988079071";
assertEquals(1.1999999f, NumberInput.parseFloat(exampleFloat));
assertEquals(1.1999999f, NumberInput.parseFloat(exampleFloat, true));
assertEquals(1.2f, (float)NumberInput.parseDouble(exampleFloat));
assertEquals(1.2f, (float)NumberInput.parseDouble(exampleFloat, true));

final String exampleFloat2 = "7.006492321624086e-46";
assertEquals("1.4E-45", Float.toString(NumberInput.parseFloat(exampleFloat2)));
assertEquals("1.4E-45", Float.toString(NumberInput.parseFloat(exampleFloat2, true)));
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.fasterxml.jackson.core.read;

import com.fasterxml.jackson.core.json.JsonFactory;
import com.fasterxml.jackson.core.json.JsonReadFeature;

public class FastParserNonStandardNumberParsingTest extends NonStandardNumberParsingTest {
private final JsonFactory fastFactory =
JsonFactory.builder()
.enable(JsonReadFeature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS)
.enable(JsonReadFeature.USE_FAST_DOUBLE_PARSER)
.build();

@Override
protected JsonFactory jsonFactory() {
return fastFactory;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.fasterxml.jackson.core.read;

import com.fasterxml.jackson.core.json.JsonFactory;
import com.fasterxml.jackson.core.json.JsonReadFeature;

public class FastParserNumberParsingTest extends NumberParsingTest {

private final JsonFactory fastFactory =
JsonFactory.builder()
.enable(JsonReadFeature.USE_FAST_DOUBLE_PARSER)
.build();

@Override
protected JsonFactory jsonFactory() {
return fastFactory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
import com.fasterxml.jackson.core.TokenStreamFactory;
import com.fasterxml.jackson.core.json.JsonFactory;
import com.fasterxml.jackson.core.json.JsonReadFeature;

// for [core#755]
public class FloatParsingTest extends BaseTest
Expand All @@ -12,22 +14,41 @@ public class FloatParsingTest extends BaseTest

public void testFloatArrayViaInputStream() throws Exception
{
_testFloatArray(MODE_INPUT_STREAM);
_testFloatArray(MODE_INPUT_STREAM_THROTTLED);
_testFloatArray(MODE_INPUT_STREAM, false);
_testFloatArray(MODE_INPUT_STREAM_THROTTLED, false);
}

public void testFloatArrayViaInputStreamWithFastParser() throws Exception
{
_testFloatArray(MODE_INPUT_STREAM, true);
_testFloatArray(MODE_INPUT_STREAM_THROTTLED, true);
}

public void testFloatArrayViaReader() throws Exception {
_testFloatArray(MODE_READER);
_testFloatArray(MODE_READER, false);
}

public void testFloatArrayViaReaderWithFastParser() throws Exception {
_testFloatArray(MODE_READER, true);
}

public void testFloatArrayViaDataInput() throws Exception {
_testFloatArray(MODE_DATA_INPUT);
_testFloatArray(MODE_DATA_INPUT, false);
}

public void testFloatArrayViaDataInputWithFasrtParser() throws Exception {
_testFloatArray(MODE_DATA_INPUT, true);
}

private void _testFloatArray(int mode) throws Exception
private void _testFloatArray(int mode, boolean useFastParser) throws Exception
{
// construct new instance to reduce buffer recycling etc:
TokenStreamFactory jsonF = newStreamFactory();
final TokenStreamFactory jsonF ;
if (useFastParser) {
jsonF = JsonFactory.builder().enable(JsonReadFeature.USE_FAST_DOUBLE_PARSER).build();
} else {
jsonF = newStreamFactory();
}

JsonParser p = createParser(jsonF, mode, FLOATS_DOC);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@
import com.fasterxml.jackson.core.json.JsonReadFeature;

public class NonStandardNumberParsingTest
extends com.fasterxml.jackson.core.BaseTest
extends com.fasterxml.jackson.core.BaseTest
{
private final JsonFactory JSON_F = JsonFactory.builder()
.enable(JsonReadFeature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS)
.build();

protected JsonFactory jsonFactory() {
return JSON_F;
}

/**
* The format ".NNN" (as opposed to "0.NNN") is not valid JSON, so this should fail
*/
Expand All @@ -29,16 +33,16 @@ public void testLeadingDotInDecimal() {
}

public void testLeadingDotInDecimalAllowedAsync() {
_testLeadingDotInDecimalAllowed(JSON_F, MODE_DATA_INPUT);
_testLeadingDotInDecimalAllowed(jsonFactory(), MODE_DATA_INPUT);
}

public void testLeadingDotInDecimalAllowedBytes() {
_testLeadingDotInDecimalAllowed(JSON_F, MODE_INPUT_STREAM);
_testLeadingDotInDecimalAllowed(JSON_F, MODE_INPUT_STREAM_THROTTLED);
_testLeadingDotInDecimalAllowed(jsonFactory(), MODE_INPUT_STREAM);
_testLeadingDotInDecimalAllowed(jsonFactory(), MODE_INPUT_STREAM_THROTTLED);
}

public void testLeadingDotInDecimalAllowedReader() {
_testLeadingDotInDecimalAllowed(JSON_F, MODE_READER);
_testLeadingDotInDecimalAllowed(jsonFactory(), MODE_READER);
}

private void _testLeadingDotInDecimalAllowed(JsonFactory f, int mode)
Expand Down
Loading