ICU-21232 Improve performance of DecimalFormat#parse on long numbers

This commit is contained in:
Shane F. Carr 2020-08-26 16:43:23 -05:00
parent 95cd3904aa
commit 59cdbe1f53
5 changed files with 45 additions and 14 deletions

View File

@ -1008,13 +1008,8 @@ void DecimalQuantity::shiftLeft(int32_t numDigits) {
}
if (usingBytes) {
ensureCapacity(precision + numDigits);
int i = precision + numDigits - 1;
for (; i >= numDigits; i--) {
fBCD.bcdBytes.ptr[i] = fBCD.bcdBytes.ptr[i - numDigits];
}
for (; i >= 0; i--) {
fBCD.bcdBytes.ptr[i] = 0;
}
uprv_memmove(fBCD.bcdBytes.ptr + numDigits, fBCD.bcdBytes.ptr, precision);
uprv_memset(fBCD.bcdBytes.ptr, 0, numDigits);
} else {
fBCD.bcdLong <<= (numDigits * 4);
}

View File

@ -247,6 +247,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n
TESTCASE_AUTO(Test20961_CurrencyPluralPattern);
TESTCASE_AUTO(Test21134_ToNumberFormatter);
TESTCASE_AUTO(Test13733_StrictAndLenient);
TESTCASE_AUTO(Test21232_ParseTimeout);
TESTCASE_AUTO_END;
}
@ -10033,4 +10034,29 @@ void NumberFormatTest::Test13733_StrictAndLenient() {
}
}
void NumberFormatTest::Test21232_ParseTimeout() {
IcuTestErrorCode status(*this, "Test21232_ParseTimeout");
DecimalFormat df(status);
if (status.errDataIfFailureAndReset()) {
return;
}
UnicodeString input = u"4444444444444444444444444444444444444444";
if (quick) {
for (int32_t i = 0; i < 5; i++) {
input.append(input);
}
assertEquals("Somewhat long input of digits", 1280, input.length());
} else {
for (int32_t i = 0; i < 12; i++) {
input.append(input);
}
assertEquals("Very long input of digits", 163840, input.length());
}
Formattable result;
df.parse(input, result, status);
// Should not hang
}
#endif /* #if !UCONFIG_NO_FORMATTING */

View File

@ -303,6 +303,7 @@ class NumberFormatTest: public CalendarTimeZoneTest {
void Test20961_CurrencyPluralPattern();
void Test21134_ToNumberFormatter();
void Test13733_StrictAndLenient();
void Test21232_ParseTimeout();
private:
UBool testFormattableAsUFormattable(const char *file, int line, Formattable &f);

View File

@ -4,6 +4,7 @@ package com.ibm.icu.impl.number;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
/**
* A DecimalQuantity with internal storage as a 64-bit BCD, with fallback to a byte array for numbers
@ -123,13 +124,8 @@ public final class DecimalQuantity_DualStorageBCD extends DecimalQuantity_Abstra
}
if (usingBytes) {
ensureCapacity(precision + numDigits);
int i = precision + numDigits - 1;
for (; i >= numDigits; i--) {
bcdBytes[i] = bcdBytes[i - numDigits];
}
for (; i >= 0; i--) {
bcdBytes[i] = 0;
}
System.arraycopy(bcdBytes, 0, bcdBytes, numDigits, precision);
Arrays.fill(bcdBytes, 0, numDigits, (byte) 0);
} else {
bcdLong <<= (numDigits * 4);
}

View File

@ -6906,4 +6906,17 @@ public class NumberFormatTest extends TestFmwk {
parsedLenientValue, expectedLenientParse);
}
}
@Test
public void Test21232_ParseTimeout() throws ParseException {
DecimalFormat df = new DecimalFormat();
StringBuilder input = new StringBuilder();
input.append("4444444444444444444444444444444444444444");
for (int i = 0; i < 8; i++) {
input.append(input);
}
assertEquals("Long input of digits", 10240, input.length());
df.parse(input.toString());
// Should not hang
}
}