Coverage Report

Created: 2026-03-31 06:12

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