diff --git a/src/main/java/com/fasterxml/jackson/core/io/doubleparser/AbstractFloatingPointBitsFromCharArray.java b/src/main/java/com/fasterxml/jackson/core/io/doubleparser/AbstractFloatingPointBitsFromCharArray.java index f3672d5508..f1e2a4d840 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/doubleparser/AbstractFloatingPointBitsFromCharArray.java +++ b/src/main/java/com/fasterxml/jackson/core/io/doubleparser/AbstractFloatingPointBitsFromCharArray.java @@ -338,7 +338,7 @@ private long parseHexFloatLiteral( // ------------------------ index = skipWhitespace(str, index, endIndex); if (illegal || index < endIndex - || digitCount == 0 && str[virtualIndexOfPoint] != '.' + || digitCount == 0 || !hasExponent) { return PARSE_ERROR; } diff --git a/src/main/java/com/fasterxml/jackson/core/io/doubleparser/AbstractFloatingPointBitsFromCharSequence.java b/src/main/java/com/fasterxml/jackson/core/io/doubleparser/AbstractFloatingPointBitsFromCharSequence.java index 0e2a27bec9..5d3ed5ca7c 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/doubleparser/AbstractFloatingPointBitsFromCharSequence.java +++ b/src/main/java/com/fasterxml/jackson/core/io/doubleparser/AbstractFloatingPointBitsFromCharSequence.java @@ -328,7 +328,7 @@ private long parseHexFloatLiteral( // ------------------------ index = skipWhitespace(str, index, endIndex); if (illegal || index < endIndex - || digitCount == 0 && str.charAt(virtualIndexOfPoint) != '.' + || digitCount == 0 || !hasExponent) { return PARSE_ERROR; } diff --git a/src/main/java/com/fasterxml/jackson/core/io/doubleparser/FastDoubleSwar.java b/src/main/java/com/fasterxml/jackson/core/io/doubleparser/FastDoubleSwar.java index 8677643a3d..afd806421c 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/doubleparser/FastDoubleSwar.java +++ b/src/main/java/com/fasterxml/jackson/core/io/doubleparser/FastDoubleSwar.java @@ -132,9 +132,9 @@ public static int tryToParseEightDigitsUtf8(long chunk) { } // The last 2 multiplications are independent of each other. - val = (val * (1 + (10 << 8))) >>> 8; - val = (((val & 0xff_000000ffL) * (100 + (100_0000L << 32))) - + (((val >>> 16) & 0xff_000000ffL) * (1 + (1_0000L << 32)))) >>> 32; + val = val * (1 + (10 << 8)) >>> 8; + val = (val & 0xff_000000ffL) * (100 + (100_0000L << 32)) + + (val >>> 16 & 0xff_000000ffL) * (1 + (1_0000L << 32)) >>> 32; return (int) val; } @@ -326,4 +326,4 @@ public static long readLongFromByteArrayBigEndian(byte[] a, int offset) { | ((a[offset + 6] & 0xffL) << 8) | (a[offset + 7] & 0xffL); } -} \ No newline at end of file +} diff --git a/src/main/java/com/fasterxml/jackson/core/io/doubleparser/package-info.java b/src/main/java/com/fasterxml/jackson/core/io/doubleparser/package-info.java index 15067a0a32..742c80f19c 100644 --- a/src/main/java/com/fasterxml/jackson/core/io/doubleparser/package-info.java +++ b/src/main/java/com/fasterxml/jackson/core/io/doubleparser/package-info.java @@ -42,14 +42,6 @@ * * *
- *
HexSignificand: - *
HexNumeral - *
HexNumeral {@code .} - *
{@code 0x} [HexDigits] {@code .} HexDigits - *
{@code 0X} [HexDigits] {@code .} HexDigits - *
- * - *
*
BinaryExponent: *
BinaryExponentIndicator SignedInteger *
diff --git a/src/test/java/com/fasterxml/jackson/core/io/doubleparser/AbstractDoubleHandPickedTest.java b/src/test/java/com/fasterxml/jackson/core/io/doubleparser/AbstractDoubleHandPickedTest.java index 23a430f0f5..f13f965484 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/doubleparser/AbstractDoubleHandPickedTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/doubleparser/AbstractDoubleHandPickedTest.java @@ -89,6 +89,10 @@ List dynamicTestsIllegalInputs() { dynamicTest("0x123.456789abcde", () -> testIllegalInput("0x123.456789abcde")), dynamicTest(".", () -> testIllegalInput(".")), dynamicTest("0x.", () -> testIllegalInput("0x.")), + dynamicTest("0x", () -> testIllegalInput("0x")), + dynamicTest("0x1", () -> testIllegalInput("0x1")), + dynamicTest("0xp1", () -> testIllegalInput("0xp1")), + dynamicTest("0x1.", () -> testIllegalInput("0x1.")), dynamicTest(".e2", () -> testIllegalInput(".e2")) ); } diff --git a/src/test/java/com/fasterxml/jackson/core/io/doubleparser/AbstractLexicallyGeneratedTest.java b/src/test/java/com/fasterxml/jackson/core/io/doubleparser/AbstractLexicallyGeneratedTest.java index 3b7617e4d1..27f6dbb8f4 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/doubleparser/AbstractLexicallyGeneratedTest.java +++ b/src/test/java/com/fasterxml/jackson/core/io/doubleparser/AbstractLexicallyGeneratedTest.java @@ -14,8 +14,6 @@ import org.junit.jupiter.api.DynamicTest; import org.junit.jupiter.api.TestFactory; -import java.util.ArrayList; -import java.util.List; import java.util.Random; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -46,15 +44,15 @@ abstract class AbstractLexicallyGeneratedTest { * (Make sure to take a note of the seed value if * tests failed.) */ - public static final long SEED = 0;//System.nanoTime(); + public static final long SEED = System.nanoTime(); @TestFactory @Disabled - Stream dynamicTestsRandomStringFrom10SyntaxRuleWithoutWhitespace() { + Stream dynamicTestsRandomStringFrom1SyntaxRuleWithoutWhitespace() { Random rng = new Random(SEED); LexicalGenerator gen = new LexicalGenerator(false, true); - return IntStream.range(1, 10_000).mapToObj(i -> { - String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(10, rng); + return IntStream.range(0, 100).mapToObj(i -> { + String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(1, rng); return dynamicTest(i + ": " + str, () -> testAgainstJdk(str)); } @@ -63,11 +61,24 @@ Stream dynamicTestsRandomStringFrom10SyntaxRuleWithoutWhitespace() @TestFactory @Disabled - Stream dynamicTestsRandomStringFrom1SyntaxRuleWithoutWhitespace() { + Stream dynamicTestsRandomStringFrom2SyntaxRuleWithoutWhitespace() { Random rng = new Random(SEED); LexicalGenerator gen = new LexicalGenerator(false, true); - return IntStream.range(1, 10_000).mapToObj(i -> { - String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(1, rng); + return IntStream.range(0, 10_000).mapToObj(i -> { + String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(2, rng); + return dynamicTest(i + ": " + str, + () -> testAgainstJdk(str)); + } + ); + } + + @TestFactory + @Disabled + Stream dynamicTestsRandomStringFrom3SyntaxRuleWithoutWhitespace() { + Random rng = new Random(SEED); + LexicalGenerator gen = new LexicalGenerator(false, true); + return IntStream.range(0, 10_000).mapToObj(i -> { + String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(3, rng); return dynamicTest(i + ": " + str, () -> testAgainstJdk(str)); } @@ -76,11 +87,24 @@ Stream dynamicTestsRandomStringFrom1SyntaxRuleWithoutWhitespace() { @TestFactory @Disabled - Stream dynamicTestsRandomStringFrom40SyntaxRuleWithoutWhitespace() { + Stream dynamicTestsRandomStringFrom4SyntaxRuleWithoutWhitespace() { Random rng = new Random(SEED); LexicalGenerator gen = new LexicalGenerator(false, true); - return IntStream.range(1, 10_000).mapToObj(i -> { - String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(40, rng); + return IntStream.range(0, 10_000).mapToObj(i -> { + String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(4, rng); + return dynamicTest(i + ": " + str, + () -> testAgainstJdk(str)); + } + ); + } + + @TestFactory + @Disabled + Stream dynamicTestsRandomStringFrom10SyntaxRuleWithoutWhitespace() { + Random rng = new Random(SEED); + LexicalGenerator gen = new LexicalGenerator(false, true); + return IntStream.range(0, 10_000).mapToObj(i -> { + String str = gen.produceRandomInputStringFromLexicalRuleWithoutWhitespace(10, rng); return dynamicTest(i + ": " + str, () -> testAgainstJdk(str)); } @@ -102,13 +126,38 @@ Stream dynamicTestsRandomStringsOfIncreasingLengthWithWhitespace() @TestFactory @Disabled - List dynamicTestsAllSingleCharacterInputs() { - ArrayList list = new ArrayList<>(); - for (int codePoint = 0; codePoint <= Character.MAX_VALUE; codePoint++) { - String str = "" + (char) codePoint; - list.add(dynamicTest("0x" + Integer.toHexString(codePoint), () -> testAgainstJdk(str))); - } - return list; + Stream dynamicTestsAsciiCharacterInputsUpTo4Characters() { + int maxLength = 4; + Random rng = new Random(); + return IntStream.range(0, 10_000).mapToObj(i -> { + char[] ch = new char[4]; + int n = rng.nextInt(maxLength) + 1; + for (int j = 0; j < n; j++) { + ch[j] = nextAsciiChar(rng); + } + StringBuilder str = new StringBuilder(); + StringBuilder title = new StringBuilder(Integer.toString(n)); + title.append(':'); + for (int j = 0; j < 4; j++) { + char c = ch[j]; + if (c >= ' ') { + if (Character.isISOControl(c) || Character.isWhitespace(c)) { + title.append("&#x").append(Integer.toHexString(c)).append(';'); + str.append(c); + } else { + title.append((char) c); + str.append(c); + } + } + } + return dynamicTest(title.toString(), () -> testAgainstJdk(str.toString())); + }); + } + + private static char nextAsciiChar(Random rng) { + //U+0020 SPACE + //U+007F DELETE + return (char) (rng.nextInt(0x7f - 0x20) + 0x20); } /** diff --git a/src/test/java/com/fasterxml/jackson/core/io/doubleparser/LexicalGenerator.java b/src/test/java/com/fasterxml/jackson/core/io/doubleparser/LexicalGenerator.java index 5cf55154dc..91baee71b3 100644 --- a/src/test/java/com/fasterxml/jackson/core/io/doubleparser/LexicalGenerator.java +++ b/src/test/java/com/fasterxml/jackson/core/io/doubleparser/LexicalGenerator.java @@ -132,40 +132,46 @@ private int produceRandomDecimalFloatingPointLiteral(int remaining, Random rng, remaining = produceRandomDigits(remaining, rng, buf); buf.append('.'); remaining--; - if (rng.nextBoolean()) { + if (remaining > 0 && rng.nextBoolean()) { remaining = produceRandomDigits(remaining, rng, buf); } - if (rng.nextBoolean()) { + if (remaining > 0 && rng.nextBoolean()) { remaining = produceRandomExponentPart(remaining, rng, buf); } - if (rng.nextBoolean()) { + if (remaining > 0 && rng.nextBoolean()) { remaining = produceRandomFloatTypeSuffix(remaining, rng, buf); } break; case 1: buf.append('.'); remaining--; - remaining = produceRandomDigits(remaining, rng, buf); - if (rng.nextBoolean()) { + if (remaining > 0) { + remaining = produceRandomDigits(remaining, rng, buf); + } + if (remaining > 0 && rng.nextBoolean()) { remaining = produceRandomExponentPart(remaining, rng, buf); } - if (rng.nextBoolean()) { + if (remaining > 0 && rng.nextBoolean()) { remaining = produceRandomFloatTypeSuffix(remaining, rng, buf); } break; case 2: remaining = produceRandomDigits(remaining, rng, buf); - remaining = produceRandomExponentPart(remaining, rng, buf); - if (rng.nextBoolean()) { + if (remaining > 0) { + remaining = produceRandomExponentPart(remaining, rng, buf); + } + if (remaining > 0 && rng.nextBoolean()) { remaining = produceRandomFloatTypeSuffix(remaining, rng, buf); } break; case 3: remaining = produceRandomDigits(remaining, rng, buf); - if (rng.nextBoolean()) { + if (remaining > 0 && rng.nextBoolean()) { remaining = produceRandomExponentPart(remaining, rng, buf); } - remaining = produceRandomFloatTypeSuffix(remaining, rng, buf); + if (remaining > 0) { + remaining = produceRandomFloatTypeSuffix(remaining, rng, buf); + } break; } return remaining; @@ -205,10 +211,12 @@ private int produceRandomDigits(int remaining, Random rng, StringBuilder buf) { break; case 1: remaining = produceRandomDigit(remaining, rng, buf); - if (rng.nextBoolean()) { + if (remaining > 0 && rng.nextBoolean()) { remaining = produceRandomDigitsAndUnderscores(remaining, rng, buf); } - remaining = produceRandomDigit(remaining, rng, buf); + if (remaining > 0) { + remaining = produceRandomDigit(remaining, rng, buf); + } break; } @@ -226,17 +234,21 @@ private int produceRandomDigitsAndUnderscores(int remaining, Random rng, StringB switch (rng.nextInt(2)) { case 0: remaining = produceRandomDigitOrUnderscore(remaining, rng, buf); - int todo = rng.nextInt(Math.max(remaining, 1)); - for (int i = 0; i < todo; i++) { - remaining = produceRandomDigitOrUnderscore(remaining, rng, buf); + if (remaining > 0) { + int todo = rng.nextInt(Math.max(remaining, 1)); + for (int i = 0; i < todo; i++) { + remaining = produceRandomDigitOrUnderscore(remaining, rng, buf); + } } break; case 1: remaining = produceRandomDigit(remaining, rng, buf); - if (rng.nextBoolean()) { + if (remaining > 0 && rng.nextBoolean()) { remaining = produceRandomDigitsAndUnderscores(remaining, rng, buf); } - remaining = produceRandomDigit(remaining, rng, buf); + if (remaining > 0) { + remaining = produceRandomDigit(remaining, rng, buf); + } break; } return remaining; @@ -289,22 +301,28 @@ private int produceRandomFloatTypeSuffix(int remaining, Random rng, StringBuilde private int produceRandomFloatValue(int remaining, Random rng, StringBuilder buf) { switch (rng.nextInt(4)) { case 0: - if (rng.nextBoolean()) { + if (remaining > 0 && rng.nextBoolean()) { remaining = produceRandomSign(remaining, rng, buf); } - remaining = produceRandomNaNOrInfinity(remaining, rng, buf); + if (remaining > 0 && remaining > 0) { + remaining = produceRandomNaNOrInfinity(remaining, rng, buf); + } break; case 1: if (rng.nextBoolean()) { remaining = produceRandomSign(remaining, rng, buf); } - remaining = produceRandomDecimalFloatingPointLiteral(remaining, rng, buf); + if (remaining > 0) { + remaining = produceRandomDecimalFloatingPointLiteral(remaining, rng, buf); + } break; case 2: if (rng.nextBoolean()) { remaining = produceRandomSign(remaining, rng, buf); } - remaining = produceRandomHexFloatingPointLiteral(remaining, rng, buf); + if (remaining > 0) { + remaining = produceRandomHexFloatingPointLiteral(remaining, rng, buf); + } break; case 3: remaining = produceRandomSignedInteger(remaining, rng, buf); @@ -348,15 +366,21 @@ private int produceRandomHexDigitsAndUnderscores(int remaining, Random rng, Stri private int produceRandomHexFloatingPointLiteral(int remaining, Random rng, StringBuilder buf) { remaining = produceRandomHexSignificand(remaining, rng, buf); - remaining = produceRandomBinaryExponent(remaining, rng, buf); - remaining = produceRandomFloatTypeSuffix(remaining, rng, buf); + if (remaining > 0) { + remaining = produceRandomBinaryExponent(remaining, rng, buf); + } + if (remaining > 0) { + remaining = produceRandomFloatTypeSuffix(remaining, rng, buf); + } return remaining; } private int produceRandomHexNumeral(int remaining, Random rng, StringBuilder buf) { buf.append(rng.nextBoolean() ? "0x" : "0X"); remaining--; - remaining = produceRandomHexDigits(remaining, rng, buf); + if (remaining > 0) { + remaining = produceRandomHexDigits(remaining, rng, buf); + } return remaining; } @@ -382,12 +406,14 @@ private int produceRandomHexSignificand(int remaining, Random rng, StringBuilder case 2: buf.append(rng.nextBoolean() ? "0x" : "0X"); remaining--; - if (rng.nextBoolean()) { + if (remaining > 0 && rng.nextBoolean()) { + remaining = produceRandomHexDigits(remaining, rng, buf); + } + if (remaining > 0 && rng.nextBoolean()) { + buf.append('.'); + remaining--; remaining = produceRandomHexDigits(remaining, rng, buf); } - buf.append('.'); - remaining--; - remaining = produceRandomHexDigits(remaining, rng, buf); break; } return remaining; @@ -403,8 +429,12 @@ private int produceRandomHexSignificand(int remaining, Random rng, StringBuilder public String produceRandomInputStringFromLexicalRuleWithWhitespace(int remaining, Random rng) { StringBuilder buf = new StringBuilder(); remaining = produceRandomWhitespaces(remaining, rng, buf); - remaining = produceRandomFloatValue(remaining, rng, buf); - remaining = produceRandomWhitespaces(remaining, rng, buf); + if (remaining > 0) { + remaining = produceRandomFloatValue(remaining, rng, buf); + } + if (remaining > 0) { + remaining = produceRandomWhitespaces(remaining, rng, buf); + } return buf.toString(); } @@ -452,7 +482,9 @@ private int produceRandomSignedInteger(int remaining, Random rng, StringBuilder if (rng.nextBoolean()) { remaining = produceRandomSign(remaining, rng, buf); } - remaining = produceRandomDigits(remaining, rng, buf); + if (remaining > 0) { + remaining = produceRandomDigits(remaining, rng, buf); + } return remaining; }