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 UFormattedNumberData output;
523 output.quantity.setToDouble(number);
524 fields->formatter.formatImpl(&output, localStatus);
525 fieldPositionHelper(output, pos, appendTo.length(), localStatus);
526 auto appendable = UnicodeStringAppendable(appendTo);
527 output.appendTo(appendable, localStatus);
528 return appendTo;
529}
530
531UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, FieldPosition& pos,
532 UErrorCode& status) const {
533 if (U_FAILURE(status)) {
534 return appendTo; // don't overwrite status if it's already a failure.
535 }
536 if (fields == nullptr) {
537 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
538 status = U_MEMORY_ALLOCATION_ERROR;
539 appendTo.setToBogus();
540 return appendTo;
541 }
542 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatDouble(number, appendTo)) {
543 return appendTo;
544 }
545 UFormattedNumberData output;
546 output.quantity.setToDouble(number);
547 fields->formatter.formatImpl(&output, status);
548 fieldPositionHelper(output, pos, appendTo.length(), status);
549 auto appendable = UnicodeStringAppendable(appendTo);
550 output.appendTo(appendable, status);
551 return appendTo;
552}
553
554UnicodeString&
555DecimalFormat::format(double number, UnicodeString& appendTo, FieldPositionIterator* posIter,
556 UErrorCode& status) const {
557 if (U_FAILURE(status)) {
558 return appendTo; // don't overwrite status if it's already a failure.
559 }
560 if (fields == nullptr) {
561 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
562 status = U_MEMORY_ALLOCATION_ERROR;
563 appendTo.setToBogus();
564 return appendTo;
565 }
566 if (posIter == nullptr && fastFormatDouble(number, appendTo)) {
567 return appendTo;
568 }
569 UFormattedNumberData output;
570 output.quantity.setToDouble(number);
571 fields->formatter.formatImpl(&output, status);
572 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
573 auto appendable = UnicodeStringAppendable(appendTo);
574 output.appendTo(appendable, status);
575 return appendTo;
576}
577
578UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos) const {
579 return format(static_cast<int64_t> (number), appendTo, pos);
580}
581
582UnicodeString& DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPosition& pos,
583 UErrorCode& status) const {
584 return format(static_cast<int64_t> (number), appendTo, pos, status);
585}
586
587UnicodeString&
588DecimalFormat::format(int32_t number, UnicodeString& appendTo, FieldPositionIterator* posIter,
589 UErrorCode& status) const {
590 return format(static_cast<int64_t> (number), appendTo, posIter, status);
591}
592
593UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos) const {
594 if (fields == nullptr) {
595 appendTo.setToBogus();
596 return appendTo;
597 }
598 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) {
599 return appendTo;
600 }
601 UErrorCode localStatus = U_ZERO_ERROR;
602 UFormattedNumberData output;
603 output.quantity.setToLong(number);
604 fields->formatter.formatImpl(&output, localStatus);
605 fieldPositionHelper(output, pos, appendTo.length(), localStatus);
606 auto appendable = UnicodeStringAppendable(appendTo);
607 output.appendTo(appendable, localStatus);
608 return appendTo;
609}
610
611UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos,
612 UErrorCode& status) const {
613 if (U_FAILURE(status)) {
614 return appendTo; // don't overwrite status if it's already a failure.
615 }
616 if (fields == nullptr) {
617 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
618 status = U_MEMORY_ALLOCATION_ERROR;
619 appendTo.setToBogus();
620 return appendTo;
621 }
622 if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) {
623 return appendTo;
624 }
625 UFormattedNumberData output;
626 output.quantity.setToLong(number);
627 fields->formatter.formatImpl(&output, status);
628 fieldPositionHelper(output, pos, appendTo.length(), status);
629 auto appendable = UnicodeStringAppendable(appendTo);
630 output.appendTo(appendable, status);
631 return appendTo;
632}
633
634UnicodeString&
635DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPositionIterator* posIter,
636 UErrorCode& status) const {
637 if (U_FAILURE(status)) {
638 return appendTo; // don't overwrite status if it's already a failure.
639 }
640 if (fields == nullptr) {
641 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
642 status = U_MEMORY_ALLOCATION_ERROR;
643 appendTo.setToBogus();
644 return appendTo;
645 }
646 if (posIter == nullptr && fastFormatInt64(number, appendTo)) {
647 return appendTo;
648 }
649 UFormattedNumberData output;
650 output.quantity.setToLong(number);
651 fields->formatter.formatImpl(&output, status);
652 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
653 auto appendable = UnicodeStringAppendable(appendTo);
654 output.appendTo(appendable, status);
655 return appendTo;
656}
657
658UnicodeString&
659DecimalFormat::format(StringPiece number, UnicodeString& appendTo, FieldPositionIterator* posIter,
660 UErrorCode& status) const {
661 if (U_FAILURE(status)) {
662 return appendTo; // don't overwrite status if it's already a failure.
663 }
664 if (fields == nullptr) {
665 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
666 status = U_MEMORY_ALLOCATION_ERROR;
667 appendTo.setToBogus();
668 return appendTo;
669 }
670 UFormattedNumberData output;
671 output.quantity.setToDecNumber(number, status);
672 fields->formatter.formatImpl(&output, status);
673 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
674 auto appendable = UnicodeStringAppendable(appendTo);
675 output.appendTo(appendable, status);
676 return appendTo;
677}
678
679UnicodeString& DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo,
680 FieldPositionIterator* posIter, UErrorCode& status) const {
681 if (U_FAILURE(status)) {
682 return appendTo; // don't overwrite status if it's already a failure.
683 }
684 if (fields == nullptr) {
685 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
686 status = U_MEMORY_ALLOCATION_ERROR;
687 appendTo.setToBogus();
688 return appendTo;
689 }
690 UFormattedNumberData output;
691 output.quantity = number;
692 fields->formatter.formatImpl(&output, status);
693 fieldPositionIteratorHelper(output, posIter, appendTo.length(), status);
694 auto appendable = UnicodeStringAppendable(appendTo);
695 output.appendTo(appendable, status);
696 return appendTo;
697}
698
699UnicodeString&
700DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, FieldPosition& pos,
701 UErrorCode& status) const {
702 if (U_FAILURE(status)) {
703 return appendTo; // don't overwrite status if it's already a failure.
704 }
705 if (fields == nullptr) {
706 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
707 status = U_MEMORY_ALLOCATION_ERROR;
708 appendTo.setToBogus();
709 return appendTo;
710 }
711 UFormattedNumberData output;
712 output.quantity = number;
713 fields->formatter.formatImpl(&output, status);
714 fieldPositionHelper(output, pos, appendTo.length(), status);
715 auto appendable = UnicodeStringAppendable(appendTo);
716 output.appendTo(appendable, status);
717 return appendTo;
718}
719
720void DecimalFormat::parse(const UnicodeString& text, Formattable& output,
721 ParsePosition& parsePosition) const {
722 if (fields == nullptr) {
723 return;
724 }
725 if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) {
726 if (parsePosition.getIndex() == text.length()) {
727 // If there is nothing to parse, it is an error
728 parsePosition.setErrorIndex(parsePosition.getIndex());
729 }
730 return;
731 }
732
733 ErrorCode status;
734 ParsedNumber result;
735 // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the
736 // parseCurrency method (backwards compatibility)
737 int32_t startIndex = parsePosition.getIndex();
738 const NumberParserImpl* parser = getParser(status);
739 if (U_FAILURE(status)) {
740 return; // unfortunately no way to report back the error.
741 }
742 parser->parse(text, startIndex, true, result, status);
743 if (U_FAILURE(status)) {
744 return; // unfortunately no way to report back the error.
745 }
746 // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here?
747 if (result.success()) {
748 parsePosition.setIndex(result.charEnd);
749 result.populateFormattable(output, parser->getParseFlags());
750 } else {
751 parsePosition.setErrorIndex(startIndex + result.charEnd);
752 }
753}
754
755CurrencyAmount* DecimalFormat::parseCurrency(const UnicodeString& text, ParsePosition& parsePosition) const {
756 if (fields == nullptr) {
757 return nullptr;
758 }
759 if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) {
760 return nullptr;
761 }
762
763 ErrorCode status;
764 ParsedNumber result;
765 // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the
766 // parseCurrency method (backwards compatibility)
767 int32_t startIndex = parsePosition.getIndex();
768 const NumberParserImpl* parser = getCurrencyParser(status);
769 if (U_FAILURE(status)) {
770 return nullptr;
771 }
772 parser->parse(text, startIndex, true, result, status);
773 if (U_FAILURE(status)) {
774 return nullptr;
775 }
776 // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here?
777 if (result.success()) {
778 parsePosition.setIndex(result.charEnd);
779 Formattable formattable;
780 result.populateFormattable(formattable, parser->getParseFlags());
781 LocalPointer<CurrencyAmount> currencyAmount(
782 new CurrencyAmount(formattable, result.currencyCode, status), status);
783 if (U_FAILURE(status)) {
784 return nullptr;
785 }
786 return currencyAmount.orphan();
787 } else {
788 parsePosition.setErrorIndex(startIndex + result.charEnd);
789 return nullptr;
790 }
791}
792
793const DecimalFormatSymbols* DecimalFormat::getDecimalFormatSymbols(void) const {
794 if (fields == nullptr) {
795 return nullptr;
796 }
797 return fields->symbols.getAlias();
798}
799
800void DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) {
801 if (symbolsToAdopt == nullptr) {
802 return; // do not allow caller to set fields->symbols to NULL
803 }
804 // we must take ownership of symbolsToAdopt, even in a failure case.
805 LocalPointer<DecimalFormatSymbols> dfs(symbolsToAdopt);
806 if (fields == nullptr) {
807 return;
808 }
809 fields->symbols.adoptInstead(dfs.orphan());
810 touchNoError();
811}
812
813void DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) {
814 if (fields == nullptr) {
815 return;
816 }
817 UErrorCode status = U_ZERO_ERROR;
818 LocalPointer<DecimalFormatSymbols> dfs(new DecimalFormatSymbols(symbols), status);
819 if (U_FAILURE(status)) {
820 // We failed to allocate DecimalFormatSymbols, release fields and its members.
821 // We must have a fully complete fields object, we cannot have partially populated members.
822 delete fields;
823 fields = nullptr;
824 return;
825 }
826 fields->symbols.adoptInstead(dfs.orphan());
827 touchNoError();
828}
829
830const CurrencyPluralInfo* DecimalFormat::getCurrencyPluralInfo(void) const {
831 if (fields == nullptr) {
832 return nullptr;
833 }
834 return fields->properties.currencyPluralInfo.fPtr.getAlias();
835}
836
837void DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) {
838 // TODO: should we guard against nullptr input, like in adoptDecimalFormatSymbols?
839 // we must take ownership of toAdopt, even in a failure case.
840 LocalPointer<CurrencyPluralInfo> cpi(toAdopt);
841 if (fields == nullptr) {
842 return;
843 }
844 fields->properties.currencyPluralInfo.fPtr.adoptInstead(cpi.orphan());
845 touchNoError();
846}
847
848void DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) {
849 if (fields == nullptr) {
850 return;
851 }
852 if (fields->properties.currencyPluralInfo.fPtr.isNull()) {
853 // Note: clone() can fail with OOM error, but we have no way to report it. :(
854 fields->properties.currencyPluralInfo.fPtr.adoptInstead(info.clone());
855 } else {
856 *fields->properties.currencyPluralInfo.fPtr = info; // copy-assignment operator
857 }
858 touchNoError();
859}
860
861UnicodeString& DecimalFormat::getPositivePrefix(UnicodeString& result) const {
862 if (fields == nullptr) {
863 result.setToBogus();
864 return result;
865 }
866 UErrorCode status = U_ZERO_ERROR;
867 fields->formatter.getAffixImpl(true, false, result, status);
868 if (U_FAILURE(status)) { result.setToBogus(); }
869 return result;
870}
871
872void DecimalFormat::setPositivePrefix(const UnicodeString& newValue) {
873 if (fields == nullptr) {
874 return;
875 }
876 if (newValue == fields->properties.positivePrefix) { return; }
877 fields->properties.positivePrefix = newValue;
878 touchNoError();
879}
880
881UnicodeString& DecimalFormat::getNegativePrefix(UnicodeString& result) const {
882 if (fields == nullptr) {
883 result.setToBogus();
884 return result;
885 }
886 UErrorCode status = U_ZERO_ERROR;
887 fields->formatter.getAffixImpl(true, true, result, status);
888 if (U_FAILURE(status)) { result.setToBogus(); }
889 return result;
890}
891
892void DecimalFormat::setNegativePrefix(const UnicodeString& newValue) {
893 if (fields == nullptr) {
894 return;
895 }
896 if (newValue == fields->properties.negativePrefix) { return; }
897 fields->properties.negativePrefix = newValue;
898 touchNoError();
899}
900
901UnicodeString& DecimalFormat::getPositiveSuffix(UnicodeString& result) const {
902 if (fields == nullptr) {
903 result.setToBogus();
904 return result;
905 }
906 UErrorCode status = U_ZERO_ERROR;
907 fields->formatter.getAffixImpl(false, false, result, status);
908 if (U_FAILURE(status)) { result.setToBogus(); }
909 return result;
910}
911
912void DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) {
913 if (fields == nullptr) {
914 return;
915 }
916 if (newValue == fields->properties.positiveSuffix) { return; }
917 fields->properties.positiveSuffix = newValue;
918 touchNoError();
919}
920
921UnicodeString& DecimalFormat::getNegativeSuffix(UnicodeString& result) const {
922 if (fields == nullptr) {
923 result.setToBogus();
924 return result;
925 }
926 UErrorCode status = U_ZERO_ERROR;
927 fields->formatter.getAffixImpl(false, true, result, status);
928 if (U_FAILURE(status)) { result.setToBogus(); }
929 return result;
930}
931
932void DecimalFormat::setNegativeSuffix(const UnicodeString& newValue) {
933 if (fields == nullptr) {
934 return;
935 }
936 if (newValue == fields->properties.negativeSuffix) { return; }
937 fields->properties.negativeSuffix = newValue;
938 touchNoError();
939}
940
941UBool DecimalFormat::isSignAlwaysShown() const {
942 // Not much we can do to report an error.
943 if (fields == nullptr) {
944 return DecimalFormatProperties::getDefault().signAlwaysShown;
945 }
946 return fields->properties.signAlwaysShown;
947}
948
949void DecimalFormat::setSignAlwaysShown(UBool value) {
950 if (fields == nullptr) { return; }
951 if (UBOOL_TO_BOOL(value) == fields->properties.signAlwaysShown) { return; }
952 fields->properties.signAlwaysShown = value;
953 touchNoError();
954}
955
956int32_t DecimalFormat::getMultiplier(void) const {
957 const DecimalFormatProperties *dfp;
958 // Not much we can do to report an error.
959 if (fields == nullptr) {
960 // Fallback to using the default instance of DecimalFormatProperties.
961 dfp = &(DecimalFormatProperties::getDefault());
962 } else {
963 dfp = &fields->properties;
964 }
965 if (dfp->multiplier != 1) {
966 return dfp->multiplier;
967 } else if (dfp->magnitudeMultiplier != 0) {
968 return static_cast<int32_t>(uprv_pow10(dfp->magnitudeMultiplier));
969 } else {
970 return 1;
971 }
972}
973
974void DecimalFormat::setMultiplier(int32_t multiplier) {
975 if (fields == nullptr) {
976 return;
977 }
978 if (multiplier == 0) {
979 multiplier = 1; // one being the benign default value for a multiplier.
980 }
981
982 // Try to convert to a magnitude multiplier first
983 int delta = 0;
984 int value = multiplier;
985 while (value != 1) {
986 delta++;
987 int temp = value / 10;
988 if (temp * 10 != value) {
989 delta = -1;
990 break;
991 }
992 value = temp;
993 }
994 if (delta != -1) {
995 fields->properties.magnitudeMultiplier = delta;
996 fields->properties.multiplier = 1;
997 } else {
998 fields->properties.magnitudeMultiplier = 0;
999 fields->properties.multiplier = multiplier;
1000 }
1001 touchNoError();
1002}
1003
1004int32_t DecimalFormat::getMultiplierScale() const {
1005 // Not much we can do to report an error.
1006 if (fields == nullptr) {
1007 // Fallback to using the default instance of DecimalFormatProperties.
1008 return DecimalFormatProperties::getDefault().multiplierScale;
1009 }
1010 return fields->properties.multiplierScale;
1011}
1012
1013void DecimalFormat::setMultiplierScale(int32_t newValue) {
1014 if (fields == nullptr) { return; }
1015 if (newValue == fields->properties.multiplierScale) { return; }
1016 fields->properties.multiplierScale = newValue;
1017 touchNoError();
1018}
1019
1020double DecimalFormat::getRoundingIncrement(void) const {
1021 // Not much we can do to report an error.
1022 if (fields == nullptr) {
1023 // Fallback to using the default instance of DecimalFormatProperties.
1024 return DecimalFormatProperties::getDefault().roundingIncrement;
1025 }
1026 return fields->exportedProperties.roundingIncrement;
1027}
1028
1029void DecimalFormat::setRoundingIncrement(double newValue) {
1030 if (fields == nullptr) { return; }
1031 if (newValue == fields->properties.roundingIncrement) { return; }
1032 fields->properties.roundingIncrement = newValue;
1033 touchNoError();
1034}
1035
1036ERoundingMode DecimalFormat::getRoundingMode(void) const {
1037 // Not much we can do to report an error.
1038 if (fields == nullptr) {
1039 // Fallback to using the default instance of DecimalFormatProperties.
1040 return static_cast<ERoundingMode>(DecimalFormatProperties::getDefault().roundingMode.getNoError());
1041 }
1042 // UNumberFormatRoundingMode and ERoundingMode have the same values.
1043 return static_cast<ERoundingMode>(fields->exportedProperties.roundingMode.getNoError());
1044}
1045
1046void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) {
1047 if (fields == nullptr) { return; }
1048 auto uRoundingMode = static_cast<UNumberFormatRoundingMode>(roundingMode);
1049 if (!fields->properties.roundingMode.isNull() && uRoundingMode == fields->properties.roundingMode.getNoError()) {
1050 return;
1051 }
1052 NumberFormat::setMaximumIntegerDigits(roundingMode); // to set field for compatibility
1053 fields->properties.roundingMode = uRoundingMode;
1054 touchNoError();
1055}
1056
1057int32_t DecimalFormat::getFormatWidth(void) const {
1058 // Not much we can do to report an error.
1059 if (fields == nullptr) {
1060 // Fallback to using the default instance of DecimalFormatProperties.
1061 return DecimalFormatProperties::getDefault().formatWidth;
1062 }
1063 return fields->properties.formatWidth;
1064}
1065
1066void DecimalFormat::setFormatWidth(int32_t width) {
1067 if (fields == nullptr) { return; }
1068 if (width == fields->properties.formatWidth) { return; }
1069 fields->properties.formatWidth = width;
1070 touchNoError();
1071}
1072
1073UnicodeString DecimalFormat::getPadCharacterString() const {
1074 if (fields == nullptr || fields->properties.padString.isBogus()) {
1075 // Readonly-alias the static string kFallbackPaddingString
1076 return {TRUE, kFallbackPaddingString, -1};
1077 } else {
1078 return fields->properties.padString;
1079 }
1080}
1081
1082void DecimalFormat::setPadCharacter(const UnicodeString& padChar) {
1083 if (fields == nullptr) { return; }
1084 if (padChar == fields->properties.padString) { return; }
1085 if (padChar.length() > 0) {
1086 fields->properties.padString = UnicodeString(padChar.char32At(0));
1087 } else {
1088 fields->properties.padString.setToBogus();
1089 }
1090 touchNoError();
1091}
1092
1093EPadPosition DecimalFormat::getPadPosition(void) const {
1094 if (fields == nullptr || fields->properties.padPosition.isNull()) {
1095 return EPadPosition::kPadBeforePrefix;
1096 } else {
1097 // UNumberFormatPadPosition and EPadPosition have the same values.
1098 return static_cast<EPadPosition>(fields->properties.padPosition.getNoError());
1099 }
1100}
1101
1102void DecimalFormat::setPadPosition(EPadPosition padPos) {
1103 if (fields == nullptr) { return; }
1104 auto uPadPos = static_cast<UNumberFormatPadPosition>(padPos);
1105 if (!fields->properties.padPosition.isNull() && uPadPos == fields->properties.padPosition.getNoError()) {
1106 return;
1107 }
1108 fields->properties.padPosition = uPadPos;
1109 touchNoError();
1110}
1111
1112UBool DecimalFormat::isScientificNotation(void) const {
1113 // Not much we can do to report an error.
1114 if (fields == nullptr) {
1115 // Fallback to using the default instance of DecimalFormatProperties.
1116 return (DecimalFormatProperties::getDefault().minimumExponentDigits != -1);
1117 }
1118 return (fields->properties.minimumExponentDigits != -1);
1119}
1120
1121void DecimalFormat::setScientificNotation(UBool useScientific) {
1122 if (fields == nullptr) { return; }
1123 int32_t minExp = useScientific ? 1 : -1;
1124 if (fields->properties.minimumExponentDigits == minExp) { return; }
1125 if (useScientific) {
1126 fields->properties.minimumExponentDigits = 1;
1127 } else {
1128 fields->properties.minimumExponentDigits = -1;
1129 }
1130 touchNoError();
1131}
1132
1133int8_t DecimalFormat::getMinimumExponentDigits(void) const {
1134 // Not much we can do to report an error.
1135 if (fields == nullptr) {
1136 // Fallback to using the default instance of DecimalFormatProperties.
1137 return static_cast<int8_t>(DecimalFormatProperties::getDefault().minimumExponentDigits);
1138 }
1139 return static_cast<int8_t>(fields->properties.minimumExponentDigits);
1140}
1141
1142void DecimalFormat::setMinimumExponentDigits(int8_t minExpDig) {
1143 if (fields == nullptr) { return; }
1144 if (minExpDig == fields->properties.minimumExponentDigits) { return; }
1145 fields->properties.minimumExponentDigits = minExpDig;
1146 touchNoError();
1147}
1148
1149UBool DecimalFormat::isExponentSignAlwaysShown(void) const {
1150 // Not much we can do to report an error.
1151 if (fields == nullptr) {
1152 // Fallback to using the default instance of DecimalFormatProperties.
1153 return DecimalFormatProperties::getDefault().exponentSignAlwaysShown;
1154 }
1155 return fields->properties.exponentSignAlwaysShown;
1156}
1157
1158void DecimalFormat::setExponentSignAlwaysShown(UBool expSignAlways) {
1159 if (fields == nullptr) { return; }
1160 if (UBOOL_TO_BOOL(expSignAlways) == fields->properties.exponentSignAlwaysShown) { return; }
1161 fields->properties.exponentSignAlwaysShown = expSignAlways;
1162 touchNoError();
1163}
1164
1165int32_t DecimalFormat::getGroupingSize(void) const {
1166 int32_t groupingSize;
1167 // Not much we can do to report an error.
1168 if (fields == nullptr) {
1169 // Fallback to using the default instance of DecimalFormatProperties.
1170 groupingSize = DecimalFormatProperties::getDefault().groupingSize;
1171 } else {
1172 groupingSize = fields->properties.groupingSize;
1173 }
1174 if (groupingSize < 0) {
1175 return 0;
1176 }
1177 return groupingSize;
1178}
1179
1180void DecimalFormat::setGroupingSize(int32_t newValue) {
1181 if (fields == nullptr) { return; }
1182 if (newValue == fields->properties.groupingSize) { return; }
1183 fields->properties.groupingSize = newValue;
1184 touchNoError();
1185}
1186
1187int32_t DecimalFormat::getSecondaryGroupingSize(void) const {
1188 int32_t grouping2;
1189 // Not much we can do to report an error.
1190 if (fields == nullptr) {
1191 // Fallback to using the default instance of DecimalFormatProperties.
1192 grouping2 = DecimalFormatProperties::getDefault().secondaryGroupingSize;
1193 } else {
1194 grouping2 = fields->properties.secondaryGroupingSize;
1195 }
1196 if (grouping2 < 0) {
1197 return 0;
1198 }
1199 return grouping2;
1200}
1201
1202void DecimalFormat::setSecondaryGroupingSize(int32_t newValue) {
1203 if (fields == nullptr) { return; }
1204 if (newValue == fields->properties.secondaryGroupingSize) { return; }
1205 fields->properties.secondaryGroupingSize = newValue;
1206 touchNoError();
1207}
1208
1209int32_t DecimalFormat::getMinimumGroupingDigits() const {
1210 // Not much we can do to report an error.
1211 if (fields == nullptr) {
1212 // Fallback to using the default instance of DecimalFormatProperties.
1213 return DecimalFormatProperties::getDefault().minimumGroupingDigits;
1214 }
1215 return fields->properties.minimumGroupingDigits;
1216}
1217
1218void DecimalFormat::setMinimumGroupingDigits(int32_t newValue) {
1219 if (fields == nullptr) { return; }
1220 if (newValue == fields->properties.minimumGroupingDigits) { return; }
1221 fields->properties.minimumGroupingDigits = newValue;
1222 touchNoError();
1223}
1224
1225UBool DecimalFormat::isDecimalSeparatorAlwaysShown(void) const {
1226 // Not much we can do to report an error.
1227 if (fields == nullptr) {
1228 // Fallback to using the default instance of DecimalFormatProperties.
1229 return DecimalFormatProperties::getDefault().decimalSeparatorAlwaysShown;
1230 }
1231 return fields->properties.decimalSeparatorAlwaysShown;
1232}
1233
1234void DecimalFormat::setDecimalSeparatorAlwaysShown(UBool newValue) {
1235 if (fields == nullptr) { return; }
1236 if (UBOOL_TO_BOOL(newValue) == fields->properties.decimalSeparatorAlwaysShown) { return; }
1237 fields->properties.decimalSeparatorAlwaysShown = newValue;
1238 touchNoError();
1239}
1240
1241UBool DecimalFormat::isDecimalPatternMatchRequired(void) const {
1242 // Not much we can do to report an error.
1243 if (fields == nullptr) {
1244 // Fallback to using the default instance of DecimalFormatProperties.
1245 return DecimalFormatProperties::getDefault().decimalPatternMatchRequired;
1246 }
1247 return fields->properties.decimalPatternMatchRequired;
1248}
1249
1250void DecimalFormat::setDecimalPatternMatchRequired(UBool newValue) {
1251 if (fields == nullptr) { return; }
1252 if (UBOOL_TO_BOOL(newValue) == fields->properties.decimalPatternMatchRequired) { return; }
1253 fields->properties.decimalPatternMatchRequired = newValue;
1254 touchNoError();
1255}
1256
1257UBool DecimalFormat::isParseNoExponent() const {
1258 // Not much we can do to report an error.
1259 if (fields == nullptr) {
1260 // Fallback to using the default instance of DecimalFormatProperties.
1261 return DecimalFormatProperties::getDefault().parseNoExponent;
1262 }
1263 return fields->properties.parseNoExponent;
1264}
1265
1266void DecimalFormat::setParseNoExponent(UBool value) {
1267 if (fields == nullptr) { return; }
1268 if (UBOOL_TO_BOOL(value) == fields->properties.parseNoExponent) { return; }
1269 fields->properties.parseNoExponent = value;
1270 touchNoError();
1271}
1272
1273UBool DecimalFormat::isParseCaseSensitive() const {
1274 // Not much we can do to report an error.
1275 if (fields == nullptr) {
1276 // Fallback to using the default instance of DecimalFormatProperties.
1277 return DecimalFormatProperties::getDefault().parseCaseSensitive;
1278 }
1279 return fields->properties.parseCaseSensitive;
1280}
1281
1282void DecimalFormat::setParseCaseSensitive(UBool value) {
1283 if (fields == nullptr) { return; }
1284 if (UBOOL_TO_BOOL(value) == fields->properties.parseCaseSensitive) { return; }
1285 fields->properties.parseCaseSensitive = value;
1286 touchNoError();
1287}
1288
1289UBool DecimalFormat::isFormatFailIfMoreThanMaxDigits() const {
1290 // Not much we can do to report an error.
1291 if (fields == nullptr) {
1292 // Fallback to using the default instance of DecimalFormatProperties.
1293 return DecimalFormatProperties::getDefault().formatFailIfMoreThanMaxDigits;
1294 }
1295 return fields->properties.formatFailIfMoreThanMaxDigits;
1296}
1297
1298void DecimalFormat::setFormatFailIfMoreThanMaxDigits(UBool value) {
1299 if (fields == nullptr) { return; }
1300 if (UBOOL_TO_BOOL(value) == fields->properties.formatFailIfMoreThanMaxDigits) { return; }
1301 fields->properties.formatFailIfMoreThanMaxDigits = value;
1302 touchNoError();
1303}
1304
1305UnicodeString& DecimalFormat::toPattern(UnicodeString& result) const {
1306 if (fields == nullptr) {
1307 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1308 result.setToBogus();
1309 return result;
1310 }
1311 // Pull some properties from exportedProperties and others from properties
1312 // to keep affix patterns intact. In particular, pull rounding properties
1313 // so that CurrencyUsage is reflected properly.
1314 // TODO: Consider putting this logic in number_patternstring.cpp instead.
1315 ErrorCode localStatus;
1316 DecimalFormatProperties tprops(fields->properties);
1317 bool useCurrency = (
1318 !tprops.currency.isNull() ||
1319 !tprops.currencyPluralInfo.fPtr.isNull() ||
1320 !tprops.currencyUsage.isNull() ||
1321 AffixUtils::hasCurrencySymbols(tprops.positivePrefixPattern, localStatus) ||
1322 AffixUtils::hasCurrencySymbols(tprops.positiveSuffixPattern, localStatus) ||
1323 AffixUtils::hasCurrencySymbols(tprops.negativePrefixPattern, localStatus) ||
1324 AffixUtils::hasCurrencySymbols(tprops.negativeSuffixPattern, localStatus));
1325 if (useCurrency) {
1326 tprops.minimumFractionDigits = fields->exportedProperties.minimumFractionDigits;
1327 tprops.maximumFractionDigits = fields->exportedProperties.maximumFractionDigits;
1328 tprops.roundingIncrement = fields->exportedProperties.roundingIncrement;
1329 }
1330 result = PatternStringUtils::propertiesToPatternString(tprops, localStatus);
1331 return result;
1332}
1333
1334UnicodeString& DecimalFormat::toLocalizedPattern(UnicodeString& result) const {
1335 if (fields == nullptr) {
1336 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1337 result.setToBogus();
1338 return result;
1339 }
1340 ErrorCode localStatus;
1341 result = toPattern(result);
1342 result = PatternStringUtils::convertLocalized(result, *fields->symbols, true, localStatus);
1343 return result;
1344}
1345
1346void DecimalFormat::applyPattern(const UnicodeString& pattern, UParseError&, UErrorCode& status) {
1347 // TODO: What is parseError for?
1348 applyPattern(pattern, status);
1349}
1350
1351void DecimalFormat::applyPattern(const UnicodeString& pattern, 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 setPropertiesFromPattern(pattern, IGNORE_ROUNDING_NEVER, status);
1360 touch(status);
1361}
1362
1363void DecimalFormat::applyLocalizedPattern(const UnicodeString& localizedPattern, UParseError&,
1364 UErrorCode& status) {
1365 // TODO: What is parseError for?
1366 applyLocalizedPattern(localizedPattern, status);
1367}
1368
1369void DecimalFormat::applyLocalizedPattern(const UnicodeString& localizedPattern, UErrorCode& status) {
1370 // don't overwrite status if it's already a failure.
1371 if (U_FAILURE(status)) { return; }
1372 if (fields == nullptr) {
1373 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1374 status = U_MEMORY_ALLOCATION_ERROR;
1375 return;
1376 }
1377 UnicodeString pattern = PatternStringUtils::convertLocalized(
1378 localizedPattern, *fields->symbols, false, status);
1379 applyPattern(pattern, status);
1380}
1381
1382void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) {
1383 if (fields == nullptr) { return; }
1384 if (newValue == fields->properties.maximumIntegerDigits) { return; }
1385 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1386 int32_t min = fields->properties.minimumIntegerDigits;
1387 if (min >= 0 && min > newValue) {
1388 fields->properties.minimumIntegerDigits = newValue;
1389 }
1390 fields->properties.maximumIntegerDigits = newValue;
1391 touchNoError();
1392}
1393
1394void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) {
1395 if (fields == nullptr) { return; }
1396 if (newValue == fields->properties.minimumIntegerDigits) { return; }
1397 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1398 int32_t max = fields->properties.maximumIntegerDigits;
1399 if (max >= 0 && max < newValue) {
1400 fields->properties.maximumIntegerDigits = newValue;
1401 }
1402 fields->properties.minimumIntegerDigits = newValue;
1403 touchNoError();
1404}
1405
1406void DecimalFormat::setMaximumFractionDigits(int32_t newValue) {
1407 if (fields == nullptr) { return; }
1408 if (newValue == fields->properties.maximumFractionDigits) { return; }
1409 // cap for backward compatibility, formerly 340, now 999
1410 if (newValue > kMaxIntFracSig) {
1411 newValue = kMaxIntFracSig;
1412 }
1413 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1414 int32_t min = fields->properties.minimumFractionDigits;
1415 if (min >= 0 && min > newValue) {
1416 fields->properties.minimumFractionDigits = newValue;
1417 }
1418 fields->properties.maximumFractionDigits = newValue;
1419 touchNoError();
1420}
1421
1422void DecimalFormat::setMinimumFractionDigits(int32_t newValue) {
1423 if (fields == nullptr) { return; }
1424 if (newValue == fields->properties.minimumFractionDigits) { return; }
1425 // For backwards compatibility, conflicting min/max need to keep the most recent setting.
1426 int32_t max = fields->properties.maximumFractionDigits;
1427 if (max >= 0 && max < newValue) {
1428 fields->properties.maximumFractionDigits = newValue;
1429 }
1430 fields->properties.minimumFractionDigits = newValue;
1431 touchNoError();
1432}
1433
1434int32_t DecimalFormat::getMinimumSignificantDigits() const {
1435 // Not much we can do to report an error.
1436 if (fields == nullptr) {
1437 // Fallback to using the default instance of DecimalFormatProperties.
1438 return DecimalFormatProperties::getDefault().minimumSignificantDigits;
1439 }
1440 return fields->exportedProperties.minimumSignificantDigits;
1441}
1442
1443int32_t DecimalFormat::getMaximumSignificantDigits() const {
1444 // Not much we can do to report an error.
1445 if (fields == nullptr) {
1446 // Fallback to using the default instance of DecimalFormatProperties.
1447 return DecimalFormatProperties::getDefault().maximumSignificantDigits;
1448 }
1449 return fields->exportedProperties.maximumSignificantDigits;
1450}
1451
1452void DecimalFormat::setMinimumSignificantDigits(int32_t value) {
1453 if (fields == nullptr) { return; }
1454 if (value == fields->properties.minimumSignificantDigits) { return; }
1455 int32_t max = fields->properties.maximumSignificantDigits;
1456 if (max >= 0 && max < value) {
1457 fields->properties.maximumSignificantDigits = value;
1458 }
1459 fields->properties.minimumSignificantDigits = value;
1460 touchNoError();
1461}
1462
1463void DecimalFormat::setMaximumSignificantDigits(int32_t value) {
1464 if (fields == nullptr) { return; }
1465 if (value == fields->properties.maximumSignificantDigits) { return; }
1466 int32_t min = fields->properties.minimumSignificantDigits;
1467 if (min >= 0 && min > value) {
1468 fields->properties.minimumSignificantDigits = value;
1469 }
1470 fields->properties.maximumSignificantDigits = value;
1471 touchNoError();
1472}
1473
1474UBool DecimalFormat::areSignificantDigitsUsed() const {
1475 const DecimalFormatProperties* dfp;
1476 // Not much we can do to report an error.
1477 if (fields == nullptr) {
1478 // Fallback to using the default instance of DecimalFormatProperties.
1479 dfp = &(DecimalFormatProperties::getDefault());
1480 } else {
1481 dfp = &fields->properties;
1482 }
1483 return dfp->minimumSignificantDigits != -1 || dfp->maximumSignificantDigits != -1;
1484}
1485
1486void DecimalFormat::setSignificantDigitsUsed(UBool useSignificantDigits) {
1487 if (fields == nullptr) { return; }
1488
1489 // These are the default values from the old implementation.
1490 if (useSignificantDigits) {
1491 if (fields->properties.minimumSignificantDigits != -1 ||
1492 fields->properties.maximumSignificantDigits != -1) {
1493 return;
1494 }
1495 } else {
1496 if (fields->properties.minimumSignificantDigits == -1 &&
1497 fields->properties.maximumSignificantDigits == -1) {
1498 return;
1499 }
1500 }
1501 int32_t minSig = useSignificantDigits ? 1 : -1;
1502 int32_t maxSig = useSignificantDigits ? 6 : -1;
1503 fields->properties.minimumSignificantDigits = minSig;
1504 fields->properties.maximumSignificantDigits = maxSig;
1505 touchNoError();
1506}
1507
1508void DecimalFormat::setCurrency(const char16_t* theCurrency, UErrorCode& ec) {
1509 // don't overwrite ec if it's already a failure.
1510 if (U_FAILURE(ec)) { return; }
1511 if (fields == nullptr) {
1512 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1513 ec = U_MEMORY_ALLOCATION_ERROR;
1514 return;
1515 }
1516 CurrencyUnit currencyUnit(theCurrency, ec);
1517 if (U_FAILURE(ec)) { return; }
1518 if (!fields->properties.currency.isNull() && fields->properties.currency.getNoError() == currencyUnit) {
1519 return;
1520 }
1521 NumberFormat::setCurrency(theCurrency, ec); // to set field for compatibility
1522 fields->properties.currency = currencyUnit;
1523 // In Java, the DecimalFormatSymbols is mutable. Why not in C++?
1524 LocalPointer<DecimalFormatSymbols> newSymbols(new DecimalFormatSymbols(*fields->symbols), ec);
1525 newSymbols->setCurrency(currencyUnit.getISOCurrency(), ec);
1526 fields->symbols.adoptInsteadAndCheckErrorCode(newSymbols.orphan(), ec);
1527 touch(ec);
1528}
1529
1530void DecimalFormat::setCurrency(const char16_t* theCurrency) {
1531 ErrorCode localStatus;
1532 setCurrency(theCurrency, localStatus);
1533}
1534
1535void DecimalFormat::setCurrencyUsage(UCurrencyUsage newUsage, UErrorCode* ec) {
1536 // don't overwrite ec if it's already a failure.
1537 if (U_FAILURE(*ec)) { return; }
1538 if (fields == nullptr) {
1539 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1540 *ec = U_MEMORY_ALLOCATION_ERROR;
1541 return;
1542 }
1543 if (!fields->properties.currencyUsage.isNull() && newUsage == fields->properties.currencyUsage.getNoError()) {
1544 return;
1545 }
1546 fields->properties.currencyUsage = newUsage;
1547 touch(*ec);
1548}
1549
1550UCurrencyUsage DecimalFormat::getCurrencyUsage() const {
1551 // CurrencyUsage is not exported, so we have to get it from the input property bag.
1552 // TODO: Should we export CurrencyUsage instead?
1553 if (fields == nullptr || fields->properties.currencyUsage.isNull()) {
1554 return UCURR_USAGE_STANDARD;
1555 }
1556 return fields->properties.currencyUsage.getNoError();
1557}
1558
1559void
1560DecimalFormat::formatToDecimalQuantity(double number, DecimalQuantity& output, UErrorCode& status) const {
1561 // don't overwrite status if it's already a failure.
1562 if (U_FAILURE(status)) { return; }
1563 if (fields == nullptr) {
1564 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1565 status = U_MEMORY_ALLOCATION_ERROR;
1566 return;
1567 }
1568 fields->formatter.formatDouble(number, status).getDecimalQuantity(output, status);
1569}
1570
1571void DecimalFormat::formatToDecimalQuantity(const Formattable& number, DecimalQuantity& output,
1572 UErrorCode& status) const {
1573 // don't overwrite status if it's already a failure.
1574 if (U_FAILURE(status)) { return; }
1575 if (fields == nullptr) {
1576 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1577 status = U_MEMORY_ALLOCATION_ERROR;
1578 return;
1579 }
1580 UFormattedNumberData obj;
1581 number.populateDecimalQuantity(obj.quantity, status);
1582 fields->formatter.formatImpl(&obj, status);
1583 output = std::move(obj.quantity);
1584}
1585
1586const number::LocalizedNumberFormatter* DecimalFormat::toNumberFormatter(UErrorCode& status) const {
1587 // We sometimes need to return nullptr here (see ICU-20380)
1588 if (U_FAILURE(status)) { return nullptr; }
1589 if (fields == nullptr) {
1590 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1591 status = U_MEMORY_ALLOCATION_ERROR;
1592 return nullptr;
1593 }
1594 return &fields->formatter;
1595}
1596
1597/** Rebuilds the formatter object from the property bag. */
1598void DecimalFormat::touch(UErrorCode& status) {
1599 if (U_FAILURE(status)) {
1600 return;
1601 }
1602 if (fields == nullptr) {
1603 // We only get here if an OOM error happend during construction, copy construction, assignment, or modification.
1604 // For regular construction, the caller should have checked the status variable for errors.
1605 // For copy construction, there is unfortunately nothing to report the error, so we need to guard against
1606 // this possible bad state here and set the status to an error.
1607 status = U_MEMORY_ALLOCATION_ERROR;
1608 return;
1609 }
1610
1611 // In C++, fields->symbols is the source of truth for the locale.
1612 Locale locale = fields->symbols->getLocale();
1613
1614 // Note: The formatter is relatively cheap to create, and we need it to populate fields->exportedProperties,
1615 // so automatically recompute it here. The parser is a bit more expensive and is not needed until the
1616 // parse method is called, so defer that until needed.
1617 // TODO: Only update the pieces that changed instead of re-computing the whole formatter?
1618
1619 // Since memory has already been allocated for the formatter, we can move assign a stack-allocated object
1620 // and don't need to call new. (Which is slower and could possibly fail).
1621 fields->formatter = NumberPropertyMapper::create(
1622 fields->properties, *fields->symbols, fields->warehouse, fields->exportedProperties, status
1623 ).locale(locale);
1624
1625 // Do this after fields->exportedProperties are set up
1626 setupFastFormat();
1627
1628 // Delete the parsers if they were made previously
1629 delete fields->atomicParser.exchange(nullptr);
1630 delete fields->atomicCurrencyParser.exchange(nullptr);
1631
1632 // In order for the getters to work, we need to populate some fields in NumberFormat.
1633 NumberFormat::setCurrency(fields->exportedProperties.currency.get(status).getISOCurrency(), status);
1634 NumberFormat::setMaximumIntegerDigits(fields->exportedProperties.maximumIntegerDigits);
1635 NumberFormat::setMinimumIntegerDigits(fields->exportedProperties.minimumIntegerDigits);
1636 NumberFormat::setMaximumFractionDigits(fields->exportedProperties.maximumFractionDigits);
1637 NumberFormat::setMinimumFractionDigits(fields->exportedProperties.minimumFractionDigits);
1638 // fImpl->properties, not fields->exportedProperties, since this information comes from the pattern:
1639 NumberFormat::setGroupingUsed(fields->properties.groupingUsed);
1640}
1641
1642void DecimalFormat::touchNoError() {
1643 UErrorCode localStatus = U_ZERO_ERROR;
1644 touch(localStatus);
1645}
1646
1647void DecimalFormat::setPropertiesFromPattern(const UnicodeString& pattern, int32_t ignoreRounding,
1648 UErrorCode& status) {
1649 if (U_SUCCESS(status)) {
1650 // Cast workaround to get around putting the enum in the public header file
1651 auto actualIgnoreRounding = static_cast<IgnoreRounding>(ignoreRounding);
1652 PatternParser::parseToExistingProperties(pattern, fields->properties, actualIgnoreRounding, status);
1653 }
1654}
1655
1656const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& status) const {
1657 // TODO: Move this into umutex.h? (similar logic also in numrange_fluent.cpp)
1658 // See ICU-20146
1659
1660 if (U_FAILURE(status)) {
1661 return nullptr;
1662 }
1663
1664 // First try to get the pre-computed parser
1665 auto* ptr = fields->atomicParser.load();
1666 if (ptr != nullptr) {
1667 return ptr;
1668 }
1669
1670 // Try computing the parser on our own
1671 auto* temp = NumberParserImpl::createParserFromProperties(fields->properties, *fields->symbols, false, status);
1672 if (U_FAILURE(status)) {
1673 return nullptr;
1674 }
1675 if (temp == nullptr) {
1676 status = U_MEMORY_ALLOCATION_ERROR;
1677 return nullptr;
1678 }
1679
1680 // Note: ptr starts as nullptr; during compare_exchange,
1681 // it is set to what is actually stored in the atomic
1682 // if another thread beat us to computing the parser object.
1683 auto* nonConstThis = const_cast<DecimalFormat*>(this);
1684 if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, temp)) {
1685 // Another thread beat us to computing the parser
1686 delete temp;
1687 return ptr;
1688 } else {
1689 // Our copy of the parser got stored in the atomic
1690 return temp;
1691 }
1692}
1693
1694const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorCode& status) const {
1695 if (U_FAILURE(status)) { return nullptr; }
1696
1697 // First try to get the pre-computed parser
1698 auto* ptr = fields->atomicCurrencyParser.load();
1699 if (ptr != nullptr) {
1700 return ptr;
1701 }
1702
1703 // Try computing the parser on our own
1704 auto* temp = NumberParserImpl::createParserFromProperties(fields->properties, *fields->symbols, true, status);
1705 if (temp == nullptr) {
1706 status = U_MEMORY_ALLOCATION_ERROR;
1707 // although we may still dereference, call sites should be guarded
1708 }
1709
1710 // Note: ptr starts as nullptr; during compare_exchange, it is set to what is actually stored in the
1711 // atomic if another thread beat us to computing the parser object.
1712 auto* nonConstThis = const_cast<DecimalFormat*>(this);
1713 if (!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, temp)) {
1714 // Another thread beat us to computing the parser
1715 delete temp;
1716 return ptr;
1717 } else {
1718 // Our copy of the parser got stored in the atomic
1719 return temp;
1720 }
1721}
1722
1723void
1724DecimalFormat::fieldPositionHelper(
1725 const UFormattedNumberData& formatted,
1726 FieldPosition& fieldPosition,
1727 int32_t offset,
1728 UErrorCode& status) {
1729 if (U_FAILURE(status)) { return; }
1730 // always return first occurrence:
1731 fieldPosition.setBeginIndex(0);
1732 fieldPosition.setEndIndex(0);
1733 bool found = formatted.nextFieldPosition(fieldPosition, status);
1734 if (found && offset != 0) {
1735 FieldPositionOnlyHandler fpoh(fieldPosition);
1736 fpoh.shiftLast(offset);
1737 }
1738}
1739
1740void
1741DecimalFormat::fieldPositionIteratorHelper(
1742 const UFormattedNumberData& formatted,
1743 FieldPositionIterator* fpi,
1744 int32_t offset,
1745 UErrorCode& status) {
1746 if (U_SUCCESS(status) && (fpi != nullptr)) {
1747 FieldPositionIteratorHandler fpih(fpi, status);
1748 fpih.setShift(offset);
1749 formatted.getAllFieldPositions(fpih, status);
1750 }
1751}
1752
1753// To debug fast-format, change void(x) to printf(x)
1754#define trace(x) void(x)
1755
1756void DecimalFormat::setupFastFormat() {
1757 // Check the majority of properties:
1758 if (!fields->properties.equalsDefaultExceptFastFormat()) {
1759 trace("no fast format: equality\n");
1760 fields->canUseFastFormat = false;
1761 return;
1762 }
1763
1764 // Now check the remaining properties.
1765 // Nontrivial affixes:
1766 UBool trivialPP = fields->properties.positivePrefixPattern.isEmpty();
1767 UBool trivialPS = fields->properties.positiveSuffixPattern.isEmpty();
1768 UBool trivialNP = fields->properties.negativePrefixPattern.isBogus() || (
1769 fields->properties.negativePrefixPattern.length() == 1 &&
1770 fields->properties.negativePrefixPattern.charAt(0) == u'-');
1771 UBool trivialNS = fields->properties.negativeSuffixPattern.isEmpty();
1772 if (!trivialPP || !trivialPS || !trivialNP || !trivialNS) {
1773 trace("no fast format: affixes\n");
1774 fields->canUseFastFormat = false;
1775 return;
1776 }
1777
1778 // Grouping (secondary grouping is forbidden in equalsDefaultExceptFastFormat):
1779 bool groupingUsed = fields->properties.groupingUsed;
1780 int32_t groupingSize = fields->properties.groupingSize;
1781 bool unusualGroupingSize = groupingSize > 0 && groupingSize != 3;
1782 const UnicodeString& groupingString = fields->symbols->getConstSymbol(DecimalFormatSymbols::kGroupingSeparatorSymbol);
1783 if (groupingUsed && (unusualGroupingSize || groupingString.length() != 1)) {
1784 trace("no fast format: grouping\n");
1785 fields->canUseFastFormat = false;
1786 return;
1787 }
1788
1789 // Integer length:
1790 int32_t minInt = fields->exportedProperties.minimumIntegerDigits;
1791 int32_t maxInt = fields->exportedProperties.maximumIntegerDigits;
1792 // Fastpath supports up to only 10 digits (length of INT32_MIN)
1793 if (minInt > 10) {
1794 trace("no fast format: integer\n");
1795 fields->canUseFastFormat = false;
1796 return;
1797 }
1798
1799 // Fraction length (no fraction part allowed in fast path):
1800 int32_t minFrac = fields->exportedProperties.minimumFractionDigits;
1801 if (minFrac > 0) {
1802 trace("no fast format: fraction\n");
1803 fields->canUseFastFormat = false;
1804 return;
1805 }
1806
1807 // Other symbols:
1808 const UnicodeString& minusSignString = fields->symbols->getConstSymbol(DecimalFormatSymbols::kMinusSignSymbol);
1809 UChar32 codePointZero = fields->symbols->getCodePointZero();
1810 if (minusSignString.length() != 1 || U16_LENGTH(codePointZero) != 1) {
1811 trace("no fast format: symbols\n");
1812 fields->canUseFastFormat = false;
1813 return;
1814 }
1815
1816 // Good to go!
1817 trace("can use fast format!\n");
1818 fields->canUseFastFormat = true;
1819 fields->fastData.cpZero = static_cast<char16_t>(codePointZero);
1820 fields->fastData.cpGroupingSeparator = groupingUsed && groupingSize == 3 ? groupingString.charAt(0) : 0;
1821 fields->fastData.cpMinusSign = minusSignString.charAt(0);
1822 fields->fastData.minInt = (minInt < 0 || minInt > 127) ? 0 : static_cast<int8_t>(minInt);
1823 fields->fastData.maxInt = (maxInt < 0 || maxInt > 127) ? 127 : static_cast<int8_t>(maxInt);
1824}
1825
1826bool DecimalFormat::fastFormatDouble(double input, UnicodeString& output) const {
1827 if (!fields->canUseFastFormat) {
1828 return false;
1829 }
1830 if (std::isnan(input)
1831 || uprv_trunc(input) != input
1832 || input <= INT32_MIN
1833 || input > INT32_MAX) {
1834 return false;
1835 }
1836 doFastFormatInt32(static_cast<int32_t>(input), std::signbit(input), output);
1837 return true;
1838}
1839
1840bool DecimalFormat::fastFormatInt64(int64_t input, UnicodeString& output) const {
1841 if (!fields->canUseFastFormat) {
1842 return false;
1843 }
1844 if (input <= INT32_MIN || input > INT32_MAX) {
1845 return false;
1846 }
1847 doFastFormatInt32(static_cast<int32_t>(input), input < 0, output);
1848 return true;
1849}
1850
1851void DecimalFormat::doFastFormatInt32(int32_t input, bool isNegative, UnicodeString& output) const {
1852 U_ASSERT(fields->canUseFastFormat);
1853 if (isNegative) {
1854 output.append(fields->fastData.cpMinusSign);
1855 U_ASSERT(input != INT32_MIN); // handled by callers
1856 input = -input;
1857 }
1858 // Cap at int32_t to make the buffer small and operations fast.
1859 // Longest string: "2,147,483,648" (13 chars in length)
1860 static constexpr int32_t localCapacity = 13;
1861 char16_t localBuffer[localCapacity];
1862 char16_t* ptr = localBuffer + localCapacity;
1863 int8_t group = 0;
1864 int8_t minInt = (fields->fastData.minInt < 1)? 1: fields->fastData.minInt;
1865 for (int8_t i = 0; i < fields->fastData.maxInt && (input != 0 || i < minInt); i++) {
1866 if (group++ == 3 && fields->fastData.cpGroupingSeparator != 0) {
1867 *(--ptr) = fields->fastData.cpGroupingSeparator;
1868 group = 1;
1869 }
1870 std::div_t res = std::div(input, 10);
1871 *(--ptr) = static_cast<char16_t>(fields->fastData.cpZero + res.rem);
1872 input = res.quot;
1873 }
1874 int32_t len = localCapacity - static_cast<int32_t>(ptr - localBuffer);
1875 output.append(ptr, len);
1876}
1877
1878
1879#endif /* #if !UCONFIG_NO_FORMATTING */
1880