• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2017 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 #include "unicode/utypes.h"
5 
6 #if !UCONFIG_NO_FORMATTING
7 
8 #include "cstring.h"
9 #include "unicode/ures.h"
10 #include "uresimp.h"
11 #include "charstr.h"
12 #include "number_formatimpl.h"
13 #include "unicode/numfmt.h"
14 #include "number_patternstring.h"
15 #include "number_utils.h"
16 #include "unicode/numberformatter.h"
17 #include "unicode/dcfmtsym.h"
18 #include "number_scientific.h"
19 #include "number_compact.h"
20 #include "uresimp.h"
21 #include "ureslocs.h"
22 
23 using namespace icu;
24 using namespace icu::number;
25 using namespace icu::number::impl;
26 
27 
NumberFormatterImpl(const MacroProps & macros,UErrorCode & status)28 NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, UErrorCode& status)
29     : NumberFormatterImpl(macros, true, status) {
30 }
31 
formatStatic(const MacroProps & macros,UFormattedNumberData * results,UErrorCode & status)32 int32_t NumberFormatterImpl::formatStatic(const MacroProps &macros, UFormattedNumberData *results,
33                                           UErrorCode &status) {
34     DecimalQuantity &inValue = results->quantity;
35     FormattedStringBuilder &outString = results->getStringRef();
36     NumberFormatterImpl impl(macros, false, status);
37     MicroProps& micros = impl.preProcessUnsafe(inValue, status);
38     if (U_FAILURE(status)) { return 0; }
39     int32_t length = writeNumber(micros, inValue, outString, 0, status);
40     length += writeAffixes(micros, outString, 0, length, status);
41     results->outputUnit = std::move(micros.outputUnit);
42     return length;
43 }
44 
getPrefixSuffixStatic(const MacroProps & macros,Signum signum,StandardPlural::Form plural,FormattedStringBuilder & outString,UErrorCode & status)45 int32_t NumberFormatterImpl::getPrefixSuffixStatic(const MacroProps& macros, Signum signum,
46                                                    StandardPlural::Form plural,
47                                                    FormattedStringBuilder& outString, UErrorCode& status) {
48     NumberFormatterImpl impl(macros, false, status);
49     return impl.getPrefixSuffixUnsafe(signum, plural, outString, status);
50 }
51 
52 // NOTE: C++ SPECIFIC DIFFERENCE FROM JAVA:
53 // The "safe" apply method uses a new MicroProps. In the MicroPropsGenerator, fMicros is copied into the new instance.
54 // The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation.
55 // See MicroProps::processQuantity() for details.
56 
format(UFormattedNumberData * results,UErrorCode & status) const57 int32_t NumberFormatterImpl::format(UFormattedNumberData *results, UErrorCode &status) const {
58     DecimalQuantity &inValue = results->quantity;
59     FormattedStringBuilder &outString = results->getStringRef();
60     MicroProps micros;
61     preProcess(inValue, micros, status);
62     if (U_FAILURE(status)) { return 0; }
63     int32_t length = writeNumber(micros, inValue, outString, 0, status);
64     length += writeAffixes(micros, outString, 0, length, status);
65     results->outputUnit = std::move(micros.outputUnit);
66     return length;
67 }
68 
preProcess(DecimalQuantity & inValue,MicroProps & microsOut,UErrorCode & status) const69 void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut,
70                                      UErrorCode& status) const {
71     if (U_FAILURE(status)) { return; }
72     if (fMicroPropsGenerator == nullptr) {
73         status = U_INTERNAL_PROGRAM_ERROR;
74         return;
75     }
76     fMicroPropsGenerator->processQuantity(inValue, microsOut, status);
77     microsOut.integerWidth.apply(inValue, status);
78 }
79 
preProcessUnsafe(DecimalQuantity & inValue,UErrorCode & status)80 MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErrorCode& status) {
81     if (U_FAILURE(status)) {
82         return fMicros; // must always return a value
83     }
84     if (fMicroPropsGenerator == nullptr) {
85         status = U_INTERNAL_PROGRAM_ERROR;
86         return fMicros; // must always return a value
87     }
88     fMicroPropsGenerator->processQuantity(inValue, fMicros, status);
89     fMicros.integerWidth.apply(inValue, status);
90     return fMicros;
91 }
92 
getPrefixSuffix(Signum signum,StandardPlural::Form plural,FormattedStringBuilder & outString,UErrorCode & status) const93 int32_t NumberFormatterImpl::getPrefixSuffix(Signum signum, StandardPlural::Form plural,
94                                              FormattedStringBuilder& outString, UErrorCode& status) const {
95     if (U_FAILURE(status)) { return 0; }
96     // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier).
97     // Safe path: use fImmutablePatternModifier.
98     const Modifier* modifier = fImmutablePatternModifier->getModifier(signum, plural);
99     modifier->apply(outString, 0, 0, status);
100     if (U_FAILURE(status)) { return 0; }
101     return modifier->getPrefixLength();
102 }
103 
getPrefixSuffixUnsafe(Signum signum,StandardPlural::Form plural,FormattedStringBuilder & outString,UErrorCode & status)104 int32_t NumberFormatterImpl::getPrefixSuffixUnsafe(Signum signum, StandardPlural::Form plural,
105                                                    FormattedStringBuilder& outString, UErrorCode& status) {
106     if (U_FAILURE(status)) { return 0; }
107     // #13453: DecimalFormat wants the affixes from the pattern only (modMiddle, aka pattern modifier).
108     // Unsafe path: use fPatternModifier.
109     fPatternModifier->setNumberProperties(signum, plural);
110     fPatternModifier->apply(outString, 0, 0, status);
111     if (U_FAILURE(status)) { return 0; }
112     return fPatternModifier->getPrefixLength();
113 }
114 
NumberFormatterImpl(const MacroProps & macros,bool safe,UErrorCode & status)115 NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) {
116     fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status);
117 }
118 
119 //////////
120 
121 const MicroPropsGenerator*
macrosToMicroGenerator(const MacroProps & macros,bool safe,UErrorCode & status)122 NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, UErrorCode& status) {
123     if (U_FAILURE(status)) { return nullptr; }
124     const MicroPropsGenerator* chain = &fMicros;
125 
126     // Check that macros is error-free before continuing.
127     if (macros.copyErrorTo(status)) {
128         return nullptr;
129     }
130 
131     // TODO: Accept currency symbols from DecimalFormatSymbols?
132 
133     // Pre-compute a few values for efficiency.
134     bool isCurrency = utils::unitIsCurrency(macros.unit);
135     bool isBaseUnit = utils::unitIsBaseUnit(macros.unit);
136     bool isPercent = utils::unitIsPercent(macros.unit);
137     bool isPermille = utils::unitIsPermille(macros.unit);
138     bool isCompactNotation = macros.notation.fType == Notation::NTN_COMPACT;
139     bool isAccounting =
140             macros.sign == UNUM_SIGN_ACCOUNTING || macros.sign == UNUM_SIGN_ACCOUNTING_ALWAYS ||
141             macros.sign == UNUM_SIGN_ACCOUNTING_EXCEPT_ZERO;
142     CurrencyUnit currency(u"", status);
143     if (isCurrency) {
144         currency = CurrencyUnit(macros.unit, status); // Restore CurrencyUnit from MeasureUnit
145     }
146     UNumberUnitWidth unitWidth = UNUM_UNIT_WIDTH_SHORT;
147     if (macros.unitWidth != UNUM_UNIT_WIDTH_COUNT) {
148         unitWidth = macros.unitWidth;
149     }
150     // Use CLDR unit data for all MeasureUnits (not currency and not
151     // no-unit), except use the dedicated percent pattern for percent and
152     // permille. However, use the CLDR unit data for percent/permille if a
153     // long name was requested OR if compact notation is being used, since
154     // compact notation overrides the middle modifier (micros.modMiddle)
155     // normally used for the percent pattern.
156     bool isCldrUnit = !isCurrency
157         && !isBaseUnit
158         && (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME
159             || !(isPercent || isPermille)
160             || isCompactNotation
161         );
162     bool isMixedUnit = isCldrUnit && (uprv_strcmp(macros.unit.getType(), "") == 0) &&
163                        macros.unit.getComplexity(status) == UMEASURE_UNIT_MIXED;
164 
165     // Select the numbering system.
166     LocalPointer<const NumberingSystem> nsLocal;
167     const NumberingSystem* ns;
168     if (macros.symbols.isNumberingSystem()) {
169         ns = macros.symbols.getNumberingSystem();
170     } else {
171         // TODO: Is there a way to avoid creating the NumberingSystem object?
172         ns = NumberingSystem::createInstance(macros.locale, status);
173         // Give ownership to the function scope.
174         nsLocal.adoptInstead(ns);
175     }
176     const char* nsName = U_SUCCESS(status) ? ns->getName() : "latn";
177     uprv_strncpy(fMicros.nsName, nsName, 8);
178     fMicros.nsName[8] = 0; // guarantee NUL-terminated
179 
180     // Resolve the symbols. Do this here because currency may need to customize them.
181     if (macros.symbols.isDecimalFormatSymbols()) {
182         fMicros.symbols = macros.symbols.getDecimalFormatSymbols();
183     } else {
184         LocalPointer<DecimalFormatSymbols> newSymbols(
185             new DecimalFormatSymbols(macros.locale, *ns, status), status);
186         if (U_FAILURE(status)) {
187             return nullptr;
188         }
189         if (isCurrency) {
190             newSymbols->setCurrency(currency.getISOCurrency(), status);
191             if (U_FAILURE(status)) {
192                 return nullptr;
193             }
194         }
195         fMicros.symbols = newSymbols.getAlias();
196         fSymbols.adoptInstead(newSymbols.orphan());
197     }
198 
199     // Load and parse the pattern string. It is used for grouping sizes and affixes only.
200     // If we are formatting currency, check for a currency-specific pattern.
201     const char16_t* pattern = nullptr;
202     if (isCurrency && fMicros.symbols->getCurrencyPattern() != nullptr) {
203         pattern = fMicros.symbols->getCurrencyPattern();
204     }
205     if (pattern == nullptr) {
206         CldrPatternStyle patternStyle;
207         if (isCldrUnit) {
208             patternStyle = CLDR_PATTERN_STYLE_DECIMAL;
209         } else if (isPercent || isPermille) {
210             patternStyle = CLDR_PATTERN_STYLE_PERCENT;
211         } else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) {
212             patternStyle = CLDR_PATTERN_STYLE_DECIMAL;
213         } else if (isAccounting) {
214             // NOTE: Although ACCOUNTING and ACCOUNTING_ALWAYS are only supported in currencies right now,
215             // the API contract allows us to add support to other units in the future.
216             patternStyle = CLDR_PATTERN_STYLE_ACCOUNTING;
217         } else {
218             patternStyle = CLDR_PATTERN_STYLE_CURRENCY;
219         }
220         pattern = utils::getPatternForStyle(macros.locale, nsName, patternStyle, status);
221         if (U_FAILURE(status)) {
222             return nullptr;
223         }
224     }
225     auto patternInfo = new ParsedPatternInfo();
226     if (patternInfo == nullptr) {
227         status = U_MEMORY_ALLOCATION_ERROR;
228         return nullptr;
229     }
230     fPatternInfo.adoptInstead(patternInfo);
231     PatternParser::parseToPatternInfo(UnicodeString(pattern), *patternInfo, status);
232     if (U_FAILURE(status)) {
233         return nullptr;
234     }
235 
236     /////////////////////////////////////////////////////////////////////////////////////
237     /// START POPULATING THE DEFAULT MICROPROPS AND BUILDING THE MICROPROPS GENERATOR ///
238     /////////////////////////////////////////////////////////////////////////////////////
239 
240     // Unit Preferences and Conversions as our first step
241     if (macros.usage.isSet()) {
242         if (!isCldrUnit) {
243             // We only support "usage" when the input unit is specified, and is
244             // a CLDR Unit.
245             status = U_ILLEGAL_ARGUMENT_ERROR;
246             return nullptr;
247         }
248         auto usagePrefsHandler =
249             new UsagePrefsHandler(macros.locale, macros.unit, macros.usage.fUsage, chain, status);
250         fUsagePrefsHandler.adoptInsteadAndCheckErrorCode(usagePrefsHandler, status);
251         chain = fUsagePrefsHandler.getAlias();
252     } else if (isMixedUnit) {
253         MeasureUnitImpl temp;
254         const MeasureUnitImpl &outputUnit = MeasureUnitImpl::forMeasureUnit(macros.unit, temp, status);
255         auto unitConversionHandler =
256             new UnitConversionHandler(outputUnit.units[0]->build(status), macros.unit, chain, status);
257         fUnitConversionHandler.adoptInsteadAndCheckErrorCode(unitConversionHandler, status);
258         chain = fUnitConversionHandler.getAlias();
259     }
260 
261     // Multiplier
262     if (macros.scale.isValid()) {
263         fMicros.helpers.multiplier.setAndChain(macros.scale, chain);
264         chain = &fMicros.helpers.multiplier;
265     }
266 
267     // Rounding strategy
268     Precision precision;
269     if (!macros.precision.isBogus()) {
270         precision = macros.precision;
271     } else if (isCompactNotation) {
272         precision = Precision::integer().withMinDigits(2);
273     } else if (isCurrency) {
274         precision = Precision::currency(UCURR_USAGE_STANDARD);
275     } else if (macros.usage.isSet()) {
276         // Bogus Precision - it will get set in the UsagePrefsHandler instead
277         precision = Precision();
278     } else {
279         precision = Precision::maxFraction(6);
280     }
281     UNumberFormatRoundingMode roundingMode;
282     roundingMode = macros.roundingMode;
283     fMicros.rounder = {precision, roundingMode, currency, status};
284     if (U_FAILURE(status)) {
285         return nullptr;
286     }
287 
288     // Grouping strategy
289     if (!macros.grouper.isBogus()) {
290         fMicros.grouping = macros.grouper;
291     } else if (isCompactNotation) {
292         // Compact notation uses minGrouping by default since ICU 59
293         fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2);
294     } else {
295         fMicros.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO);
296     }
297     fMicros.grouping.setLocaleData(*fPatternInfo, macros.locale);
298 
299     // Padding strategy
300     if (!macros.padder.isBogus()) {
301         fMicros.padding = macros.padder;
302     } else {
303         fMicros.padding = Padder::none();
304     }
305 
306     // Integer width
307     if (!macros.integerWidth.isBogus()) {
308         fMicros.integerWidth = macros.integerWidth;
309     } else {
310         fMicros.integerWidth = IntegerWidth::standard();
311     }
312 
313     // Sign display
314     if (macros.sign != UNUM_SIGN_COUNT) {
315         fMicros.sign = macros.sign;
316     } else {
317         fMicros.sign = UNUM_SIGN_AUTO;
318     }
319 
320     // Decimal mark display
321     if (macros.decimal != UNUM_DECIMAL_SEPARATOR_COUNT) {
322         fMicros.decimal = macros.decimal;
323     } else {
324         fMicros.decimal = UNUM_DECIMAL_SEPARATOR_AUTO;
325     }
326 
327     // Use monetary separator symbols
328     fMicros.useCurrency = isCurrency;
329 
330     // Inner modifier (scientific notation)
331     if (macros.notation.fType == Notation::NTN_SCIENTIFIC) {
332         auto newScientificHandler = new ScientificHandler(&macros.notation, fMicros.symbols, chain);
333         if (newScientificHandler == nullptr) {
334             status = U_MEMORY_ALLOCATION_ERROR;
335             return nullptr;
336         }
337         fScientificHandler.adoptInstead(newScientificHandler);
338         chain = fScientificHandler.getAlias();
339     } else {
340         // No inner modifier required
341         fMicros.modInner = &fMicros.helpers.emptyStrongModifier;
342     }
343 
344     // Middle modifier (patterns, positive/negative, currency symbols, percent)
345     auto patternModifier = new MutablePatternModifier(false);
346     if (patternModifier == nullptr) {
347         status = U_MEMORY_ALLOCATION_ERROR;
348         return nullptr;
349     }
350     fPatternModifier.adoptInstead(patternModifier);
351     patternModifier->setPatternInfo(
352             macros.affixProvider != nullptr ? macros.affixProvider
353                                             : static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias()),
354             kUndefinedField);
355     patternModifier->setPatternAttributes(fMicros.sign, isPermille);
356     if (patternModifier->needsPlurals()) {
357         patternModifier->setSymbols(
358                 fMicros.symbols,
359                 currency,
360                 unitWidth,
361                 resolvePluralRules(macros.rules, macros.locale, status),
362                 status);
363     } else {
364         patternModifier->setSymbols(fMicros.symbols, currency, unitWidth, nullptr, status);
365     }
366     if (safe) {
367         fImmutablePatternModifier.adoptInsteadAndCheckErrorCode(patternModifier->createImmutable(status),
368                                                                 status);
369     }
370     if (U_FAILURE(status)) {
371         return nullptr;
372     }
373 
374     // Outer modifier (CLDR units and currency long names)
375     if (isCldrUnit) {
376         if (macros.usage.isSet()) {
377             fLongNameMultiplexer.adoptInsteadAndCheckErrorCode(
378                 LongNameMultiplexer::forMeasureUnits(
379                     macros.locale, *fUsagePrefsHandler->getOutputUnits(), unitWidth,
380                     resolvePluralRules(macros.rules, macros.locale, status), chain, status),
381                 status);
382             chain = fLongNameMultiplexer.getAlias();
383         } else if (isMixedUnit) {
384             fMixedUnitLongNameHandler.adoptInsteadAndCheckErrorCode(new MixedUnitLongNameHandler(),
385                                                                     status);
386             MixedUnitLongNameHandler::forMeasureUnit(
387                 macros.locale, macros.unit, unitWidth,
388                 resolvePluralRules(macros.rules, macros.locale, status), chain,
389                 fMixedUnitLongNameHandler.getAlias(), status);
390             chain = fMixedUnitLongNameHandler.getAlias();
391         } else {
392             fLongNameHandler.adoptInsteadAndCheckErrorCode(new LongNameHandler(), status);
393             LongNameHandler::forMeasureUnit(macros.locale, macros.unit, macros.perUnit, unitWidth,
394                                             resolvePluralRules(macros.rules, macros.locale, status),
395                                             chain, fLongNameHandler.getAlias(), status);
396             chain = fLongNameHandler.getAlias();
397         }
398     } else if (isCurrency && unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) {
399         fLongNameHandler.adoptInsteadAndCheckErrorCode(
400             LongNameHandler::forCurrencyLongNames(
401                 macros.locale, currency, resolvePluralRules(macros.rules, macros.locale, status), chain,
402                 status),
403             status);
404         chain = fLongNameHandler.getAlias();
405     } else {
406         // No outer modifier required
407         fMicros.modOuter = &fMicros.helpers.emptyWeakModifier;
408     }
409     if (U_FAILURE(status)) {
410         return nullptr;
411     }
412 
413     // Compact notation
414     if (isCompactNotation) {
415         CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME)
416                                   ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL;
417         auto newCompactHandler = new CompactHandler(
418             macros.notation.fUnion.compactStyle,
419             macros.locale,
420             nsName,
421             compactType,
422             resolvePluralRules(macros.rules, macros.locale, status),
423             patternModifier,
424             safe,
425             chain,
426             status);
427         if (U_FAILURE(status)) {
428             return nullptr;
429         }
430         if (newCompactHandler == nullptr) {
431             status = U_MEMORY_ALLOCATION_ERROR;
432             return nullptr;
433         }
434         fCompactHandler.adoptInstead(newCompactHandler);
435         chain = fCompactHandler.getAlias();
436     }
437     if (U_FAILURE(status)) {
438         return nullptr;
439     }
440 
441     // Always add the pattern modifier as the last element of the chain.
442     if (safe) {
443         fImmutablePatternModifier->addToChain(chain);
444         chain = fImmutablePatternModifier.getAlias();
445     } else {
446         patternModifier->addToChain(chain);
447         chain = patternModifier;
448     }
449 
450     return chain;
451 }
452 
453 const PluralRules*
resolvePluralRules(const PluralRules * rulesPtr,const Locale & locale,UErrorCode & status)454 NumberFormatterImpl::resolvePluralRules(const PluralRules* rulesPtr, const Locale& locale,
455                                         UErrorCode& status) {
456     if (rulesPtr != nullptr) {
457         return rulesPtr;
458     }
459     // Lazily create PluralRules
460     if (fRules.isNull()) {
461         fRules.adoptInstead(PluralRules::forLocale(locale, status));
462     }
463     return fRules.getAlias();
464 }
465 
writeAffixes(const MicroProps & micros,FormattedStringBuilder & string,int32_t start,int32_t end,UErrorCode & status)466 int32_t NumberFormatterImpl::writeAffixes(const MicroProps& micros, FormattedStringBuilder& string,
467                                           int32_t start, int32_t end, UErrorCode& status) {
468     U_ASSERT(micros.modOuter != nullptr);
469     // Always apply the inner modifier (which is "strong").
470     int32_t length = micros.modInner->apply(string, start, end, status);
471     if (micros.padding.isValid()) {
472         length += micros.padding
473                 .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status);
474     } else {
475         length += micros.modMiddle->apply(string, start, length + end, status);
476         length += micros.modOuter->apply(string, start, length + end, status);
477     }
478     return length;
479 }
480 
writeNumber(const MicroProps & micros,DecimalQuantity & quantity,FormattedStringBuilder & string,int32_t index,UErrorCode & status)481 int32_t NumberFormatterImpl::writeNumber(const MicroProps& micros, DecimalQuantity& quantity,
482                                          FormattedStringBuilder& string, int32_t index,
483                                          UErrorCode& status) {
484     int32_t length = 0;
485     if (quantity.isInfinite()) {
486         length += string.insert(
487                 length + index,
488                 micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol),
489                 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
490                 status);
491 
492     } else if (quantity.isNaN()) {
493         length += string.insert(
494                 length + index,
495                 micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol),
496                 {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
497                 status);
498 
499     } else {
500         // Add the integer digits
501         length += writeIntegerDigits(micros, quantity, string, length + index, status);
502 
503         // Add the decimal point
504         if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) {
505             length += string.insert(
506                     length + index,
507                     micros.useCurrency ? micros.symbols->getSymbol(
508                             DecimalFormatSymbols::ENumberFormatSymbol::kMonetarySeparatorSymbol) : micros
509                             .symbols
510                             ->getSymbol(
511                                     DecimalFormatSymbols::ENumberFormatSymbol::kDecimalSeparatorSymbol),
512                     {UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD},
513                     status);
514         }
515 
516         // Add the fraction digits
517         length += writeFractionDigits(micros, quantity, string, length + index, status);
518 
519         if (length == 0) {
520             // Force output of the digit for value 0
521             length += utils::insertDigitFromSymbols(
522                     string,
523                     index,
524                     0,
525                     *micros.symbols,
526                     {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
527                     status);
528         }
529     }
530 
531     return length;
532 }
533 
writeIntegerDigits(const MicroProps & micros,DecimalQuantity & quantity,FormattedStringBuilder & string,int32_t index,UErrorCode & status)534 int32_t NumberFormatterImpl::writeIntegerDigits(const MicroProps& micros, DecimalQuantity& quantity,
535                                                 FormattedStringBuilder& string, int32_t index,
536                                                 UErrorCode& status) {
537     int length = 0;
538     int integerCount = quantity.getUpperDisplayMagnitude() + 1;
539     for (int i = 0; i < integerCount; i++) {
540         // Add grouping separator
541         if (micros.grouping.groupAtPosition(i, quantity)) {
542             length += string.insert(
543                     index,
544                     micros.useCurrency ? micros.symbols->getSymbol(
545                             DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol)
546                                        : micros.symbols->getSymbol(
547                             DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol),
548                     {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD},
549                     status);
550         }
551 
552         // Get and append the next digit value
553         int8_t nextDigit = quantity.getDigit(i);
554         length += utils::insertDigitFromSymbols(
555                 string,
556                 index,
557                 nextDigit,
558                 *micros.symbols,
559                 {UFIELD_CATEGORY_NUMBER,
560                 UNUM_INTEGER_FIELD},
561                 status);
562     }
563     return length;
564 }
565 
writeFractionDigits(const MicroProps & micros,DecimalQuantity & quantity,FormattedStringBuilder & string,int32_t index,UErrorCode & status)566 int32_t NumberFormatterImpl::writeFractionDigits(const MicroProps& micros, DecimalQuantity& quantity,
567                                                  FormattedStringBuilder& string, int32_t index,
568                                                  UErrorCode& status) {
569     int length = 0;
570     int fractionCount = -quantity.getLowerDisplayMagnitude();
571     for (int i = 0; i < fractionCount; i++) {
572         // Get and append the next digit value
573         int8_t nextDigit = quantity.getDigit(-i - 1);
574         length += utils::insertDigitFromSymbols(
575                 string,
576                 length + index,
577                 nextDigit,
578                 *micros.symbols,
579                 {UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD},
580                 status);
581     }
582     return length;
583 }
584 
585 #endif /* #if !UCONFIG_NO_FORMATTING */
586