All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fasterxml.jackson.core.io.doubleparser.AbstractFloatingPointBitsFromCharSequence Maven / Gradle / Ivy

There is a newer version: 2024.03.6
Show newest version
/**
 * 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); }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy