Coverage Report

Created: 2025-10-24 06:54

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
550
    : NumberFormatterImpl(macros, true, status) {
30
550
}
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.9k
    int32_t length = writeNumber(micros.simple, inValue, outString, 0, status);
40
10.9k
    length += writeAffixes(micros, outString, 0, length, status);
41
10.9k
    results->outputUnit = std::move(micros.outputUnit);
42
10.9k
    results->gender = micros.gender;
43
10.9k
    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
1.25M
int32_t NumberFormatterImpl::format(UFormattedNumberData *results, UErrorCode &status) const {
59
1.25M
    DecimalQuantity &inValue = results->quantity;
60
1.25M
    FormattedStringBuilder &outString = results->getStringRef();
61
1.25M
    MicroProps micros;
62
1.25M
    preProcess(inValue, micros, status);
63
1.25M
    if (U_FAILURE(status)) { return 0; }
64
1.25M
    int32_t length = writeNumber(micros.simple, inValue, outString, 0, status);
65
1.25M
    length += writeAffixes(micros, outString, 0, length, status);
66
1.25M
    results->outputUnit = std::move(micros.outputUnit);
67
1.25M
    results->gender = micros.gender;
68
1.25M
    return length;
69
1.25M
}
70
71
void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut,
72
1.25M
                                     UErrorCode& status) const {
73
1.25M
    if (U_FAILURE(status)) { return; }
74
1.25M
    if (fMicroPropsGenerator == nullptr) {
75
0
        status = U_INTERNAL_PROGRAM_ERROR;
76
0
        return;
77
0
    }
78
1.25M
    fMicroPropsGenerator->processQuantity(inValue, microsOut, status);
79
1.25M
    microsOut.integerWidth.apply(inValue, status);
80
1.25M
}
81
82
11.0k
MicroProps& NumberFormatterImpl::preProcessUnsafe(DecimalQuantity& inValue, UErrorCode& status) {
83
11.0k
    if (U_FAILURE(status)) {
84
116
        return fMicros; // must always return a value
85
116
    }
86
10.9k
    if (fMicroPropsGenerator == nullptr) {
87
0
        status = U_INTERNAL_PROGRAM_ERROR;
88
0
        return fMicros; // must always return a value
89
0
    }
90
10.9k
    fMicroPropsGenerator->processQuantity(inValue, fMicros, status);
91
10.9k
    fMicros.integerWidth.apply(inValue, status);
92
10.9k
    return fMicros;
93
10.9k
}
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
106
        return nullptr;
131
106
    }
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.60k
        && (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME
163
1.60k
            || !(isPercent || isPermille)
164
1.60k
            || isCompactNotation
165
1.60k
        );
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.19k
        fMicros.simple.symbols = macros.symbols.getDecimalFormatSymbols();
190
9.28k
    } else {
191
9.28k
        LocalPointer<DecimalFormatSymbols> newSymbols(
192
9.28k
            new DecimalFormatSymbols(macros.locale, *ns, status), status);
193
9.28k
        if (U_FAILURE(status)) {
194
0
            return nullptr;
195
0
        }
196
9.28k
        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.28k
        fMicros.simple.symbols = newSymbols.getAlias();
203
9.28k
        fSymbols.adoptInstead(newSymbols.orphan());
204
9.28k
    }
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.55k
            patternStyle = CLDR_PATTERN_STYLE_DECIMAL;
216
9.92k
        } else if (isPercent || isPermille) {
217
52
            patternStyle = CLDR_PATTERN_STYLE_PERCENT;
218
9.87k
        } else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) {
219
9.87k
            patternStyle = CLDR_PATTERN_STYLE_DECIMAL;
220
9.87k
        } 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
10
        if (!isCldrUnit) {
250
            // We only support "usage" when the input unit is specified, and is
251
            // a CLDR Unit.
252
10
            status = U_ILLEGAL_ARGUMENT_ERROR;
253
10
            return nullptr;
254
10
        }
255
0
        auto* usagePrefsHandler =
256
0
            new UsagePrefsHandler(macros.locale, macros.unit, macros.usage.fValue, chain, status);
257
0
        fUsagePrefsHandler.adoptInsteadAndCheckErrorCode(usagePrefsHandler, status);
258
0
        chain = fUsagePrefsHandler.getAlias();
259
11.4k
    } 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.53k
        fMicros.helpers.multiplier.setAndChain(macros.scale, chain);
268
2.53k
        chain = &fMicros.helpers.multiplier;
