Coverage Report

Created: 2026-01-25 06:58

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
585
    : NumberFormatterImpl(macros, true, status) {
30
585
}
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.06M
int32_t NumberFormatterImpl::format(UFormattedNumberData *results, UErrorCode &status) const {
59
2.06M
    DecimalQuantity &inValue = results->quantity;
60
2.06M
    FormattedStringBuilder &outString = results->getStringRef();
61
2.06M
    MicroProps micros;
62
2.06M
    preProcess(inValue, micros, status);
63
2.06M
    if (U_FAILURE(status)) { return 0; }
64
2.06M
    int32_t length = writeNumber(micros.simple, inValue, outString, 0, status);
65
2.06M
    length += writeAffixes(micros, outString, 0, length, status);
66
2.06M
    results->outputUnit = std::move(micros.outputUnit);
67
2.06M
    results->gender = micros.gender;
68
2.06M
    return length;
69
2.06M
}
70
71
void NumberFormatterImpl::preProcess(DecimalQuantity& inValue, MicroProps& microsOut,
72
2.06M
                                     UErrorCode& status) const {
73
2.06M
    if (U_FAILURE(status)) { return; }
74
2.06M
    if (fMicroPropsGenerator == nullptr) {
75
0
        status = U_INTERNAL_PROGRAM_ERROR;
76
0
        return;
77
0
    }
78
2.06M
    fMicroPropsGenerator->processQuantity(inValue, microsOut, status);
79
2.06M
    microsOut.integerWidth.apply(inValue, status);
80
2.06M
}
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.6k
NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) {
118
11.6k
    fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status);
119
11.6k
}
120
121
//////////
122
123
const MicroPropsGenerator*
124
11.6k
NumberFormatterImpl::macrosToMicroGenerator(const MacroProps& macros, bool safe, UErrorCode& status) {
125
11.6k
    if (U_FAILURE(status)) { return nullptr; }
126
11.6k
    const MicroPropsGenerator* chain = &fMicros;
127
128
    // Check that macros is error-free before continuing.
129
11.6k
    if (macros.copyErrorTo(status)) {
130
124
        return nullptr;
131
124
    }
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.74k
        && (unitWidth == UNUM_UNIT_WIDTH_FULL_NAME
163
1.74k
            || !(isPercent || isPermille)
164
1.74k
            || isCompactNotation
165
1.74k
        );
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.21k
    } else {
191
9.21k
        LocalPointer<DecimalFormatSymbols> newSymbols(
192
9.21k
            new DecimalFormatSymbols(macros.locale, *ns, status), status);
193
9.21k
        if (U_FAILURE(status)) {
194
0
            return nullptr;
195
0
        }
196
9.21k
        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.21k
        fMicros.simple.symbols = newSymbols.getAlias();
203
9.21k
        fSymbols.adoptInstead(newSymbols.orphan());
204
9.21k
    }
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.68k
            patternStyle = CLDR_PATTERN_STYLE_DECIMAL;
216
9.79k
        } else if (isPercent || isPermille) {
217
60
            patternStyle = CLDR_PATTERN_STYLE_PERCENT;
218
9.73k
        } else if (!isCurrency || unitWidth == UNUM_UNIT_WIDTH_FULL_NAME) {
219
9.73k
            patternStyle = CLDR_PATTERN_STYLE_DECIMAL;
220
9.73k
        } 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
118
        if (!isCldrUnit) {
250
            // We only support "usage" when the input unit is specified, and is
251
            // a CLDR Unit.
252
22
            status = U_ILLEGAL_ARGUMENT_ERROR;
253
22
            return nullptr;
254
22
        }
255
96
        auto* usagePrefsHandler =
256
96
            new UsagePrefsHandler(macros.locale, macros.unit, macros.usage.fValue, chain, status);
257
96
        fUsagePrefsHandler.adoptInsteadAndCheckErrorCode(usagePrefsHandler, status);
