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