269
2.53k
    }
270
271
    // Rounding strategy
272
11.4k
    Precision precision;
273
11.4k
    if (!macros.precision.isBogus()) {
274
3.24k
        precision = macros.precision;
275
8.22k
    } else if (isCompactNotation) {
276
2.56k
        precision = Precision::integer().withMinDigits(2);
277
5.65k
    } else if (isCurrency) {
278
0
        precision = Precision::currency(UCURR_USAGE_STANDARD);
279
5.65k
    } else if (macros.usage.isSet()) {
280
        // Bogus Precision - it will get set in the UsagePrefsHandler instead
281
0
        precision = Precision();
282
5.65k
    } else {
283
5.65k
        precision = Precision::maxFraction(6);
284
5.65k
    }
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
0
        return nullptr;
290
0
    }
291
292
    // Grouping strategy
293
11.4k
    if (!macros.grouper.isBogus()) {
294
2.22k
        fMicros.simple.grouping = macros.grouper;
295
9.24k
    } else if (isCompactNotation) {
296
        // Compact notation uses minGrouping by default since ICU 59
297
3.16k
        fMicros.simple.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2);
298
6.08k
    } else {
299
6.08k
        fMicros.simple.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO);
300
6.08k
    }
301
11.4k
    fMicros.simple.grouping.setLocaleData(*fPatternInfo, macros.locale);
302
303
    // Padding strategy
304
11.4k
    if (!macros.padder.isBogus()) {
305
0
        fMicros.padding = macros.padder;
306
11.4k
    } else {
307
11.4k
        fMicros.padding = Padder::none();
308
11.4k
    }
309
310
    // Integer width
311
11.4k
    if (!macros.integerWidth.isBogus()) {
312
2.28k
        fMicros.integerWidth = macros.integerWidth;
313
9.18k
    } else {
314
9.18k
        fMicros.integerWidth = IntegerWidth::standard();
315
9.18k
    }
316
317
    // Sign display
318
11.4k
    if (macros.sign != UNUM_SIGN_COUNT) {
319
2.23k
        fMicros.sign = macros.sign;
320
9.23k
    } else {
321
9.23k
        fMicros.sign = UNUM_SIGN_AUTO;
322
9.23k
    }
323
324
    // Decimal mark display
325
11.4k
    if (macros.decimal != UNUM_DECIMAL_SEPARATOR_COUNT) {
326
2.19k
        fMicros.simple.decimal = macros.decimal;
327
9.27k
    } else {
328
9.27k
        fMicros.simple.decimal = UNUM_DECIMAL_SEPARATOR_AUTO;
329
9.27k
    }
330
331
    // Use monetary separator symbols
332
11.4k
    fMicros.simple.useCurrency = isCurrency;
333
334
    // Inner modifier (scientific notation)
335
11.4k
    if (macros.notation.fType == Notation::NTN_SCIENTIFIC) {
336
168
        auto* newScientificHandler =
337
168
            new ScientificHandler(&macros.notation, fMicros.simple.symbols, chain);
338
168
        if (newScientificHandler == nullptr) {
339
0
            status = U_MEMORY_ALLOCATION_ERROR;
340
0
            return nullptr;
341
0
        }
342
168
        fScientificHandler.adoptInstead(newScientificHandler);
343
168
        chain = fScientificHandler.getAlias();
344
11.3k
    } else {
345
        // No inner modifier required
346
11.3k
        fMicros.modInner = &fMicros.helpers.emptyStrongModifier;
347
11.3k
    }
348
349
    // Middle modifier (patterns, positive/negative, currency symbols, percent)
350
11.4k
    auto* patternModifier = new MutablePatternModifier(false);
351
11.4k
    if (patternModifier == nullptr) {
352
0
        status = U_MEMORY_ALLOCATION_ERROR;
353
0
        return nullptr;
354
0
    }
355
11.4k
    fPatternModifier.adoptInstead(patternModifier);
356
11.4k
    const AffixPatternProvider* affixProvider =
357
11.4k
        macros.affixProvider != nullptr && (
358
                // For more information on this condition, see ICU-22073
359
2.19k
                !isCompactNotation || isCurrency == macros.affixProvider->hasCurrencySign())
360
11.4k
            ? macros.affixProvider
361
11.4k
            : static_cast<const AffixPatternProvider*>(fPatternInfo.getAlias());
362
11.4k
    patternModifier->setPatternInfo(affixProvider, kUndefinedField);
