Coverage Report

Created: 2025-06-24 06:43

/src/icu/source/i18n/measunit_impl.h
Line
Count
Source (jump to first uncovered line)
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
15
U_NAMESPACE_BEGIN
16
17
namespace number {
18
namespace impl {
19
class LongNameHandler;
20
}
21
} // namespace number
22
23
static const char16_t kDefaultCurrency[] = u"XXX";
24
static const char kDefaultCurrency8[] = "XXX";
25
26
/**
27
 * Looks up the "unitQuantity" (aka "type" or "category") of a base unit
28
 * identifier. The category is returned via `result`, which must initially be
29
 * empty.
30
 *
31
 * This only supports base units: other units must be resolved to base units
32
 * before passing to this function, otherwise U_UNSUPPORTED_ERROR status will be
33
 * returned.
34
 *
35
 * Categories are found in `unitQuantities` in the `units` resource (see
36
 * `units.txt`).
37
 */
38
CharString U_I18N_API getUnitQuantity(StringPiece baseUnitIdentifier, UErrorCode &status);
39
40
/**
41
 * A struct representing a single unit (optional SI or binary prefix, and dimensionality).
42
 */
43
struct U_I18N_API 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
    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
    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
        // Sort by official quantity order
100
0
        int32_t thisQuantity = this->getUnitCategoryIndex();
101
0
        int32_t otherQuantity = other.getUnitCategoryIndex();
102
0
        if (thisQuantity < otherQuantity) {
103
0
            return -1;
104
0
        }
105
0
        if (thisQuantity > otherQuantity) {
106
0
            return 1;
107
0
        }
108
        // If quantity order didn't help, then we go by index.
109
0
        if (index < other.index) {
110
0
            return -1;
111
0
        }
112
0
        if (index > other.index) {
113
0
            return 1;
114
0
        }
115
        // TODO: revisit if the spec dictates prefix sort order - it doesn't
116
        // currently. For now we're sorting binary prefixes before SI prefixes,
117
        // as per enum values order.
118
0
        if (unitPrefix < other.unitPrefix) {
119
0
            return -1;
120
0
        }
121
0
        if (unitPrefix > other.unitPrefix) {
122
0
            return 1;
123
0
        }
124
0
        return 0;
125
0
    }
126
127
    /**
128
     * Return whether this SingleUnitImpl is compatible with another for the purpose of coalescing.
129
     *
130
     * Units with the same base unit and SI or binary prefix should match, except that they must also
131
     * have the same dimensionality sign, such that we don't merge numerator and denominator.
132
     */
133
0
    bool isCompatibleWith(const SingleUnitImpl& other) const {
134
0
        return (compareTo(other) == 0);
135
0
    }
136
137
    /**
138
     * Returns true if this unit is the "dimensionless base unit", as produced
139
     * by the MeasureUnit() default constructor. (This does not include the
140
     * likes of concentrations or angles.)
141
     */
142
0
    bool isDimensionless() const {
143
0
        return index == -1;
144
0
    }
145
146
    /**
147
     * Simple unit index, unique for every simple unit, -1 for the dimensionless
148
     * unit. This is an index into a string list in measunit_extra.cpp, as
149
     * loaded by SimpleUnitIdentifiersSink.
150
     *
151
     * The default value is -1, meaning the dimensionless unit:
152
     * isDimensionless() will return true, until index is changed.
153
     */
154
    int32_t index = -1;
155
156
    /**
157
     * SI or binary prefix.
158
     *
159
     * This is ignored for the dimensionless unit.
160
     */
161
    UMeasurePrefix unitPrefix = UMEASURE_PREFIX_ONE;
162
163
    /**
164
     * Dimensionality.
165
     *
166
     * This is meaningless for the dimensionless unit.
167
     */
168
    int32_t dimensionality = 1;
