diff --git a/icu4c/source/i18n/number_decimalquantity.cpp b/icu4c/source/i18n/number_decimalquantity.cpp index e646c546458..cd6e7b14d28 100644 --- a/icu4c/source/i18n/number_decimalquantity.cpp +++ b/icu4c/source/i18n/number_decimalquantity.cpp @@ -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); } diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp index b1eb796bc1e..50a08c0b082 100644 --- a/icu4c/source/test/intltest/numfmtst.cpp +++ b/icu4c/source/test/intltest/numfmtst.cpp @@ -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 */ diff --git a/icu4c/source/test/intltest/numfmtst.h b/icu4c/source/test/intltest/numfmtst.h index 4e2d797b107..24ca15a07b9 100644 --- a/icu4c/source/test/intltest/numfmtst.h +++ b/icu4c/source/test/intltest/numfmtst.h @@ -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); diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java index 05612af295e..0c25de32550 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_DualStorageBCD.java @@ -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); } diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java index babe1594910..98ce7178e79 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java @@ -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 + } }