com.fasterxml.jackson.jr.private_.io.doubleparser.AbstractFloatingPointBitsFromCharSequence Maven / Gradle / Ivy
Show all versions of jackson-jr-all Show documentation
/**
* References:
*
* - This class has been derived from "FastDoubleParser".
* - Copyright (c) Werner Randelshofer. Apache 2.0 License.
* github.com.
*
*/
package com.fasterxml.jackson.core.io.doubleparser;
/**
* Parses a {@code FloatingPointLiteral} from a {@link CharSequence}.
*
* This class should have a type parameter for the return value of its parse
* methods. Unfortunately Java does not support type parameters for primitive
* types. As a workaround we use {@code long}. A {@code long} has enough bits to
* fit a {@code double} value or a {@code float} value.
*
* See {@link com.fasterxml.jackson.core.io.doubleparser} for the grammar of
* {@code FloatingPointLiteral}.
*/
abstract class AbstractFloatingPointBitsFromCharSequence extends AbstractFloatValueParser {
private boolean isDigit(char c) {
return '0' <= c && c <= '9';
}
/**
* Parses a {@code DecimalFloatingPointLiteral} production with optional
* trailing white space until the end of the text.
* Given that we have already consumed the optional leading zero of
* the {@code DecSignificand}.
*
*
* - DecimalFloatingPointLiteralWithWhiteSpace:
* - DecimalFloatingPointLiteral [WhiteSpace] EOT
*
*
* See {@link com.fasterxml.jackson.core.io.doubleparser} for the grammar of
* {@code DecimalFloatingPointLiteral} and {@code DecSignificand}.
*
* @param str a string
* @param index start index inclusive of the {@code DecimalFloatingPointLiteralWithWhiteSpace}
* @param endIndex end index (exclusive)
* @param isNegative true if the float value is negative
* @param hasLeadingZero true if we have consumed the optional leading zero
* @return the bit pattern of the parsed value, if the input is legal;
* otherwise, {@code -1L}.
*/
private long parseDecFloatLiteral(CharSequence str, int index, int startIndex, int endIndex, boolean isNegative, boolean hasLeadingZero) {
// Parse significand
// -----------------
// Note: a multiplication by a constant is cheaper than an
// arbitrary integer multiplication.
long significand = 0;// significand is treated as an unsigned long
final int significandStartIndex = index;
int virtualIndexOfPoint = -1;
boolean illegal = false;
char ch = 0;
for (; index < endIndex; index++) {
ch = str.charAt(index);
if (isDigit(ch)) {
// This might overflow, we deal with it later.
significand = 10 * significand + ch - '0';
} else if (ch == '.') {
illegal |= virtualIndexOfPoint >= 0;
virtualIndexOfPoint = index;
for (; index < endIndex - 8; index += 8) {
int eightDigits = tryToParseEightDigits(str, index + 1);
if (eightDigits < 0) {
break;
}
// This might overflow, we deal with it later.
significand = 100_000_000L * significand + eightDigits;
}
} else {
break;
}
}
final int digitCount;
final int significandEndIndex = index;
int exponent;
if (virtualIndexOfPoint < 0) {
digitCount = significandEndIndex - significandStartIndex;
virtualIndexOfPoint = significandEndIndex;
exponent = 0;
} else {
digitCount = significandEndIndex - significandStartIndex - 1;
exponent = virtualIndexOfPoint - significandEndIndex + 1;
}
// Parse exponent number
// ---------------------
int expNumber = 0;
if (ch == 'e' || ch == 'E') {
ch = ++index < endIndex ? str.charAt(index) : 0;
boolean neg_exp = ch == '-';
if (neg_exp || ch == '+') {
ch = ++index < endIndex ? str.charAt(index) : 0;
}
illegal |= !isDigit(ch);
do {
// Guard against overflow
if (expNumber < AbstractFloatValueParser.MAX_EXPONENT_NUMBER) {
expNumber = 10 * expNumber + ch - '0';
}
ch = ++index < endIndex ? str.charAt(index) : 0;
} while (isDigit(ch));
if (neg_exp) {
expNumber = -expNumber;
}
exponent += expNumber;
}
// Skip optional FloatTypeSuffix
// ------------------------
if (index < endIndex && (ch == 'd' || ch == 'D' || ch == 'f' || ch == 'F')) {
index++;
}
// Skip trailing whitespace and check if FloatingPointLiteral is complete
// ------------------------
index = skipWhitespace(str, index, endIndex);
if (illegal || index < endIndex
|| !hasLeadingZero && digitCount == 0) {
return PARSE_ERROR;
}
// Re-parse significand in case of a potential overflow
// -----------------------------------------------
final boolean isSignificandTruncated;
int skipCountInTruncatedDigits = 0;//counts +1 if we skipped over the decimal point
int exponentOfTruncatedSignificand;
if (digitCount > 19) {
significand = 0;
for (index = significandStartIndex; index < significandEndIndex; index++) {
ch = str.charAt(index);
if (ch == '.') {
skipCountInTruncatedDigits++;
} else {
if (Long.compareUnsigned(significand, AbstractFloatValueParser.MINIMAL_NINETEEN_DIGIT_INTEGER) < 0) {
significand = 10 * significand + ch - '0';
} else {
break;
}
}
}
isSignificandTruncated = (index < significandEndIndex);
exponentOfTruncatedSignificand = virtualIndexOfPoint - index + skipCountInTruncatedDigits + expNumber;
} else {
isSignificandTruncated = false;
exponentOfTruncatedSignificand = 0;
}
return valueOfFloatLiteral(str, startIndex, endIndex, isNegative, significand, exponent, isSignificandTruncated,
exponentOfTruncatedSignificand);
}
/**
* Parses a {@code FloatingPointLiteral} production with optional leading and trailing
* white space.
*
*
* - FloatingPointLiteralWithWhiteSpace:
* - [WhiteSpace] FloatingPointLiteral [WhiteSpace]
*
*
* See {@link com.fasterxml.jackson.core.io.doubleparser} for the grammar of
* {@code FloatingPointLiteral}.
*
* @param str a string containing a {@code FloatingPointLiteralWithWhiteSpace}
* @param offset start offset of {@code FloatingPointLiteralWithWhiteSpace} in {@code str}
* @param length length of {@code FloatingPointLiteralWithWhiteSpace} in {@code str}
* @return the bit pattern of the parsed value, if the input is legal;
* otherwise, {@code -1L}.
*/
public long parseFloatingPointLiteral(CharSequence str, int offset, int length) {
final int endIndex = offset + length;
if (offset < 0 || endIndex > str.length()) {
return PARSE_ERROR;
}
// Skip leading whitespace
// -------------------
int index = skipWhitespace(str, offset, endIndex);
if (index == endIndex) {
return PARSE_ERROR;
}
char ch = str.charAt(index);
// Parse optional sign
// -------------------
final boolean isNegative = ch == '-';
if (isNegative || ch == '+') {
ch = ++index < endIndex ? str.charAt(index) : 0;
if (ch == 0) {
return PARSE_ERROR;
}
}
// Parse NaN or Infinity
// ---------------------
if (ch >= 'I') {
return ch == 'N'
? parseNaN(str, index, endIndex)
: parseInfinity(str, index, endIndex, isNegative);
}
// Parse optional leading zero
// ---------------------------
final boolean hasLeadingZero = ch == '0';
if (hasLeadingZero) {
ch = ++index < endIndex ? str.charAt(index) : 0;
if (ch == 'x' || ch == 'X') {
return parseHexFloatLiteral(str, index + 1, offset, endIndex, isNegative);
}
}
return parseDecFloatLiteral(str, index, offset, endIndex, isNegative, hasLeadingZero);
/*
if (FALL_BACK_TO_BIGDECIMAL) {
return parseDecFloatLiteralBigDecimal(str, index, offset, endIndex, isNegative, hasLeadingZero);
} else {
return parseDecFloatLiteralDecimal(str, index, offset, endIndex, isNegative, hasLeadingZero);
}
*/
}
/**
* Parses the following rules
* (more rules are defined in {@link AbstractFloatValueParser}):
*
* - RestOfHexFloatingPointLiteral:
*
- RestOfHexSignificand BinaryExponent
*
*
*
* - RestOfHexSignificand:
*
- HexDigits
*
- HexDigits {@code .}
*
- [HexDigits] {@code .} HexDigits
*
*
* @param str the input string
* @param index index to the first character of RestOfHexFloatingPointLiteral
* @param startIndex the start index of the string
* @param endIndex the end index of the string
* @param isNegative if the resulting number is negative
* @return the bit pattern of the parsed value, if the input is legal;
* otherwise, {@code -1L}.
*/
private long parseHexFloatLiteral(
CharSequence str, int index, int startIndex, int endIndex, boolean isNegative) {
// Parse HexSignificand
// ------------
long significand = 0;// significand is treated as an unsigned long
int exponent = 0;
final int significandStartIndex = index;
int virtualIndexOfPoint = -1;
final int digitCount;
boolean illegal = false;
char ch = 0;
for (; index < endIndex; index++) {
ch = str.charAt(index);
// Table look up is faster than a sequence of if-else-branches.
int hexValue = ch > 127 ? AbstractFloatValueParser.OTHER_CLASS : AbstractFloatValueParser.CHAR_TO_HEX_MAP[ch];
if (hexValue >= 0) {
significand = (significand << 4) | hexValue;// This might overflow, we deal with it later.
} else if (hexValue == AbstractFloatValueParser.DECIMAL_POINT_CLASS) {
illegal |= virtualIndexOfPoint >= 0;
virtualIndexOfPoint = index;
/*
for (;index < endIndex - 8; index += 8) {
long parsed = tryToParseEightHexDigits(str, index + 1);
if (parsed >= 0) {
// This might overflow, we deal with it later.
digits = (digits << 32) + parsed;
} else {
break;
}
}
*/
} else {
break;
}
}
final int significandEndIndex = index;
if (virtualIndexOfPoint < 0) {
digitCount = significandEndIndex - significandStartIndex;
virtualIndexOfPoint = significandEndIndex;
} else {
digitCount = significandEndIndex - significandStartIndex - 1;
exponent = Math.min(virtualIndexOfPoint - index + 1, AbstractFloatValueParser.MAX_EXPONENT_NUMBER) * 4;
}
// Parse exponent
// --------------
int expNumber = 0;
final boolean hasExponent = (ch == 'p') || (ch == 'P');
if (hasExponent) {
ch = ++index < endIndex ? str.charAt(index) : 0;
boolean neg_exp = ch == '-';
if (neg_exp || ch == '+') {
ch = ++index < endIndex ? str.charAt(index) : 0;
}
illegal |= !isDigit(ch);
do {
// Guard against overflow
if (expNumber < AbstractFloatValueParser.MAX_EXPONENT_NUMBER) {
expNumber = 10 * expNumber + ch - '0';
}
ch = ++index < endIndex ? str.charAt(index) : 0;
} while (isDigit(ch));
if (neg_exp) {
expNumber = -expNumber;
}
exponent += expNumber;
}
// Skip optional FloatTypeSuffix
// ------------------------
if (index < endIndex && (ch == 'd' || ch == 'D' || ch == 'f' || ch == 'F')) {
index++;
}
// Skip trailing whitespace and check if FloatingPointLiteral is complete
// ------------------------
index = skipWhitespace(str, index, endIndex);
if (illegal || index < endIndex
|| digitCount == 0
|| !hasExponent) {
return PARSE_ERROR;
}
// Re-parse significand in case of a potential overflow
// -----------------------------------------------
final boolean isSignificandTruncated;
int skipCountInTruncatedDigits = 0;//counts +1 if we skipped over the decimal point
if (digitCount > 16) {
significand = 0;
for (index = significandStartIndex; index < significandEndIndex; index++) {
ch = str.charAt(index);
// Table look up is faster than a sequence of if-else-branches.
int hexValue = ch > 127 ? AbstractFloatValueParser.OTHER_CLASS : AbstractFloatValueParser.CHAR_TO_HEX_MAP[ch];
if (hexValue >= 0) {
if (Long.compareUnsigned(significand, AbstractFloatValueParser.MINIMAL_NINETEEN_DIGIT_INTEGER) < 0) {
significand = (significand << 4) | hexValue;
} else {
break;
}
} else {
skipCountInTruncatedDigits++;
}
}
isSignificandTruncated = (index < significandEndIndex);
} else {
isSignificandTruncated = false;
}
return valueOfHexLiteral(str, startIndex, endIndex, isNegative, significand, exponent, isSignificandTruncated,
virtualIndexOfPoint - index + skipCountInTruncatedDigits + expNumber);
}
/**
* Parses a {@code Infinity} production with optional trailing white space
* until the end of the text.
*
*
* - InfinityWithWhiteSpace:
* - {@code Infinity} [WhiteSpace] EOT
*
*
*
* @param str a string
* @param index index of the "I" character
* @param endIndex end index (exclusive)
* @return a positive or negative infinity value
* @throws NumberFormatException on parsing failure
*/
private long parseInfinity(CharSequence str, int index, int endIndex, boolean negative) {
if (index + 7 < endIndex
&& str.charAt(index) == 'I'
&& str.charAt(index + 1) == 'n'
&& str.charAt(index + 2) == 'f'
&& str.charAt(index + 3) == 'i'
&& str.charAt(index + 4) == 'n'
&& str.charAt(index + 5) == 'i'
&& str.charAt(index + 6) == 't'
&& str.charAt(index + 7) == 'y'
) {
index = skipWhitespace(str, index + 8, endIndex);
if (index == endIndex) {
return negative ? negativeInfinity() : positiveInfinity();
}
}
return PARSE_ERROR;
}
/**
* Parses a {@code Nan} production with optional trailing white space
* until the end of the text.
* Given that the String contains a 'N' character at the current
* {@code index}.
*
*
* - NanWithWhiteSpace:
* - {@code NaN} [WhiteSpace] EOT
*
*
*
* @param str a string that contains a "N" character at {@code index}
* @param index index of the "N" character
* @param endIndex end index (exclusive)
* @return a NaN value
* @throws NumberFormatException on parsing failure
*/
private long parseNaN(CharSequence str, int index, int endIndex) {
if (index + 2 < endIndex
// && str.charAt(index) == 'N'
&& str.charAt(index + 1) == 'a'
&& str.charAt(index + 2) == 'N') {
index = skipWhitespace(str, index + 3, endIndex);
if (index == endIndex) {
return nan();
}
}
return PARSE_ERROR;
}
/**
* Skips optional white space in the provided string
*
* @param str a string
* @param index start index (inclusive) of the optional white space
* @param endIndex end index (exclusive) of the optional white space
* @return index after the optional white space
*/
private int skipWhitespace(CharSequence str, int index, int endIndex) {
for (; index < endIndex; index++) {
if (str.charAt(index) > ' ') {
break;
}
}
return index;
}
private int tryToParseEightDigits(CharSequence str, int offset) {
// Performance: We extract the chars in two steps so that we
// can benefit from out of order execution in the CPU.
long first = str.charAt(offset)
| (long) str.charAt(offset + 1) << 16
| (long) str.charAt(offset + 2) << 32
| (long) str.charAt(offset + 3) << 48;
long second = str.charAt(offset + 4)
| (long) str.charAt(offset + 5) << 16
| (long) str.charAt(offset + 6) << 32
| (long) str.charAt(offset + 7) << 48;
return FastDoubleSwar.tryToParseEightDigitsUtf16(first, second);
}
/**
* @return a NaN constant in the specialized type wrapped in a {@code long}
*/
abstract long nan();
/**
* @return a negative infinity constant in the specialized type wrapped in a
* {@code long}
*/
abstract long negativeInfinity();
/**
* @return a positive infinity constant in the specialized type wrapped in a
* {@code long}
*/
abstract long positiveInfinity();
/**
* Computes a float value from the given components of a decimal float
* literal.
*
* @param str the string that contains the float literal (and maybe more)
* @param startIndex the start index (inclusive) of the float literal
* inside the string
* @param endIndex the end index (exclusive) of the float literal inside
* the string
* @param isNegative whether the float value is negative
* @param significand the significand of the float value (can be truncated)
* @param exponent the exponent of the float value
* @param isSignificandTruncated whether the significand is truncated
* @param exponentOfTruncatedSignificand the exponent value of the truncated
* significand
* @return the bit pattern of the parsed value, if the input is legal;
* otherwise, {@code -1L}.
*/
abstract long valueOfFloatLiteral(
CharSequence str, int startIndex, int endIndex,
boolean isNegative, long significand, int exponent,
boolean isSignificandTruncated, int exponentOfTruncatedSignificand);
/**
* Computes a float value from the given components of a hexadecimal float
* literal.
*
* @param str the string that contains the float literal (and maybe more)
* @param startIndex the start index (inclusive) of the float literal
* inside the string
* @param endIndex the end index (exclusive) of the float literal inside
* the string
* @param isNegative whether the float value is negative
* @param significand the significand of the float value (can be truncated)
* @param exponent the exponent of the float value
* @param isSignificandTruncated whether the significand is truncated
* @param exponentOfTruncatedSignificand the exponent value of the truncated
* significand
* @return the bit pattern of the parsed value, if the input is legal;
* otherwise, {@code -1L}.
*/
abstract long valueOfHexLiteral(
CharSequence str, int startIndex, int endIndex,
boolean isNegative, long significand, int exponent,
boolean isSignificandTruncated, int exponentOfTruncatedSignificand);
}