363
11.4k
    patternModifier->setPatternAttributes(fMicros.sign, isPermille, macros.approximately);
364
11.4k
    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.4k
    } else {
372
11.4k
        patternModifier->setSymbols(fMicros.simple.symbols, currency, unitWidth, nullptr, status);
373
11.4k
    }
374
11.4k
    if (safe) {
375
550
        fImmutablePatternModifier.adoptInsteadAndCheckErrorCode(patternModifier->createImmutable(status),
376
550
                                                                status);
377
550
    }
378
11.4k
    if (U_FAILURE(status)) {
379
0
        return nullptr;
380
0
    }
381
382
    // currencyAsDecimal
383
11.4k
    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.4k
    if (isCldrUnit) {
389
1.55k
        const char *unitDisplayCase = "";
390
1.55k
        if (macros.unitDisplayCase.isSet()) {
391
0
            unitDisplayCase = macros.unitDisplayCase.fValue;
392
0
        }
393
1.55k
        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.55k
        } 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.55k
        } else {
409
1.55k
            MeasureUnit unit = macros.unit;
410
1.55k
            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.55k
            fLongNameHandler.adoptInsteadAndCheckErrorCode(new LongNameHandler(), status);
424
1.55k
            LongNameHandler::forMeasureUnit(macros.locale, unit, unitWidth, unitDisplayCase,
425
1.55k
                                            resolvePluralRules(macros.rules, macros.locale, status),
426
1.55k
                                            chain, fLongNameHandler.getAlias(), status);
427
1.55k
            chain = fLongNameHandler.getAlias();
428
1.55k
        }
429
9.91k
    } 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.91k
    } else {
437
        // No outer modifier required
438
9.91k
        fMicros.modOuter = &fMicros.helpers.emptyWeakModifier;
439
9.91k
    }
440
11.4k
    if (U_FAILURE(status)) {
441
0
        return nullptr;
442
0
    }
443
444
    // Compact notation
445
11.4k
    if (isCompactNotation) {
446
3.16k
        CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME)
447
3.16k
                                  ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL;
448
3.16k
        auto* newCompactHandler = new CompactHandler(
449
3.16k
            macros.notation.fUnion.compactStyle,
450
3.16k
            macros.locale,
451
3.16k
            nsName,
452
3.16k
            compactType,
453
3.16k
            resolvePluralRules(macros.rules, macros.locale, status),
454
3.16k
            patternModifier,
455
3.16k
            safe,
456
3.16k
            chain,
457
3.16k
            status);
458
3.16k
        if (U_FAILURE(status)) {
459
0
            return nullptr;
460
0
        }
461
3.16k
        if (newCompactHandler == nullptr) {
462
0
            status = U_MEMORY_ALLOCATION_ERROR;
463
0
            return nullptr;
464
0
        }
465
3.16k
        fCompactHandler.adoptInstead(newCompactHandler);
466
3.16k
        chain = fCompactHandler.getAlias();
467
3.16k
    }
468
11.4k
    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.4k
    if (safe) {
474
550
        fImmutablePatternModifier->addToChain(chain);
475
550
        chain = fImmutablePatternModifier.getAlias();
476
10.9k
    } else {
477
10.9k
        patternModifier->addToChain(chain);
478
10.9k
        chain = patternModifier;
479
10.9k
    }
480
481
11.4k
    return chain;
482
11.4k
}
483
484
const PluralRules*
485
NumberFormatterImpl::resolvePluralRules(
486
        const PluralRules* rulesPtr,
487
        const Locale& locale,
488
4.72k
        UErrorCode& status) {
489
4.72k
    if (rulesPtr != nullptr) {
490
0
        return rulesPtr;
491
0
    }
492
    // Lazily create PluralRules
493
4.72k
    if (fRules.isNull()) {
494
3.16k
        fRules.adoptInstead(PluralRules::forLocale(locale, status));
495
3.16k
    }
496
4.72k
    return fRules.getAlias();
497
4.72k
}
498
499
int32_t NumberFormatterImpl::writeAffixes(
500
        const MicroProps& micros,
501
        FormattedStringBuilder& string,
502
        int32_t start,
503
        int32_t end,
504
1.26M
        UErrorCode& status) {
505
1.26M
    U_ASSERT(micros.modOuter != nullptr);
506
    // Always apply the inner modifier (which is "strong").
507
1.26M
    int32_t length = micros.modInner->apply(string, start, end, status);
508
1.26M
    if (micros.padding.isValid()) {
509
0
        length += micros.padding
510
0
                .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status);
511
1.26M
    } else {
512
1.26M
        length += micros.modMiddle->apply(string, start, length + end, status);
513
1.26M
        length += micros.modOuter->apply(string, start, length + end, status);
514
1.26M
    }
