123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376 |
- // © 2020 and later: Unicode, Inc. and others.
- // License & terms of use: http://www.unicode.org/copyright.html
- #ifndef __MEASUNIT_IMPL_H__
- #define __MEASUNIT_IMPL_H__
- #include "unicode/utypes.h"
- #if !UCONFIG_NO_FORMATTING
- #include "unicode/measunit.h"
- #include "cmemory.h"
- #include "charstr.h"
- U_NAMESPACE_BEGIN
- namespace number {
- namespace impl {
- class LongNameHandler;
- }
- } // namespace number
- static const char16_t kDefaultCurrency[] = u"XXX";
- static const char kDefaultCurrency8[] = "XXX";
- /**
- * Looks up the "unitQuantity" (aka "type" or "category") of a base unit
- * identifier. The category is returned via `result`, which must initially be
- * empty.
- *
- * This only supports base units: other units must be resolved to base units
- * before passing to this function, otherwise U_UNSUPPORTED_ERROR status may be
- * returned.
- *
- * Categories are found in `unitQuantities` in the `units` resource (see
- * `units.txt`).
- */
- // TODO: make this function accepts any `MeasureUnit` as Java and move it to the `UnitsData` class.
- CharString U_I18N_API getUnitQuantity(const MeasureUnitImpl &baseMeasureUnitImpl, UErrorCode &status);
- /**
- * A struct representing a single unit (optional SI or binary prefix, and dimensionality).
- */
- struct U_I18N_API SingleUnitImpl : public UMemory {
- /**
- * Gets a single unit from the MeasureUnit. If there are multiple single units, sets an error
- * code and returns the base dimensionless unit. Parses if necessary.
- */
- static SingleUnitImpl forMeasureUnit(const MeasureUnit& measureUnit, UErrorCode& status);
- /** Transform this SingleUnitImpl into a MeasureUnit, simplifying if possible. */
- MeasureUnit build(UErrorCode& status) const;
- /**
- * Returns the "simple unit ID", without SI or dimensionality prefix: this
- * instance may represent a square-kilometer, but only "meter" will be
- * returned.
- *
- * The returned pointer points at memory that exists for the duration of the
- * program's running.
- */
- const char *getSimpleUnitID() const;
- /**
- * Generates and append a neutral identifier string for a single unit which means we do not include
- * the dimension signal.
- */
- void appendNeutralIdentifier(CharString &result, UErrorCode &status) const;
- /**
- * Returns the index of this unit's "quantity" in unitQuantities (in
- * measunit_extra.cpp). The value of this index determines sort order for
- * normalization of unit identifiers.
- */
- int32_t getUnitCategoryIndex() const;
- /**
- * Compare this SingleUnitImpl to another SingleUnitImpl for the sake of
- * sorting and coalescing.
- *
- * Sort order of units is specified by UTS #35
- * (https://unicode.org/reports/tr35/tr35-info.html#Unit_Identifier_Normalization).
- *
- * Takes the sign of dimensionality into account, but not the absolute
- * value: per-meter is not considered the same as meter, but meter is
- * considered the same as square-meter.
- *
- * The dimensionless unit generally does not get compared, but if it did, it
- * would sort before other units by virtue of index being < 0 and
- * dimensionality not being negative.
- */
- int32_t compareTo(const SingleUnitImpl& other) const {
- if (dimensionality < 0 && other.dimensionality > 0) {
- // Positive dimensions first
- return 1;
- }
- if (dimensionality > 0 && other.dimensionality < 0) {
- return -1;
- }
- // Sort by official quantity order
- int32_t thisQuantity = this->getUnitCategoryIndex();
- int32_t otherQuantity = other.getUnitCategoryIndex();
- if (thisQuantity < otherQuantity) {
- return -1;
- }
- if (thisQuantity > otherQuantity) {
- return 1;
- }
- // If quantity order didn't help, then we go by index.
- if (index < other.index) {
- return -1;
- }
- if (index > other.index) {
- return 1;
- }
- // When comparing binary prefixes vs SI prefixes, instead of comparing the actual values, we can
- // multiply the binary prefix power by 3 and compare the powers. if they are equal, we can can
- // compare the bases.
- // NOTE: this methodology will fail if the binary prefix more than or equal 98.
- int32_t unitBase = umeas_getPrefixBase(unitPrefix);
- int32_t otherUnitBase = umeas_getPrefixBase(other.unitPrefix);
- // Values for comparison purposes only.
- int32_t unitPower = unitBase == 1024 /* Binary Prefix */ ? umeas_getPrefixPower(unitPrefix) * 3
- : umeas_getPrefixPower(unitPrefix);
- int32_t otherUnitPower =
- otherUnitBase == 1024 /* Binary Prefix */ ? umeas_getPrefixPower(other.unitPrefix) * 3
- : umeas_getPrefixPower(other.unitPrefix);
- // NOTE: if the unitPower is less than the other,
- // we return 1 not -1. Thus because we want th sorting order
- // for the bigger prefix to be before the smaller.
- // Example: megabyte should come before kilobyte.
- if (unitPower < otherUnitPower) {
- return 1;
- }
- if (unitPower > otherUnitPower) {
- return -1;
- }
- if (unitBase < otherUnitBase) {
- return 1;
- }
- if (unitBase > otherUnitBase) {
- return -1;
- }
- return 0;
- }
- /**
- * Return whether this SingleUnitImpl is compatible with another for the purpose of coalescing.
- *
- * Units with the same base unit and SI or binary prefix should match, except that they must also
- * have the same dimensionality sign, such that we don't merge numerator and denominator.
- */
- bool isCompatibleWith(const SingleUnitImpl& other) const {
- return (compareTo(other) == 0);
- }
- /**
- * Returns true if this unit is the "dimensionless base unit", as produced
- * by the MeasureUnit() default constructor. (This does not include the
- * likes of concentrations or angles.)
- */
- bool isDimensionless() const {
- return index == -1;
- }
- /**
- * Simple unit index, unique for every simple unit, -1 for the dimensionless
- * unit. This is an index into a string list in measunit_extra.cpp, as
- * loaded by SimpleUnitIdentifiersSink.
- *
- * The default value is -1, meaning the dimensionless unit:
- * isDimensionless() will return true, until index is changed.
- */
- int32_t index = -1;
- /**
- * SI or binary prefix.
- *
- * This is ignored for the dimensionless unit.
- */
- UMeasurePrefix unitPrefix = UMEASURE_PREFIX_ONE;
- /**
- * Dimensionality.
- *
- * This is meaningless for the dimensionless unit.
- */
- int32_t dimensionality = 1;
- };
- // Forward declaration
- struct MeasureUnitImplWithIndex;
- // Export explicit template instantiations of MaybeStackArray, MemoryPool and
- // MaybeStackVector. This is required when building DLLs for Windows. (See
- // datefmt.h, collationiterator.h, erarules.h and others for similar examples.)
- #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
- template class U_I18N_API MaybeStackArray<SingleUnitImpl *, 8>;
- template class U_I18N_API MemoryPool<SingleUnitImpl, 8>;
- template class U_I18N_API MaybeStackVector<SingleUnitImpl, 8>;
- #endif
- /**
- * Internal representation of measurement units. Capable of representing all complexities of units,
- * including mixed and compound units.
- */
- class U_I18N_API MeasureUnitImpl : public UMemory {
- public:
- MeasureUnitImpl() = default;
- MeasureUnitImpl(MeasureUnitImpl &&other) = default;
- // No copy constructor, use MeasureUnitImpl::copy() to make it explicit.
- MeasureUnitImpl(const MeasureUnitImpl &other, UErrorCode &status) = delete;
- MeasureUnitImpl(const SingleUnitImpl &singleUnit, UErrorCode &status);
- MeasureUnitImpl &operator=(MeasureUnitImpl &&other) noexcept = default;
- /** Extract the MeasureUnitImpl from a MeasureUnit. */
- static inline const MeasureUnitImpl *get(const MeasureUnit &measureUnit) {
- return measureUnit.fImpl;
- }
- /**
- * Parse a unit identifier into a MeasureUnitImpl.
- *
- * @param identifier The unit identifier string.
- * @param status Set if the identifier string is not valid.
- * @return A newly parsed value object. Behaviour of this unit is
- * unspecified if an error is returned via status.
- */
- static MeasureUnitImpl forIdentifier(StringPiece identifier, UErrorCode& status);
- /**
- * Extract the MeasureUnitImpl from a MeasureUnit, or parse if it is not present.
- *
- * @param measureUnit The source MeasureUnit.
- * @param memory A place to write the new MeasureUnitImpl if parsing is required.
- * @param status Set if an error occurs.
- * @return A reference to either measureUnit.fImpl or memory.
- */
- static const MeasureUnitImpl& forMeasureUnit(
- const MeasureUnit& measureUnit, MeasureUnitImpl& memory, UErrorCode& status);
- /**
- * Extract the MeasureUnitImpl from a MeasureUnit, or parse if it is not present.
- *
- * @param measureUnit The source MeasureUnit.
- * @param status Set if an error occurs.
- * @return A value object, either newly parsed or copied from measureUnit.
- */
- static MeasureUnitImpl forMeasureUnitMaybeCopy(
- const MeasureUnit& measureUnit, UErrorCode& status);
- /**
- * Used for currency units.
- */
- static inline MeasureUnitImpl forCurrencyCode(StringPiece currencyCode) {
- MeasureUnitImpl result;
- UErrorCode localStatus = U_ZERO_ERROR;
- result.identifier.append(currencyCode, localStatus);
- // localStatus is not expected to fail since currencyCode should be 3 chars long
- return result;
- }
- /** Transform this MeasureUnitImpl into a MeasureUnit, simplifying if possible. */
- MeasureUnit build(UErrorCode& status) &&;
- /**
- * Create a copy of this MeasureUnitImpl. Don't use copy constructor to make this explicit.
- */
- MeasureUnitImpl copy(UErrorCode& status) const;
- /**
- * Extracts the list of all the individual units inside the `MeasureUnitImpl` with their indices.
- * For example:
- * - if the `MeasureUnitImpl` is `foot-per-hour`
- * it will return a list of 1 {(0, `foot-per-hour`)}
- * - if the `MeasureUnitImpl` is `foot-and-inch`
- * it will return a list of 2 {(0, `foot`), (1, `inch`)}
- */
- MaybeStackVector<MeasureUnitImplWithIndex>
- extractIndividualUnitsWithIndices(UErrorCode &status) const;
- /** Mutates this MeasureUnitImpl to take the reciprocal. */
- void takeReciprocal(UErrorCode& status);
- /**
- * Returns a simplified version of the unit.
- * NOTE: the simplification happen when there are two units equals in their base unit and their
- * prefixes.
- *
- * Example 1: "square-meter-per-meter" --> "meter"
- * Example 2: "square-millimeter-per-meter" --> "square-millimeter-per-meter"
- */
- MeasureUnitImpl copyAndSimplify(UErrorCode &status) const;
- /**
- * Mutates this MeasureUnitImpl to append a single unit.
- *
- * @return true if a new item was added. If unit is the dimensionless unit,
- * it is never added: the return value will always be false.
- */
- bool appendSingleUnit(const SingleUnitImpl& singleUnit, UErrorCode& status);
- /**
- * Normalizes a MeasureUnitImpl and generate the identifier string in place.
- */
- void serialize(UErrorCode &status);
- /** The complexity, either SINGLE, COMPOUND, or MIXED. */
- UMeasureUnitComplexity complexity = UMEASURE_UNIT_SINGLE;
- /**
- * The list of single units. These may be summed or multiplied, based on the
- * value of the complexity field.
- *
- * The "dimensionless" unit (SingleUnitImpl default constructor) must not be
- * added to this list.
- */
- MaybeStackVector<SingleUnitImpl> singleUnits;
- /**
- * The full unit identifier. Owned by the MeasureUnitImpl. Empty if not computed.
- */
- CharString identifier;
- // For calling serialize
- // TODO(icu-units#147): revisit serialization
- friend class number::impl::LongNameHandler;
- };
- struct U_I18N_API MeasureUnitImplWithIndex : public UMemory {
- const int32_t index;
- MeasureUnitImpl unitImpl;
- // Makes a copy of unitImpl.
- MeasureUnitImplWithIndex(int32_t index, const MeasureUnitImpl &unitImpl, UErrorCode &status)
- : index(index), unitImpl(unitImpl.copy(status)) {
- }
- MeasureUnitImplWithIndex(int32_t index, const SingleUnitImpl &singleUnitImpl, UErrorCode &status)
- : index(index), unitImpl(MeasureUnitImpl(singleUnitImpl, status)) {
- }
- };
- // Export explicit template instantiations of MaybeStackArray, MemoryPool and
- // MaybeStackVector. This is required when building DLLs for Windows. (See
- // datefmt.h, collationiterator.h, erarules.h and others for similar examples.)
- #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
- template class U_I18N_API MaybeStackArray<MeasureUnitImplWithIndex *, 8>;
- template class U_I18N_API MemoryPool<MeasureUnitImplWithIndex, 8>;
- template class U_I18N_API MaybeStackVector<MeasureUnitImplWithIndex, 8>;
- // Export an explicit template instantiation of the LocalPointer that is used as a
- // data member of MeasureUnitImpl.
- // (When building DLLs for Windows this is required.)
- #if defined(_MSC_VER)
- // Ignore warning 4661 as LocalPointerBase does not use operator== or operator!=
- #pragma warning(push)
- #pragma warning(disable : 4661)
- #endif
- template class U_I18N_API LocalPointerBase<MeasureUnitImpl>;
- template class U_I18N_API LocalPointer<MeasureUnitImpl>;
- #if defined(_MSC_VER)
- #pragma warning(pop)
- #endif
- #endif
- U_NAMESPACE_END
- #endif /* #if !UCONFIG_NO_FORMATTING */
- #endif //__MEASUNIT_IMPL_H__
|