Skip to content

Commit f9250ff

Browse files
committed
init work on inlining fastdoubleparser
add one doubleparser test case (as a start) uptake latest code update float parse don't parse floats as doubles first add feature to control double parser impl refactor params remove some deprecations (will be hard to change all calls in jackson-databind to use new methods) add fast-double-parser tests add fast float support Update NumberInput.java update tests Create ReaderTest.java remove char array support add JsonReadFeature.USE_FAST_DOUBLE_PARSER Update FloatParsingTest.java float support Update TextBuffer.java add tests Delete AbstractFloatValueFromCharArray.java latest code from fastdoubleparser update tests latest Werner Randelshofer code update licenses Update package-info.java Update package-info.java
1 parent fac1930 commit f9250ff

File tree

11 files changed

+202
-32
lines changed

11 files changed

+202
-32
lines changed

src/main/java/com/fasterxml/jackson/core/JsonParser.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,17 @@ public enum Feature {
325325
*/
326326
INCLUDE_SOURCE_IN_LOCATION(true),
327327

328+
/**
329+
* Feature that determines whether we use the built-in {@link Double#parseDouble(String)} code to parse
330+
* doubles or if we use {@link com.fasterxml.jackson.core.io.doubleparser}
331+
* instead.
332+
*<p>
333+
* This setting is disabled by default.
334+
*
335+
* @since 2.14
336+
*/
337+
USE_FAST_DOUBLE_PARSER(false),
338+
328339
;
329340

330341
/**

src/main/java/com/fasterxml/jackson/core/base/ParserBase.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -893,11 +893,11 @@ private void _parseSlowFloat(int expType) throws IOException
893893
_numberBigDecimal = _textBuffer.contentsAsDecimal();
894894
_numTypesValid = NR_BIGDECIMAL;
895895
} else if (expType == NR_FLOAT) {
896-
_numberFloat = _textBuffer.contentsAsFloat();
896+
_numberFloat = _textBuffer.contentsAsFloat(isEnabled(Feature.USE_FAST_DOUBLE_PARSER));
897897
_numTypesValid = NR_FLOAT;
898898
} else {
899899
// Otherwise double has to do
900-
_numberDouble = _textBuffer.contentsAsDouble();
900+
_numberDouble = _textBuffer.contentsAsDouble(isEnabled(Feature.USE_FAST_DOUBLE_PARSER));
901901
_numTypesValid = NR_DOUBLE;
902902
}
903903
} catch (NumberFormatException nex) {
@@ -927,7 +927,7 @@ private void _parseSlowInt(int expType) throws IOException
927927
_reportTooLongIntegral(expType, numStr);
928928
}
929929
if ((expType == NR_DOUBLE) || (expType == NR_FLOAT)) {
930-
_numberDouble = NumberInput.parseDouble(numStr);
930+
_numberDouble = NumberInput.parseDouble(numStr, isEnabled(Feature.USE_FAST_DOUBLE_PARSER));
931931
_numTypesValid = NR_DOUBLE;
932932
} else {
933933
// nope, need the heavy guns... (rare case)

src/main/java/com/fasterxml/jackson/core/io/NumberInput.java

Lines changed: 63 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.fasterxml.jackson.core.io;
22

3+
import com.fasterxml.jackson.core.io.doubleparser.FastDoubleParser;
4+
import com.fasterxml.jackson.core.io.doubleparser.FastFloatParser;
5+
36
import java.math.BigDecimal;
47

58
public final class NumberInput
@@ -239,7 +242,9 @@ public static int parseAsInt(String s, int def)
239242
// if other symbols, parse as Double, coerce
240243
if (c > '9' || c < '0') {
241244
try {
242-
return (int) parseDouble(s);
245+
//useFastParser=true is used because there is a lot less risk that small changes in result will have an affect
246+
//and performance benefit is useful
247+
return (int) parseDouble(s, true);
243248
} catch (NumberFormatException e) {
244249
return def;
245250
}
@@ -276,7 +281,9 @@ public static long parseAsLong(String s, long def)
276281
// if other symbols, parse as Double, coerce
277282
if (c > '9' || c < '0') {
278283
try {
279-
return (long) parseDouble(s);
284+
//useFastParser=true is used because there is a lot less risk that small changes in result will have an affect
285+
//and performance benefit is useful
286+
return (long) parseDouble(s, true);
280287
} catch (NumberFormatException e) {
281288
return def;
282289
}
@@ -287,8 +294,26 @@ public static long parseAsLong(String s, long def)
287294
} catch (NumberFormatException e) { }
288295
return def;
289296
}
290-
291-
public static double parseAsDouble(String s, double def)
297+
298+
/**
299+
* @param s a string representing a number to parse
300+
* @param def the default to return if `s` is not a parseable number
301+
* @return closest matching double (or `def` if there is an issue with `s`) where useFastParser=false
302+
* @see #parseAsDouble(String, double, boolean)
303+
*/
304+
public static double parseAsDouble(final String s, final double def)
305+
{
306+
return parseAsDouble(s, def, false);
307+
}
308+
309+
/**
310+
* @param s a string representing a number to parse
311+
* @param def the default to return if `s` is not a parseable number
312+
* @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser}
313+
* @return closest matching double (or `def` if there is an issue with `s`)
314+
* @since 2.14
315+
*/
316+
public static double parseAsDouble(String s, final double def, final boolean useFastParser)
292317
{
293318
if (s == null) { return def; }
294319
s = s.trim();
@@ -297,23 +322,52 @@ public static double parseAsDouble(String s, double def)
297322
return def;
298323
}
299324
try {
300-
return parseDouble(s);
325+
return parseDouble(s, useFastParser);
301326
} catch (NumberFormatException e) { }
302327
return def;
303328
}
304329

