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 ¬ation) 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 ¯os, 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