Coverage Report

Created: 2026-09-28 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/icu/icu4c/source/i18n/units_data.cpp
Line
Count
Source
1
// © 2020 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 "bytesinkutil.h"
9
#include "charstr.h"
10
#include "cstring.h"
11
#include "measunit_impl.h"
12
#include "number_decimalquantity.h"
13
#include "resource.h"
14
#include "uassert.h"
15
#include "ulocimp.h"
16
#include "unicode/locid.h"
17
#include "unicode/unistr.h"
18
#include "unicode/ures.h"
19
#include "units_data.h"
20
#include "uresimp.h"
21
#include "util.h"
22
#include <utility>
23
24
U_NAMESPACE_BEGIN
25
namespace units {
26
27
namespace {
28
29
using icu::number::impl::DecimalQuantity;
30
31
0
void trimSpaces(CharString& factor, UErrorCode& status){
32
0
   CharString trimmed;
33
0
   for (int i = 0 ; i < factor.length(); i++) {
34
0
       if (factor[i] == ' ') continue;
35
36
0
       trimmed.append(factor[i], status);
37
0
   }
38
39
0
   factor = std::move(trimmed);
40
0
}
41
42
/**
43
 * A ResourceSink that collects conversion rate information.
44
 *
45
 * This class is for use by ures_getAllItemsWithFallback.
46
 */
47
class ConversionRateDataSink : public ResourceSink {
48
  public:
49
    /**
50
     * Constructor.
51
     * @param out The vector to which ConversionRateInfo instances are to be
52
     * added. This vector must outlive the use of the ResourceSink.
53
     */
54
0
    explicit ConversionRateDataSink(MaybeStackVector<ConversionRateInfo> *out) : outVector(out) {}
55
56
    /**
57
     * Method for use by `ures_getAllItemsWithFallback`. Adds the unit
58
     * conversion rates that are found in `value` to the output vector.
59
     *
60
     * @param source This string must be "convertUnits": the resource that this
61
     * class supports reading.
62
     * @param value The "convertUnits" resource, containing unit conversion rate
63
     * information.
64
     * @param noFallback Ignored.
65
     * @param status The standard ICU error code output parameter.
66
     */
67
0
    void put(const char *source, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override {
68
0
        if (U_FAILURE(status)) { return; }
69
0
        if (uprv_strcmp(source, "convertUnits") != 0) {
70
            // This is very strict, however it is the cheapest way to be sure
71
            // that with `value`, we're looking at the convertUnits table.
72
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
73
0
            return;
74
0
        }
75
0
        ResourceTable conversionRateTable = value.getTable(status);
76
0
        const char *srcUnit;
77
        // We're reusing `value`, which seems to be a common pattern:
78
0
        for (int32_t unit = 0; conversionRateTable.getKeyAndValue(unit, srcUnit, value); unit++) {
79
0
            ResourceTable unitTable = value.getTable(status);
80
0
            const char *key;
81
0
            UnicodeString baseUnit = ICU_Utility::makeBogusString();
82
0
            UnicodeString factor = ICU_Utility::makeBogusString();
83
0
            UnicodeString offset = ICU_Utility::makeBogusString();
84
0
            UnicodeString special = ICU_Utility::makeBogusString();
85
0
            UnicodeString systems = ICU_Utility::makeBogusString();
86
0
            for (int32_t i = 0; unitTable.getKeyAndValue(i, key, value); i++) {
87
0
                if (uprv_strcmp(key, "target") == 0) {
88
0
                    baseUnit = value.getUnicodeString(status);
89
0
                } else if (uprv_strcmp(key, "factor") == 0) {
90
0
                    factor = value.getUnicodeString(status);
91
0
                } else if (uprv_strcmp(key, "offset") == 0) {
92
0
                    offset = value.getUnicodeString(status);
93
0
                } else if (uprv_strcmp(key, "special") == 0) {
94
0
                    special = value.getUnicodeString(status); // the name of a special mapping used instead of factor + optional offset.
95
0
                } else if (uprv_strcmp(key, "systems") == 0) {
96
0
                    systems = value.getUnicodeString(status);
97
0
                }
98
0
            }
99
0
            if (U_FAILURE(status)) { return; }
100
0
            if (baseUnit.isBogus() || (factor.isBogus() && special.isBogus())) {
101
                // We could not find a usable conversion rate: bad resource.
102
0
                status = U_MISSING_RESOURCE_ERROR;
103
0
                return;
104
0
            }
105
106
            // We don't have this ConversionRateInfo yet: add it.
107
0
            ConversionRateInfo *cr = outVector->emplaceBack();
108
0
            if (!cr) {
109
0
                status = U_MEMORY_ALLOCATION_ERROR;
110
0
                return;
111
0
            } else {
112
0
                cr->sourceUnit = srcUnit;
113
0
                if (cr->sourceUnit.isEmpty() != (*srcUnit == '\0')) {
114
0
                    status = U_MEMORY_ALLOCATION_ERROR;
115
0
                }
116
0
                copyInvariantChars(baseUnit, cr->baseUnit, status);
117
0
                if (U_SUCCESS(status) && !factor.isBogus()) {
118
0
                    CharString tmp;
119
0
                    tmp.appendInvariantChars(factor, status);
120
0
                    trimSpaces(tmp, status);
121
0
                    if (U_SUCCESS(status)) {
122
0
                        cr->factor = tmp.toStringPiece();
123
0
                        if (cr->factor.isEmpty() != tmp.isEmpty()) {
124
0
                            status = U_MEMORY_ALLOCATION_ERROR;
125
0
                        }
126
0
                    }
127
0
                }
128
0
                if (!offset.isBogus()) { copyInvariantChars(offset, cr->offset, status); }
129
0
                if (!special.isBogus()) { copyInvariantChars(special, cr->specialMappingName, status); }
130
0
                copyInvariantChars(systems, cr->systems, status);
131
0
            }
132
0
        }
133
0
    }
134
135
  private:
136
    MaybeStackVector<ConversionRateInfo> *outVector;
137
};
138
139
0
bool operator<(const UnitPreferenceMetadata &a, const UnitPreferenceMetadata &b) {
140
0
    return a.compareTo(b) < 0;
141
0
}
142
143
/**
144
 * A ResourceSink that collects unit preferences information.
145
 *
146
 * This class is for use by ures_getAllItemsWithFallback.
147
 */
148
class UnitPreferencesSink : public ResourceSink {
149
  public:
150
    /**
151
     * Constructor.
152
     * @param outPrefs The vector to which UnitPreference instances are to be
153
     * added. This vector must outlive the use of the ResourceSink.
154
     * @param outMetadata  The vector to which UnitPreferenceMetadata instances
155
     * are to be added. This vector must outlive the use of the ResourceSink.
156
     */
157
    explicit UnitPreferencesSink(MaybeStackVector<UnitPreference> *outPrefs,
158
                                 MaybeStackVector<UnitPreferenceMetadata> *outMetadata)
159
0
        : preferences(outPrefs), metadata(outMetadata) {}
160
161
    /**
162
     * Method for use by `ures_getAllItemsWithFallback`. Adds the unit
163
     * preferences info that are found in `value` to the output vector.
164
     *
165
     * @param source This string must be "unitPreferenceData": the resource that
166
     * this class supports reading.
167
     * @param value The "unitPreferenceData" resource, containing unit
168
     * preferences data.
169
     * @param noFallback Ignored.
170
     * @param status The standard ICU error code output parameter. Note: if an
171
     * error is returned, outPrefs and outMetadata may be inconsistent.
172
     */
173
0
    void put(const char *key, ResourceValue &value, UBool /*noFallback*/, UErrorCode &status) override {
174
0
        if (U_FAILURE(status)) { return; }
175
0
        if (uprv_strcmp(key, "unitPreferenceData") != 0) {
176
            // This is very strict, however it is the cheapest way to be sure
177
            // that with `value`, we're looking at the convertUnits table.
178
0
            status = U_ILLEGAL_ARGUMENT_ERROR;
179
0
            return;
180
0
        }
181
        // The unitPreferenceData structure (see data/misc/units.txt) contains a
182
        // hierarchy of category/usage/region, within which are a set of
183
        // preferences. Hence three for-loops and another loop for the
184
        // preferences themselves:
185
0
        ResourceTable unitPreferenceDataTable = value.getTable(status);
186
0
        const char *category;
187
0
        for (int32_t i = 0; unitPreferenceDataTable.getKeyAndValue(i, category, value); i++) {
188
0
            ResourceTable categoryTable = value.getTable(status);
189
0
            const char *usage;
190
0
            for (int32_t j = 0; categoryTable.getKeyAndValue(j, usage, value); j++) {
191
0
                ResourceTable regionTable = value.getTable(status);
192
0
                const char *region;
193
0
                for (int32_t k = 0; regionTable.getKeyAndValue(k, region, value); k++) {
194
                    // `value` now contains the set of preferences for
195
                    // category/usage/region.
196
0
                    ResourceArray unitPrefs = value.getArray(status);
197
0
                    if (U_FAILURE(status)) { return; }
198
0
                    int32_t prefLen = unitPrefs.getSize();
199
200
                    // Update metadata for this set of preferences.
201
0
                    UnitPreferenceMetadata *meta = metadata->emplaceBack(
202
0
                        category, usage, region, preferences->length(), prefLen, status);
203
0
                    if (!meta) {
204
0
                        status = U_MEMORY_ALLOCATION_ERROR;
205
0
                        return;
206
0
                    }
207
0
                    if (U_FAILURE(status)) { return; }
208
0
                    if (metadata->length() > 1) {
209
                        // Verify that unit preferences are sorted and
210
                        // without duplicates.
211
0
                        if (!(*(*metadata)[metadata->length() - 2] <
212
0
                              *(*metadata)[metadata->length() - 1])) {
213
0
                            status = U_INVALID_FORMAT_ERROR;
214
0
                            return;
215
0
                        }
216
0
                    }
217
218
                    // Collect the individual preferences.
219
0
                    for (int32_t i = 0; unitPrefs.getValue(i, value); i++) {
220
0
                        UnitPreference *up = preferences->emplaceBack();
221
0
                        if (!up) {
222
0
                            status = U_MEMORY_ALLOCATION_ERROR;
223
0
                            return;
224
0
                        }
225
0
                        ResourceTable unitPref = value.getTable(status);
226
0
                        if (U_FAILURE(status)) { return; }
227
0
                        for (int32_t i = 0; unitPref.getKeyAndValue(i, key, value); ++i) {
228
0
                            if (uprv_strcmp(key, "unit") == 0) {
229
0
                                copyInvariantChars(value.getUnicodeString(status), up->unit, status);
230
0
                            } else if (uprv_strcmp(key, "geq") == 0) {
231
0
                                int32_t length;
232
0
                                const char16_t *g = value.getString(length, status);
233
0
                                CharString geq;
234
0
                                geq.appendInvariantChars(g, length, status);
235
0
                                DecimalQuantity dq;
236
0
                                dq.setToDecNumber(geq.data(), status);
237
0
                                up->geq = dq.toDouble();
238
0
                            } else if (uprv_strcmp(key, "skeleton") == 0) {
239
0
                                up->skeleton = value.getUnicodeString(status);
240
0
                            }
241
0
                        }
242
0
                    }
243
0
                }
244
0
            }
245
0
        }
246
0
    }
247
248
  private:
249
    MaybeStackVector<UnitPreference> *preferences;
250
    MaybeStackVector<UnitPreferenceMetadata> *metadata;
251
};
252
253
int32_t binarySearch(const MaybeStackVector<UnitPreferenceMetadata> *metadata,
254
                     const UnitPreferenceMetadata &desired, bool *foundCategory, bool *foundUsage,
255
0
                     bool *foundRegion, UErrorCode &status) {
256
0
    if (U_FAILURE(status)) { return -1; }
257
0
    int32_t start = 0;
258
0
    int32_t end = metadata->length();
259
0
    *foundCategory = false;
260
0
    *foundUsage = false;
261
0
    *foundRegion = false;
262
0
    while (start < end) {
263
0
        int32_t mid = (start + end) / 2;
264
0
        int32_t cmp = (*metadata)[mid]->compareTo(desired, foundCategory, foundUsage, foundRegion);
265
0
        if (cmp < 0) {
266
0
            start = mid + 1;
267
0
        } else if (cmp > 0) {
268
0
            end = mid;
269
0
        } else {
270
0
            return mid;
271
0
        }
272
0
    }
273
0
    return -1;
274
0
}
275
276
/**
277
 * Finds the UnitPreferenceMetadata instance that matches the given category,
278
 * usage and region: if missing, region falls back to "001", and usage
279
 * repeatedly drops tailing components, eventually trying "default"
280
 * ("land-agriculture-grain" -> "land-agriculture" -> "land" -> "default").
281
 *
282
 * @param metadata The full list of UnitPreferenceMetadata instances.
283
 * @param category The category to search for. See getUnitCategory().
284
 * @param usage The usage for which formatting preferences is needed. If the
285
 * given usage is not known, automatic fallback occurs, see function description
286
 * above.
287
 * @param region The region for which preferences are needed. If there are no
288
 * region-specific preferences, this function automatically falls back to the
289
 * "001" region (global).
290
 * @param status The standard ICU error code output parameter.
291
 *   * If an invalid category is given, status will be U_ILLEGAL_ARGUMENT_ERROR.
292
 *   * If fallback to "default" or "001" didn't resolve, status will be
293
 *     U_MISSING_RESOURCE.
294
 * @return The index into the metadata vector which represents the appropriate
295
 * preferences. If appropriate preferences are not found, -1 is returned.
296
 */
297
int32_t getPreferenceMetadataIndex(const MaybeStackVector<UnitPreferenceMetadata> *metadata,
298
                                   StringPiece category, StringPiece usage, StringPiece region,
299
0
                                   UErrorCode &status) {
300
0
    if (U_FAILURE(status)) { return -1; }
301
0
    bool foundCategory, foundUsage, foundRegion;
302
0
    UnitPreferenceMetadata desired(category, usage, region, -1, -1, status);
303
0
    int32_t idx = binarySearch(metadata, desired, &foundCategory, &foundUsage, &foundRegion, status);
304
0
    if (U_FAILURE(status)) { return -1; }
305
0
    if (idx >= 0) { return idx; }
306
0
    if (!foundCategory) {
307
        // TODO: failures can happen if units::getUnitCategory returns a category
308
        // that does not appear in unitPreferenceData. Do we want a unit test that
309
        // checks unitPreferenceData has full coverage of categories? Or just trust
310
        // CLDR?
311
0
        status = U_ILLEGAL_ARGUMENT_ERROR;
312
0
        return -1;
313
0
    }
314
0
    U_ASSERT(foundCategory);
315
0
    while (!foundUsage) {
316
0
        int32_t lastDashIdx = desired.usage.lastIndexOf('-');
317
0
        if (lastDashIdx > 0) {
318
0
            desired.usage.truncate(lastDashIdx);
319
0
        } else if (uprv_strcmp(desired.usage.data(), "default") != 0) {
320
0
            desired.usage.truncate(0).append("default", status);
321
0
        } else {
322
            // "default" is not supposed to be missing for any valid category.
323
0
            status = U_MISSING_RESOURCE_ERROR;
324
0
            return -1;
325
0
        }
326
0
        idx = binarySearch(metadata, desired, &foundCategory, &foundUsage, &foundRegion, status);
327
0
        if (U_FAILURE(status)) { return -1; }
328
0
    }
329
0
    U_ASSERT(foundCategory);
330
0
    U_ASSERT(foundUsage);
331
0
    if (!foundRegion) {
332
0
        if (uprv_strcmp(desired.region.data(), "001") != 0) {
333
0
            desired.region.truncate(0).append("001", status);
334
0
            idx = binarySearch(metadata, desired, &foundCategory, &foundUsage, &foundRegion, status);
335
0
        }
336
0
        if (!foundRegion) {
337
            // "001" is not supposed to be missing for any valid usage.
338
0
            status = U_MISSING_RESOURCE_ERROR;
339
0
            return -1;
340
0
        }
341
0
    }
342
0
    U_ASSERT(foundCategory);
343
0
    U_ASSERT(foundUsage);
344
0
    U_ASSERT(foundRegion);
345
0
    U_ASSERT(idx >= 0);
346
0
    return idx;
347
0
}
348
349
} // namespace
350
351
UnitPreferenceMetadata::UnitPreferenceMetadata(StringPiece category, StringPiece usage,
352
                                               StringPiece region, int32_t prefsOffset,
353
0
                                               int32_t prefsCount, UErrorCode &status) {
354
0
    this->category.append(category, status);
355
0
    this->usage.append(usage, status);
356
0
    this->region.append(region, status);
357
0
    this->prefsOffset = prefsOffset;
358
0
    this->prefsCount = prefsCount;
359
0
}
360
361
0
int32_t UnitPreferenceMetadata::compareTo(const UnitPreferenceMetadata &other) const {
362
0
    int32_t cmp = uprv_strcmp(category.data(), other.category.data());
363
0
    if (cmp == 0) {
364
0
        cmp = uprv_strcmp(usage.data(), other.usage.data());
365
0
    }
366
0
    if (cmp == 0) {
367
0
        cmp = uprv_strcmp(region.data(), other.region.data());
368
0
    }
369
0
    return cmp;
370
0
}
371
372
int32_t UnitPreferenceMetadata::compareTo(const UnitPreferenceMetadata &other, bool *foundCategory,
373
0
                                          bool *foundUsage, bool *foundRegion) const {
374
0
    int32_t cmp = uprv_strcmp(category.data(), other.category.data());
375
0
    if (cmp == 0) {
376
0
        *foundCategory = true;
377
0
        cmp = uprv_strcmp(usage.data(), other.usage.data());
378
0
    }
379
0
    if (cmp == 0) {
380
0
        *foundUsage = true;
381
0
        cmp = uprv_strcmp(region.data(), other.region.data());
382
0
    }
383
0
    if (cmp == 0) {
384
0
        *foundRegion = true;
385
0
    }
386
0
    return cmp;
387
0
}
388
389
// TODO: this may be unnecessary. Fold into ConversionRates class? Or move to anonymous namespace?
390
0
void U_I18N_API getAllConversionRates(MaybeStackVector<ConversionRateInfo> &result, UErrorCode &status) {
391
0
    LocalUResourceBundlePointer unitsBundle(ures_openDirect(nullptr, "units", &status));
392
0
    ConversionRateDataSink sink(&result);
393
0
    ures_getAllItemsWithFallback(unitsBundle.getAlias(), "convertUnits", sink, status);
394
0
}
395
396
const ConversionRateInfo *ConversionRates::extractConversionInfo(StringPiece source,
397
0
                                                                 UErrorCode &status) const {
398
0
    for (size_t i = 0, n = conversionInfo_.length(); i < n; ++i) {
399
0
        if (uprv_strncmp(conversionInfo_[i]->sourceUnit.data(), source.data(), source.size()) == 0) {
400
0
            return conversionInfo_[i];
401
0
        }
402
0
    }
403
404
0
    status = U_INTERNAL_PROGRAM_ERROR;
405
0
    return nullptr;
406
0
}
407
408
0
UnitPreferences::UnitPreferences(UErrorCode& status) {
409
0
    LocalUResourceBundlePointer unitsBundle(ures_openDirect(nullptr, "units", &status));
410
0
    UnitPreferencesSink sink(&unitPrefs_, &metadata_);
411
0
    ures_getAllItemsWithFallback(unitsBundle.getAlias(), "unitPreferenceData", sink, status);
412
0
}
413
414
0
CharString getKeyWordValue(const Locale &locale, StringPiece kw, UErrorCode &status) {
415
0
    if (U_FAILURE(status)) { return {}; }
416
0
    auto result = locale.getKeywordValue<CharString>(kw, status);
417
0
    if (U_SUCCESS(status) && result.isEmpty()) {
418
0
        status = U_MISSING_RESOURCE_ERROR;
419
0
    }
420
0
    return result;
421
0
}
422
423
MaybeStackVector<UnitPreference> UnitPreferences::getPreferencesFor(StringPiece category,
424
                                                                    StringPiece usage,
425
                                                                    const Locale& locale,
426
0
                                                                    UErrorCode& status) const {
427
0
    MaybeStackVector<UnitPreference> result;
428
429
    // TODO: remove this once all the categories are allowed.
430
    // WARNING: when this is removed please make sure to keep the "fahrenhe" => "fahrenheit" mapping
431
0
    UErrorCode internalMuStatus = U_ZERO_ERROR;
432
0
    if (category.compare("temperature") == 0) {
433
0
        CharString localeUnitCharString = getKeyWordValue(locale, "mu", internalMuStatus);
434
0
        if (U_SUCCESS(internalMuStatus)) {
435
            // The value for -u-mu- is `fahrenhe`, but CLDR and everything else uses `fahrenheit`
436
0
            if (localeUnitCharString == "fahrenhe") {
437
0
                localeUnitCharString = CharString("fahrenheit", status);
438
0
            }
439
            // TODO: use the unit category as Java especially when all the categories are allowed..
440
0
            if (localeUnitCharString == "celsius"
441
0
                || localeUnitCharString == "fahrenheit"
442
0
                || localeUnitCharString == "kelvin"
443
0
            ) {
444
0
                UnitPreference unitPref;
445
0
                unitPref.unit = localeUnitCharString.toStringPiece();
446
0
                if (unitPref.unit.isEmpty() != localeUnitCharString.isEmpty()) {
447
0
                    status = U_MISSING_RESOURCE_ERROR;
448
0
                    return result;
449
0
                }
450
0
                result.emplaceBackAndCheckErrorCode(status, unitPref);
451
0
                return result;
452
0
            }
453
0
        }
454
0
    }
455
456
0
    CharString region = ulocimp_getRegionForSupplementalData(locale.getName(), true, status);
457
458
    // Check the locale system tag, e.g `ms=metric`.
459
0
    UErrorCode internalMeasureTagStatus = U_ZERO_ERROR;
460
0
    CharString localeSystem = getKeyWordValue(locale, "measure", internalMeasureTagStatus);
461
0
    bool isLocaleSystem = false;
462
0
    if (U_SUCCESS(internalMeasureTagStatus) && (localeSystem == "metric" || localeSystem == "ussystem" || localeSystem == "uksystem")) {
463
0
        isLocaleSystem = true;
464
0
    }
465
466
0
    int32_t idx =
467
0
        getPreferenceMetadataIndex(&metadata_, category, usage, region.toStringPiece(), status);
468
0
    if (U_FAILURE(status)) {
469
0
        return result;
470
0
    }
471
472
0
    U_ASSERT(idx >= 0); // Failures should have been taken care of by `status`.
473
0
    const UnitPreferenceMetadata *m = metadata_[idx];
474
        
475
0
    if (isLocaleSystem) {
476
        // if the locale ID specifies a measurment system, check if ALL of the units we got back
477
        // are members of that system (or are "metric_adjacent", which we consider to match all
478
        // the systems)
479
0
        bool unitsMatchSystem = true;
480
0
        ConversionRates rates(status);
481
0
        for (int32_t i = 0; unitsMatchSystem && i < m->prefsCount; i++) {
482
0
            const UnitPreference& unitPref = *(unitPrefs_[i + m->prefsOffset]);
483
0
            MeasureUnitImpl measureUnit = MeasureUnitImpl::forIdentifier(unitPref.unit.data(), status);
484
0
            for (int32_t j = 0; unitsMatchSystem && j < measureUnit.singleUnits.length(); j++) {
485
0
                const SingleUnitImpl* singleUnit = measureUnit.singleUnits[j];
486
0
                const ConversionRateInfo* rateInfo = rates.extractConversionInfo(singleUnit->getSimpleUnitID(), status);
487
0
                const char* systems = rateInfo->systems.data();
488
                // "metric-adjacent" is considered to match all the locale systems
489
0
                if (uprv_strstr(systems, "metric_adjacent") == nullptr) {
490
0
                    if (uprv_strstr(systems, localeSystem.data()) == nullptr) {
491
0
                        unitsMatchSystem = false;
492
0
                    }
493
0
                }
494
0
            }
495
0
        }
496
        
497
        // if any of the units we got back above don't match the mearurement system the locale ID asked for,
498
        // throw out the region and just load the units for the base region for the requested measurement system
499
0
        if (!unitsMatchSystem) {
500
0
            region.clear();
501
0
            if (localeSystem == "ussystem") {
502
0
                region.append("US", status);
503
0
            } else if (localeSystem == "uksystem") {
504
0
                region.append("GB", status);
505
0
            } else {
506
0
                region.append("001", status);
507
0
            }
508
0
            idx = getPreferenceMetadataIndex(&metadata_, category, usage, region.toStringPiece(), status);
509
0
            if (U_FAILURE(status)) {
510
0
                return result;
511
0
            }
512
            
513
0
            m = metadata_[idx];
514
0
        }
515
0
    }
516
        
517
0
    for (int32_t i = 0; i < m->prefsCount; i++) {
518
0
        result.emplaceBackAndCheckErrorCode(status, *(unitPrefs_[i + m->prefsOffset]));
519
0
    }
520
0
    return result;
521
0
}
522
523
} // namespace units
524
U_NAMESPACE_END
525
526
#endif /* #if !UCONFIG_NO_FORMATTING */