258
96
        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.55k
        fMicros.helpers.multiplier.setAndChain(macros.scale, chain);
268
2.55k
        chain = &fMicros.helpers.multiplier;
269
2.55k
    }
270
271
    // Rounding strategy
272
11.4k
    Precision precision;
273
11.4k
    if (!macros.precision.isBogus()) {
274
3.31k
        precision = macros.precision;
275
8.14k
    } else if (isCompactNotation) {
276
2.79k
        precision = Precision::integer().withMinDigits(2);
277
5.35k
    } else if (isCurrency) {
278
0
        precision = Precision::currency(UCURR_USAGE_STANDARD);
279
5.35k
    } else if (macros.usage.isSet()) {
280
        // Bogus Precision - it will get set in the UsagePrefsHandler instead
281
0
        precision = Precision();
282
5.35k
    } else {
283
5.35k
        precision = Precision::maxFraction(6);
284
5.35k
    }
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
96
        return nullptr;
290
96
    }
291
292
    // Grouping strategy
293
11.3k
    if (!macros.grouper.isBogus()) {
294
2.29k
        fMicros.simple.grouping = macros.grouper;
295
9.07k
    } else if (isCompactNotation) {
296
        // Compact notation uses minGrouping by default since ICU 59
297
3.33k
        fMicros.simple.grouping = Grouper::forStrategy(UNUM_GROUPING_MIN2);
298
5.73k
    } else {
299
5.73k
        fMicros.simple.grouping = Grouper::forStrategy(UNUM_GROUPING_AUTO);
300
5.73k
    }
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.34k
        fMicros.integerWidth = macros.integerWidth;
313
9.01k
    } else {
314
9.01k
        fMicros.integerWidth = IntegerWidth::standard();
315
9.01k
    }
316
317
    // Sign display
318
11.3k
    if (macros.sign != UNUM_SIGN_COUNT) {
319
2.29k
        fMicros.sign = macros.sign;
320
9.06k
    } else {
321
9.06k
        fMicros.sign = UNUM_SIGN_AUTO;
322
9.06k
    }
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.09k
    } else {
328
9.09k
        fMicros.simple.decimal = UNUM_DECIMAL_SEPARATOR_AUTO;
329
9.09k
    }
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
166
        auto* newScientificHandler =
337
166
            new ScientificHandler(&macros.notation, fMicros.simple.symbols, chain);
338
166
        if (newScientificHandler == nullptr) {
339
0
            status = U_MEMORY_ALLOCATION_ERROR;
340
0
            return nullptr;
341
0
        }
342
166
        fScientificHandler.adoptInstead(newScientificHandler);
343
166
        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
585
        fImmutablePatternModifier.adoptInsteadAndCheckErrorCode(patternModifier->createImmutable(status),
376
585
                                                                status);
377
585
    }
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.59k
        const char *unitDisplayCase = "";
390
1.59k
        if (macros.unitDisplayCase.isSet()) {
391
0
            unitDisplayCase = macros.unitDisplayCase.fValue;
392
0
        }
393
1.59k
        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.59k
        } 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.59k
        } else {
409
1.59k
            MeasureUnit unit = macros.unit;
410
1.59k
            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.59k
            fLongNameHandler.adoptInsteadAndCheckErrorCode(new LongNameHandler(), status);
424
1.59k
            LongNameHandler::forMeasureUnit(macros.locale, unit, unitWidth, unitDisplayCase,
425
1.59k
                                            resolvePluralRules(macros.rules, macros.locale, status),
426
1.59k
                                            chain, fLongNameHandler.getAlias(), status);
427
1.59k
            chain = fLongNameHandler.getAlias();
428
1.59k
        }
429
9.77k
    } 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.77k
    } else {
437
        // No outer modifier required
438
9.77k
        fMicros.modOuter = &fMicros.helpers.emptyWeakModifier;
439
9.77k
    }
