]> git.saurik.com Git - apple/icu.git/blame_incremental - icuSources/i18n/decimfmt.cpp
ICU-66108.tar.gz
[apple/icu.git] / icuSources / i18n / decimfmt.cpp
... / ...
CommitLineData
1// © 2018 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
7
8// Allow implicit conversion from char16_t* to UnicodeString for this file:
9// Helpful in toString methods and elsewhere.
10#define UNISTR_FROM_STRING_EXPLICIT
11
12#include <cmath>
13#include <cstdlib>
14#include <stdlib.h>
15#include "unicode/errorcode.h"
16#include "unicode/decimfmt.h"
17#include "unicode/ucurr.h"
18#include "unicode/ustring.h"
19#include "number_decimalquantity.h"
20#include "number_types.h"
21#include "numparse_impl.h"
22#include "number_mapper.h"
23#include "number_patternstring.h"
24#include "putilimp.h"
25#include "number_utils.h"
26#include "number_utypes.h"
27
28using namespace icu;
29using namespace icu::number;
30using namespace icu::number::impl;
31using namespace icu::numparse;
32using namespace icu::numparse::impl;
33using ERoundingMode = icu::DecimalFormat::ERoundingMode;
34using EPadPosition = icu::DecimalFormat::EPadPosition;
35
36// MSVC VS2015 warns C4805 when comparing bool with UBool, VS2017 no longer emits this warning.
37// TODO: Move this macro into a better place?
38#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
39#define UBOOL_TO_BOOL(b) static_cast<bool>(b)
40#else
41#define UBOOL_TO_BOOL(b) b
42#endif
43
44
45UOBJECT_DEFINE_RTTI_IMPLEMENTATION(DecimalFormat)
46
47
48DecimalFormat::DecimalFormat(UErrorCode& status)
49 : DecimalFormat(nullptr, status) {
50 if (U_FAILURE(status)) { return; }
51 // Use the default locale and decimal pattern.
52 const char* localeName = Locale::getDefault().getName();
53 LocalPointer<NumberingSystem> ns(NumberingSystem::createInstance(status));
54 UnicodeString patternString = utils::getPatternForStyle(
55 localeName,
56 ns->getName(),
57 CLDR_PATTERN_STYLE_DECIMAL,
58 status);
59 setPropertiesFromPattern(patternString, IGNORE_ROUNDING_IF_CURRENCY, status);
60 touch(status);
61}
62
63DecimalFormat::DecimalFormat(const UnicodeString& pattern, UErrorCode& status)
64 : DecimalFormat(nullptr, status) {
65 if (U_FAILURE(status)) { return; }
66 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
67 touch(status);
68}
69
70DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt,
71 UErrorCode& status)
72 : DecimalFormat(symbolsToAdopt, status) {
73 if (U_FAILURE(status)) { return; }
74 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
75 touch(status);
76}
77
78DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt,
79 UNumberFormatStyle style, UErrorCode& status)
80 : DecimalFormat(symbolsToAdopt, status) {
81 if (U_FAILURE(status)) { return; }
82 // If choice is a currency type, ignore the rounding information.
83 if (style == UNumberFormatStyle::UNUM_CURRENCY ||
84 style == UNumberFormatStyle::UNUM_CURRENCY_ISO ||
85 style == UNumberFormatStyle::UNUM_CURRENCY_ACCOUNTING ||
86 style == UNumberFormatStyle::UNUM_CASH_CURRENCY ||
87 style == UNumberFormatStyle::UNUM_CURRENCY_STANDARD ||
88 style == UNumberFormatStyle::UNUM_CURRENCY_PLURAL) {
89 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_ALWAYS, status);
90 } else {
91 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
92 }
93 // Note: in Java, CurrencyPluralInfo is set in NumberFormat.java, but in C++, it is not set there,
94 // so we have to set it here.
95 if (style == UNumberFormatStyle::UNUM_CURRENCY_PLURAL) {
96 LocalPointer<CurrencyPluralInfo> cpi(
97 new CurrencyPluralInfo(fields->symbols->getLocale(), status),
98 status);
99 if (U_FAILURE(status)) { return; }
100 fields->properties.currencyPluralInfo.fPtr.adoptInstead(cpi.orphan());
101 }
102 touch(status);
103}
104
105DecimalFormat::DecimalFormat(const DecimalFormatSymbols* symbolsToAdopt, UErrorCode& status) {
106 // we must take ownership of symbolsToAdopt, even in a failure case.
107 LocalPointer<const DecimalFormatSymbols> adoptedSymbols(symbolsToAdopt);
108 if (U_FAILURE(status)) {
109 return;
110 }
111 fields = new DecimalFormatFields();
112 if (fields == nullptr) {
113 status = U_MEMORY_ALLOCATION_ERROR;
114 return;
115 }
116 if (adoptedSymbols.isNull()) {
117 fields->symbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(status), status);
118 } else {
119 fields->symbols.adoptInsteadAndCheckErrorCode(adoptedSymbols.orphan(), status);
120 }
121 if (U_FAILURE(status)) {
122 delete fields;
123 fields = nullptr;
124 }
125}
126
127#if UCONFIG_HAVE_PARSEALLINPUT
128
129void DecimalFormat::setParseAllInput(UNumberFormatAttributeValue value) {
130 if (fields == nullptr) { return; }
131 if (value == fields->properties.parseAllInput) { return; }
132 fields->properties.parseAllInput = value;
133}
134
135#endif
136
137DecimalFormat&
138DecimalFormat::setAttribute(UNumberFormatAttribute attr, int32_t newValue, UErrorCode& status) {
139 if (U_FAILURE(status)) { return *this; }
140
141 if (fields == nullptr) {
142 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
143 status = U_MEMORY_ALLOCATION_ERROR;
144 return *this;
145 }
146
147 switch (attr) {
148 case UNUM_LENIENT_PARSE:
149 setLenient(newValue != 0);
150 break;
151
152 case UNUM_PARSE_INT_ONLY:
153 setParseIntegerOnly(newValue != 0);
154 break;
155
156 case UNUM_GROUPING_USED:
157 setGroupingUsed(newValue != 0);
158 break;
159
160 case UNUM_DECIMAL_ALWAYS_SHOWN:
161 setDecimalSeparatorAlwaysShown(newValue != 0);
162 break;
163
164 case UNUM_MAX_INTEGER_DIGITS:
165 setMaximumIntegerDigits(newValue);
166 break;
167
168 case UNUM_MIN_INTEGER_DIGITS:
169 setMinimumIntegerDigits(newValue);
170 break;
171
172 case UNUM_INTEGER_DIGITS:
173 setMinimumIntegerDigits(newValue);
174 setMaximumIntegerDigits(newValue);
175 break;
176
177 case UNUM_MAX_FRACTION_DIGITS:
178 setMaximumFractionDigits(newValue);
179 break;
180
181 case UNUM_MIN_FRACTION_DIGITS:
182 setMinimumFractionDigits(newValue);
183 break;
184
185 case UNUM_FRACTION_DIGITS:
186 setMinimumFractionDigits(newValue);
187 setMaximumFractionDigits(newValue);
188 break;
189
190 case UNUM_SIGNIFICANT_DIGITS_USED:
191 setSignificantDigitsUsed(newValue != 0);
192 break;
193
194 case UNUM_MAX_SIGNIFICANT_DIGITS:
195 setMaximumSignificantDigits(newValue);
196 break;
197
198 case UNUM_MIN_SIGNIFICANT_DIGITS:
199 setMinimumSignificantDigits(newValue);
200 break;
201
202 case UNUM_MULTIPLIER:
203 setMultiplier(newValue);
204 break;
205
206 case UNUM_SCALE:
207 setMultiplierScale(newValue);
208 break;
209
210 case UNUM_GROUPING_SIZE:
211 setGroupingSize(newValue);
212 break;
213
214 case UNUM_ROUNDING_MODE:
215 setRoundingMode((DecimalFormat::ERoundingMode) newValue);
216 break;
217
218 case UNUM_FORMAT_WIDTH:
219 setFormatWidth(newValue);
220 break;
221
222 case UNUM_PADDING_POSITION:
223 /** The position at which padding will take place. */
224 setPadPosition((DecimalFormat::EPadPosition) newValue);
225 break;
226
227 case UNUM_SECONDARY_GROUPING_SIZE:
228 setSecondaryGroupingSize(newValue);
229 break;
230
231#if UCONFIG_HAVE_PARSEALLINPUT
232 case UNUM_PARSE_ALL_INPUT:
233 setParseAllInput((UNumberFormatAttributeValue) newValue);
234 break;
235#endif
236
237 case UNUM_PARSE_NO_EXPONENT:
238 setParseNoExponent((UBool) newValue);
239 break;
240
241 case UNUM_PARSE_DECIMAL_MARK_REQUIRED:
242 setDecimalPatternMatchRequired((UBool) newValue);
243 break;
244
245 case UNUM_CURRENCY_USAGE:
246 setCurrencyUsage((UCurrencyUsage) newValue, &status);
247 break;
248
249 case UNUM_MINIMUM_GROUPING_DIGITS:
250 setMinimumGroupingDigits(newValue);
251 break;
252
253 case UNUM_PARSE_CASE_SENSITIVE:
254 setParseCaseSensitive(static_cast<UBool>(newValue));
255 break;
256
257 case UNUM_SIGN_ALWAYS_SHOWN:
258 setSignAlwaysShown(static_cast<UBool>(newValue));
259 break;
260
261 case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS:
262 setFormatFailIfMoreThanMaxDigits(static_cast<UBool>(newValue));
263 break;
264
265 case UNUM_FORMAT_WITH_FULL_PRECISION: // Apple addition for <rdar://problem/39240173>
266 {
267 bool boolVal = UBOOL_TO_BOOL(static_cast<UBool>(newValue));
268 if (boolVal != fields->properties.formatFullPrecision) {
269 fields->properties.formatFullPrecision = boolVal;
270 touchNoError();
271 }
272 }
273 break;
274
275 default:
276 status = U_UNSUPPORTED_ERROR;
277 break;
278 }
279 return *this;
280}
281
282int32_t DecimalFormat::getAttribute(UNumberFormatAttribute attr, UErrorCode& status) const {
283 if (U_FAILURE(status)) { return -1; }
284
285 if (fields == nullptr) {
286 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
287 status = U_MEMORY_ALLOCATION_ERROR;
288 return -1;
289 }
290
291 switch (attr) {
292 case UNUM_LENIENT_PARSE:
293 return isLenient();
294
295 case UNUM_PARSE_INT_ONLY:
296 return isParseIntegerOnly();
297
298 case UNUM_GROUPING_USED:
299 return isGroupingUsed();
300
301 case UNUM_DECIMAL_ALWAYS_SHOWN:
302 return isDecimalSeparatorAlwaysShown();
303
304 case UNUM_MAX_INTEGER_DIGITS:
305 return getMaximumIntegerDigits();
306
307 case UNUM_MIN_INTEGER_DIGITS:
308 return getMinimumIntegerDigits();
309
310 case UNUM_INTEGER_DIGITS:
311 // TBD: what should this return?
312 return getMinimumIntegerDigits();
313
314 case UNUM_MAX_FRACTION_DIGITS:
315 return getMaximumFractionDigits();
316
317 case UNUM_MIN_FRACTION_DIGITS:
318 return getMinimumFractionDigits();
319
320 case UNUM_FRACTION_DIGITS:
321 // TBD: what should this return?
322 return getMinimumFractionDigits();
323
324 case UNUM_SIGNIFICANT_DIGITS_USED:
325 return areSignificantDigitsUsed();
326
327 case UNUM_MAX_SIGNIFICANT_DIGITS:
328 return getMaximumSignificantDigits();
329
330 case UNUM_MIN_SIGNIFICANT_DIGITS:
331 return getMinimumSignificantDigits();
332
333 case UNUM_MULTIPLIER:
334 return getMultiplier();
335
336 case UNUM_SCALE:
337 return getMultiplierScale();
338
339 case UNUM_GROUPING_SIZE:
340 return getGroupingSize();
341
342 case UNUM_ROUNDING_MODE:
343 return getRoundingMode();
344
345 case UNUM_FORMAT_WIDTH:
346 return getFormatWidth();
347
348 case UNUM_PADDING_POSITION:
349 return getPadPosition();
350
351 case UNUM_SECONDARY_GROUPING_SIZE:
352 return getSecondaryGroupingSize();
353
354 case UNUM_PARSE_NO_EXPONENT:
355 return isParseNoExponent();
356
357 case UNUM_PARSE_DECIMAL_MARK_REQUIRED:
358 return isDecimalPatternMatchRequired();
359
360 case UNUM_CURRENCY_USAGE:
361 return getCurrencyUsage();
362
363 case UNUM_MINIMUM_GROUPING_DIGITS:
364 return getMinimumGroupingDigits();
365
366 case UNUM_PARSE_CASE_SENSITIVE:
367 return isParseCaseSensitive();
368
369 case UNUM_SIGN_ALWAYS_SHOWN:
370 return isSignAlwaysShown();
371
372 case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS:
373 return isFormatFailIfMoreThanMaxDigits();
374
375 case UNUM_FORMAT_WITH_FULL_PRECISION: // Apple addition for <rdar://problem/39240173>
376 return (UBool)fields->properties.formatFullPrecision;
377
378 default:
379 status = U_UNSUPPORTED_ERROR;
380 break;
381 }
382
383 return -1; /* undefined */
384}
385
386void DecimalFormat::setGroupingUsed(UBool enabled) {
387 if (fields == nullptr) {
388 return;
389 }
390 if (UBOOL_TO_BOOL(enabled) == fields->properties.groupingUsed) { return; }
391 NumberFormat::setGroupingUsed(enabled); // to set field for compatibility
392 fields->properties.groupingUsed = enabled;
393 touchNoError();
394}
395
396void DecimalFormat::setParseIntegerOnly(UBool value) {
397 if (fields == nullptr) {
398 return;
399 }
400 if (UBOOL_TO_BOOL(value) == fields->properties.parseIntegerOnly) { return; }
401 NumberFormat::setParseIntegerOnly(value); // to set field for compatibility
402 fields->properties.parseIntegerOnly = value;
403 touchNoError();
404}
405
406void DecimalFormat::setLenient(UBool enable) {
407 if (fields == nullptr) {
408 return;
409 }
410 ParseMode mode = enable ? PARSE_MODE_LENIENT : PARSE_MODE_STRICT;
411 if (!fields->properties.parseMode.isNull() && mode == fields->properties.parseMode.getNoError()) { return; }
412 NumberFormat::setLenient(enable); // to set field for compatibility
413 fields->properties.parseMode = mode;
414 touchNoError();
415}
416
417DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt,
418 UParseError&, UErrorCode& status)
419 : DecimalFormat(symbolsToAdopt, status) {
420 if (U_FAILURE(status)) { return; }
421 // TODO: What is parseError for?
422 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
423 touch(status);
424}
425
426DecimalFormat::DecimalFormat(const UnicodeString& pattern, const DecimalFormatSymbols& symbols,
427 UErrorCode& status)
428 : DecimalFormat(nullptr, status) {
429 if (U_FAILURE(status)) { return; }
430 LocalPointer<DecimalFormatSymbols> dfs(new DecimalFormatSymbols(symbols), status);
431 if (U_FAILURE(status)) {
432 // If we failed to allocate DecimalFormatSymbols, then release fields and its members.
433 // We must have a fully complete fields object, we cannot have partially populated members.
434 delete fields;
435 fields = nullptr;
436 status = U_MEMORY_ALLOCATION_ERROR;
437 return;
438 }
439 fields->symbols.adoptInstead(dfs.orphan());
440 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
441 touch(status);
442}
443
444DecimalFormat::DecimalFormat(const DecimalFormat& source) : NumberFormat(source) {
445 // If the object that we are copying from is invalid, no point in going further.
446 if (source.fields == nullptr) {
447 return;
448 }
449 // Note: it is not safe to copy fields->formatter or fWarehouse directly because fields->formatter might have
450 // dangling pointers to fields inside fWarehouse. The safe thing is to re-construct fields->formatter from
451 // the property bag, despite being somewhat slower.
452 fields = new DecimalFormatFields(source.fields->properties);
453 if (fields == nullptr) {
454 return; // no way to report an error.
455 }
456 UErrorCode status = U_ZERO_ERROR;
457 fields->symbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(*source.fields->symbols), status);
458 // In order to simplify error handling logic in the various getters/setters/etc, we do not allow
459 // any partially populated DecimalFormatFields object. We must have a fully complete fields object
460 // or else we set it to nullptr.
461 if (U_FAILURE(status)) {
462 delete fields;
463 fields = nullptr;
464 return;
465 }
466 touch(status);
467}
468
469DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) {
470 // guard against self-assignment
471 if (this == &rhs) {
472 return *this;
473 }
474 // Make sure both objects are valid.
475 if (fields == nullptr || rhs.fields == nullptr) {
476 return *this; // unfortunately, no way to report an error.
477 }
478 fields->properties = rhs.fields->properties;
479 fields->exportedProperties.clear();
480 UErrorCode status = U_ZERO_ERROR;
481 LocalPointer<DecimalFormatSymbols> dfs(new DecimalFormatSymbols(*rhs.fields->symbols), status);
482 if (U_FAILURE(status)) {
483 // We failed to allocate DecimalFormatSymbols, release fields and its members.
484 // We must have a fully complete fields object, we cannot have partially populated members.
485 delete fields;
486 fields = nullptr;
487 return *this;
488 }
489 fields->symbols.adoptInstead(dfs.orphan());
490 touch(status);
491
492 return *this;
493}
494
495DecimalFormat::~DecimalFormat() {
496 if (fields == nullptr) { return; }
497
498 delete fields->atomicParser.exchange(nullptr);
499 delete fields->atomicCurrencyParser.exchange(nullptr);
500 delete fields;
501}
502
503DecimalFormat* DecimalFormat::clone() const {
504 // can only clone valid objects.
505 if (fields == nullptr) {
506 return nullptr;
507 }
508 LocalPointer<DecimalFormat> df(new DecimalFormat(*this));
509 if (df.isValid() && df->fields != nullptr) {
510 return df.orphan();
511 }
512 return nullptr;
513}
514
515UBool DecimalFormat::operator==(const Format& other) const {
516 auto* otherDF = dynamic_cast<const DecimalFormat*>(&other);
517 if (otherDF == nullptr) {
518 return false;
519 }
520 // If either object is in an invalid state, prevent dereferencing nullptr below.
521 // Additionally, invalid objects should not be considered equal to anything.
522 if (fields == nullptr || otherDF->fields == nullptr) {
523 return false;
524 }
525 return fields->properties == otherDF->fields->properties && *fields->symbols == *otherDF->fields->symbols;
526}
527
528UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos) const {
529 if (fields == nullptr) {
530 appendTo.setToBogus();
531 return appendTo;
532 }
533 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) {
534 return appendTo;
535 }
536 UErrorCode localStatus = U_ZERO_ERROR;
537 FormattedNumber output = fields->formatter.formatDouble(number, localStatus);
538 fieldPositionHelper(output, pos, appendTo.length(), localStatus);
539 auto appendable = UnicodeStringAppendable(appendTo);
540 output.appendTo(appendable, localStatus);
541 return appendTo;
542}
543
544UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos,
545 UErrorCode& status) const {
546 if (U_FAILURE(status)) {
547 return appendTo; // don't overwrite status if it's already a failure.
548 }
549 if (fields == nullptr) {
550 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
551 status = U_MEMORY_ALLOCATION_ERROR;
552 appendTo.setToBogus();
553 return appendTo;
554 }
555 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) {
556 return appendTo;
557 }
558 FormattedNumber output = fields->formatter.formatDouble(number, status);
559 fieldPositionHelper(output, pos, appendTo.length(), status);
560 auto appendable = UnicodeStringAppendable(appendTo);
561 output.appendTo(appendable, status);
562 return appendTo;
563}
564
565UnicodeString&
566DecimalFormat::format(double number, UnicodeString& appendTo, FieldPositionIterator* posIter,
567 UErrorCode& status) const {
568 if (U_FAILURE(status)) {
569 return appendTo; // don't overwrite status if it's already a failure.
570 }
571 if (fields == nullptr) {
572 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
573 status = U_MEMORY_ALLOCATION_ERROR;
574 appendTo.setToBogus();
575 return appendTo;
576 }
577 if (posIter == nullptr && fastFormatDouble(number, appendTo)) {
578 return appendTo;
579 }
580 FormattedNumber output = fields->formatter.formatDouble(number, status);
581 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
582 auto appendable = UnicodeStringAppendable(appendTo);
583 output.appendTo(appendable, status);
584 return appendTo;
585}
586
587UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos) const {
588 return format(static_cast<int64_t> (number), appendTo, pos);
589}
590
591UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos,
592 UErrorCode& status) const {
593 return format(static_cast<int64_t> (number), appendTo, pos, status);
594}
595
596UnicodeString&
597DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPositionIterator* posIter,
598 UErrorCode& status) const {
599 return format(static_cast<int64_t> (number), appendTo, posIter, status);
600}
601
602UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos) const {
603 if (fields == nullptr) {
604 appendTo.setToBogus();
605 return appendTo;
606 }
607 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) {
608 return appendTo;
609 }
610 UErrorCode localStatus = U_ZERO_ERROR;
611 FormattedNumber output = fields->formatter.formatInt(number, localStatus);
612 fieldPositionHelper(output, pos, appendTo.length(), localStatus);
613 auto appendable = UnicodeStringAppendable(appendTo);
614 output.appendTo(appendable, localStatus);
615 return appendTo;
616}
617
618UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos,
619 UErrorCode& status) const {
620 if (U_FAILURE(status)) {
621 return appendTo; // don't overwrite status if it's already a failure.
622 }
623 if (fields == nullptr) {
624 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
625 status = U_MEMORY_ALLOCATION_ERROR;
626 appendTo.setToBogus();
627 return appendTo;
628 }
629 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) {
630 return appendTo;
631 }
632 FormattedNumber output = fields->formatter.formatInt(number, status);
633 fieldPositionHelper(output, pos, appendTo.length(), status);
634 auto appendable = UnicodeStringAppendable(appendTo);
635 output.appendTo(appendable, status);
636 return appendTo;
637}
638
639UnicodeString&
640DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPositionIterator* posIter,
641 UErrorCode& status) const {
642 if (U_FAILURE(status)) {
643 return appendTo; // don't overwrite status if it's already a failure.
644 }
645 if (fields == nullptr) {
646 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
647 status = U_MEMORY_ALLOCATION_ERROR;
648 appendTo.setToBogus();
649 return appendTo;
650 }
651 if (posIter == nullptr && fastFormatInt64(number, appendTo)) {
652 return appendTo;
653 }
654 FormattedNumber output = fields->formatter.formatInt(number, status);
655 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
656 auto appendable = UnicodeStringAppendable(appendTo);
657 output.appendTo(appendable, status);
658 return appendTo;
659}
660
661UnicodeString&
662DecimalFormat::format(StringPiece number, UnicodeString& appendTo, FieldPositionIterator* posIter,
663 UErrorCode& status) const {
664 if (U_FAILURE(status)) {
665 return appendTo; // don't overwrite status if it's already a failure.
666 }
667 if (fields == nullptr) {
668 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
669 status = U_MEMORY_ALLOCATION_ERROR;
670 appendTo.setToBogus();
671 return appendTo;
672 }
673 FormattedNumber output = fields->formatter.formatDecimal(number, status);
674 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
675 auto appendable = UnicodeStringAppendable(appendTo);
676 output.appendTo(appendable, status);
677 return appendTo;
678}
679
680UnicodeString& DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo,
681 FieldPositionIterator* posIter, UErrorCode& status) const {
682 if (U_FAILURE(status)) {
683 return appendTo; // don't overwrite status if it's already a failure.
684 }
685 if (fields == nullptr) {
686 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
687 status = U_MEMORY_ALLOCATION_ERROR;
688 appendTo.setToBogus();
689 return appendTo;
690 }
691 FormattedNumber output = fields->formatter.formatDecimalQuantity(number, status);
692 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
693 auto appendable = UnicodeStringAppendable(appendTo);
694 output.appendTo(appendable, status);
695 return appendTo;
696}
697
698UnicodeString&
699DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, FieldPosition& pos,
700 UErrorCode& status) const {
701 if (U_FAILURE(status)) {
702 return appendTo; // don't overwrite status if it's already a failure.
703 }
704 if (fields == nullptr) {
705 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
706 status = U_MEMORY_ALLOCATION_ERROR;
707 appendTo.setToBogus();
708 return appendTo;
709 }
710 FormattedNumber output = fields->formatter.formatDecimalQuantity(number, status);
711 fieldPositionHelper(output, pos, appendTo.length(), status);
712 auto appendable = UnicodeStringAppendable(appendTo);
713 output.appendTo(appendable, status);
714 return appendTo;
715}
716
717void DecimalFormat::parse(const UnicodeString& text, Formattable& output,
718 ParsePosition& parsePosition) const {
719 if (fields == nullptr) {
720 return;
721 }
722 if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) {
723 if (parsePosition.getIndex() == text.length()) {
724 // If there is nothing to parse, it is an error
725 parsePosition.setErrorIndex(parsePosition.getIndex());
726 }
727 return;
728 }
729
730 ErrorCode status;
731 ParsedNumber result;
732 // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the
733 // parseCurrency method (backwards compatibility)
734 int32_t startIndex = parsePosition.getIndex();
735 const NumberParserImpl* parser = getParser(status);
736 if (U_FAILURE(status)) {
737 return; // unfortunately no way to report back the error.
738 }
739 parser->parse(text, startIndex, true, result, status);
740 if (U_FAILURE(status)) {
741 return; // unfortunately no way to report back the error.
742 }
743 // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here?
744 if (result.success()) {
745 parsePosition.setIndex(result.charEnd);
746 result.populateFormattable(output, parser->getParseFlags());
747 } else {
748 parsePosition.setErrorIndex(startIndex + result.charEnd);
749 }
750}
751
752CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text, ParsePosition& parsePosition) const {
753 if (fields == nullptr) {
754 return nullptr;
755 }
756 if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) {
757 return nullptr;
758 }
759
760 ErrorCode status;
761 ParsedNumber result;
762 // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the
763 // parseCurrency method (backwards compatibility)
764 int32_t startIndex = parsePosition.getIndex();
765 const NumberParserImpl* parser = getCurrencyParser(status);
766 if (U_FAILURE(status)) {
767 return nullptr;
768 }
769 parser->parse(text, startIndex, true, result, status);
770 if (U_FAILURE(status)) {
771 return nullptr;
772 }
773 // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here?
774 if (result.success()) {
775 parsePosition.setIndex(result.charEnd);
776 Formattable formattable;
777 result.populateFormattable(formattable, parser->getParseFlags());
778 LocalPointer<CurrencyAmount> currencyAmount(
779 new CurrencyAmount(formattable, result.currencyCode, status), status);
780 if (U_FAILURE(status)) {
781 return nullptr;
782 }
783 return currencyAmount.orphan();
784 } else {
785 parsePosition.setErrorIndex(startIndex + result.charEnd);
786 return nullptr;
787 }
788}
789
790const DecimalFormatSymbols* DecimalFormat::getDecimalFormatSymbols(void) const {
791 if (fields == nullptr) {
792 return nullptr;
793 }
794 return fields->symbols.getAlias();
795}
796
797void DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) {
798 if (symbolsToAdopt == nullptr) {
799 return; // do not allow caller to set fields->symbols to NULL
800 }
801 // we must take ownership of symbolsToAdopt, even in a failure case.
802 LocalPointer<DecimalFormatSymbols> dfs(symbolsToAdopt);
803 if (fields == nullptr) {
804 return;
805 }
806 fields->symbols.adoptInstead(dfs.orphan());
807 touchNoError();
808}
809
810void DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) {
811 if (fields == nullptr) {
812 return;
813 }
814 UErrorCode status = U_ZERO_ERROR;
815 LocalPointer<DecimalFormatSymbols> dfs(new DecimalFormatSymbols(symbols), status);
816 if (U_FAILURE(status)) {
817 // We failed to allocate DecimalFormatSymbols, release fields and its members.
818 // We must have a fully complete fields object, we cannot have partially populated members.
819 delete fields;
820 fields = nullptr;
821 return;
822 }
823 fields->symbols.adoptInstead(dfs.orphan());
824 touchNoError();
825}
826
827const CurrencyPluralInfo* DecimalFormat::getCurrencyPluralInfo(void) const {
828 if (fields == nullptr) {
829 return nullptr;
830 }
831 return fields->properties.currencyPluralInfo.fPtr.getAlias();
832}
833
834void DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) {
835 // TODO: should we guard against nullptr input, like in adoptDecimalFormatSymbols?
836 // we must take ownership of toAdopt, even in a failure case.
837 LocalPointer<CurrencyPluralInfo> cpi(toAdopt);
838 if (fields == nullptr) {
839 return;
840 }
841 fields->properties.currencyPluralInfo.fPtr.adoptInstead(cpi.orphan());
842 touchNoError();
843}
844
845void DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) {
846 if (fields == nullptr) {
847 return;
848 }
849 if (fields->properties.currencyPluralInfo.fPtr.isNull()) {
850 // Note: clone() can fail with OOM error, but we have no way to report it. :(
851 fields->properties.currencyPluralInfo.fPtr.adoptInstead(info.clone());
852 } else {
853 *fields->properties.currencyPluralInfo.fPtr = info; // copy-assignment operator
854 }
855 touchNoError();
856}
857
858UnicodeString& DecimalFormat::getPositivePrefix(UnicodeString& result) const {
859 if (fields == nullptr) {
860 result.setToBogus();
861 return result;
862 }
863 UErrorCode status = U_ZERO_ERROR;
864 fields->formatter.getAffixImpl(true, false, result, status);
865 if (U_FAILURE(status)) { result.setToBogus(); }
866 return result;
867}
868
869void DecimalFormat::setPositivePrefix(const UnicodeString& newValue) {
870 if (fields == nullptr) {
871 return;
872 }
873 if (newValue == fields->properties.positivePrefix) { return; }
874 fields->properties.positivePrefix = newValue;
875 touchNoError();
876}
877
878UnicodeString& DecimalFormat::getNegativePrefix(UnicodeString& result) const {
879 if (fields == nullptr) {
880 result.setToBogus();
881 return result;
882 }
883 UErrorCode status = U_ZERO_ERROR;
884 fields->formatter.getAffixImpl(true, true, result, status);
885 if (U_FAILURE(status)) { result.setToBogus(); }
886 return result;
887}
888
889void DecimalFormat::setNegativePrefix(const UnicodeString& newValue) {
890 if (fields == nullptr) {
891 return;
892 }
893 if (newValue == fields->properties.negativePrefix) { return; }
894 fields->properties.negativePrefix = newValue;
895 touchNoError();
896}
897
898UnicodeString& DecimalFormat::getPositiveSuffix(UnicodeString& result) const {
899 if (fields == nullptr) {
900 result.setToBogus();
901 return result;
902 }
903 UErrorCode status = U_ZERO_ERROR;
904 fields->formatter.getAffixImpl(false, false, result, status);
905 if (U_FAILURE(status)) { result.setToBogus(); }
906 return result;
907}
908
909void DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) {
910 if (fields == nullptr) {
911 return;
912 }
913 if (newValue == fields->properties.positiveSuffix) { return; }
914 fields->properties.positiveSuffix = newValue;
915 touchNoError();
916}
917
918UnicodeString& DecimalFormat::getNegativeSuffix(UnicodeString& result) const {
919 if (fields == nullptr) {
920 result.setToBogus();
921 return result;
922 }
923 UErrorCode status = U_ZERO_ERROR;
924 fields->formatter.getAffixImpl(false, true, result, status);
925 if (U_FAILURE(status)) { result.setToBogus(); }
926 return result;
927}
928
929void DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) {
930 if (fields == nullptr) {
931 return;
932 }
933 if (newValue == fields->properties.negativeSuffix) { return; }
934 fields->properties.negativeSuffix = newValue;
935 touchNoError();
936}
937
938UBool DecimalFormat::isSignAlwaysShown() const {
939 // Not much we can do to report an error.
940 if (fields == nullptr) {
941 return DecimalFormatProperties::getDefault().signAlwaysShown;
942 }
943 return fields->properties.signAlwaysShown;
944}
945
946void DecimalFormat::setSignAlwaysShown(UBool value) {
947 if (fields == nullptr) { return; }
948 if (UBOOL_TO_BOOL(value) == fields->properties.signAlwaysShown) { return; }
949 fields->properties.signAlwaysShown = value;
950 touchNoError();
951}
952
953int32_t DecimalFormat::getMultiplier(void) const {
954 const DecimalFormatProperties *dfp;
955 // Not much we can do to report an error.
956 if (fields == nullptr) {
957 // Fallback to using the default instance of DecimalFormatProperties.
958 dfp = &(DecimalFormatProperties::getDefault());
959 } else {
960 dfp = &fields->properties;
961 }
962 if (dfp->multiplier != 1) {
963 return dfp->multiplier;
964 } else if (dfp->magnitudeMultiplier != 0) {
965 return static_cast<int32_t>(uprv_pow10(dfp->magnitudeMultiplier));
966 } else {
967 return 1;
968 }
969}
970
971void DecimalFormat::setMultiplier(int32_t multiplier) {
972 if (fields == nullptr) {
973 return;
974 }
975 if (multiplier == 0) {
976 multiplier = 1; // one being the benign default value for a multiplier.
977 }
978
979 // Try to convert to a magnitude multiplier first
980 int delta = 0;
981 int value = multiplier;
982 while (value != 1) {
983 delta++;
984 int temp = value / 10;
985 if (temp * 10 != value) {
986 delta = -1;
987 break;
988 }
989 value = temp;
990 }
991 if (delta != -1) {
992 fields->properties.magnitudeMultiplier = delta;
993 fields->properties.multiplier = 1;
994 } else {
995 fields->properties.magnitudeMultiplier = 0;
996 fields->properties.multiplier = multiplier;
997 }
998 touchNoError();
999}
1000
1001int32_t DecimalFormat::getMultiplierScale() const {
1002 // Not much we can do to report an error.
1003 if (fields == nullptr) {
1004 // Fallback to using the default instance of DecimalFormatProperties.
1005 return DecimalFormatProperties::getDefault().multiplierScale;
1006 }
1007 return fields->properties.multiplierScale;
1008}
1009
1010void DecimalFormat::setMultiplierScale(int32_t newValue) {
1011 if (fields == nullptr) { return; }
1012 if (newValue == fields->properties.multiplierScale) { return; }
1013 fields->properties.multiplierScale = newValue;
1014 touchNoError();
1015}
1016
1017double DecimalFormat::getRoundingIncrement(void) const {
1018 // Not much we can do to report an error.
1019 if (fields == nullptr) {
1020 // Fallback to using the default instance of DecimalFormatProperties.
1021 return DecimalFormatProperties::getDefault().roundingIncrement;
1022 }
1023 return fields->exportedProperties.roundingIncrement;
1024}
1025
1026void DecimalFormat::setRoundingIncrement(double newValue) {
1027 if (fields == nullptr) { return; }
1028 if (newValue == fields->properties.roundingIncrement) { return; }
1029 fields->properties.roundingIncrement = newValue;
1030 touchNoError();
1031}
1032
1033ERoundingMode DecimalFormat::getRoundingMode(void) const {
1034 // Not much we can do to report an error.
1035 if (fields == nullptr) {
1036 // Fallback to using the default instance of DecimalFormatProperties.
1037 return static_cast<ERoundingMode>(DecimalFormatProperties::getDefault().roundingMode.getNoError());
1038 }
1039 // UNumberFormatRoundingMode and ERoundingMode have the same values.
1040 return static_cast<ERoundingMode>(fields->exportedProperties.roundingMode.getNoError());
1041}
1042
1043void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) {
1044 if (fields == nullptr) { return; }
1045 auto uRoundingMode = static_cast<UNumberFormatRoundingMode>(roundingMode);
1046 if (!fields->properties.roundingMode.isNull() && uRoundingMode == fields->properties.roundingMode.getNoError()) {
1047 return;
1048 }
1049 NumberFormat::setMaximumIntegerDigits(roundingMode); // to set field for compatibility
1050 fields->properties.roundingMode = uRoundingMode;
1051 touchNoError();
1052}
1053
1054int32_t DecimalFormat::getFormatWidth(void) const {
1055 // Not much we can do to report an error.
1056 if (fields == nullptr) {
1057 // Fallback to using the default instance of DecimalFormatProperties.
1058 return DecimalFormatProperties::getDefault().formatWidth;
1059 }
1060 return fields->properties.formatWidth;
1061}
1062
1063void DecimalFormat::setFormatWidth(int32_t width) {
1064 if (fields == nullptr) { return; }
1065 if (width == fields->properties.formatWidth) { return; }
1066 fields->properties.formatWidth = width;
1067 touchNoError();
1068}
1069
1070UnicodeString DecimalFormat::getPadCharacterString() const {
1071 if (fields == nullptr || fields->properties.padString.isBogus()) {
1072 // Readonly-alias the static string kFallbackPaddingString
1073 return {TRUE, kFallbackPaddingString, -1};
1074 } else {
1075 return fields->properties.padString;
1076 }
1077}
1078
1079void DecimalFormat::setPadCharacter(const UnicodeString& padChar) {
1080 if (fields == nullptr) { return; }
1081 if (padChar == fields->properties.padString) { return; }
1082 if (padChar.length() > 0) {
1083 fields->properties.padString = UnicodeString(padChar.char32At(0));
1084 } else {
1085 fields->properties.padString.setToBogus();
1086 }
1087 touchNoError();
1088}
1089
1090EPadPosition DecimalFormat::getPadPosition(void) const {
1091 if (fields == nullptr || fields->properties.padPosition.isNull()) {
1092 return EPadPosition::kPadBeforePrefix;
1093 } else {
1094 // UNumberFormatPadPosition and EPadPosition have the same values.
1095 return static_cast<EPadPosition>(fields->properties.padPosition.getNoError());
1096 }
1097}
1098
1099void DecimalFormat::setPadPosition(EPadPosition padPos) {
1100 if (fields == nullptr) { return; }
1101 auto uPadPos = static_cast<UNumberFormatPadPosition>(padPos);
1102 if (!fields->properties.padPosition.isNull() && uPadPos == fields->properties.padPosition.getNoError()) {
1103 return;
1104 }
1105 fields->properties.padPosition = uPadPos;
1106 touchNoError();
1107}
1108
1109UBool DecimalFormat::isScientificNotation(void) const {
1110 // Not much we can do to report an error.
1111 if (fields == nullptr) {
1112 // Fallback to using the default instance of DecimalFormatProperties.
1113 return (DecimalFormatProperties::getDefault().minimumExponentDigits != -1);
1114 }
1115 return (fields->properties.minimumExponentDigits != -1);
1116}
1117
1118void DecimalFormat::setScientificNotation(UBool useScientific) {
1119 if (fields == nullptr) { return; }
1120 int32_t minExp = useScientific ? 1 : -1;
1121 if (fields->properties.minimumExponentDigits == minExp) { return; }
1122 if (useScientific) {
1123 fields->properties.minimumExponentDigits = 1;
1124 } else {
1125 fields->properties.minimumExponentDigits = -1;
1126 }
1127 touchNoError();
1128}
1129
1130int8_t DecimalFormat::getMinimumExponentDigits(void) const {
1131 // Not much we can do to report an error.
1132 if (fields == nullptr) {
1133 // Fallback to using the default instance of DecimalFormatProperties.
1134 return static_cast<int8_t>(DecimalFormatProperties::getDefault().minimumExponentDigits);
1135 }
1136 return static_cast<int8_t>(fields->properties.minimumExponentDigits);
1137}
1138
1139void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) {
1140 if (fields == nullptr) { return; }
1141 if (minExpDig == fields->properties.minimumExponentDigits) { return; }
1142 fields->properties.minimumExponentDigits = minExpDig;
1143 touchNoError();
1144}
1145
1146UBool DecimalFormat::isExponentSignAlwaysShown(void) const {
1147 // Not much we can do to report an error.
1148 if (fields == nullptr) {
1149 // Fallback to using the default instance of DecimalFormatProperties.
1150 return DecimalFormatProperties::getDefault().exponentSignAlwaysShown;
1151 }
1152 return fields->properties.exponentSignAlwaysShown;
1153}
1154
1155void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) {
1156 if (fields == nullptr) { return; }
1157 if (UBOOL_TO_BOOL(expSignAlways) == fields->properties.exponentSignAlwaysShown) { return; }
1158 fields->properties.exponentSignAlwaysShown = expSignAlways;
1159 touchNoError();
1160}
1161
1162int32_t DecimalFormat::getGroupingSize(void) const {
1163 int32_t groupingSize;
1164 // Not much we can do to report an error.
1165 if (fields == nullptr) {
1166 // Fallback to using the default instance of DecimalFormatProperties.
1167 groupingSize = DecimalFormatProperties::getDefault().groupingSize;
1168 } else {
1169 groupingSize = fields->properties.groupingSize;
1170 }
1171 if (groupingSize < 0) {
1172 return 0;
1173 }
1174 return groupingSize;
1175}
1176
1177void DecimalFormat::setGroupingSize(int32_t newValue) {
1178 if (fields == nullptr) { return; }
1179 if (newValue == fields->properties.groupingSize) { return; }
1180 fields->properties.groupingSize = newValue;
1181 touchNoError();
1182}
1183
1184int32_t DecimalFormat::getSecondaryGroupingSize(void) const {
1185 int32_t grouping2;
1186 // Not much we can do to report an error.
1187 if (fields == nullptr) {
1188 // Fallback to using the default instance of DecimalFormatProperties.
1189 grouping2 = DecimalFormatProperties::getDefault().secondaryGroupingSize;
1190 } else {
1191 grouping2 = fields->properties.secondaryGroupingSize;
1192 }
1193 if (grouping2 < 0) {
1194 return 0;
1195 }
1196 return grouping2;
1197}
1198
1199void DecimalFormat::setSecondaryGroupingSize(int32_t newValue) {
1200 if (fields == nullptr) { return; }
1201 if (newValue == fields->properties.secondaryGroupingSize) { return; }
1202 fields->properties.secondaryGroupingSize = newValue;
1203 touchNoError();
1204}
1205
1206int32_t DecimalFormat::getMinimumGroupingDigits() const {
1207 // Not much we can do to report an error.
1208 if (fields == nullptr) {
1209 // Fallback to using the default instance of DecimalFormatProperties.
1210 return DecimalFormatProperties::getDefault().minimumGroupingDigits;
1211 }
1212 return fields->properties.minimumGroupingDigits;
1213}
1214
1215void DecimalFormat::setMinimumGroupingDigits(int32_t newValue) {
1216 if (fields == nullptr) { return; }
1217 if (newValue == fields->properties.minimumGroupingDigits) { return; }
1218 fields->properties.minimumGroupingDigits = newValue;
1219 touchNoError();
1220}
1221
1222UBool DecimalFormat::isDecimalSeparatorAlwaysShown(void) const {
1223 // Not much we can do to report an error.
1224 if (fields == nullptr) {
1225 // Fallback to using the default instance of DecimalFormatProperties.
1226 return DecimalFormatProperties::getDefault().decimalSeparatorAlwaysShown;
1227 }
1228 return fields->properties.decimalSeparatorAlwaysShown;
1229}
1230
1231void DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) {
1232 if (fields == nullptr) { return; }
1233 if (UBOOL_TO_BOOL(newValue) == fields->properties.decimalSeparatorAlwaysShown) { return; }
1234 fields->properties.decimalSeparatorAlwaysShown = newValue;
1235 touchNoError();
1236}
1237
1238UBool DecimalFormat::isDecimalPatternMatchRequired(void) const {
1239 // Not much we can do to report an error.
1240 if (fields == nullptr) {
1241 // Fallback to using the default instance of DecimalFormatProperties.
1242 return DecimalFormatProperties::getDefault().decimalPatternMatchRequired;
1243 }
1244 return fields->properties.decimalPatternMatchRequired;
1245}
1246
1247void DecimalFormat::setDecimalPatternMatchRequired(UBool newValue) {
1248 if (fields == nullptr) { return; }
1249 if (UBOOL_TO_BOOL(newValue) == fields->properties.decimalPatternMatchRequired) { return; }
1250 fields->properties.decimalPatternMatchRequired = newValue;
1251 touchNoError();
1252}
1253
1254UBool DecimalFormat::isParseNoExponent() const {
1255 // Not much we can do to report an error.
1256 if (fields == nullptr) {
1257 // Fallback to using the default instance of DecimalFormatProperties.
1258 return DecimalFormatProperties::getDefault().parseNoExponent;
1259 }
1260 return fields->properties.parseNoExponent;
1261}
1262
1263void DecimalFormat::setParseNoExponent(UBool value) {
1264 if (fields == nullptr) { return; }
1265 if (UBOOL_TO_BOOL(value) == fields->properties.parseNoExponent) { return; }
1266 fields->properties.parseNoExponent = value;
1267 touchNoError();
1268}
1269
1270UBool DecimalFormat::isParseCaseSensitive() const {
1271 // Not much we can do to report an error.
1272 if (fields == nullptr) {
1273 // Fallback to using the default instance of DecimalFormatProperties.
1274 return DecimalFormatProperties::getDefault().parseCaseSensitive;
1275 }
1276 return fields->properties.parseCaseSensitive;
1277}
1278
1279void DecimalFormat::setParseCaseSensitive(UBool value) {
1280 if (fields == nullptr) { return; }
1281 if (UBOOL_TO_BOOL(value) == fields->properties.parseCaseSensitive) { return; }
1282 fields->properties.parseCaseSensitive = value;
1283 touchNoError();
1284}
1285
1286UBool DecimalFormat::isFormatFailIfMoreThanMaxDigits() const {
1287 // Not much we can do to report an error.
1288 if (fields == nullptr) {
1289 // Fallback to using the default instance of DecimalFormatProperties.
1290 return DecimalFormatProperties::getDefault().formatFailIfMoreThanMaxDigits;
1291 }
1292 return fields->properties.formatFailIfMoreThanMaxDigits;
1293}
1294
1295void DecimalFormat::setFormatFailIfMoreThanMaxDigits(UBool value) {
1296 if (fields == nullptr) { return; }
1297 if (UBOOL_TO_BOOL(value) == fields->properties.formatFailIfMoreThanMaxDigits) { return; }
1298 fields->properties.formatFailIfMoreThanMaxDigits = value;
1299 touchNoError();
1300}
1301
1302UnicodeString& DecimalFormat::toPattern(UnicodeString& result) const {
1303 if (fields == nullptr) {
1304 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1305 result.setToBogus();
1306 return result;
1307 }
1308 // Pull some properties from exportedProperties and others from properties
1309 // to keep affix patterns intact. In particular, pull rounding properties
1310 // so that CurrencyUsage is reflected properly.
1311 // TODO: Consider putting this logic in number_patternstring.cpp instead.
1312 ErrorCode localStatus;
1313 DecimalFormatProperties tprops(fields->properties);
1314 bool useCurrency = (
1315 !tprops.currency.isNull() ||
1316 !tprops.currencyPluralInfo.fPtr.isNull() ||
1317 !tprops.currencyUsage.isNull() ||
1318 AffixUtils::hasCurrencySymbols(tprops.positivePrefixPattern, localStatus) ||
1319 AffixUtils::hasCurrencySymbols(tprops.positiveSuffixPattern, localStatus) ||
1320 AffixUtils::hasCurrencySymbols(tprops.negativePrefixPattern, localStatus) ||
1321 AffixUtils::hasCurrencySymbols(tprops.negativeSuffixPattern, localStatus));
1322 if (useCurrency) {
1323 tprops.minimumFractionDigits = fields->exportedProperties.minimumFractionDigits;
1324 tprops.maximumFractionDigits = fields->exportedProperties.maximumFractionDigits;
1325 tprops.roundingIncrement = fields->exportedProperties.roundingIncrement;
1326 }
1327 result = PatternStringUtils::propertiesToPatternString(tprops, localStatus);
1328 return result;
1329}
1330
1331UnicodeString& DecimalFormat::toLocalizedPattern(UnicodeString& result) const {
1332 if (fields == nullptr) {
1333 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1334 result.setToBogus();
1335 return result;
1336 }
1337 ErrorCode localStatus;
1338 result = toPattern(result);
1339 result = PatternStringUtils::convertLocalized(result, *fields->symbols, true, localStatus);
1340 return result;
1341}
1342
1343void DecimalFormat::applyPattern(const UnicodeString& pattern, UParseError&, UErrorCode& status) {
1344 // TODO: What is parseError for?
1345 applyPattern(pattern, status);
1346}
1347
1348void DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) {
1349 // don't overwrite status if it's already a failure.
1350 if (U_FAILURE(status)) { return; }
1351 if (fields == nullptr) {
1352 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1353 status = U_MEMORY_ALLOCATION_ERROR;
1354 return;
1355 }
1356 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_NEVER, status);
1357 touch(status);
1358}
1359
1360void DecimalFormat::applyLocalizedPattern(const UnicodeString& localizedPattern, UParseError&,
1361 UErrorCode& status) {
1362 // TODO: What is parseError for?
1363 applyLocalizedPattern(localizedPattern, status);
1364}
1365
1366void DecimalFormat::applyLocalizedPattern(const UnicodeString& localizedPattern, UErrorCode& status) {
1367 // don't overwrite status if it's already a failure.
1368 if (U_FAILURE(status)) { return; }
1369 if (fields == nullptr) {
1370 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1371 status = U_MEMORY_ALLOCATION_ERROR;
1372 return;
1373 }
1374 UnicodeString pattern = PatternStringUtils::convertLocalized(
1375 localizedPattern, *fields->symbols, false, status);
1376 applyPattern(pattern, status);
1377}
1378
1379void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) {
1380 if (fields == nullptr) { return; }
1381 if (newValue == fields->properties.maximumIntegerDigits) { return; }
1382 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1383 int32_t min = fields->properties.minimumIntegerDigits;
1384 if (min >= 0 && min > newValue) {
1385 fields->properties.minimumIntegerDigits = newValue;
1386 }
1387 fields->properties.maximumIntegerDigits = newValue;
1388 touchNoError();
1389}
1390
1391void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) {
1392 if (fields == nullptr) { return; }
1393 if (newValue == fields->properties.minimumIntegerDigits) { return; }
1394 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1395 int32_t max = fields->properties.maximumIntegerDigits;
1396 if (max >= 0 && max < newValue) {
1397 fields->properties.maximumIntegerDigits = newValue;
1398 }
1399 fields->properties.minimumIntegerDigits = newValue;
1400 touchNoError();
1401}
1402
1403void DecimalFormat::setMaximumFractionDigits(int32_t newValue) {
1404 if (fields == nullptr) { return; }
1405 if (newValue == fields->properties.maximumFractionDigits) { return; }
1406 // cap for backward compatibility, limit to 340 <rdar://problem/50113359>
1407 if (newValue > 340) {
1408 newValue = 340;
1409 }
1410 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1411 int32_t min = fields->properties.minimumFractionDigits;
1412 if (min >= 0 && min > newValue) {
1413 fields->properties.minimumFractionDigits = newValue;
1414 }
1415 fields->properties.maximumFractionDigits = newValue;
1416 touchNoError();
1417}
1418
1419void DecimalFormat::setMinimumFractionDigits(int32_t newValue) {
1420 if (fields == nullptr) { return; }
1421 if (newValue == fields->properties.minimumFractionDigits) { return; }
1422 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1423 int32_t max = fields->properties.maximumFractionDigits;
1424 if (max >= 0 && max < newValue) {
1425 fields->properties.maximumFractionDigits = newValue;
1426 }
1427 fields->properties.minimumFractionDigits = newValue;
1428 touchNoError();
1429}
1430
1431int32_t DecimalFormat::getMinimumSignificantDigits() const {
1432 // Not much we can do to report an error.
1433 if (fields == nullptr) {
1434 // Fallback to using the default instance of DecimalFormatProperties.
1435 return DecimalFormatProperties::getDefault().minimumSignificantDigits;
1436 }
1437 return fields->exportedProperties.minimumSignificantDigits;
1438}
1439
1440int32_t DecimalFormat::getMaximumSignificantDigits() const {
1441 // Not much we can do to report an error.
1442 if (fields == nullptr) {
1443 // Fallback to using the default instance of DecimalFormatProperties.
1444 return DecimalFormatProperties::getDefault().maximumSignificantDigits;
1445 }
1446 return fields->exportedProperties.maximumSignificantDigits;
1447}
1448
1449void DecimalFormat::setMinimumSignificantDigits(int32_t value) {
1450 if (fields == nullptr) { return; }
1451 if (value == fields->properties.minimumSignificantDigits) { return; }
1452 int32_t max = fields->properties.maximumSignificantDigits;
1453 if (max >= 0 && max < value) {
1454 fields->properties.maximumSignificantDigits = value;
1455 }
1456 fields->properties.minimumSignificantDigits = value;
1457 touchNoError();
1458}
1459
1460void DecimalFormat::setMaximumSignificantDigits(int32_t value) {
1461 if (fields == nullptr) { return; }
1462 if (value == fields->properties.maximumSignificantDigits) { return; }
1463 int32_t min = fields->properties.minimumSignificantDigits;
1464 if (min >= 0 && min > value) {
1465 fields->properties.minimumSignificantDigits = value;
1466 }
1467 fields->properties.maximumSignificantDigits = value;
1468 touchNoError();
1469}
1470
1471UBool DecimalFormat::areSignificantDigitsUsed() const {
1472 const DecimalFormatProperties* dfp;
1473 // Not much we can do to report an error.
1474 if (fields == nullptr) {
1475 // Fallback to using the default instance of DecimalFormatProperties.
1476 dfp = &(DecimalFormatProperties::getDefault());
1477 } else {
1478 dfp = &fields->properties;
1479 }
1480 return dfp->minimumSignificantDigits != -1 || dfp->maximumSignificantDigits != -1;
1481}
1482
1483void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) {
1484 if (fields == nullptr) { return; }
1485
1486 // These are the default values from the old implementation.
1487 if (useSignificantDigits) {
1488 if (fields->properties.minimumSignificantDigits != -1 ||
1489 fields->properties.maximumSignificantDigits != -1) {
1490 return;
1491 }
1492 } else {
1493 if (fields->properties.minimumSignificantDigits == -1 &&
1494 fields->properties.maximumSignificantDigits == -1) {
1495 return;
1496 }
1497 }
1498 int32_t minSig = useSignificantDigits ? 1 : -1;
1499 int32_t maxSig = useSignificantDigits ? 6 : -1;
1500 fields->properties.minimumSignificantDigits = minSig;
1501 fields->properties.maximumSignificantDigits = maxSig;
1502 touchNoError();
1503}
1504
1505// Group-set several settings used for numbers in date formats. Apple rdar://50064762
1506// Equivalent to:
1507// setGroupingUsed(FALSE);
1508// setDecimalSeparatorAlwaysShown(FALSE);
1509// setParseIntegerOnly(TRUE);
1510// setMinimumFractionDigits(0);
1511void DecimalFormat::setDateSettings(void) {
1512 if (fields == nullptr) {
1513 return;
1514 }
1515 UBool didChange = FALSE;
1516
1517 if (fields->properties.groupingUsed) {
1518 NumberFormat::setGroupingUsed(FALSE); // to set field for compatibility
1519 fields->properties.groupingUsed = false;
1520 didChange = TRUE;
1521 }
1522
1523 if (fields->properties.decimalSeparatorAlwaysShown) {
1524 fields->properties.decimalSeparatorAlwaysShown = false;
1525 didChange = TRUE;
1526 }
1527
1528 if (!fields->properties.parseIntegerOnly) {
1529 NumberFormat::setParseIntegerOnly(TRUE); // to set field for compatibility
1530 fields->properties.parseIntegerOnly = true;
1531 didChange = TRUE;
1532 }
1533
1534 if (fields->properties.minimumFractionDigits != 0) {
1535 fields->properties.minimumFractionDigits = 0;
1536 didChange = TRUE;
1537 }
1538
1539 if (didChange) {
1540 touchNoError();
1541 }
1542}
1543
1544void DecimalFormat::setCurrency(const char16_t* theCurrency, UErrorCode& ec) {
1545 // don't overwrite ec if it's already a failure.
1546 if (U_FAILURE(ec)) { return; }
1547 if (fields == nullptr) {
1548 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1549 ec = U_MEMORY_ALLOCATION_ERROR;
1550 return;
1551 }
1552 // <rdar://problem/49544607> Restore behavior in which empty currency sets locale default
1553 UChar localeCurr[4];
1554 if (theCurrency==nullptr || theCurrency[0]==0) {
1555 UErrorCode getCurrStatus = U_ZERO_ERROR;
1556 int32_t currLen = ucurr_forLocale(fields->symbols->getLocale().getName(), localeCurr, UPRV_LENGTHOF(localeCurr), &getCurrStatus);
1557 if (U_SUCCESS(getCurrStatus) && currLen==3) {
1558 localeCurr[3] = 0;
1559 theCurrency = localeCurr;
1560 }
1561 }
1562 //
1563 CurrencyUnit currencyUnit(theCurrency, ec);
1564 if (U_FAILURE(ec)) { return; }
1565 if (!fields->properties.currency.isNull() && fields->properties.currency.getNoError() == currencyUnit) {
1566 return;
1567 }
1568 NumberFormat::setCurrency(theCurrency, ec); // to set field for compatibility
1569 fields->properties.currency = currencyUnit;
1570 // TODO: Set values in fields->symbols, too?
1571 touchNoError();
1572}
1573
1574void DecimalFormat::setCurrency(const char16_t* theCurrency) {
1575 ErrorCode localStatus;
1576 setCurrency(theCurrency, localStatus);
1577}
1578
1579void DecimalFormat::setCurrencyUsage(UCurrencyUsage newUsage, UErrorCode* ec) {
1580 // don't overwrite ec if it's already a failure.
1581 if (U_FAILURE(*ec)) { return; }
1582 if (fields == nullptr) {
1583 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1584 *ec = U_MEMORY_ALLOCATION_ERROR;
1585 return;
1586 }
1587 if (!fields->properties.currencyUsage.isNull() && newUsage == fields->properties.currencyUsage.getNoError()) {
1588 return;
1589 }
1590 fields->properties.currencyUsage = newUsage;
1591 touch(*ec);
1592}
1593
1594UCurrencyUsage DecimalFormat::getCurrencyUsage() const {
1595 // CurrencyUsage is not exported, so we have to get it from the input property bag.
1596 // TODO: Should we export CurrencyUsage instead?
1597 if (fields == nullptr || fields->properties.currencyUsage.isNull()) {
1598 return UCURR_USAGE_STANDARD;
1599 }
1600 return fields->properties.currencyUsage.getNoError();
1601}
1602
1603void
1604DecimalFormat::formatToDecimalQuantity(double number, DecimalQuantity& output, UErrorCode& status) const {
1605 // don't overwrite status if it's already a failure.
1606 if (U_FAILURE(status)) { return; }
1607 if (fields == nullptr) {
1608 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1609 status = U_MEMORY_ALLOCATION_ERROR;
1610 return;
1611 }
1612 fields->formatter.formatDouble(number, status).getDecimalQuantity(output, status);
1613}
1614
1615void DecimalFormat::formatToDecimalQuantity(const Formattable& number, DecimalQuantity& output,
1616 UErrorCode& status) const {
1617 // don't overwrite status if it's already a failure.
1618 if (U_FAILURE(status)) { return; }
1619 if (fields == nullptr) {
1620 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1621 status = U_MEMORY_ALLOCATION_ERROR;
1622 return;
1623 }
1624 UFormattedNumberData obj;
1625 number.populateDecimalQuantity(obj.quantity, status);
1626 fields->formatter.formatImpl(&obj, status);
1627 output = std::move(obj.quantity);
1628}
1629
1630const number::LocalizedNumberFormatter* DecimalFormat::toNumberFormatter(UErrorCode& status) const {
1631 // We sometimes need to return nullptr here (see ICU-20380)
1632 if (U_FAILURE(status)) { return nullptr; }
1633 if (fields == nullptr) {
1634 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1635 status = U_MEMORY_ALLOCATION_ERROR;
1636 return nullptr;
1637 }
1638 return &fields->formatter;
1639}
1640
1641// Apple <rdar://problem/49955427>
1642void DecimalFormat::setDFSShallowCopy(UBool shallow) {
1643 if (fields != nullptr) {
1644 fields->formatter.setDFSShallowCopy(shallow);
1645 }
1646}
1647
1648/** Rebuilds the formatter object from the property bag. */
1649void DecimalFormat::touch(UErrorCode& status) {
1650 if (U_FAILURE(status)) {
1651 return;
1652 }
1653 if (fields == nullptr) {
1654 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1655 // For regular construction, the caller should have checked the status variable for errors.
1656 // For copy construction, there is unfortunately nothing to report the error, so we need to guard against
1657 // this possible bad state here and set the status to an error.
1658 status = U_MEMORY_ALLOCATION_ERROR;
1659 return;
1660 }
1661
1662 // In C++, fields->symbols is the source of truth for the locale.
1663 Locale locale = fields->symbols->getLocale();
1664
1665 // Note: The formatter is relatively cheap to create, and we need it to populate fields->exportedProperties,
1666 // so automatically recompute it here. The parser is a bit more expensive and is not needed until the
1667 // parse method is called, so defer that until needed.
1668 // TODO: Only update the pieces that changed instead of re-computing the whole formatter?
1669
1670 // Since memory has already been allocated for the formatter, we can move assign a stack-allocated object
1671 // and don't need to call new. (Which is slower and could possibly fail).
1672 fields->formatter = NumberPropertyMapper::create(
1673 fields->properties, *fields->symbols, fields->warehouse, fields->exportedProperties, status
1674 ).locale(locale);
1675
1676 // Do this after fields->exportedProperties are set up
1677 setupFastFormat();
1678
1679 // Delete the parsers if they were made previously
1680 delete fields->atomicParser.exchange(nullptr);
1681 delete fields->atomicCurrencyParser.exchange(nullptr);
1682
1683 // In order for the getters to work, we need to populate some fields in NumberFormat.
1684 const UChar* newCurr = u"";
1685 CurrencyUnit currency = fields->exportedProperties.currency.get(status);
1686 if (U_SUCCESS(status)) {
1687 // currency.getISOCurrency() is an inline that just returns a pointer to currency's
1688 // internal field char16_t isoCode[4], cannot be NULL if currency is valid:
1689 newCurr = (const UChar*)currency.getISOCurrency();
1690 // NumberFormat::getCurrency() just returns a pointer to the superclass's
1691 // internal field char16_t fCurrency[4], cannot be NULL:
1692 const UChar* haveCurr = (const UChar*)NumberFormat::getCurrency();
1693 if (u_strcmp(newCurr,u"XXX")==0 && u_strcmp(haveCurr,u"XXX")!=0) { // <rdar://51985640>
1694 // We did not get here via DecimalFormat::setCurrency(u"XXX", ...)
1695 newCurr = u"";
1696 }
1697 }
1698 NumberFormat::setCurrency(newCurr, status);
1699 NumberFormat::setMaximumIntegerDigits(fields->exportedProperties.maximumIntegerDigits);
1700 NumberFormat::setMinimumIntegerDigits(fields->exportedProperties.minimumIntegerDigits);
1701 NumberFormat::setMaximumFractionDigits(fields->exportedProperties.maximumFractionDigits);
1702 NumberFormat::setMinimumFractionDigits(fields->exportedProperties.minimumFractionDigits);
1703 // fImpl->properties, not fields->exportedProperties, since this information comes from the pattern:
1704 NumberFormat::setGroupingUsed(fields->properties.groupingUsed);
1705}
1706
1707void DecimalFormat::touchNoError() {
1708 UErrorCode localStatus = U_ZERO_ERROR;
1709 touch(localStatus);
1710}
1711
1712void DecimalFormat::setPropertiesFromPattern(const UnicodeString& pattern, int32_t ignoreRounding,
1713 UErrorCode& status) {
1714 if (U_SUCCESS(status)) {
1715 // Cast workaround to get around putting the enum in the public header file
1716 auto actualIgnoreRounding = static_cast<IgnoreRounding>(ignoreRounding);
1717 PatternParser::parseToExistingProperties(pattern, fields->properties, actualIgnoreRounding, status);
1718 }
1719}
1720
1721const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& status) const {
1722 // TODO: Move this into umutex.h? (similar logic also in numrange_fluent.cpp)
1723 // See ICU-20146
1724
1725 if (U_FAILURE(status)) {
1726 return nullptr;
1727 }
1728
1729 // First try to get the pre-computed parser
1730 auto* ptr = fields->atomicParser.load();
1731 if (ptr != nullptr) {
1732 return ptr;
1733 }
1734
1735 // Try computing the parser on our own
1736 auto* temp = NumberParserImpl::createParserFromProperties(fields->properties, *fields->symbols, false, status);
1737 if (U_FAILURE(status)) {
1738 return nullptr;
1739 }
1740 if (temp == nullptr) {
1741 status = U_MEMORY_ALLOCATION_ERROR;
1742 return nullptr;
1743 }
1744
1745 // Note: ptr starts as nullptr; during compare_exchange,
1746 // it is set to what is actually stored in the atomic
1747 // if another thread beat us to computing the parser object.
1748 auto* nonConstThis = const_cast<DecimalFormat*>(this);
1749 if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, temp)) {
1750 // Another thread beat us to computing the parser
1751 delete temp;
1752 return ptr;
1753 } else {
1754 // Our copy of the parser got stored in the atomic
1755 return temp;
1756 }
1757}
1758
1759const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorCode& status) const {
1760 if (U_FAILURE(status)) { return nullptr; }
1761
1762 // First try to get the pre-computed parser
1763 auto* ptr = fields->atomicCurrencyParser.load();
1764 if (ptr != nullptr) {
1765 return ptr;
1766 }
1767
1768 // Try computing the parser on our own
1769 auto* temp = NumberParserImpl::createParserFromProperties(fields->properties, *fields->symbols, true, status);
1770 if (temp == nullptr) {
1771 status = U_MEMORY_ALLOCATION_ERROR;
1772 // although we may still dereference, call sites should be guarded
1773 }
1774
1775 // Note: ptr starts as nullptr; during compare_exchange, it is set to what is actually stored in the
1776 // atomic if another thread beat us to computing the parser object.
1777 auto* nonConstThis = const_cast<DecimalFormat*>(this);
1778 if (!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, temp)) {
1779 // Another thread beat us to computing the parser
1780 delete temp;
1781 return ptr;
1782 } else {
1783 // Our copy of the parser got stored in the atomic
1784 return temp;
1785 }
1786}
1787
1788void
1789DecimalFormat::fieldPositionHelper(const number::FormattedNumber& formatted, FieldPosition& fieldPosition,
1790 int32_t offset, UErrorCode& status) {
1791 if (U_FAILURE(status)) { return; }
1792 // always return first occurrence:
1793 fieldPosition.setBeginIndex(0);
1794 fieldPosition.setEndIndex(0);
1795 bool found = formatted.nextFieldPosition(fieldPosition, status);
1796 if (found && offset != 0) {
1797 FieldPositionOnlyHandler fpoh(fieldPosition);
1798 fpoh.shiftLast(offset);
1799 }
1800}
1801
1802void
1803DecimalFormat::fieldPositionIteratorHelper(const number::FormattedNumber& formatted, FieldPositionIterator* fpi,
1804 int32_t offset, UErrorCode& status) {
1805 if (U_SUCCESS(status) && (fpi != nullptr)) {
1806 FieldPositionIteratorHandler fpih(fpi, status);
1807 fpih.setShift(offset);
1808 formatted.getAllFieldPositionsImpl(fpih, status);
1809 }
1810}
1811
1812// To debug fast-format, change void(x) to printf(x)
1813#define trace(x) void(x)
1814
1815void DecimalFormat::setupFastFormat() {
1816 // Check the majority of properties:
1817 if (!fields->properties.equalsDefaultExceptFastFormat()) {
1818 trace("no fast format: equality\n");
1819 fields->canUseFastFormat = false;
1820 return;
1821 }
1822
1823 // Now check the remaining properties.
1824 // Nontrivial affixes:
1825 UBool trivialPP = fields->properties.positivePrefixPattern.isEmpty();
1826 UBool trivialPS = fields->properties.positiveSuffixPattern.isEmpty();
1827 UBool trivialNP = fields->properties.negativePrefixPattern.isBogus() || (
1828 fields->properties.negativePrefixPattern.length() == 1 &&
1829 fields->properties.negativePrefixPattern.charAt(0) == u'-');
1830 UBool trivialNS = fields->properties.negativeSuffixPattern.isEmpty();
1831 if (!trivialPP || !trivialPS || !trivialNP || !trivialNS) {
1832 trace("no fast format: affixes\n");
1833 fields->canUseFastFormat = false;
1834 return;
1835 }
1836
1837 // Grouping (secondary grouping is forbidden in equalsDefaultExceptFastFormat):
1838 bool groupingUsed = fields->properties.groupingUsed;
1839 int32_t groupingSize = fields->properties.groupingSize;
1840 bool unusualGroupingSize = groupingSize > 0 && groupingSize != 3;
1841 const UnicodeString& groupingString = fields->symbols->getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol);
1842 if (groupingUsed && (unusualGroupingSize || groupingString.length() != 1)) {
1843 trace("no fast format: grouping\n");
1844 fields->canUseFastFormat = false;
1845 return;
1846 }
1847
1848 // Integer length:
1849 int32_t minInt = fields->exportedProperties.minimumIntegerDigits;
1850 int32_t maxInt = fields->exportedProperties.maximumIntegerDigits;
1851 // Fastpath supports up to only 10 digits (length of INT32_MIN)
1852 if (minInt > 10) {
1853 trace("no fast format: integer\n");
1854 fields->canUseFastFormat = false;
1855 return;
1856 }
1857
1858 // Fraction length (no fraction part allowed in fast path):
1859 int32_t minFrac = fields->exportedProperties.minimumFractionDigits;
1860 if (minFrac > 0) {
1861 trace("no fast format: fraction\n");
1862 fields->canUseFastFormat = false;
1863 return;
1864 }
1865
1866 // Other symbols:
1867 const UnicodeString& minusSignString = fields->symbols->getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
1868 UChar32 codePointZero = fields->symbols->getCodePointZero();
1869 if (minusSignString.length() != 1 || U16_LENGTH(codePointZero) != 1) {
1870 trace("no fast format: symbols\n");
1871 fields->canUseFastFormat = false;
1872 return;
1873 }
1874
1875 // Good to go!
1876 trace("can use fast format!\n");
1877 fields->canUseFastFormat = true;
1878 fields->fastData.cpZero = static_cast<char16_t>(codePointZero);
1879 fields->fastData.cpGroupingSeparator = groupingUsed && groupingSize == 3 ? groupingString.charAt(0) : 0;
1880 fields->fastData.cpMinusSign = minusSignString.charAt(0);
1881 fields->fastData.minInt = (minInt < 0 || minInt > 127) ? 0 : static_cast<int8_t>(minInt);
1882 fields->fastData.maxInt = (maxInt < 0 || maxInt > 127) ? 127 : static_cast<int8_t>(maxInt);
1883}
1884
1885bool DecimalFormat::fastFormatDouble(double input, UnicodeString& output) const {
1886 if (!fields->canUseFastFormat) {
1887 return false;
1888 }
1889 if (std::isnan(input)
1890 || std::trunc(input) != input
1891 || input <= INT32_MIN
1892 || input > INT32_MAX) {
1893 return false;
1894 }
1895 doFastFormatInt32(static_cast<int32_t>(input), std::signbit(input), output);
1896 return true;
1897}
1898
1899bool DecimalFormat::fastFormatInt64(int64_t input, UnicodeString& output) const {
1900 if (!fields->canUseFastFormat) {
1901 return false;
1902 }
1903 if (input <= INT32_MIN || input > INT32_MAX) {
1904 return false;
1905 }
1906 doFastFormatInt32(static_cast<int32_t>(input), input < 0, output);
1907 return true;
1908}
1909
1910void DecimalFormat::doFastFormatInt32(int32_t input, bool isNegative, UnicodeString& output) const {
1911 U_ASSERT(fields->canUseFastFormat);
1912 if (isNegative) {
1913 output.append(fields->fastData.cpMinusSign);
1914 U_ASSERT(input != INT32_MIN); // handled by callers
1915 input = -input;
1916 }
1917 // Cap at int32_t to make the buffer small and operations fast.
1918 // Longest string: "2,147,483,648" (13 chars in length)
1919 static constexpr int32_t localCapacity = 13;
1920 char16_t localBuffer[localCapacity];
1921 char16_t* ptr = localBuffer + localCapacity;
1922 int8_t group = 0;
1923 int8_t minInt = (fields->fastData.minInt < 1)? 1: fields->fastData.minInt; // rdar://54569257
1924 for (int8_t i = 0; i < fields->fastData.maxInt && (input != 0 || i < minInt); i++) {
1925 if (group++ == 3 && fields->fastData.cpGroupingSeparator != 0) {
1926 *(--ptr) = fields->fastData.cpGroupingSeparator;
1927 group = 1;
1928 }
1929 std::div_t res = std::div(input, 10);
1930 *(--ptr) = static_cast<char16_t>(fields->fastData.cpZero + res.rem);
1931 input = res.quot;
1932 }
1933 int32_t len = localCapacity - static_cast<int32_t>(ptr - localBuffer);
1934 output.append(ptr, len);
1935}
1936
1937
1938#endif /* #if !UCONFIG_NO_FORMATTING */