169
};
170
171
// Forward declaration
172
struct MeasureUnitImplWithIndex;
173
174
// Export explicit template instantiations of MaybeStackArray, MemoryPool and
175
// MaybeStackVector. This is required when building DLLs for Windows. (See
176
// datefmt.h, collationiterator.h, erarules.h and others for similar examples.)
177
#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
178
template class U_I18N_API MaybeStackArray<SingleUnitImpl *, 8>;
179
template class U_I18N_API MemoryPool<SingleUnitImpl, 8>;
180
template class U_I18N_API MaybeStackVector<SingleUnitImpl, 8>;
181
#endif
182
183
/**
184
 * Internal representation of measurement units. Capable of representing all complexities of units,
185
 * including mixed and compound units.
186
 */
187
class U_I18N_API MeasureUnitImpl : public UMemory {
188
  public:
189
0
    MeasureUnitImpl() = default;
190
0
    MeasureUnitImpl(MeasureUnitImpl &&other) = default;
191
    // No copy constructor, use MeasureUnitImpl::copy() to make it explicit.
192
    MeasureUnitImpl(const MeasureUnitImpl &other, UErrorCode &status) = delete;
193
    MeasureUnitImpl(const SingleUnitImpl &singleUnit, UErrorCode &status);
194
195
0
    MeasureUnitImpl &operator=(MeasureUnitImpl &&other) noexcept = default;
196
197
    /** Extract the MeasureUnitImpl from a MeasureUnit. */
198
0
    static inline const MeasureUnitImpl *get(const MeasureUnit &measureUnit) {
199
0
        return measureUnit.fImpl;
200
0
    }
201
202
    /**
203
     * Parse a unit identifier into a MeasureUnitImpl.
204
     *
205
     * @param identifier The unit identifier string.
206
     * @param status Set if the identifier string is not valid.
207
     * @return A newly parsed value object. Behaviour of this unit is
208
     * unspecified if an error is returned via status.
209
     */
210
    static MeasureUnitImpl forIdentifier(StringPiece identifier, UErrorCode& status);
211
212
    /**
213
     * Extract the MeasureUnitImpl from a MeasureUnit, or parse if it is not present.
214
     * 
215
     * @param measureUnit The source MeasureUnit.
216
     * @param memory A place to write the new MeasureUnitImpl if parsing is required.
217
     * @param status Set if an error occurs.
218
     * @return A reference to either measureUnit.fImpl or memory.
219
     */
220
    static const MeasureUnitImpl& forMeasureUnit(
221
        const MeasureUnit& measureUnit, MeasureUnitImpl& memory, UErrorCode& status);
222
223
    /**
224
     * Extract the MeasureUnitImpl from a MeasureUnit, or parse if it is not present.
225
     *
226
     * @param measureUnit The source MeasureUnit.
227
     * @param status Set if an error occurs.
228
     * @return A value object, either newly parsed or copied from measureUnit.
229
     */
230
    static MeasureUnitImpl forMeasureUnitMaybeCopy(
231
        const MeasureUnit& measureUnit, UErrorCode& status);
232
233
    /**
234
     * Used for currency units.
235
     */
236
0
    static inline MeasureUnitImpl forCurrencyCode(StringPiece currencyCode) {
237
0
        MeasureUnitImpl result;
238
0
        UErrorCode localStatus = U_ZERO_ERROR;
239
0
        result.identifier.append(currencyCode, localStatus);
240
        // localStatus is not expected to fail since currencyCode should be 3 chars long
241
0
        return result;
242
0
    }
243
244
    /** Transform this MeasureUnitImpl into a MeasureUnit, simplifying if possible. */
245
    MeasureUnit build(UErrorCode& status) &&;
246
247
    /**
248
     * Create a copy of this MeasureUnitImpl. Don't use copy constructor to make this explicit.
249
     */
250
    MeasureUnitImpl copy(UErrorCode& status) const;
251
252
    /**
253
     * Extracts the list of all the individual units inside the `MeasureUnitImpl` with their indices.
254
     *      For example:    
255
     *          -   if the `MeasureUnitImpl` is `foot-per-hour`
256
     *                  it will return a list of 1 {(0, `foot-per-hour`)} 
257
     *          -   if the `MeasureUnitImpl` is `foot-and-inch` 
258
     *                  it will return a list of 2 {(0, `foot`), (1, `inch`)}
259
     */
260
    MaybeStackVector<MeasureUnitImplWithIndex>
261
    extractIndividualUnitsWithIndices(UErrorCode &status) const;
262
263
    /** Mutates this MeasureUnitImpl to take the reciprocal. */
264
    void takeReciprocal(UErrorCode& status);
265
266
    /**
267
     * Mutates this MeasureUnitImpl to append a single unit.
268
     *
269
     * @return true if a new item was added. If unit is the dimensionless unit,
270
     * it is never added: the return value will always be false.
271
     */
272
    bool appendSingleUnit(const SingleUnitImpl& singleUnit, UErrorCode& status);
273
274
    /** The complexity, either SINGLE, COMPOUND, or MIXED. */
275
    UMeasureUnitComplexity complexity = UMEASURE_UNIT_SINGLE;
276
277
    /**
278
     * The list of single units. These may be summed or multiplied, based on the
279
     * value of the complexity field.
280
     *
281
     * The "dimensionless" unit (SingleUnitImpl default constructor) must not be
282
     * added to this list.
283
     */
284
    MaybeStackVector<SingleUnitImpl> singleUnits;
285
286
    /**
287
     * The full unit identifier.  Owned by the MeasureUnitImpl.  Empty if not computed.
288
     */
289
    CharString identifier;
290
291
  private:
292
    /**
293
     * Normalizes a MeasureUnitImpl and generate the identifier string in place.
294
     */
295
    void serialize(UErrorCode &status);
296
297
    // For calling serialize
298
    // TODO(icu-units#147): revisit serialization
299
    friend class number::impl::LongNameHandler;
300
};
301
302
struct U_I18N_API MeasureUnitImplWithIndex : public UMemory {
303
    const int32_t index;
304
    MeasureUnitImpl unitImpl;
305
    // Makes a copy of unitImpl.
306
    MeasureUnitImplWithIndex(int32_t index, const MeasureUnitImpl &unitImpl, UErrorCode &status)
307
0
        : index(index), unitImpl(unitImpl.copy(status)) {
308
0
    }
309
    MeasureUnitImplWithIndex(int32_t index, const SingleUnitImpl &singleUnitImpl, UErrorCode &status)
310
0
        : index(index), unitImpl(MeasureUnitImpl(singleUnitImpl, status)) {
311
0
    }
312
};
313
314
// Export explicit template instantiations of MaybeStackArray, MemoryPool and
315
// MaybeStackVector. This is required when building DLLs for Windows. (See
316
// datefmt.h, collationiterator.h, erarules.h and others for similar examples.)
317
#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
318
template class U_I18N_API MaybeStackArray<MeasureUnitImplWithIndex *, 8>;
319
template class U_I18N_API MemoryPool<MeasureUnitImplWithIndex, 8>;
320
template class U_I18N_API MaybeStackVector<MeasureUnitImplWithIndex, 8>;
321
322
// Export an explicit template instantiation of the LocalPointer that is used as a
323
// data member of MeasureUnitImpl.
324
// (When building DLLs for Windows this is required.)
325
#if defined(_MSC_VER)
326
// Ignore warning 4661 as LocalPointerBase does not use operator== or operator!=
327
#pragma warning(push)
328
#pragma warning(disable : 4661)
329
#endif
330
template class U_I18N_API LocalPointerBase<MeasureUnitImpl>;
331
template class U_I18N_API LocalPointer<MeasureUnitImpl>;
332
#if defined(_MSC_VER)
333
#pragma warning(pop)
334
#endif
335
#endif
336
337
U_NAMESPACE_END
338
339
#endif /* #if !UCONFIG_NO_FORMATTING */
340
#endif //__MEASUNIT_IMPL_H__