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