blob: 76c3a7ce5c5d16047f388950a221a2caaafc1c32 [file] [log] [blame]
Jungshik Shinb3189662017-11-07 11:18:34 -08001// © 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
14using namespace icu;
15using namespace icu::number;
16using namespace icu::number::impl;
17
18template<typename Derived>
19Derived 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
26template<typename Derived>
27Derived 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
35template<typename Derived>
36Derived 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
48template<typename Derived>
49Derived 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
56template<typename Derived>
57Derived NumberFormatterSettings<Derived>::grouping(const Grouper &grouper) const {
58 Derived copy(*this);
59 copy.fMacros.grouper = grouper;
60 return copy;
61}
62
63template<typename Derived>
64Derived NumberFormatterSettings<Derived>::integerWidth(const IntegerWidth &style) const {
65 Derived copy(*this);
66 copy.fMacros.integerWidth = style;
67 return copy;
68}
69
70template<typename Derived>
71Derived NumberFormatterSettings<Derived>::symbols(const DecimalFormatSymbols &symbols) const {
72 Derived copy(*this);
73 copy.fMacros.symbols.setTo(symbols);
74 return copy;
75}
76
77template<typename Derived>
78Derived NumberFormatterSettings<Derived>::adoptSymbols(const NumberingSystem *ns) const {
79 Derived copy(*this);
80 copy.fMacros.symbols.setTo(ns);
81 return copy;
82}
83
84template<typename Derived>
85Derived NumberFormatterSettings<Derived>::unitWidth(const UNumberUnitWidth &width) const {
86 Derived copy(*this);
87 copy.fMacros.unitWidth = width;
88 return copy;
89}
90
91template<typename Derived>
92Derived NumberFormatterSettings<Derived>::sign(const UNumberSignDisplay &style) const {
93 Derived copy(*this);
94 copy.fMacros.sign = style;
95 return copy;
96}
97
98template<typename Derived>
99Derived NumberFormatterSettings<Derived>::decimal(const UNumberDecimalSeparatorDisplay &style) const {
100 Derived copy(*this);
101 copy.fMacros.decimal = style;
102 return copy;
103}
104
105template<typename Derived>
106Derived NumberFormatterSettings<Derived>::padding(const Padder &padder) const {
107 Derived copy(*this);
108 copy.fMacros.padder = padder;
109 return copy;
110}
111
112template<typename Derived>
113Derived 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
121template
122class icu::number::NumberFormatterSettings<icu::number::UnlocalizedNumberFormatter>;
123template
124class icu::number::NumberFormatterSettings<icu::number::LocalizedNumberFormatter>;
125
126
127UnlocalizedNumberFormatter NumberFormatter::with() {
128 UnlocalizedNumberFormatter result;
129 return result;
130}
131
132LocalizedNumberFormatter 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
137UnlocalizedNumberFormatter::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
144LocalizedNumberFormatter::LocalizedNumberFormatter(
145 const NumberFormatterSettings <LocalizedNumberFormatter> &other)
146 : NumberFormatterSettings<LocalizedNumberFormatter>(other) {
147 // No additional copies required
148}
149
150LocalizedNumberFormatter::LocalizedNumberFormatter(const MacroProps &macros, const Locale &locale) {
151 fMacros = macros;
152 fMacros.locale = locale;
153}
154
155LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale &locale) const {
156 return LocalizedNumberFormatter(fMacros, locale);
157}
158
159SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper &other) {
160 doCopyFrom(other);
161}
162
163SymbolsWrapper &SymbolsWrapper::operator=(const SymbolsWrapper &other) {
164 if (this == &other) {
165 return *this;
166 }
167 doCleanup();
168 doCopyFrom(other);
169 return *this;
170}
171
172SymbolsWrapper::~SymbolsWrapper() {
173 doCleanup();
174}
175
176void SymbolsWrapper::setTo(const DecimalFormatSymbols &dfs) {
177 doCleanup();
178 fType = SYMPTR_DFS;
179 fPtr.dfs = new DecimalFormatSymbols(dfs);
180}
181
182void SymbolsWrapper::setTo(const NumberingSystem *ns) {
183 doCleanup();
184 fType = SYMPTR_NS;
185 fPtr.ns = ns;
186}
187
188void 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
213void 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
227bool SymbolsWrapper::isDecimalFormatSymbols() const {
228 return fType == SYMPTR_DFS;
229}
230
231bool SymbolsWrapper::isNumberingSystem() const {
232 return fType == SYMPTR_NS;
233}
234
235const DecimalFormatSymbols* SymbolsWrapper::getDecimalFormatSymbols() const {
236 U_ASSERT(fType == SYMPTR_DFS);
237 return fPtr.dfs;
238}
239
240const NumberingSystem* SymbolsWrapper::getNumberingSystem() const {
241 U_ASSERT(fType == SYMPTR_NS);
242 return fPtr.ns;
243}
244
245LocalizedNumberFormatter::~LocalizedNumberFormatter() {
246 delete fCompiled;
247}
248
249FormattedNumber 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
260FormattedNumber 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
271FormattedNumber 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
282FormattedNumber
283LocalizedNumberFormatter::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
329UnicodeString 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
337Appendable &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
346void 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
355void
356FormattedNumber::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
365FormattedNumber::~FormattedNumber() {
366 delete fResults;
367}
368
369#endif /* #if !UCONFIG_NO_FORMATTING */