440
11.3k
    if (U_FAILURE(status)) {
441
0
        return nullptr;
442
0
    }
443
444
    // Compact notation
445
11.3k
    if (isCompactNotation) {
446
3.33k
        CompactType compactType = (isCurrency && unitWidth != UNUM_UNIT_WIDTH_FULL_NAME)
447
3.33k
                                  ? CompactType::TYPE_CURRENCY : CompactType::TYPE_DECIMAL;
448
3.33k
        auto* newCompactHandler = new CompactHandler(
449
3.33k
            macros.notation.fUnion.compactStyle,
450
3.33k
            macros.locale,
451
3.33k
            nsName,
452
3.33k
            compactType,
453
3.33k
            resolvePluralRules(macros.rules, macros.locale, status),
454
3.33k
            patternModifier,
455
3.33k
            safe,
456
3.33k
            chain,
457
3.33k
            status);
458
3.33k
        if (U_FAILURE(status)) {
459
0
            return nullptr;
460
0
        }
461
3.33k
        if (newCompactHandler == nullptr) {
462
0
            status = U_MEMORY_ALLOCATION_ERROR;
463
0
            return nullptr;
464
0
        }
465
3.33k
        fCompactHandler.adoptInstead(newCompactHandler);
466
3.33k
        chain = fCompactHandler.getAlias();
467
3.33k
    }
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
585
        fImmutablePatternModifier->addToChain(chain);
475
585
        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.92k
        UErrorCode& status) {
489
4.92k
    if (rulesPtr != nullptr) {
490
0
        return rulesPtr;
491
0
    }
492
    // Lazily create PluralRules
493
4.92k
    if (fRules.isNull()) {
494
3.33k
        fRules.adoptInstead(PluralRules::forLocale(locale, status));
495
3.33k
    }
496
4.92k
    return fRules.getAlias();
497
4.92k
}
498
499
int32_t NumberFormatterImpl::writeAffixes(
500
        const MicroProps& micros,
501
        FormattedStringBuilder& string,
502
        int32_t start,
503
        int32_t end,
504
2.07M
        UErrorCode& status) {
505
2.07M
    U_ASSERT(micros.modOuter != nullptr);
506
    // Always apply the inner modifier (which is "strong").
507
2.07M
    int32_t length = micros.modInner->apply(string, start, end, status);
508
2.07M
    if (micros.padding.isValid()) {
509
0
        length += micros.padding
510
0
                .padAndApply(*micros.modMiddle, *micros.modOuter, string, start, length + end, status);
511
2.07M
    } else {
512
2.07M
        length += micros.modMiddle->apply(string, start, length + end, status);
513
2.07M
        length += micros.modOuter->apply(string, start, length + end, status);
514
2.07M
    }
515
2.07M
    return length;
516
2.07M
}
517
518
int32_t NumberFormatterImpl::writeNumber(
519
        const SimpleMicroProps& micros,
520
        DecimalQuantity& quantity,
521
        FormattedStringBuilder& string,
522
        int32_t index,
523
2.17M
        UErrorCode& status) {
524
2.17M
    int32_t length = 0;
525
2.17M
    if (quantity.isInfinite()) {
526
285
        length += string.insert(
527
285
                length + index,
528
285
                micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kInfinitySymbol),
529
285
                {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
530
285
                status);
531
532
2.17M
    } else if (quantity.isNaN()) {
533
2.96k
        length += string.insert(
534
2.96k
                length + index,
535
2.96k
                micros.symbols->getSymbol(DecimalFormatSymbols::ENumberFormatSymbol::kNaNSymbol),
536
2.96k
                {UFIELD_CATEGORY_NUMBER, UNUM_INTEGER_FIELD},
537
2.96k
                status);
538
539
2.16M
    } else {
540
        // Add the integer digits
541
2.16M
        length += writeIntegerDigits(
542
2.16M
            micros,
543
2.16M
            quantity,
544
2.16M
            string,
545
2.16M
            length + index,
546
2.16M
            status);
547
548
        // Add the decimal point
549
2.16M
        if (quantity.getLowerDisplayMagnitude() < 0 || micros.decimal == UNUM_DECIMAL_SEPARATOR_ALWAYS) {
550
22.0k
            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
22.0k
            } 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
22.0k
            } else {
564
22.0k
                length += string.insert(
565
22.0k
                    length + index,
566
22.0k
                    micros.symbols->getSymbol(
567
22.0k
                        DecimalFormatSymbols::ENumberFormatSymbol::kDecimalSeparatorSymbol),
568
22.0k
                    {UFIELD_CATEGORY_NUMBER, UNUM_DECIMAL_SEPARATOR_FIELD},
569
22.0k
                    status);
570
22.0k
            }
571
22.0k
        }
