Skip to content

Make CSVDecoder use lazy parsing of BigInteger/BigDecimal #351

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 24, 2022
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,16 @@ public class CsvDecoder

protected BigDecimal _numberBigDecimal;

/**
* Textual number representation captured from input in cases lazy-parsing
* is desired.
*<p>
* As of 2.14, this only applies to {@link BigInteger} and {@link BigDecimal}.
*
* @since 2.14
*/
protected String _numberString;

/*
/**********************************************************************
/* Life-cycle
Expand Down Expand Up @@ -1019,12 +1029,12 @@ public Number getNumberValue(boolean exact) throws IOException
return Long.valueOf(_numberLong);
}
if ((_numTypesValid & NR_BIGINT) != 0) {
return _numberBigInt;
return _getBigInteger();
}
// And then floating point types. But here optimal type
// needs to be big decimal, to avoid losing any data?
if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
return _numberBigDecimal;
return _getBigDecimal();
}
if ((_numTypesValid & NR_DOUBLE) == 0) { // sanity check
_throwInternal();
Expand Down Expand Up @@ -1093,7 +1103,7 @@ public BigInteger getBigIntegerValue() throws IOException
convertNumberToBigInteger();
}
}
return _numberBigInt;
return _getBigInteger();
}

public float getFloatValue() throws IOException
Expand Down Expand Up @@ -1126,6 +1136,40 @@ public BigDecimal getDecimalValue() throws IOException
convertNumberToBigDecimal();
}
}
return _getBigDecimal();
}

/**
* Internal accessor that needs to be used for accessing number value of type
* {@link BigInteger} which -- as of 2.14 -- is typically lazily parsed.
*
* @since 2.14
*/
protected BigInteger _getBigInteger() {
if (_numberBigInt != null) {
return _numberBigInt;
} else if (_numberString == null) {
throw new IllegalStateException("cannot get BigInteger from current parser state");
}
_numberBigInt = NumberInput.parseBigInteger(_numberString);
_numberString = null;
return _numberBigInt;
}

/**
* Internal accessor that needs to be used for accessing number value of type
* {@link BigDecimal} which -- as of 2.14 -- is typically lazily parsed.
*
* @since 2.14
*/
protected BigDecimal _getBigDecimal() {
if (_numberBigDecimal != null) {
return _numberBigDecimal;
} else if (_numberString == null) {
throw new IllegalStateException("cannot get BigDecimal from current parser state");
}
_numberBigDecimal = NumberInput.parseBigDecimal(_numberString);
_numberString = null;
return _numberBigDecimal;
}

Expand Down Expand Up @@ -1226,7 +1270,8 @@ private final void _parseSlowFloatValue(boolean exactNumber)
*/
try {
if (exactNumber) {
_numberBigDecimal = _textBuffer.contentsAsDecimal();
_numberBigDecimal = null;
_numberString = _textBuffer.contentsAsString();
_numTypesValid = NR_BIGDECIMAL;
} else {
// Otherwise double has to do
Expand All @@ -1251,7 +1296,8 @@ private final void _parseSlowIntValue(char[] buf, int offset, int len,
_numTypesValid = NR_LONG;
} else {
// nope, need the heavy guns... (rare case)
_numberBigInt = NumberInput.parseBigInteger(numStr);
_numberBigInt = null;
_numberString = numStr;
_numTypesValid = NR_BIGINT;
}
} catch (NumberFormatException nex) {
Expand All @@ -1278,19 +1324,20 @@ protected void convertNumberToInt() throws IOException
_numberInt = result;
} else if ((_numTypesValid & NR_BIGINT) != 0) {
// !!! Should check for range...
_numberInt = _numberBigInt.intValue();
_numberInt = _getBigInteger().intValue();
} else if ((_numTypesValid & NR_DOUBLE) != 0) {
// Need to check boundaries
if (_numberDouble < MIN_INT_D || _numberDouble > MAX_INT_D) {
reportOverflowInt();
}
_numberInt = (int) _numberDouble;
} else if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
if (BD_MIN_INT.compareTo(_numberBigDecimal) > 0
|| BD_MAX_INT.compareTo(_numberBigDecimal) < 0) {
final BigDecimal bigDecimal = _getBigDecimal();
if (BD_MIN_INT.compareTo(bigDecimal) > 0
|| BD_MAX_INT.compareTo(bigDecimal) < 0) {
reportOverflowInt();
}
_numberInt = _numberBigDecimal.intValue();
_numberInt = bigDecimal.intValue();
} else {
_throwInternal(); // should never get here
}
Expand All @@ -1304,19 +1351,20 @@ protected void convertNumberToLong() throws IOException
_numberLong = _numberInt;
} else if ((_numTypesValid & NR_BIGINT) != 0) {
// !!! Should check for range...
_numberLong = _numberBigInt.longValue();
_numberLong = _getBigInteger().longValue();
} else if ((_numTypesValid & NR_DOUBLE) != 0) {
// Need to check boundaries
if (_numberDouble < MIN_LONG_D || _numberDouble > MAX_LONG_D) {
reportOverflowLong();
}
_numberLong = (long) _numberDouble;
} else if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
if (BD_MIN_LONG.compareTo(_numberBigDecimal) > 0
|| BD_MAX_LONG.compareTo(_numberBigDecimal) < 0) {
final BigDecimal bigDecimal = _getBigDecimal();
if (BD_MIN_LONG.compareTo(bigDecimal) > 0
|| BD_MAX_LONG.compareTo(bigDecimal) < 0) {
reportOverflowLong();
}
_numberLong = _numberBigDecimal.longValue();
_numberLong = bigDecimal.longValue();
} else {
_throwInternal(); // should never get here
}
Expand All @@ -1329,7 +1377,7 @@ protected void convertNumberToBigInteger()
{
if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
// here it'll just get truncated, no exceptions thrown
_numberBigInt = _numberBigDecimal.toBigInteger();
_numberBigInt = _getBigDecimal().toBigInteger();
} else if ((_numTypesValid & NR_LONG) != 0) {
_numberBigInt = BigInteger.valueOf(_numberLong);
} else if ((_numTypesValid & NR_INT) != 0) {
Expand All @@ -1352,9 +1400,9 @@ protected void convertNumberToDouble()
*/

if ((_numTypesValid & NR_BIGDECIMAL) != 0) {
_numberDouble = _numberBigDecimal.doubleValue();
_numberDouble = _getBigDecimal().doubleValue();
} else if ((_numTypesValid & NR_BIGINT) != 0) {
_numberDouble = _numberBigInt.doubleValue();
_numberDouble = _getBigInteger().doubleValue();
} else if ((_numTypesValid & NR_LONG) != 0) {
_numberDouble = _numberLong;
} else if ((_numTypesValid & NR_INT) != 0) {
Expand All @@ -1374,7 +1422,7 @@ protected void convertNumberToBigDecimal() throws IOException
*/
_numberBigDecimal = NumberInput.parseBigDecimal(getText());
} else if ((_numTypesValid & NR_BIGINT) != 0) {
_numberBigDecimal = new BigDecimal(_numberBigInt);
_numberBigDecimal = new BigDecimal(_getBigInteger());
} else if ((_numTypesValid & NR_LONG) != 0) {
_numberBigDecimal = BigDecimal.valueOf(_numberLong);
} else if ((_numTypesValid & NR_INT) != 0) {
Expand Down