ICU-21134 Copy additional data when toNumberFormatter is used
See #1156
This commit is contained in:
parent
ec7e29f2b6
commit
3ff6627ce6
@ -13,6 +13,7 @@
|
|||||||
#include "number_asformat.h"
|
#include "number_asformat.h"
|
||||||
#include "number_utils.h"
|
#include "number_utils.h"
|
||||||
#include "number_utypes.h"
|
#include "number_utypes.h"
|
||||||
|
#include "number_mapper.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "fphdlimp.h"
|
#include "fphdlimp.h"
|
||||||
|
|
||||||
@ -400,7 +401,8 @@ LocalizedNumberFormatter::LocalizedNumberFormatter(const LNF& other)
|
|||||||
|
|
||||||
LocalizedNumberFormatter::LocalizedNumberFormatter(const NFS<LNF>& other)
|
LocalizedNumberFormatter::LocalizedNumberFormatter(const NFS<LNF>& other)
|
||||||
: NFS<LNF>(other) {
|
: NFS<LNF>(other) {
|
||||||
// No additional fields to assign (let call count and compiled formatter reset to defaults)
|
UErrorCode localStatus = U_ZERO_ERROR; // Can't bubble up the error
|
||||||
|
lnfCopyHelper(static_cast<const LNF&>(other), localStatus);
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalizedNumberFormatter::LocalizedNumberFormatter(LocalizedNumberFormatter&& src) U_NOEXCEPT
|
LocalizedNumberFormatter::LocalizedNumberFormatter(LocalizedNumberFormatter&& src) U_NOEXCEPT
|
||||||
@ -408,38 +410,25 @@ LocalizedNumberFormatter::LocalizedNumberFormatter(LocalizedNumberFormatter&& sr
|
|||||||
|
|
||||||
LocalizedNumberFormatter::LocalizedNumberFormatter(NFS<LNF>&& src) U_NOEXCEPT
|
LocalizedNumberFormatter::LocalizedNumberFormatter(NFS<LNF>&& src) U_NOEXCEPT
|
||||||
: NFS<LNF>(std::move(src)) {
|
: NFS<LNF>(std::move(src)) {
|
||||||
// For the move operators, copy over the compiled formatter.
|
lnfMoveHelper(std::move(static_cast<LNF&&>(src)));
|
||||||
// Note: if the formatter is not compiled, call count information is lost.
|
|
||||||
if (static_cast<LNF&&>(src).fCompiled != nullptr) {
|
|
||||||
lnfMoveHelper(static_cast<LNF&&>(src));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalizedNumberFormatter& LocalizedNumberFormatter::operator=(const LNF& other) {
|
LocalizedNumberFormatter& LocalizedNumberFormatter::operator=(const LNF& other) {
|
||||||
NFS<LNF>::operator=(static_cast<const NFS<LNF>&>(other));
|
NFS<LNF>::operator=(static_cast<const NFS<LNF>&>(other));
|
||||||
// Reset to default values.
|
UErrorCode localStatus = U_ZERO_ERROR; // Can't bubble up the error
|
||||||
clear();
|
lnfCopyHelper(other, localStatus);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalizedNumberFormatter& LocalizedNumberFormatter::operator=(LNF&& src) U_NOEXCEPT {
|
LocalizedNumberFormatter& LocalizedNumberFormatter::operator=(LNF&& src) U_NOEXCEPT {
|
||||||
NFS<LNF>::operator=(static_cast<NFS<LNF>&&>(src));
|
NFS<LNF>::operator=(static_cast<NFS<LNF>&&>(src));
|
||||||
// For the move operators, copy over the compiled formatter.
|
lnfMoveHelper(std::move(src));
|
||||||
// Note: if the formatter is not compiled, call count information is lost.
|
|
||||||
if (static_cast<LNF&&>(src).fCompiled != nullptr) {
|
|
||||||
// Formatter is compiled
|
|
||||||
lnfMoveHelper(static_cast<LNF&&>(src));
|
|
||||||
} else {
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LocalizedNumberFormatter::clear() {
|
void LocalizedNumberFormatter::resetCompiled() {
|
||||||
// Reset to default values.
|
|
||||||
auto* callCount = reinterpret_cast<u_atomic_int32_t*>(fUnsafeCallCount);
|
auto* callCount = reinterpret_cast<u_atomic_int32_t*>(fUnsafeCallCount);
|
||||||
umtx_storeRelease(*callCount, 0);
|
umtx_storeRelease(*callCount, 0);
|
||||||
delete fCompiled;
|
|
||||||
fCompiled = nullptr;
|
fCompiled = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,19 +436,56 @@ void LocalizedNumberFormatter::lnfMoveHelper(LNF&& src) {
|
|||||||
// Copy over the compiled formatter and set call count to INT32_MIN as in computeCompiled().
|
// Copy over the compiled formatter and set call count to INT32_MIN as in computeCompiled().
|
||||||
// Don't copy the call count directly because doing so requires a loadAcquire/storeRelease.
|
// Don't copy the call count directly because doing so requires a loadAcquire/storeRelease.
|
||||||
// The bits themselves appear to be platform-dependent, so copying them might not be safe.
|
// The bits themselves appear to be platform-dependent, so copying them might not be safe.
|
||||||
auto* callCount = reinterpret_cast<u_atomic_int32_t*>(fUnsafeCallCount);
|
|
||||||
umtx_storeRelease(*callCount, INT32_MIN);
|
|
||||||
delete fCompiled;
|
delete fCompiled;
|
||||||
fCompiled = src.fCompiled;
|
if (src.fCompiled != nullptr) {
|
||||||
// Reset the source object to leave it in a safe state.
|
auto* callCount = reinterpret_cast<u_atomic_int32_t*>(fUnsafeCallCount);
|
||||||
auto* srcCallCount = reinterpret_cast<u_atomic_int32_t*>(src.fUnsafeCallCount);
|
umtx_storeRelease(*callCount, INT32_MIN);
|
||||||
umtx_storeRelease(*srcCallCount, 0);
|
fCompiled = src.fCompiled;
|
||||||
src.fCompiled = nullptr;
|
// Reset the source object to leave it in a safe state.
|
||||||
|
src.resetCompiled();
|
||||||
|
} else {
|
||||||
|
resetCompiled();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unconditionally move the warehouse
|
||||||
|
delete fWarehouse;
|
||||||
|
fWarehouse = src.fWarehouse;
|
||||||
|
src.fWarehouse = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LocalizedNumberFormatter::lnfCopyHelper(const LNF&, UErrorCode& status) {
|
||||||
|
// When copying, always reset the compiled formatter.
|
||||||
|
delete fCompiled;
|
||||||
|
resetCompiled();
|
||||||
|
|
||||||
|
// If MacroProps has a reference to AffixPatternProvider, we need to copy it.
|
||||||
|
// If MacroProps has a reference to PluralRules, copy that one, too.
|
||||||
|
delete fWarehouse;
|
||||||
|
if (fMacros.affixProvider || fMacros.rules) {
|
||||||
|
LocalPointer<DecimalFormatWarehouse> warehouse(new DecimalFormatWarehouse(), status);
|
||||||
|
if (U_FAILURE(status)) {
|
||||||
|
fWarehouse = nullptr;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fMacros.affixProvider) {
|
||||||
|
warehouse->affixProvider.setTo(fMacros.affixProvider, status);
|
||||||
|
fMacros.affixProvider = &warehouse->affixProvider.get();
|
||||||
|
}
|
||||||
|
if (fMacros.rules) {
|
||||||
|
warehouse->rules.adoptInsteadAndCheckErrorCode(
|
||||||
|
new PluralRules(*fMacros.rules), status);
|
||||||
|
fMacros.rules = warehouse->rules.getAlias();
|
||||||
|
}
|
||||||
|
fWarehouse = warehouse.orphan();
|
||||||
|
} else {
|
||||||
|
fWarehouse = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
LocalizedNumberFormatter::~LocalizedNumberFormatter() {
|
LocalizedNumberFormatter::~LocalizedNumberFormatter() {
|
||||||
delete fCompiled;
|
delete fCompiled;
|
||||||
|
delete fWarehouse;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalizedNumberFormatter::LocalizedNumberFormatter(const MacroProps& macros, const Locale& locale) {
|
LocalizedNumberFormatter::LocalizedNumberFormatter(const MacroProps& macros, const Locale& locale) {
|
||||||
|
@ -136,6 +136,16 @@ class AutoAffixPatternProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void setTo(const AffixPatternProvider* provider, UErrorCode& status) {
|
||||||
|
if (auto ptr = dynamic_cast<const PropertiesAffixPatternProvider*>(provider)) {
|
||||||
|
propertiesAPP = *ptr;
|
||||||
|
} else if (auto ptr = dynamic_cast<const CurrencyPluralInfoAffixProvider*>(provider)) {
|
||||||
|
currencyPluralInfoAPP = *ptr;
|
||||||
|
} else {
|
||||||
|
status = U_INTERNAL_PROGRAM_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline const AffixPatternProvider& get() const {
|
inline const AffixPatternProvider& get() const {
|
||||||
if (!currencyPluralInfoAPP.isBogus()) {
|
if (!currencyPluralInfoAPP.isBogus()) {
|
||||||
return currencyPluralInfoAPP;
|
return currencyPluralInfoAPP;
|
||||||
@ -153,9 +163,9 @@ class AutoAffixPatternProvider {
|
|||||||
/**
|
/**
|
||||||
* A struct for ownership of a few objects needed for formatting.
|
* A struct for ownership of a few objects needed for formatting.
|
||||||
*/
|
*/
|
||||||
struct DecimalFormatWarehouse {
|
struct DecimalFormatWarehouse : public UMemory {
|
||||||
AutoAffixPatternProvider affixProvider;
|
AutoAffixPatternProvider affixProvider;
|
||||||
|
LocalPointer<PluralRules> rules;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -157,6 +157,7 @@ struct RangeMacroProps;
|
|||||||
struct UFormattedNumberImpl;
|
struct UFormattedNumberImpl;
|
||||||
class MutablePatternModifier;
|
class MutablePatternModifier;
|
||||||
class ImmutablePatternModifier;
|
class ImmutablePatternModifier;
|
||||||
|
struct DecimalFormatWarehouse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for NumberRangeFormatter and implemented in numrange_fluent.cpp.
|
* Used for NumberRangeFormatter and implemented in numrange_fluent.cpp.
|
||||||
@ -2385,6 +2386,10 @@ class U_I18N_API LocalizedNumberFormatter
|
|||||||
const impl::NumberFormatterImpl* fCompiled {nullptr};
|
const impl::NumberFormatterImpl* fCompiled {nullptr};
|
||||||
char fUnsafeCallCount[8] {}; // internally cast to u_atomic_int32_t
|
char fUnsafeCallCount[8] {}; // internally cast to u_atomic_int32_t
|
||||||
|
|
||||||
|
// Owned pointer to a DecimalFormatWarehouse, used when copying a LocalizedNumberFormatter
|
||||||
|
// from a DecimalFormat.
|
||||||
|
const impl::DecimalFormatWarehouse* fWarehouse {nullptr};
|
||||||
|
|
||||||
explicit LocalizedNumberFormatter(const NumberFormatterSettings<LocalizedNumberFormatter>& other);
|
explicit LocalizedNumberFormatter(const NumberFormatterSettings<LocalizedNumberFormatter>& other);
|
||||||
|
|
||||||
explicit LocalizedNumberFormatter(NumberFormatterSettings<LocalizedNumberFormatter>&& src) U_NOEXCEPT;
|
explicit LocalizedNumberFormatter(NumberFormatterSettings<LocalizedNumberFormatter>&& src) U_NOEXCEPT;
|
||||||
@ -2393,10 +2398,12 @@ class U_I18N_API LocalizedNumberFormatter
|
|||||||
|
|
||||||
LocalizedNumberFormatter(impl::MacroProps &¯os, const Locale &locale);
|
LocalizedNumberFormatter(impl::MacroProps &¯os, const Locale &locale);
|
||||||
|
|
||||||
void clear();
|
void resetCompiled();
|
||||||
|
|
||||||
void lnfMoveHelper(LocalizedNumberFormatter&& src);
|
void lnfMoveHelper(LocalizedNumberFormatter&& src);
|
||||||
|
|
||||||
|
void lnfCopyHelper(const LocalizedNumberFormatter& src, UErrorCode& status);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return true if the compiled formatter is available.
|
* @return true if the compiled formatter is available.
|
||||||
*/
|
*/
|
||||||
|
@ -245,6 +245,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n
|
|||||||
TESTCASE_AUTO(Test13735_GroupingSizeGetter);
|
TESTCASE_AUTO(Test13735_GroupingSizeGetter);
|
||||||
TESTCASE_AUTO(Test13734_StrictFlexibleWhitespace);
|
TESTCASE_AUTO(Test13734_StrictFlexibleWhitespace);
|
||||||
TESTCASE_AUTO(Test20961_CurrencyPluralPattern);
|
TESTCASE_AUTO(Test20961_CurrencyPluralPattern);
|
||||||
|
TESTCASE_AUTO(Test21134_ToNumberFormatter);
|
||||||
TESTCASE_AUTO_END;
|
TESTCASE_AUTO_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9835,4 +9836,61 @@ void NumberFormatTest::Test20961_CurrencyPluralPattern() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NumberFormatTest::Test21134_ToNumberFormatter() {
|
||||||
|
IcuTestErrorCode status(*this, "Test21134_ToNumberFormatter");
|
||||||
|
LocalizedNumberFormatter outer1;
|
||||||
|
LocalizedNumberFormatter outer2;
|
||||||
|
LocalPointer<LocalizedNumberFormatter> outer3;
|
||||||
|
{
|
||||||
|
// Case 1: new formatter object
|
||||||
|
DecimalFormat inner(u"a0b", {"en", status}, status);
|
||||||
|
if (auto ptr = inner.toNumberFormatter(status)) {
|
||||||
|
// Copy assignment
|
||||||
|
outer1 = *ptr;
|
||||||
|
} else {
|
||||||
|
status.errIfFailureAndReset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Case 2: compiled formatter object (used at least 3 times)
|
||||||
|
DecimalFormat inner(u"c0d", {"en", status}, status);
|
||||||
|
UnicodeString dummy;
|
||||||
|
inner.format(100, dummy);
|
||||||
|
inner.format(100, dummy);
|
||||||
|
inner.format(100, dummy);
|
||||||
|
if (auto ptr = inner.toNumberFormatter(status)) {
|
||||||
|
// Copy assignment
|
||||||
|
outer2 = *ptr;
|
||||||
|
} else {
|
||||||
|
status.errIfFailureAndReset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
// Case 3: currency plural info (different code path)
|
||||||
|
LocalPointer<DecimalFormat> inner(static_cast<DecimalFormat*>(
|
||||||
|
DecimalFormat::createInstance("en-US", UNUM_CURRENCY_PLURAL, status)));
|
||||||
|
if (auto ptr = inner->toNumberFormatter(status)) {
|
||||||
|
// Copy constructor
|
||||||
|
outer3.adoptInsteadAndCheckErrorCode(new LocalizedNumberFormatter(*ptr), status);
|
||||||
|
} else {
|
||||||
|
status.errIfFailureAndReset();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto result1 = outer1.formatDouble(99, status);
|
||||||
|
assertEquals("Using NumberFormatter from DecimalFormat, new version",
|
||||||
|
u"a99b",
|
||||||
|
result1.toTempString(status));
|
||||||
|
auto result2 = outer2.formatDouble(99, status);
|
||||||
|
assertEquals("Using NumberFormatter from DecimalFormat, compiled version",
|
||||||
|
u"c99d",
|
||||||
|
result2.toTempString(status));
|
||||||
|
auto result3 = outer3->formatDouble(99, status);
|
||||||
|
assertEquals("Using NumberFormatter from DecimalFormat, compiled version",
|
||||||
|
u"99.00 US dollars",
|
||||||
|
result3.toTempString(status));
|
||||||
|
}
|
||||||
|
|
||||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||||
|
@ -301,6 +301,7 @@ class NumberFormatTest: public CalendarTimeZoneTest {
|
|||||||
void Test13735_GroupingSizeGetter();
|
void Test13735_GroupingSizeGetter();
|
||||||
void Test13734_StrictFlexibleWhitespace();
|
void Test13734_StrictFlexibleWhitespace();
|
||||||
void Test20961_CurrencyPluralPattern();
|
void Test20961_CurrencyPluralPattern();
|
||||||
|
void Test21134_ToNumberFormatter();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UBool testFormattableAsUFormattable(const char *file, int line, Formattable &f);
|
UBool testFormattableAsUFormattable(const char *file, int line, Formattable &f);
|
||||||
|
Loading…
Reference in New Issue
Block a user