572
573
        // Add the fraction digits
574
2.16M
        length += writeFractionDigits(micros, quantity, string, length + index, status);
575
576
2.16M
        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.16M
    }
587
588
2.17M
    return length;
589
2.17M
}
590
591
int32_t NumberFormatterImpl::writeIntegerDigits(
592
        const SimpleMicroProps& micros,
593
        DecimalQuantity& quantity,
594
        FormattedStringBuilder& string,
595
        int32_t index,
596
2.16M
        UErrorCode& status) {
597
2.16M
    int length = 0;
598
2.16M
    int integerCount = quantity.getUpperDisplayMagnitude() + 1;
599
686M
    for (int i = 0; i < integerCount; i++) {
600
        // Add grouping separator
601
683M
        if (micros.grouping.groupAtPosition(i, quantity)) {
602
232M
            length += string.insert(
603
232M
                    index,
604
232M
                    micros.useCurrency ? micros.symbols->getSymbol(
605
0
                            DecimalFormatSymbols::ENumberFormatSymbol::kMonetaryGroupingSeparatorSymbol)
606
232M
                                       : micros.symbols->getSymbol(
607
232M
                            DecimalFormatSymbols::ENumberFormatSymbol::kGroupingSeparatorSymbol),
608
232M
                    {UFIELD_CATEGORY_NUMBER, UNUM_GROUPING_SEPARATOR_FIELD},
609
232M
                    status);
610
232M
        }
611
612
        // Get and append the next digit value
613
683M
        int8_t nextDigit = quantity.getDigit(i);
614
683M
        length += utils::insertDigitFromSymbols(
615
683M
                string,
616
683M
                index,
617
683M
                nextDigit,
618
683M
                *micros.symbols,
619
683M
                {UFIELD_CATEGORY_NUMBER,
620
683M
                UNUM_INTEGER_FIELD},
621
683M
                status);
622
683M
    }
623
2.16M
    return length;
624
2.16M
}
625
626
int32_t NumberFormatterImpl::writeFractionDigits(
627
        const SimpleMicroProps& micros,
628
        DecimalQuantity& quantity,
629
        FormattedStringBuilder& string,
630
        int32_t index,
631
2.16M
        UErrorCode& status) {
632
2.16M
    int length = 0;
633
2.16M
    int fractionCount = -quantity.getLowerDisplayMagnitude();
634
745M
    for (int i = 0; i < fractionCount; i++) {
635
        // Get and append the next digit value
636
743M
        int8_t nextDigit = quantity.getDigit(-i - 1);
637
743M
        length += utils::insertDigitFromSymbols(
638
743M
                string,
639
743M
                length + index,
640
743M
                nextDigit,
641
743M
                *micros.symbols,
642
743M
                {UFIELD_CATEGORY_NUMBER, UNUM_FRACTION_FIELD},
643
743M
                status);
644
743M
    }
645
2.16M
    return length;
646
2.16M
}
647
648
#endif /* #if !UCONFIG_NO_FORMATTING */