515
1.26M
    return length;
516
1.26M
}
517
518
int32_t NumberFormatterImpl::writeNumber(
519
        const SimpleMicroProps& micros,
520
        DecimalQuantity& quantity,
521
        FormattedStringBuilder& string,
522
        int32_t index,
523
1.36M
        UErrorCode& status) {
524
1.36M
    int32_t length = 0;
525
1.36M
    if (quantity.isInfinite()) {
526
280
        length += string.insert(
527
280
                length + index,
528
280
                micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol),
529
280
                {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
530
280
                status);
531
532
1.36M
    } else if (quantity.isNaN()) {
533
2.62k
        length += string.insert(
534
2.62k
                length + index,
535
2.62k
                micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol),
536
2.62k
                {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
537
2.62k
                status);
538
539
1.35M
    } else {
540
        // Add the integer digits
541
1.35M
        length += writeIntegerDigits(
542
1.35M
            micros,
543
1.35M
            quantity,
544
1.35M
            string,
545
1.35M
            length + index,
546
1.35M
            status);
547
548
        // Add the decimal point
549
1.35M
        if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) {
550
20.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
20.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
20.4k
            } else {
564
20.4k
                length += string.insert(
565
20.4k
                    length + index,
566
20.4k
                    micros.symbols->getSymbol(
567
20.4k
                        DecimalFormatSymbols::ENumberFormatSymbol::kDecimalSeparatorSymbol),
568
20.4k
                    {UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD},
569
20.4k
                    status);
570
20.4k
            }
571
20.4k
        }
572
573
        // Add the fraction digits
574
1.35M
        length += writeFractionDigits(micros, quantity, string, length + index, status);
575
576
1.35M
        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
1.35M
    }
587
588
1.36M
    return length;
589
1.36M
}
590
591
int32_t NumberFormatterImpl::writeIntegerDigits(
592
        const SimpleMicroProps& micros,
593
        DecimalQuantity& quantity,
594
        FormattedStringBuilder& string,
595
        int32_t index,
596
1.35M
        UErrorCode& status) {
597
1.35M
    int length = 0;
598
1.35M
    int integerCount = quantity.getUpperDisplayMagnitude() + 1;
599
750M
    for (int i = 0; i < integerCount; i++) {
600
        // Add grouping separator
601
749M
        if (micros.grouping.groupAtPosition(i, quantity)) {
602
252M
            length += string.insert(
603
252M
                    index,
604
252M
                    micros.useCurrency ? micros.symbols->getSymbol(
605
0
                            DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol)
606
252M
                                       : micros.symbols->getSymbol(
607
252M
                            DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol),
608
252M
                    {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD},
609
252M
                    status);
610
252M
        }
611
612
        // Get and append the next digit value
613
749M
        int8_t nextDigit = quantity.getDigit(i);
614
749M
        length += utils::insertDigitFromSymbols(
615
749M
                string,
616
749M
                index,
617
749M
                nextDigit,
618
749M
                *micros.symbols,
619
749M
                {UFIELD_CATEGORY_NUMBER,
620
749M
                UNUM_INTEGER_FIELD},
621
749M
                status);
622
749M
    }
623
1.35M
    return length;
624
1.35M
}
625
626
int32_t NumberFormatterImpl::writeFractionDigits(
627
        const SimpleMicroProps& micros,
628
        DecimalQuantity& quantity,
629
        FormattedStringBuilder& string,
630
        int32_t index,
631
1.35M
        UErrorCode& status) {
632
1.35M
    int length = 0;
633
1.35M
    int fractionCount = -quantity.getLowerDisplayMagnitude();
634
669M
    for (int i = 0; i < fractionCount; i++) {
635
        // Get and append the next digit value
636
667M
        int8_t nextDigit = quantity.getDigit(-i - 1);
637
667M
        length += utils::insertDigitFromSymbols(
638
667M
                string,
639
667M
                length + index,
640
667M
                nextDigit,
641
667M
                *micros.symbols,
642
667M
                {UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD},
643
667M
                status);
644
667M
    }
645
1.35M
    return length;
646
1.35M
}
647
648
#endif /* #if !UCONFIG_NO_FORMATTING */