Coverage Report

Created: 2025-12-07 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/icu/icu4c/source/i18n/measunit_impl.h
Line
Count
Source
1
// © 2020 and later: Unicode, Inc. and others.
2
// License & terms of use: http://www.unicode.org/copyright.html
3
4
#ifndef __MEASUNIT_IMPL_H__
5
#define __MEASUNIT_IMPL_H__
6
7
#include "unicode/utypes.h"
8
9
#if !UCONFIG_NO_FORMATTING
10
11
#include "unicode/measunit.h"
12
#include "cmemory.h"
13
#include "charstr.h"
14
#include "fixedstring.h"
15
16
U_NAMESPACE_BEGIN
17
18
namespace number::impl {
19
class LongNameHandler;
20
}
21
22
static const char16_t kDefaultCurrency[] = u"XXX";
23
static const char kDefaultCurrency8[] = "XXX";
24
25
/**
26
 * Looks up the "unitQuantity" (aka "type" or "category") of a base unit
27
 * identifier. The category is returned via `result`, which must initially be
28
 * empty.
29
 *
30
 * This only supports base units: other units must be resolved to base units
31
 * before passing to this function, otherwise U_UNSUPPORTED_ERROR status may be
32
 * returned.
33
 *
34
 * Categories are found in `unitQuantities` in the `units` resource (see
35
 * `units.txt`).
36
 */
37
// TODO: make this function accepts any `MeasureUnit` as Java and move it to the `UnitsData` class.
38
CharString U_I18N_API getUnitQuantity(const MeasureUnitImpl &baseMeasureUnitImpl, UErrorCode &status);
39
40
/**
41
 * A struct representing a single unit (optional SI or binary prefix, and dimensionality).
42
 */
43
struct U_I18N_API_CLASS SingleUnitImpl : public UMemory {
44
    /**
45
     * Gets a single unit from the MeasureUnit. If there are multiple single units, sets an error
46
     * code and returns the base dimensionless unit. Parses if necessary.
47
     */
48
    U_I18N_API static SingleUnitImpl forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status);
49
50
    /** Transform this SingleUnitImpl into a MeasureUnit, simplifying if possible. */
51
    MeasureUnit build(UErrorCode& status) const;
52
53
    /**
54
     * Returns the "simple unit ID", without SI or dimensionality prefix: this
55
     * instance may represent a square-kilometer, but only "meter" will be
56
     * returned.
57
     *
58
     * The returned pointer points at memory that exists for the duration of the
59
     * program's running.
60
     */
61
    U_I18N_API const char* getSimpleUnitID() const;
62
63
    /**
64
     * Generates and append a neutral identifier string for a single unit which means we do not include
65
     * the dimension signal.
66
     */
67
    void appendNeutralIdentifier(CharString &result, UErrorCode &status) const;
68
69
    /**
70
     * Returns the index of this unit's "quantity" in unitQuantities (in
71
     * measunit_extra.cpp). The value of this index determines sort order for
72
     * normalization of unit identifiers.
73
     */
74
    int32_t getUnitCategoryIndex() const;
75
76
    /**
77
     * Compare this SingleUnitImpl to another SingleUnitImpl for the sake of
78
     * sorting and coalescing.
79
     *
80
     * Sort order of units is specified by UTS #35
81
     * (https://unicode.org/reports/tr35/tr35-info.html#Unit_Identifier_Normalization).
82
     *
83
     * Takes the sign of dimensionality into account, but not the absolute
84
     * value: per-meter is not considered the same as meter, but meter is
85
     * considered the same as square-meter.
86
     *
87
     * The dimensionless unit generally does not get compared, but if it did, it
88
     * would sort before other units by virtue of index being < 0 and
89
     * dimensionality not being negative.
90
     */
91
0
    int32_t compareTo(const SingleUnitImpl& other) const {
92
0
        if (dimensionality < 0 && other.dimensionality > 0) {
93
            // Positive dimensions first
94
0
            return 1;
95
0
        }
96
0
        if (dimensionality > 0 && other.dimensionality < 0) {
97
0
            return -1;
98
0
        }
99
100
        // Sort by official quantity order
101
0
        int32_t thisQuantity = this->getUnitCategoryIndex();
102
0
        int32_t otherQuantity = other.getUnitCategoryIndex();
103
0
        if (thisQuantity < otherQuantity) {
104
0
            return -1;
105
0
        }
106
0
        if (thisQuantity > otherQuantity) {
107
0
            return 1;
108
0
        }
109
110
        // If quantity order didn't help, then we go by index.
111
0
        if (index < other.index) {
112
0
            return -1;
113
0
        }
114
0
        if (index > other.index) {
115
0
            return 1;
116
0
        }
117
118
        // When comparing binary prefixes vs SI prefixes, instead of comparing the actual values, we can
119
        // multiply the binary prefix power by 3 and compare the powers. if they are equal, we can can
120
        // compare the bases.
121
        // NOTE: this methodology will fail if the binary prefix more than or equal 98.
122
0
        int32_t unitBase = umeas_getPrefixBase(unitPrefix);
123
0
        int32_t otherUnitBase = umeas_getPrefixBase(other.unitPrefix);
124
125
        // Values for comparison purposes only.
126
0
        int32_t unitPower = unitBase == 1024 /* Binary Prefix */ ? umeas_getPrefixPower(unitPrefix) * 3
127
0
                                                                 : umeas_getPrefixPower(unitPrefix);
128
0
        int32_t otherUnitPower =
129
0
            otherUnitBase == 1024 /* Binary Prefix */ ? umeas_getPrefixPower(other.unitPrefix) * 3
130
0
                                                      : umeas_getPrefixPower(other.unitPrefix);
131
132
        // NOTE: if the unitPower is less than the other,
133
        // we return 1 not -1. Thus because we want th sorting order
134
        // for the bigger prefix to be before the smaller.
135
        // Example: megabyte should come before kilobyte.
136
0
        if (unitPower < otherUnitPower) {
137
0
            return 1;
138
0
        }
139
0
        if (unitPower > otherUnitPower) {
140
0
            return -1;
141
0
        }
142
143
0
        if (unitBase < otherUnitBase) {
144
0
            return 1;
145
0
        }
146
0
        if (unitBase > otherUnitBase) {
147
0
            return -1;
148
0
        }
149
150
0
        return 0;
151
0
    }
