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      */
compareToSingleUnitImpl91     int32_t compareTo(const SingleUnitImpl& other) const {
92         if (dimensionality < 0 && other.dimensionality > 0) {
93             // Positive dimensions first
94             return 1;
95         }
96         if (dimensionality > 0 && other.dimensionality < 0) {
97             return -1;
98         }
99         // Sort by official quantity order
100         int32_t thisQuantity = this->getUnitCategoryIndex();
101         int32_t otherQuantity = other.getUnitCategoryIndex();
102         if (thisQuantity < otherQuantity) {
103             return -1;
104         }
105         if (thisQuantity > otherQuantity) {
106             return 1;
107         }
108         // If quantity order didn't help, then we go by index.
109         if (index < other.index) {
110             return -1;
111         }
112         if (index > other.index) {
113             return 1;
114         }
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         if (unitPrefix < other.unitPrefix) {
119             return -1;
120         }
121         if (unitPrefix > other.unitPrefix) {
122             return 1;
123         }
124         return 0;
125     }
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      */
isCompatibleWithSingleUnitImpl133     bool isCompatibleWith(const SingleUnitImpl& other) const {
134         return (compareTo(other) == 0);
135     }
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      */
isDimensionlessSingleUnitImpl142     bool isDimensionless() const {
143         return index == -1;
144     }
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     MeasureUnitImpl() = default;
190     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     MeasureUnitImpl &operator=(MeasureUnitImpl &&other) noexcept = default;
196 
197     /** Extract the MeasureUnitImpl from a MeasureUnit. */
get(const MeasureUnit & measureUnit)198     static inline const MeasureUnitImpl *get(const MeasureUnit &measureUnit) {
199         return measureUnit.fImpl;
200     }
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      */
forCurrencyCode(StringPiece currencyCode)236     static inline MeasureUnitImpl forCurrencyCode(StringPiece currencyCode) {
237         MeasureUnitImpl result;
238         UErrorCode localStatus = U_ZERO_ERROR;
239         result.identifier.append(currencyCode, localStatus);
240         // localStatus is not expected to fail since currencyCode should be 3 chars long
241         return result;
242     }
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.
MeasureUnitImplWithIndexMeasureUnitImplWithIndex306     MeasureUnitImplWithIndex(int32_t index, const MeasureUnitImpl &unitImpl, UErrorCode &status)
307         : index(index), unitImpl(unitImpl.copy(status)) {
308     }
MeasureUnitImplWithIndexMeasureUnitImplWithIndex309     MeasureUnitImplWithIndex(int32_t index, const SingleUnitImpl &singleUnitImpl, UErrorCode &status)
310         : index(index), unitImpl(MeasureUnitImpl(singleUnitImpl, status)) {
311     }
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__
341