inline const FormattedStringBuilder& getStringRef() const {
return fString;
}
+ void resetString();
/**
* Adds additional metadata used for span fields.
}
}
+void FormattedValueStringBuilderImpl::resetString() {
+ fString.clear();
+ spanIndicesCount = 0;
+}
+
// Signal the end of the string using a field that doesn't exist and that is
// different from kUndefinedField, which is used for "null field".
static constexpr Field kEndField = Field(0xf, 0xf);
void FormattedValueStringBuilderImpl::appendSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status) {
if (U_FAILURE(status)) { return; }
- U_ASSERT(spanIndices.getCapacity() >= spanValue);
- if (spanIndices.getCapacity() == spanValue) {
- if (!spanIndices.resize(spanValue * 2, spanValue)) {
+ U_ASSERT(spanIndices.getCapacity() >= spanIndicesCount);
+ if (spanIndices.getCapacity() == spanIndicesCount) {
+ if (!spanIndices.resize(spanIndicesCount * 2, spanIndicesCount)) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
}
- spanIndices[spanValue] = {category, spanValue, start, length};
+ spanIndices[spanIndicesCount] = {category, spanValue, start, length};
spanIndicesCount++;
}
void FormattedValueStringBuilderImpl::prependSpanInfo(UFieldCategory category, int32_t spanValue, int32_t start, int32_t length, UErrorCode& status) {
if (U_FAILURE(status)) { return; }
- U_ASSERT(spanIndices.getCapacity() >= spanValue);
- if (spanIndices.getCapacity() == spanValue) {
- if (!spanIndices.resize(spanValue * 2, spanValue)) {
+ U_ASSERT(spanIndices.getCapacity() >= spanIndicesCount);
+ if (spanIndices.getCapacity() == spanIndicesCount) {
+ if (!spanIndices.resize(spanIndicesCount * 2, spanIndicesCount)) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
}
}
- for (int32_t i = spanValue - 1; i >= 0; i--) {
+ for (int32_t i = spanIndicesCount - 1; i >= 0; i--) {
spanIndices[i+1] = spanIndices[i];
}
spanIndices[0] = {category, spanValue, start, length};
auto* result = UFormattedNumberApiHelper::validate(uresult, *ec);
if (U_FAILURE(*ec)) { return; }
- result->fData.getStringRef().clear();
+ result->fData.resetString();
+ result->fData.quantity.clear();
result->fData.quantity.setToLong(value);
formatter->fFormatter.formatImpl(&result->fData, *ec);
}
auto* result = UFormattedNumberApiHelper::validate(uresult, *ec);
if (U_FAILURE(*ec)) { return; }
- result->fData.getStringRef().clear();
+ result->fData.resetString();
+ result->fData.quantity.clear();
result->fData.quantity.setToDouble(value);
formatter->fFormatter.formatImpl(&result->fData, *ec);
}
auto* result = UFormattedNumberApiHelper::validate(uresult, *ec);
if (U_FAILURE(*ec)) { return; }
- result->fData.getStringRef().clear();
+ result->fData.resetString();
+ result->fData.quantity.clear();
result->fData.quantity.setToDecNumber({value, valueLen}, *ec);
if (U_FAILURE(*ec)) { return; }
formatter->fFormatter.formatImpl(&result->fData, *ec);
auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec);
if (U_FAILURE(*ec)) { return; }
- result->fData.getStringRef().clear();
+ result->fData.resetString();
+ result->fData.quantity1.clear();
+ result->fData.quantity2.clear();
result->fData.quantity1.setToDouble(first);
result->fData.quantity2.setToDouble(second);
formatter->fFormatter.formatImpl(result->fData, first == second, *ec);
auto* result = UFormattedNumberRangeApiHelper::validate(uresult, *ec);
if (U_FAILURE(*ec)) { return; }
- result->fData.getStringRef().clear();
+ result->fData.resetString();
+ result->fData.quantity1.clear();
+ result->fData.quantity2.clear();
result->fData.quantity1.setToDecNumber({first, firstLen}, *ec);
result->fData.quantity2.setToDecNumber({second, secondLen}, *ec);
formatter->fFormatter.formatImpl(result->fData, first == second, *ec);
static void TestPerUnitInArabic(void);
+static void Test21674_State(void);
+
void addUNumberFormatterTest(TestNode** root);
#define TESTCASE(x) addTest(root, &x, "tsformat/unumberformatter/" #x)
TESTCASE(TestSkeletonParseError);
TESTCASE(TestToDecimalNumber);
TESTCASE(TestPerUnitInArabic);
+ TESTCASE(Test21674_State);
}
}
unumf_closeResult(formatted);
}
+
+
+static void Test21674_State() {
+ UErrorCode status = U_ZERO_ERROR;
+ UNumberFormatter* nf = NULL;
+ UFormattedNumber* result = NULL;
+
+ nf = unumf_openForSkeletonAndLocale(u"precision-increment/0.05/w", -1, "en", &status);
+ if (!assertSuccess("unumf_openForSkeletonAndLocale", &status)) { goto cleanup; }
+
+ result = unumf_openResult(&status);
+ if (!assertSuccess("unumf_openResult", &status)) { goto cleanup; }
+
+ typedef struct TestCase {
+ double num;
+ const UChar* expected;
+ } TestCase;
+ TestCase cases[] = {
+ { 1.975, u"2" },
+ { 1.97, u"1.95" },
+ { 1.975, u"2" },
+ };
+ for (int i=0; i<3; i++) {
+ unumf_formatDouble(nf, cases[i].num, result, &status);
+ if (!assertSuccess("unumf_formatDouble", &status)) { goto cleanup; }
+
+ const UFormattedValue* formattedValue = unumf_resultAsValue(result, &status);
+ if (!assertSuccess("unumf_resultAsValue", &status)) { goto cleanup; }
+
+ int32_t length;
+ const UChar* str = ufmtval_getString(formattedValue, &length, &status);
+ if (!assertSuccess("ufmtval_getString", &status)) { goto cleanup; }
+
+ char message[] = {i + '0', '\0'};
+ assertUEquals(message, cases[i].expected, str);
+ }
+
+cleanup:
+ unumf_close(nf);
+ unumf_closeResult(result);
+}
+
+
#endif /* #if !UCONFIG_NO_FORMATTING */
void testGetDecimalNumbers();
void test21684_Performance();
void test21358_SignPosition();
+ void test21683_StateLeak();
void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = 0) override;
TESTCASE_AUTO(testGetDecimalNumbers);
TESTCASE_AUTO(test21684_Performance);
TESTCASE_AUTO(test21358_SignPosition);
+ TESTCASE_AUTO(test21683_StateLeak);
TESTCASE_AUTO_END;
}
}
}
+void NumberRangeFormatterTest::test21683_StateLeak() {
+ IcuTestErrorCode status(*this, "test21683_StateLeak");
+ UNumberRangeFormatter* nrf = nullptr;
+ UFormattedNumberRange* result = nullptr;
+ UConstrainedFieldPosition* fpos = nullptr;
+
+ struct Range {
+ double start;
+ double end;
+ const char16_t* expected;
+ int numFields;
+ } ranges[] = {
+ {1, 2, u"1\u20132", 4},
+ {1, 1, u"~1", 2},
+ };
+
+ UParseError* perror = nullptr;
+ nrf = unumrf_openForSkeletonWithCollapseAndIdentityFallback(
+ u"", -1,
+ UNUM_RANGE_COLLAPSE_AUTO,
+ UNUM_IDENTITY_FALLBACK_APPROXIMATELY,
+ "en", perror, status);
+ if (status.errIfFailureAndReset("unumrf_openForSkeletonWithCollapseAndIdentityFallback")) {
+ goto cleanup;
+ }
+
+ result = unumrf_openResult(status);
+ if (status.errIfFailureAndReset("unumrf_openResult")) { goto cleanup; }
+
+ for (auto range : ranges) {
+ unumrf_formatDoubleRange(nrf, range.start, range.end, result, status);
+ if (status.errIfFailureAndReset("unumrf_formatDoubleRange")) { goto cleanup; }
+
+ auto* formattedValue = unumrf_resultAsValue(result, status);
+ if (status.errIfFailureAndReset("unumrf_resultAsValue")) { goto cleanup; }
+
+ int32_t utf16Length;
+ const char16_t* utf16Str = ufmtval_getString(formattedValue, &utf16Length, status);
+ if (status.errIfFailureAndReset("ufmtval_getString")) { goto cleanup; }
+
+ assertEquals("Format", range.expected, utf16Str);
+
+ ucfpos_close(fpos);
+ fpos = ucfpos_open(status);
+ if (status.errIfFailureAndReset("ucfpos_open")) { goto cleanup; }
+
+ int numFields = 0;
+ while (true) {
+ bool hasMore = ufmtval_nextPosition(formattedValue, fpos, status);
+ if (status.errIfFailureAndReset("ufmtval_nextPosition")) { goto cleanup; }
+ if (!hasMore) {
+ break;
+ }
+ numFields++;
+ }
+ assertEquals("numFields", range.numFields, numFields);
+ }
+
+cleanup:
+ unumrf_close(nrf);
+ unumrf_closeResult(result);
+ ucfpos_close(fpos);
+}
+
void NumberRangeFormatterTest::assertFormatRange(
const char16_t* message,
const UnlocalizedNumberRangeFormatter& f,