Unexecuted instantiation: icu_79::SingleUnitImpl::compareTo(icu_79::SingleUnitImpl const&) const
Unexecuted instantiation: icu_79::SingleUnitImpl::compareTo(icu_79::SingleUnitImpl const&) const
152
153
    /**
154
     * Return whether this SingleUnitImpl is compatible with another for the purpose of coalescing.
155
     *
156
     * Units with the same base unit and SI or binary prefix should match, except that they must also
157
     * have the same dimensionality sign, such that we don't merge numerator and denominator.
158
     */
159
0
    bool isCompatibleWith(const SingleUnitImpl& other) const {
160
0
        return (compareTo(other) == 0);
161
0
    }
162
163
    /**
164
     * Returns true if this unit is the "dimensionless base unit", as produced
165
     * by the MeasureUnit() default constructor. (This does not include the
166
     * likes of concentrations or angles.)
167
     */
168
360
    bool isDimensionless() const {
169
360
        return index == -1;
170
360
    }
171
172
    /**
173
     * Simple unit index, unique for every simple unit, -1 for the dimensionless
174
     * unit. This is an index into a string list in measunit_extra.cpp, as
175
     * loaded by SimpleUnitIdentifiersSink.
176
     *
177
     * The default value is -1, meaning the dimensionless unit:
178
     * isDimensionless() will return true, until index is changed.
179
     */
180
    int32_t index = -1;
181
182
    /**
183
     * SI or binary prefix.
184
     *
185
     * This is ignored for the dimensionless unit.
186
     */
187
    UMeasurePrefix unitPrefix = UMEASURE_PREFIX_ONE;
188
189
    /**
190
     * Dimensionality.
191
     *
192
     * This is meaningless for the dimensionless unit.
193
     */
194
    int32_t dimensionality = 1;
195
};
196
197
// Forward declaration
198
struct MeasureUnitImplWithIndex;
199
200
/**
201
 * Internal representation of measurement units. Capable of representing all complexities of units,
202
 * including mixed and compound units.
203
 */
