/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__ |