Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 11 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/JsonParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,17 @@ public enum Feature {
*/
INCLUDE_SOURCE_IN_LOCATION(true),

/**
* 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),

;

/**
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/com/fasterxml/jackson/core/base/ParserBase.java
Original file line number Diff line number Diff line change
Expand Up @@ -893,11 +893,11 @@ private void _parseSlowFloat(int expType) throws IOException
_numberBigDecimal = _textBuffer.contentsAsDecimal();
_numTypesValid = NR_BIGDECIMAL;
} else if (expType == NR_FLOAT) {
_numberFloat = _textBuffer.contentsAsFloat();
_numberFloat = _textBuffer.contentsAsFloat(isEnabled(Feature.USE_FAST_DOUBLE_PARSER));
_numTypesValid = NR_FLOAT;
} else {
// Otherwise double has to do
_numberDouble = _textBuffer.contentsAsDouble();
_numberDouble = _textBuffer.contentsAsDouble(isEnabled(Feature.USE_FAST_DOUBLE_PARSER));
_numTypesValid = NR_DOUBLE;
}
} catch (NumberFormatException nex) {
Expand Down Expand Up @@ -927,7 +927,7 @@ private void _parseSlowInt(int expType) throws IOException
_reportTooLongIntegral(expType, numStr);
}
if ((expType == NR_DOUBLE) || (expType == NR_FLOAT)) {
_numberDouble = NumberInput.parseDouble(numStr);
_numberDouble = NumberInput.parseDouble(numStr, isEnabled(Feature.USE_FAST_DOUBLE_PARSER));
_numTypesValid = NR_DOUBLE;
} else {
// nope, need the heavy guns... (rare case)
Expand Down
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 @@ -239,7 +242,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 @@ -276,7 +281,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 @@ -287,8 +294,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 @@ -297,23 +322,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
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 @@ -182,6 +182,17 @@ public enum JsonReadFeature
*/
@SuppressWarnings("deprecation")
ALLOW_TRAILING_COMMA(false, JsonParser.Feature.ALLOW_TRAILING_COMMA),

/**
* 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, JsonParser.Feature.USE_FAST_DOUBLE_PARSER)
;

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 @@ -512,9 +512,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 @@ -525,9 +541,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.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,14 @@
package com.fasterxml.jackson.core.read;

import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;

public class FastParserNumberParsingTest extends NumberParsingTest {

private final JsonFactory fastFactory = new JsonFactory().enable(JsonParser.Feature.USE_FAST_DOUBLE_PARSER);

@Override
protected JsonFactory jsonFactory() {
return fastFactory;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,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();

JsonParser p = createParser(jsonF, mode, FLOATS_DOC);
if (useFastParser) {
p.enable(JsonParser.Feature.USE_FAST_DOUBLE_PARSER);
}

assertToken(JsonToken.START_ARRAY, p.nextToken());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ public class NonStandardNumberParsingTest
.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 @@ -27,16 +31,16 @@ public void testLeadingDotInDecimal() throws Exception {
}

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

public void testLeadingDotInDecimalAllowedBytes() throws Exception {
_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() throws Exception {
_testLeadingDotInDecimalAllowed(JSON_F, MODE_READER);
_testLeadingDotInDecimalAllowed(jsonFactory(), MODE_READER);
}

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