204
class U_I18N_API_CLASS MeasureUnitImpl : public UMemory {
205
  public:
206
14.2k
    MeasureUnitImpl() = default;
207
0
    MeasureUnitImpl(MeasureUnitImpl &&other) = default;
208
    // No copy constructor, use MeasureUnitImpl::copy() to make it explicit.
209
    MeasureUnitImpl(const MeasureUnitImpl &other, UErrorCode &status) = delete;
210
    MeasureUnitImpl(const SingleUnitImpl &singleUnit, UErrorCode &status);
211
212
0
    MeasureUnitImpl &operator=(MeasureUnitImpl &&other) noexcept = default;
213
214
    /** Extract the MeasureUnitImpl from a MeasureUnit. */
215
0
    static inline const MeasureUnitImpl *get(const MeasureUnit &measureUnit) {
216
0
        return measureUnit.fImpl;
217
0
    }
218
219
    /**
220
     * Parse a unit identifier into a MeasureUnitImpl.
221
     *
222
     * @param identifier The unit identifier string.
223
     * @param status Set if the identifier string is not valid.
224
     * @return A newly parsed value object. Behaviour of this unit is
225
     * unspecified if an error is returned via status.
226
     */
227
    U_I18N_API static MeasureUnitImpl forIdentifier(StringPiece identifier, UErrorCode& status);
228
229
    /**
230
     * Extract the MeasureUnitImpl from a MeasureUnit, or parse if it is not present.
231
     * 
232
     * @param measureUnit The source MeasureUnit.
233
     * @param memory A place to write the new MeasureUnitImpl if parsing is required.
234
     * @param status Set if an error occurs.
235
     * @return A reference to either measureUnit.fImpl or memory.
236
     */
237
    U_I18N_API static const MeasureUnitImpl& forMeasureUnit(
238
        const MeasureUnit& measureUnit, MeasureUnitImpl& memory, UErrorCode& status);
239
240
    /**
241
     * Extract the MeasureUnitImpl from a MeasureUnit, or parse if it is not present.
242
     *
243
     * @param measureUnit The source MeasureUnit.
244
     * @param status Set if an error occurs.
245
     * @return A value object, either newly parsed or copied from measureUnit.
246
     */
247
    static MeasureUnitImpl forMeasureUnitMaybeCopy(
248
        const MeasureUnit& measureUnit, UErrorCode& status);
249
250
    /**
251
     * Used for currency units.
252
     */
253
3.30k
    static inline MeasureUnitImpl forCurrencyCode(StringPiece currencyCode, UErrorCode& status) {
254
3.30k
        MeasureUnitImpl result;
255
3.30k
        if (U_SUCCESS(status)) {
256
3.30k
            result.identifier = currencyCode;
257
3.30k
            if (result.identifier.isEmpty() != currencyCode.empty()) {
258
0
                status = U_MEMORY_ALLOCATION_ERROR;
259
0
            }
260
3.30k
        }
261
3.30k
        return result;
262
3.30k
    }
263
264
    /** Transform this MeasureUnitImpl into a MeasureUnit, simplifying if possible. */
265
    U_I18N_API MeasureUnit build(UErrorCode& status) &&;
266
267
    /**
268
     * Create a copy of this MeasureUnitImpl. Don't use copy constructor to make this explicit.
269
     */
270
    MeasureUnitImpl copy(UErrorCode& status) const;
271
272
    /**
273
     * Extracts the list of all the individual units inside the `MeasureUnitImpl` with their indices.
274
     *      For example:    
275
     *          -   if the `MeasureUnitImpl` is `foot-per-hour`
276
     *                  it will return a list of 1 {(0, `foot-per-hour`)} 
277
     *          -   if the `MeasureUnitImpl` is `foot-and-inch` 
278
     *                  it will return a list of 2 {(0, `foot`), (1, `inch`)}
279
     */
280
    MaybeStackVector<MeasureUnitImplWithIndex>
281
    extractIndividualUnitsWithIndices(UErrorCode &status) const;
282
283
    /** Mutates this MeasureUnitImpl to take the reciprocal. */
284
    void takeReciprocal(UErrorCode& status);
285
286
    /**
287
     * Returns a simplified version of the unit.
288
     * NOTE: the simplification happen when there are two units equals in their base unit and their
289
     * prefixes.
290
     *
291
     * Example 1: "square-meter-per-meter" --> "meter"
292
     * Example 2: "square-millimeter-per-meter" --> "square-millimeter-per-meter"
293
     */
294
    MeasureUnitImpl copyAndSimplify(UErrorCode &status) const;
295
296
    /**
297
     * Mutates this MeasureUnitImpl to append a single unit.
298
     *
299
     * @return true if a new item was added. If unit is the dimensionless unit,
300
     * it is never added: the return value will always be false.
301
     */
302
    U_I18N_API bool appendSingleUnit(const SingleUnitImpl& singleUnit, UErrorCode& status);
303
304
    /**
305
     * Normalizes a MeasureUnitImpl and generate the identifier string in place.
306
     */
307
    void serialize(UErrorCode &status);
308
309
    /** The complexity, either SINGLE, COMPOUND, or MIXED. */
310
    UMeasureUnitComplexity complexity = UMEASURE_UNIT_SINGLE;
311
312
    /**
313
     * The list of single units. These may be summed or multiplied, based on the
314
     * value of the complexity field.
315
     *
316
     * The "dimensionless" unit (SingleUnitImpl default constructor) must not be
317
     * added to this list.
318
     */
319
    MaybeStackVector<SingleUnitImpl> singleUnits;
320
321
    /**
322
     * The full unit identifier.  Owned by the MeasureUnitImpl.  Empty if not computed.
323
     */
324
    FixedString identifier;
325
326
    /**
327
     * Represents the unit constant denominator.
328
     *
329
     * NOTE:
330
     *   if set to 0, it means that the constant is not set.
331
     */
332
    uint64_t constantDenominator = 0;
333
334
    // For calling serialize
335
    // TODO(icu-units#147): revisit serialization
336
    friend class number::impl::LongNameHandler;
337
};
338
339
struct MeasureUnitImplWithIndex : public UMemory {
340
    const int32_t index;
341
    MeasureUnitImpl unitImpl;
342
    // Makes a copy of unitImpl.
343
    MeasureUnitImplWithIndex(int32_t index, const MeasureUnitImpl &unitImpl, UErrorCode &status)
344
0
        : index(index), unitImpl(unitImpl.copy(status)) {
345
0
    }
346
    MeasureUnitImplWithIndex(int32_t index, const SingleUnitImpl &singleUnitImpl, UErrorCode &status)
347
0
        : index(index), unitImpl(MeasureUnitImpl(singleUnitImpl, status)) {
348
0
    }
349
};
350
351
U_NAMESPACE_END
352
353
#endif /* #if !UCONFIG_NO_FORMATTING */
354
#endif //__MEASUNIT_IMPL_H__