• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 #include "unicode/utypes.h"
5 
6 #if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
7 
8 #include "uassert.h"
9 #include "unicode/numberformatter.h"
10 #include "number_decimalquantity.h"
11 #include "number_formatimpl.h"
12 #include "umutex.h"
13 
14 using namespace icu;
15 using namespace icu::number;
16 using namespace icu::number::impl;
17 
18 template<typename Derived>
notation(const Notation & notation) const19 Derived NumberFormatterSettings<Derived>::notation(const Notation &notation) const {
20     Derived copy(*this);
21     // NOTE: Slicing is OK.
22     copy.fMacros.notation = notation;
23     return copy;
24 }
25 
26 template<typename Derived>
unit(const icu::MeasureUnit & unit) const27 Derived NumberFormatterSettings<Derived>::unit(const icu::MeasureUnit &unit) const {
28     Derived copy(*this);
29     // NOTE: Slicing occurs here. However, CurrencyUnit can be restored from MeasureUnit.
30     // TimeUnit may be affected, but TimeUnit is not as relevant to number formatting.
31     copy.fMacros.unit = unit;
32     return copy;
33 }
34 
35 template<typename Derived>
adoptUnit(const icu::MeasureUnit * unit) const36 Derived NumberFormatterSettings<Derived>::adoptUnit(const icu::MeasureUnit *unit) const {
37     Derived copy(*this);
38     // Just copy the unit into the MacroProps by value, and delete it since we have ownership.
39     // NOTE: Slicing occurs here. However, CurrencyUnit can be restored from MeasureUnit.
40     // TimeUnit may be affected, but TimeUnit is not as relevant to number formatting.
41     if (unit != nullptr) {
42         copy.fMacros.unit = *unit;
43         delete unit;
44     }
45     return copy;
46 }
47 
48 template<typename Derived>
rounding(const Rounder & rounder) const49 Derived NumberFormatterSettings<Derived>::rounding(const Rounder &rounder) const {
50     Derived copy(*this);
51     // NOTE: Slicing is OK.
52     copy.fMacros.rounder = rounder;
53     return copy;
54 }
55 
56 template<typename Derived>
grouping(const Grouper & grouper) const57 Derived NumberFormatterSettings<Derived>::grouping(const Grouper &grouper) const {
58     Derived copy(*this);
59     copy.fMacros.grouper = grouper;
60     return copy;
61 }
62 
63 template<typename Derived>
integerWidth(const IntegerWidth & style) const64 Derived NumberFormatterSettings<Derived>::integerWidth(const IntegerWidth &style) const {
65     Derived copy(*this);
66     copy.fMacros.integerWidth = style;
67     return copy;
68 }
69 
70 template<typename Derived>
symbols(const DecimalFormatSymbols & symbols) const71 Derived NumberFormatterSettings<Derived>::symbols(const DecimalFormatSymbols &symbols) const {
72     Derived copy(*this);
73     copy.fMacros.symbols.setTo(symbols);
74     return copy;
75 }
76 
77 template<typename Derived>
adoptSymbols(const NumberingSystem * ns) const78 Derived NumberFormatterSettings<Derived>::adoptSymbols(const NumberingSystem *ns) const {
79     Derived copy(*this);
80     copy.fMacros.symbols.setTo(ns);
81     return copy;
82 }
83 
84 template<typename Derived>
unitWidth(const UNumberUnitWidth & width) const85 Derived NumberFormatterSettings<Derived>::unitWidth(const UNumberUnitWidth &width) const {
86     Derived copy(*this);
87     copy.fMacros.unitWidth = width;
88     return copy;
89 }
90 
91 template<typename Derived>
sign(const UNumberSignDisplay & style) const92 Derived NumberFormatterSettings<Derived>::sign(const UNumberSignDisplay &style) const {
93     Derived copy(*this);
94     copy.fMacros.sign = style;
95     return copy;
96 }
97 
98 template<typename Derived>
decimal(const UNumberDecimalSeparatorDisplay & style) const99 Derived NumberFormatterSettings<Derived>::decimal(const UNumberDecimalSeparatorDisplay &style) const {
100     Derived copy(*this);
101     copy.fMacros.decimal = style;
102     return copy;
103 }
104 
105 template<typename Derived>
padding(const Padder & padder) const106 Derived NumberFormatterSettings<Derived>::padding(const Padder &padder) const {
107     Derived copy(*this);
108     copy.fMacros.padder = padder;
109     return copy;
110 }
111 
112 template<typename Derived>
threshold(int32_t threshold) const113 Derived NumberFormatterSettings<Derived>::threshold(int32_t threshold) const {
114     Derived copy(*this);
115     copy.fMacros.threshold = threshold;
116     return copy;
117 }
118 
119 // Declare all classes that implement NumberFormatterSettings
120 // See https://stackoverflow.com/a/495056/1407170
121 template
122 class icu::number::NumberFormatterSettings<icu::number::UnlocalizedNumberFormatter>;
123 template
124 class icu::number::NumberFormatterSettings<icu::number::LocalizedNumberFormatter>;
125 
126 
with()127 UnlocalizedNumberFormatter NumberFormatter::with() {
128     UnlocalizedNumberFormatter result;
129     return result;
130 }
131 
withLocale(const Locale & locale)132 LocalizedNumberFormatter NumberFormatter::withLocale(const Locale &locale) {
133     return with().locale(locale);
134 }
135 
136 // Make the child class constructor that takes the parent class call the parent class's copy constructor
UnlocalizedNumberFormatter(const NumberFormatterSettings<UnlocalizedNumberFormatter> & other)137 UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(
138         const NumberFormatterSettings <UnlocalizedNumberFormatter> &other)
139         : NumberFormatterSettings<UnlocalizedNumberFormatter>(other) {
140 }
141 
142 // Make the child class constructor that takes the parent class call the parent class's copy constructor
143 // For LocalizedNumberFormatter, also copy over the extra fields
LocalizedNumberFormatter(const NumberFormatterSettings<LocalizedNumberFormatter> & other)144 LocalizedNumberFormatter::LocalizedNumberFormatter(
145         const NumberFormatterSettings <LocalizedNumberFormatter> &other)
146         : NumberFormatterSettings<LocalizedNumberFormatter>(other) {
147     // No additional copies required
148 }
149 
LocalizedNumberFormatter(const MacroProps & macros,const Locale & locale)150 LocalizedNumberFormatter::LocalizedNumberFormatter(const MacroProps &macros, const Locale &locale) {
151     fMacros = macros;
152     fMacros.locale = locale;
153 }
154 
locale(const Locale & locale) const155 LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale &locale) const {
156     return LocalizedNumberFormatter(fMacros, locale);
157 }
158 
SymbolsWrapper(const SymbolsWrapper & other)159 SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper &other) {
160     doCopyFrom(other);
161 }
162 
operator =(const SymbolsWrapper & other)163 SymbolsWrapper &SymbolsWrapper::operator=(const SymbolsWrapper &other) {
164     if (this == &other) {
165         return *this;
166     }
167     doCleanup();
168     doCopyFrom(other);
169     return *this;
170 }
171 
~SymbolsWrapper()172 SymbolsWrapper::~SymbolsWrapper() {
173     doCleanup();
174 }
175 
setTo(const DecimalFormatSymbols & dfs)176 void SymbolsWrapper::setTo(const DecimalFormatSymbols &dfs) {
177     doCleanup();
178     fType = SYMPTR_DFS;
179     fPtr.dfs = new DecimalFormatSymbols(dfs);
180 }
181 
setTo(const NumberingSystem * ns)182 void SymbolsWrapper::setTo(const NumberingSystem *ns) {
183     doCleanup();
184     fType = SYMPTR_NS;
185     fPtr.ns = ns;
186 }
187 
doCopyFrom(const SymbolsWrapper & other)188 void SymbolsWrapper::doCopyFrom(const SymbolsWrapper &other) {
189     fType = other.fType;
190     switch (fType) {
191         case SYMPTR_NONE:
192             // No action necessary
193             break;
194         case SYMPTR_DFS:
195             // Memory allocation failures are exposed in copyErrorTo()
196             if (other.fPtr.dfs != nullptr) {
197                 fPtr.dfs = new DecimalFormatSymbols(*other.fPtr.dfs);
198             } else {
199                 fPtr.dfs = nullptr;
200             }
201             break;
202         case SYMPTR_NS:
203             // Memory allocation failures are exposed in copyErrorTo()
204             if (other.fPtr.ns != nullptr) {
205                 fPtr.ns = new NumberingSystem(*other.fPtr.ns);
206             } else {
207                 fPtr.ns = nullptr;
208             }
209             break;
210     }
211 }
212 
doCleanup()213 void SymbolsWrapper::doCleanup() {
214     switch (fType) {
215         case SYMPTR_NONE:
216             // No action necessary
217             break;
218         case SYMPTR_DFS:
219             delete fPtr.dfs;
220             break;
221         case SYMPTR_NS:
222             delete fPtr.ns;
223             break;
224     }
225 }
226 
isDecimalFormatSymbols() const227 bool SymbolsWrapper::isDecimalFormatSymbols() const {
228     return fType == SYMPTR_DFS;
229 }
230 
isNumberingSystem() const231 bool SymbolsWrapper::isNumberingSystem() const {
232     return fType == SYMPTR_NS;
233 }
234 
getDecimalFormatSymbols() const235 const DecimalFormatSymbols* SymbolsWrapper::getDecimalFormatSymbols() const {
236     U_ASSERT(fType == SYMPTR_DFS);
237     return fPtr.dfs;
238 }
239 
getNumberingSystem() const240 const NumberingSystem* SymbolsWrapper::getNumberingSystem() const {
241     U_ASSERT(fType == SYMPTR_NS);
242     return fPtr.ns;
243 }
244 
~LocalizedNumberFormatter()245 LocalizedNumberFormatter::~LocalizedNumberFormatter() {
246     delete fCompiled;
247 }
248 
formatInt(int64_t value,UErrorCode & status) const249 FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, UErrorCode &status) const {
250     if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
251     auto results = new NumberFormatterResults();
252     if (results == nullptr) {
253         status = U_MEMORY_ALLOCATION_ERROR;
254         return FormattedNumber(status);
255     }
256     results->quantity.setToLong(value);
257     return formatImpl(results, status);
258 }
259 
formatDouble(double value,UErrorCode & status) const260 FormattedNumber LocalizedNumberFormatter::formatDouble(double value, UErrorCode &status) const {
261     if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
262     auto results = new NumberFormatterResults();
263     if (results == nullptr) {
264         status = U_MEMORY_ALLOCATION_ERROR;
265         return FormattedNumber(status);
266     }
267     results->quantity.setToDouble(value);
268     return formatImpl(results, status);
269 }
270 
formatDecimal(StringPiece value,UErrorCode & status) const271 FormattedNumber LocalizedNumberFormatter::formatDecimal(StringPiece value, UErrorCode &status) const {
272     if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
273     auto results = new NumberFormatterResults();
274     if (results == nullptr) {
275         status = U_MEMORY_ALLOCATION_ERROR;
276         return FormattedNumber(status);
277     }
278     results->quantity.setToDecNumber(value);
279     return formatImpl(results, status);
280 }
281 
282 FormattedNumber
formatImpl(impl::NumberFormatterResults * results,UErrorCode & status) const283 LocalizedNumberFormatter::formatImpl(impl::NumberFormatterResults *results, UErrorCode &status) const {
284     // fUnsafeCallCount contains memory to be interpreted as an atomic int, most commonly
285     // std::atomic<int32_t>.  Since the type of atomic int is platform-dependent, we cast the
286     // bytes in fUnsafeCallCount to u_atomic_int32_t, a typedef for the platform-dependent
287     // atomic int type defined in umutex.h.
288     static_assert(sizeof(u_atomic_int32_t) <= sizeof(fUnsafeCallCount),
289         "Atomic integer size on this platform exceeds the size allocated by fUnsafeCallCount");
290     u_atomic_int32_t* callCount = reinterpret_cast<u_atomic_int32_t*>(
291         const_cast<LocalizedNumberFormatter*>(this)->fUnsafeCallCount);
292 
293     // A positive value in the atomic int indicates that the data structure is not yet ready;
294     // a negative value indicates that it is ready. If, after the increment, the atomic int
295     // is exactly threshold, then it is the current thread's job to build the data structure.
296     // Note: We set the callCount to INT32_MIN so that if another thread proceeds to increment
297     // the atomic int, the value remains below zero.
298     int32_t currentCount = umtx_loadAcquire(*callCount);
299     if (0 <= currentCount && currentCount <= fMacros.threshold && fMacros.threshold > 0) {
300         currentCount = umtx_atomic_inc(callCount);
301     }
302 
303     if (currentCount == fMacros.threshold && fMacros.threshold > 0) {
304         // Build the data structure and then use it (slow to fast path).
305         const NumberFormatterImpl* compiled =
306             NumberFormatterImpl::fromMacros(fMacros, status);
307         U_ASSERT(fCompiled == nullptr);
308         const_cast<LocalizedNumberFormatter *>(this)->fCompiled = compiled;
309         umtx_storeRelease(*callCount, INT32_MIN);
310         compiled->apply(results->quantity, results->string, status);
311     } else if (currentCount < 0) {
312         // The data structure is already built; use it (fast path).
313         U_ASSERT(fCompiled != nullptr);
314         fCompiled->apply(results->quantity, results->string, status);
315     } else {
316         // Format the number without building the data structure (slow path).
317         NumberFormatterImpl::applyStatic(fMacros, results->quantity, results->string, status);
318     }
319 
320     // Do not save the results object if we encountered a failure.
321     if (U_SUCCESS(status)) {
322         return FormattedNumber(results);
323     } else {
324         delete results;
325         return FormattedNumber(status);
326     }
327 }
328 
toString() const329 UnicodeString FormattedNumber::toString() const {
330     if (fResults == nullptr) {
331         // TODO: http://bugs.icu-project.org/trac/ticket/13437
332         return {};
333     }
334     return fResults->string.toUnicodeString();
335 }
336 
appendTo(Appendable & appendable)337 Appendable &FormattedNumber::appendTo(Appendable &appendable) {
338     if (fResults == nullptr) {
339         // TODO: http://bugs.icu-project.org/trac/ticket/13437
340         return appendable;
341     }
342     appendable.appendString(fResults->string.chars(), fResults->string.length());
343     return appendable;
344 }
345 
populateFieldPosition(FieldPosition & fieldPosition,UErrorCode & status)346 void FormattedNumber::populateFieldPosition(FieldPosition &fieldPosition, UErrorCode &status) {
347     if (U_FAILURE(status)) { return; }
348     if (fResults == nullptr) {
349         status = fErrorCode;
350         return;
351     }
352     fResults->string.populateFieldPosition(fieldPosition, 0, status);
353 }
354 
355 void
populateFieldPositionIterator(FieldPositionIterator & iterator,UErrorCode & status)356 FormattedNumber::populateFieldPositionIterator(FieldPositionIterator &iterator, UErrorCode &status) {
357     if (U_FAILURE(status)) { return; }
358     if (fResults == nullptr) {
359         status = fErrorCode;
360         return;
361     }
362     fResults->string.populateFieldPositionIterator(iterator, status);
363 }
364 
~FormattedNumber()365 FormattedNumber::~FormattedNumber() {
366     delete fResults;
367 }
368 
369 #endif /* #if !UCONFIG_NO_FORMATTING */
370