305-
public static double parseDouble(String s) throws NumberFormatException {
306-
return Double.parseDouble(s);
330+
/**
331+
* @param s a string representing a number to parse
332+
* @return closest matching double
333+
* @throws NumberFormatException if string cannot be represented by a double where useFastParser=false
334+
* @see #parseDouble(String, boolean)
335+
*/
336+
public static double parseDouble(final String s) throws NumberFormatException {
337+
return parseDouble(s, false);
338+
}
339+
340+
/**
341+
* @param s a string representing a number to parse
342+
* @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser}
343+
* @return closest matching double
344+
* @throws NumberFormatException if string cannot be represented by a double
345+
* @since v2.14
346+
*/
347+
public static double parseDouble(final String s, final boolean useFastParser) throws NumberFormatException {
348+
return useFastParser ? FastDoubleParser.parseDouble(s) : Double.parseDouble(s);
349+
}
350+
351+
/**
352+
* @param s a string representing a number to parse
353+
* @return closest matching float
354+
* @throws NumberFormatException if string cannot be represented by a float where useFastParser=false
355+
* @see #parseFloat(String, boolean)
356+
* @since v2.14
357+
*/
358+
public static float parseFloat(final String s) throws NumberFormatException {
359+
return parseFloat(s, false);
307360
}
308361

309362
/**
310363
* @param s a string representing a number to parse
364+
* @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser}
311365
* @return closest matching float
312366
* @throws NumberFormatException if string cannot be represented by a float
313367
* @since v2.14
314368
*/
315-
public static float parseFloat(String s) throws NumberFormatException {
316-
return Float.parseFloat(s);
369+
public static float parseFloat(final String s, final boolean useFastParser) throws NumberFormatException {
370+
return useFastParser ? FastFloatParser.parseFloat(s) : Float.parseFloat(s);
317371
}
318372

319373
public static BigDecimal parseBigDecimal(String s) throws NumberFormatException {

src/main/java/com/fasterxml/jackson/core/json/JsonReadFeature.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,17 @@ public enum JsonReadFeature
182182
*/
183183
@SuppressWarnings("deprecation")
184184
ALLOW_TRAILING_COMMA(false, JsonParser.Feature.ALLOW_TRAILING_COMMA),
185+
186+
/**
187+
* Feature that determines whether we use the built-in {@link Double#parseDouble(String)} code to parse
188+
* doubles or if we use {@link com.fasterxml.jackson.core.io.doubleparser}
189+
* instead.
190+
*<p>
191+
* This setting is disabled by default.
192+
*
193+
* @since 2.14
194+
*/
195+
USE_FAST_DOUBLE_PARSER(false, JsonParser.Feature.USE_FAST_DOUBLE_PARSER)
185196
;
186197

187198
final private boolean _defaultState;

src/main/java/com/fasterxml/jackson/core/util/TextBuffer.java

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -512,9 +512,25 @@ public BigDecimal contentsAsDecimal() throws NumberFormatException
512512
* @return Buffered text value parsed as a {@link Double}, if possible
513513
*
514514
* @throws NumberFormatException if contents are not a valid Java number
515+
* @deprecated use {@link #contentsAsDouble(boolean)}
515516
*/
517+
@Deprecated
516518
public double contentsAsDouble() throws NumberFormatException {
517-
return NumberInput.parseDouble(contentsAsString());
519+
return contentsAsDouble(false);
520+
}
521+
522+
/**
523+
* Convenience method for converting contents of the buffer
524+
* into a Double value.
525+
*
526+
* @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser}
527+
* @return Buffered text value parsed as a {@link Double}, if possible
528+
*
529+
* @throws NumberFormatException if contents are not a valid Java number
530+
* @since 2.14
531+
*/
532+
public double contentsAsDouble(final boolean useFastParser) throws NumberFormatException {
533+
return NumberInput.parseDouble(contentsAsString(), useFastParser);
518534
}
519535

520536
/**
@@ -525,9 +541,25 @@ public double contentsAsDouble() throws NumberFormatException {
525541
*
526542
* @throws NumberFormatException if contents are not a valid Java number
527543
* @since 2.14
544+
* @deprecated use {@link #contentsAsFloat(boolean)}
528545
*/
546+
@Deprecated
529547
public float contentsAsFloat() throws NumberFormatException {
530-
return NumberInput.parseFloat(contentsAsString());
548+
return contentsAsFloat(false);
549+
}
550+
551+
/**
552+
* Convenience method for converting contents of the buffer
553+
* into a Float value.
554+
*
555+
* @param useFastParser whether to use {@link com.fasterxml.jackson.core.io.doubleparser}
556+
* @return Buffered text value parsed as a {@link Float}, if possible
557+
*
558+
* @throws NumberFormatException if contents are not a valid Java number
559+
* @since 2.14
560+
*/
561+
public float contentsAsFloat(final boolean useFastParser) throws NumberFormatException {
562+
return NumberInput.parseFloat(contentsAsString(), useFastParser);
531563
}
532564

533565
/**

src/test/java/com/fasterxml/jackson/core/io/TestNumberInput.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,20 @@ public void testNastySmallDouble()
99
//prior to jackson v2.14, this value used to be returned as Double.MIN_VALUE
1010
final String nastySmallDouble = "2.2250738585072012e-308";
1111
assertEquals(Double.parseDouble(nastySmallDouble), NumberInput.parseDouble(nastySmallDouble));
12+
assertEquals(Double.parseDouble(nastySmallDouble), NumberInput.parseDouble(nastySmallDouble, true));
1213
}
1314

1415
public void testParseFloat()
1516
{
1617
final String exampleFloat = "1.199999988079071";
1718
assertEquals(1.1999999f, NumberInput.parseFloat(exampleFloat));
19+
assertEquals(1.1999999f, NumberInput.parseFloat(exampleFloat, true));
1820
assertEquals(1.2f, (float)NumberInput.parseDouble(exampleFloat));
21+
assertEquals(1.2f, (float)NumberInput.parseDouble(exampleFloat, true));
22+
23+
final String exampleFloat2 = "7.006492321624086e-46";
24+
assertEquals("1.4E-45", Float.toString(NumberInput.parseFloat(exampleFloat2)));
25+
assertEquals("1.4E-45", Float.toString(NumberInput.parseFloat(exampleFloat2, true)));
1926
}
2027
}
2128

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.fasterxml.jackson.core.read;
2+
3+
import com.fasterxml.jackson.core.JsonFactory;
4+
import com.fasterxml.jackson.core.json.JsonReadFeature;
5+
6+
public class FastParserNonStandardNumberParsingTest extends NonStandardNumberParsingTest {
7+
private final JsonFactory fastFactory =
8+
JsonFactory.builder()
9+
.enable(JsonReadFeature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS)
10+
.enable(JsonReadFeature.USE_FAST_DOUBLE_PARSER)
11+
.build();
12+
13+
@Override
14+
protected JsonFactory jsonFactory() {
15+
return fastFactory;
16+
}
17+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.fasterxml.jackson.core.read;
2+
3+
import com.fasterxml.jackson.core.JsonFactory;
4+
import com.fasterxml.jackson.core.JsonParser;
5+
6+
public class FastParserNumberParsingTest extends NumberParsingTest {
7+
8+
private final JsonFactory fastFactory = new JsonFactory().enable(JsonParser.Feature.USE_FAST_DOUBLE_PARSER);
9+
10+
@Override
11+
protected JsonFactory jsonFactory() {
12+
return fastFactory;
13+
}
14+
}

src/test/java/com/fasterxml/jackson/core/read/FloatParsingTest.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,24 +12,41 @@ public class FloatParsingTest extends BaseTest
1212

1313
public void testFloatArrayViaInputStream() throws Exception
1414
{
15-
_testFloatArray(MODE_INPUT_STREAM);
16-
_testFloatArray(MODE_INPUT_STREAM_THROTTLED);
15+
_testFloatArray(MODE_INPUT_STREAM, false);
16+
_testFloatArray(MODE_INPUT_STREAM_THROTTLED, false);
17+
}
18+
19+
public void testFloatArrayViaInputStreamWithFastParser() throws Exception
20+
{
21+
_testFloatArray(MODE_INPUT_STREAM, true);
22+
_testFloatArray(MODE_INPUT_STREAM_THROTTLED, true);
1723
}
1824

1925
public void testFloatArrayViaReader() throws Exception {
20-
_testFloatArray(MODE_READER);
26+
_testFloatArray(MODE_READER, false);
27+
}
28+
29+
public void testFloatArrayViaReaderWithFastParser() throws Exception {
30+
_testFloatArray(MODE_READER, true);
2131
}
2232

2333
public void testFloatArrayViaDataInput() throws Exception {
24-
_testFloatArray(MODE_DATA_INPUT);
34+
_testFloatArray(MODE_DATA_INPUT, false);
35+
}
36+
37+
public void testFloatArrayViaDataInputWithFasrtParser() throws Exception {
38+
_testFloatArray(MODE_DATA_INPUT, true);
2539
}
2640

27-
private void _testFloatArray(int mode) throws Exception
41+
private void _testFloatArray(int mode, boolean useFastParser) throws Exception
2842
{
2943
// construct new instance to reduce buffer recycling etc:
3044
TokenStreamFactory jsonF = newStreamFactory();
3145

3246
JsonParser p = createParser(jsonF, mode, FLOATS_DOC);
47+
if (useFastParser) {
48+
p.enable(JsonParser.Feature.USE_FAST_DOUBLE_PARSER);
49+
}
3350

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

src/test/java/com/fasterxml/jackson/core/read/NonStandardNumberParsingTest.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ public class NonStandardNumberParsingTest
1010
.enable(JsonReadFeature.ALLOW_LEADING_DECIMAL_POINT_FOR_NUMBERS)
1111
.build();
1212

13+
protected JsonFactory jsonFactory() {
14+
return JSON_F;
15+
}
16+
1317
/**
1418
* The format ".NNN" (as opposed to "0.NNN") is not valid JSON, so this should fail
1519
*/
@@ -27,16 +31,16 @@ public void testLeadingDotInDecimal() throws Exception {
2731
}
2832

2933
public void testLeadingDotInDecimalAllowedAsync() throws Exception {
30-
_testLeadingDotInDecimalAllowed(JSON_F, MODE_DATA_INPUT);
34+
_testLeadingDotInDecimalAllowed(jsonFactory(), MODE_DATA_INPUT);
3135
}
3236

3337
public void testLeadingDotInDecimalAllowedBytes() throws Exception {
34-
_testLeadingDotInDecimalAllowed(JSON_F, MODE_INPUT_STREAM);
35-
_testLeadingDotInDecimalAllowed(JSON_F, MODE_INPUT_STREAM_THROTTLED);
38+
_testLeadingDotInDecimalAllowed(jsonFactory(), MODE_INPUT_STREAM);
39+
_testLeadingDotInDecimalAllowed(jsonFactory(), MODE_INPUT_STREAM_THROTTLED);
3640
}
3741

3842
public void testLeadingDotInDecimalAllowedReader() throws Exception {
39-
_testLeadingDotInDecimalAllowed(JSON_F, MODE_READER);
43+
_testLeadingDotInDecimalAllowed(jsonFactory(), MODE_READER);
4044
}
4145

4246
private void _testLeadingDotInDecimalAllowed(JsonFactory f, int mode) throws Exception

0 commit comments

Comments
 (0)