Merge pull request #15 from icu-units/use-impl

Use `Impl` libraries for all internal libraries
This commit is contained in:
Younies Mahmoud 2020-07-23 02:30:52 +02:00 committed by GitHub
commit cc786cfb3b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 268 additions and 179 deletions

View file

@ -12,22 +12,23 @@
#include "uassert.h"
#include "unicode/fmtable.h"
#include "unicode/localpointer.h"
#include "unicode/measunit.h"
#include "unicode/measure.h"
#include "unitconverter.h"
U_NAMESPACE_BEGIN
namespace units {
ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnit &inputUnit,
const MeasureUnit &outputUnits,
const ConversionRates &ratesInfo, UErrorCode &status) {
if (outputUnits.getComplexity(status) != UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
unitConverters_.emplaceBackAndCheckErrorCode(status, inputUnit, outputUnits, ratesInfo, status);
if (U_FAILURE(status)) {
return;
}
units_.emplaceBackAndCheckErrorCode(status, outputUnits);
ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnitImpl &inputUnit,
const MeasureUnitImpl &outputUnits,
const ConversionRates &ratesInfo, UErrorCode &status)
: units_(outputUnits.extractIndividualUnits(status)) {
if (U_FAILURE(status)) {
return;
}
if (units_.length() == 0) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
@ -45,47 +46,19 @@ ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnit &inputUnit,
// 2. convert the residual of 6.56168 feet (0.56168) to inches, which will be (6.74016
// inches)
// 3. then, the final result will be (6 feet and 6.74016 inches)
int32_t length;
auto singleUnits = outputUnits.splitToSingleUnits(length, status);
MaybeStackVector<MeasureUnit> singleUnitsInOrder;
for (int i = 0; i < length; ++i) {
/**
* TODO(younies): ensure units being in order by the biggest unit at first.
*
* HINT:
* MaybeStackVector<SingleUnitImpl> singleUnitsInOrder = MeasureUnitImpl::forMeasureUnitMaybeCopy(outputUnits, status).units;
* uprv_sortArray(
* singleUnitsInOrder.getAlias(),
* singleUnitsInOrder.length(),
* sizeof(singleUnitsInOrder[0]),
* compareSingleUnits,
* nullptr,
* false,
* &status);
*/
singleUnitsInOrder.emplaceBackAndCheckErrorCode(status, singleUnits[i]);
}
if (singleUnitsInOrder.length() == 0) {
status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
for (int i = 0, n = singleUnitsInOrder.length(); i < n; i++) {
for (int i = 0, n = units_.length(); i < n; i++) {
if (i == 0) { // first element
unitConverters_.emplaceBackAndCheckErrorCode(status, inputUnit, *singleUnitsInOrder[i],
ratesInfo, status);
unitConverters_.emplaceBackAndCheckErrorCode(status, inputUnit, *units_[i], ratesInfo,
status);
} else {
unitConverters_.emplaceBackAndCheckErrorCode(status, *singleUnitsInOrder[i - 1],
*singleUnitsInOrder[i], ratesInfo, status);
unitConverters_.emplaceBackAndCheckErrorCode(status, *units_[i - 1], *units_[i], ratesInfo,
status);
}
if (U_FAILURE(status)) {
return;
}
}
units_.appendAll(singleUnitsInOrder, status);
}
UBool ComplexUnitsConverter::greaterThanOrEqual(double quantity, double limit) const {
@ -106,8 +79,8 @@ MaybeStackVector<Measure> ComplexUnitsConverter::convert(double quantity, UError
Formattable formattableNewQuantity(newQuantity);
// NOTE: Measure would own its MeasureUnit.
result.emplaceBackAndCheckErrorCode(status, formattableNewQuantity,
new MeasureUnit(*units_[i]), status);
MeasureUnit *type = new MeasureUnit(units_[i]->copy(status).build(status));
result.emplaceBackAndCheckErrorCode(status, formattableNewQuantity, type, status);
// Keep the residual of the quantity.
// For example: `3.6 feet`, keep only `0.6 feet`
@ -116,8 +89,8 @@ MaybeStackVector<Measure> ComplexUnitsConverter::convert(double quantity, UError
Formattable formattableQuantity(quantity);
// NOTE: Measure would own its MeasureUnit.
result.emplaceBackAndCheckErrorCode(status, formattableQuantity, new MeasureUnit(*units_[i]),
status);
MeasureUnit *type = new MeasureUnit(units_[i]->copy(status).build(status));
result.emplaceBackAndCheckErrorCode(status, formattableQuantity, type, status);
}
}

View file

@ -8,7 +8,8 @@
#define __COMPLEXUNITSCONVERTER_H__
#include "cmemory.h"
#include "unicode/measunit.h"
#include "measunit_impl.h"
#include "unicode/errorcode.h"
#include "unicode/measure.h"
#include "unitconverter.h"
#include "unitsdata.h"
@ -26,7 +27,7 @@ namespace units {
* single unit to another single unit). Therefore, `ComplexUnitsConverter` class contains multiple
* instances of the `UnitConverter` to perform the conversion.
*/
class U_I18N_API ComplexUnitsConverter {
class U_I18N_API ComplexUnitsConverter : UMemory {
public:
/**
* Constructor of `ComplexUnitsConverter`.
@ -38,7 +39,7 @@ class U_I18N_API ComplexUnitsConverter {
* @param outputUnits represents the output unit. could be any type. (single, compound or mixed).
* @param status
*/
ComplexUnitsConverter(const MeasureUnit &inputUnit, const MeasureUnit &outputUnits,
ComplexUnitsConverter(const MeasureUnitImpl &inputUnit, const MeasureUnitImpl &outputUnits,
const ConversionRates &ratesInfo, UErrorCode &status);
// Returns true if the specified `quantity` of the `inputUnit`, expressed in terms of the biggest
@ -58,7 +59,7 @@ class U_I18N_API ComplexUnitsConverter {
private:
MaybeStackVector<UnitConverter> unitConverters_;
MaybeStackVector<MeasureUnit> units_;
MaybeStackVector<MeasureUnitImpl> units_;
};
} // namespace units

View file

@ -2302,6 +2302,14 @@ int32_t MeasureUnit::getOffset() const {
return gOffsets[fTypeId] + fSubTypeId;
}
MeasureUnitImpl MeasureUnitImpl::copy(UErrorCode &status) const {
MeasureUnitImpl result;
result.complexity = complexity;
result.units.appendAll(units, status);
result.identifier.append(identifier, status);
return result;
}
U_NAMESPACE_END
#endif /* !UNCONFIG_NO_FORMATTING */

View file

@ -775,6 +775,14 @@ const char *SingleUnitImpl::getSimpleUnitID() const {
return gSimpleUnits[index];
}
MeasureUnitImpl::MeasureUnitImpl(const MeasureUnitImpl &other, UErrorCode &status) {
*this = other.copy(status);
}
MeasureUnitImpl::MeasureUnitImpl(const SingleUnitImpl &singleUnit, UErrorCode &status) {
this->append(singleUnit, status);
}
MeasureUnitImpl MeasureUnitImpl::forIdentifier(StringPiece identifier, UErrorCode& status) {
return Parser::from(identifier, status).parse(status);
}
@ -810,12 +818,26 @@ bool MeasureUnitImpl::append(const SingleUnitImpl& singleUnit, UErrorCode& statu
return appendImpl(*this, singleUnit, status);
}
MaybeStackVector<MeasureUnitImpl> MeasureUnitImpl::extractIndividualUnits(UErrorCode &status) const {
MaybeStackVector<MeasureUnitImpl> result;
if (this->complexity != UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
result.emplaceBackAndCheckErrorCode(status, *this, status);
return result;
}
for (int32_t i = 0; i < units.length(); i++) {
result.emplaceBackAndCheckErrorCode(status, *units[i], status);
}
return result;
}
MeasureUnit MeasureUnitImpl::build(UErrorCode& status) && {
serialize(*this, status);
return MeasureUnit(std::move(*this));
}
MeasureUnit MeasureUnit::forIdentifier(StringPiece identifier, UErrorCode& status) {
return Parser::from(identifier, status).parse(status).build(status);
}

View file

@ -22,7 +22,7 @@ static const char kDefaultCurrency8[] = "XXX";
/**
* A struct representing a single unit (optional SI prefix and dimensionality).
*/
struct SingleUnitImpl : public UMemory {
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.
@ -125,7 +125,14 @@ struct SingleUnitImpl : public UMemory {
* Internal representation of measurement units. Capable of representing all complexities of units,
* including mixed and compound units.
*/
struct MeasureUnitImpl : public UMemory {
struct U_I18N_API MeasureUnitImpl : public UMemory {
MeasureUnitImpl() = default;
MeasureUnitImpl(MeasureUnitImpl &&other) = default;
MeasureUnitImpl(const MeasureUnitImpl &other, UErrorCode &status);
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;
@ -179,13 +186,17 @@ struct MeasureUnitImpl : public UMemory {
/**
* Create a copy of this MeasureUnitImpl. Don't use copy constructor to make this explicit.
*/
inline MeasureUnitImpl copy(UErrorCode& status) const {
MeasureUnitImpl result;
result.complexity = complexity;
result.units.appendAll(units, status);
result.identifier.append(identifier, status);
return result;
}
MeasureUnitImpl copy(UErrorCode& status) const;
/**
* Extracts the list of all the individual units inside the `MeasureUnitImpl`.
* For example:
* - if the `MeasureUnitImpl` is `foot-per-hour`
* it will return a list of 1 {`foot-per-hour`}
* - if the `MeasureUnitImpl` is `foot-and-inch`
* it will return a list of 2 { `foot`, `inch`}
*/
MaybeStackVector<MeasureUnitImpl> extractIndividualUnits(UErrorCode &status) const;
/** Mutates this MeasureUnitImpl to take the reciprocal. */
void takeReciprocal(UErrorCode& status);
@ -216,7 +227,6 @@ struct MeasureUnitImpl : public UMemory {
CharString identifier;
};
U_NAMESPACE_END
#endif /* #if !UCONFIG_NO_FORMATTING */

View file

@ -10,8 +10,8 @@
#include "double-conversion-string-to-double.h"
#include "measunit_impl.h"
#include "uassert.h"
#include "unicode/errorcode.h"
#include "unicode/localpointer.h"
#include "unicode/measunit.h"
#include "unicode/stringpiece.h"
#include "unitconverter.h"
#include <algorithm>
@ -235,16 +235,12 @@ Factor loadSingleFactor(StringPiece source, const ConversionRates &ratesInfo, UE
}
// Load Factor of a compound source unit.
Factor loadCompoundFactor(const MeasureUnit &source, const ConversionRates &ratesInfo,
Factor loadCompoundFactor(const MeasureUnitImpl &source, const ConversionRates &ratesInfo,
UErrorCode &status) {
Factor result;
MeasureUnitImpl memory;
const auto &compoundSourceUnit = MeasureUnitImpl::forMeasureUnit(source, memory, status);
if (U_FAILURE(status)) return result;
for (int32_t i = 0, n = compoundSourceUnit.units.length(); i < n; i++) {
auto singleUnit = *compoundSourceUnit.units[i]; // a SingleUnitImpl
for (int32_t i = 0, n = source.units.length(); i < n; i++) {
SingleUnitImpl singleUnit = *source.units[i];
Factor singleFactor = loadSingleFactor(singleUnit.getSimpleUnitID(), ratesInfo, status);
if (U_FAILURE(status)) return result;
@ -264,30 +260,35 @@ Factor loadCompoundFactor(const MeasureUnit &source, const ConversionRates &rate
/**
* Checks if the source unit and the target unit are simple. For example celsius or fahrenheit. But not
* square-celsius or square-fahrenheit.
*
* NOTE:
* Empty unit means simple unit.
*/
UBool checkSimpleUnit(const MeasureUnit &unit, UErrorCode &status) {
MeasureUnitImpl memory;
const auto &compoundSourceUnit = MeasureUnitImpl::forMeasureUnit(unit, memory, status);
UBool checkSimpleUnit(const MeasureUnitImpl &unit, UErrorCode &status) {
if (U_FAILURE(status)) return false;
if (compoundSourceUnit.complexity != UMEASURE_UNIT_SINGLE) {
if (unit.complexity != UMEASURE_UNIT_SINGLE) {
return false;
}
if (unit.units.length() == 0) {
// Empty units means simple unit.
return true;
}
U_ASSERT(compoundSourceUnit.units.length() == 1);
auto singleUnit = *(compoundSourceUnit.units[0]);
auto singleUnit = *(unit.units[0]);
if (singleUnit.dimensionality != 1 || singleUnit.siPrefix != UMEASURE_SI_PREFIX_ONE) {
return false;
}
return true;
}
/**
* Extract conversion rate from `source` to `target`
*/
void loadConversionRate(ConversionRate &conversionRate, const MeasureUnit &source,
const MeasureUnit &target, UnitsConvertibilityState unitsState,
void loadConversionRate(ConversionRate &conversionRate, const MeasureUnitImpl &source,
const MeasureUnitImpl &target, Convertibility unitsState,
const ConversionRates &ratesInfo, UErrorCode &status) {
// Represents the conversion factor from the source to the target.
Factor finalFactor;
@ -299,9 +300,9 @@ void loadConversionRate(ConversionRate &conversionRate, const MeasureUnit &sourc
// Merger Factors
finalFactor.multiplyBy(sourceToBase);
if (unitsState == UnitsConvertibilityState::CONVERTIBLE) {
if (unitsState == Convertibility::CONVERTIBLE) {
finalFactor.divideBy(targetToBase);
} else if (unitsState == UnitsConvertibilityState::RECIPROCAL) {
} else if (unitsState == Convertibility::RECIPROCAL) {
finalFactor.multiplyBy(targetToBase);
} else {
status = UErrorCode::U_ARGUMENT_TYPE_MISMATCH;
@ -321,7 +322,48 @@ void loadConversionRate(ConversionRate &conversionRate, const MeasureUnit &sourc
targetToBase.offset * targetToBase.factorDen / targetToBase.factorNum;
}
conversionRate.reciprocal = unitsState == UnitsConvertibilityState::RECIPROCAL;
conversionRate.reciprocal = unitsState == Convertibility::RECIPROCAL;
}
struct UnitIndexAndDimension : UMemory {
int32_t index = 0;
int32_t dimensionality = 0;
UnitIndexAndDimension(const SingleUnitImpl &singleUnit, int32_t multiplier) {
index = singleUnit.index;
dimensionality = singleUnit.dimensionality * multiplier;
}
};
void mergeSingleUnitWithDimension(MaybeStackVector<UnitIndexAndDimension> &unitIndicesWithDimension,
const SingleUnitImpl &shouldBeMerged, int32_t multiplier) {
for (int32_t i = 0; i < unitIndicesWithDimension.length(); i++) {
auto &unitWithIndex = *unitIndicesWithDimension[i];
if (unitWithIndex.index == shouldBeMerged.index) {
unitWithIndex.dimensionality += shouldBeMerged.dimensionality * multiplier;
return;
}
}
unitIndicesWithDimension.emplaceBack(shouldBeMerged, multiplier);
}
void mergeUnitsAndDimensions(MaybeStackVector<UnitIndexAndDimension> &unitIndicesWithDimension,
const MeasureUnitImpl &shouldBeMerged, int32_t multiplier) {
for (int32_t unit_i = 0; unit_i < shouldBeMerged.units.length(); unit_i++) {
auto singleUnit = *shouldBeMerged.units[unit_i];
mergeSingleUnitWithDimension(unitIndicesWithDimension, singleUnit, multiplier);
}
}
UBool checkAllDimensionsAreZeros(const MaybeStackVector<UnitIndexAndDimension> &dimensionVector) {
for (int32_t i = 0; i < dimensionVector.length(); i++) {
if (dimensionVector[i]->dimensionality != 0) {
return false;
}
}
return true;
}
} // namespace
@ -368,21 +410,20 @@ void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Sign
* Extracts the compound base unit of a compound unit (`source`). For example, if the source unit is
* `square-mile-per-hour`, the compound base unit will be `square-meter-per-second`
*/
MeasureUnit U_I18N_API extractCompoundBaseUnit(const MeasureUnit &source,
const ConversionRates &conversionRates,
UErrorCode &status) {
MeasureUnit result;
int32_t count;
const auto singleUnits = source.splitToSingleUnits(count, status);
MeasureUnitImpl U_I18N_API extractCompoundBaseUnit(const MeasureUnitImpl &source,
const ConversionRates &conversionRates,
UErrorCode &status) {
MeasureUnitImpl result;
if (U_FAILURE(status)) return result;
for (int i = 0; i < count; ++i) {
const auto &singleUnit = singleUnits[i];
const auto &singleUnits = source.units;
for (int i = 0, count = singleUnits.length(); i < count; ++i) {
const auto &singleUnit = *singleUnits[i];
// Extract `ConversionRateInfo` using the absolute unit. For example: in case of `square-meter`,
// we will use `meter`
const auto singleUnitImpl = SingleUnitImpl::forMeasureUnit(singleUnit, status);
const auto rateInfo =
conversionRates.extractConversionInfo(singleUnitImpl.getSimpleUnitID(), status);
conversionRates.extractConversionInfo(singleUnit.getSimpleUnitID(), status);
if (U_FAILURE(status)) {
return result;
}
@ -393,14 +434,12 @@ MeasureUnit U_I18N_API extractCompoundBaseUnit(const MeasureUnit &source,
// Multiply the power of the singleUnit by the power of the baseUnit. For example, square-hectare
// must be pow4-meter. (NOTE: hectare --> square-meter)
auto compoundBaseUnit = MeasureUnit::forIdentifier(rateInfo->baseUnit.toStringPiece(), status);
int32_t baseUnitsCount;
auto baseUnits = compoundBaseUnit.splitToSingleUnits(baseUnitsCount, status);
for (int j = 0; j < baseUnitsCount; j++) {
int8_t newDimensionality =
baseUnits[j].getDimensionality(status) * singleUnit.getDimensionality(status);
result = result.product(baseUnits[j].withDimensionality(newDimensionality, status), status);
auto baseUnits =
MeasureUnitImpl::forIdentifier(rateInfo->baseUnit.toStringPiece(), status).units;
for (int32_t i = 0, baseUnitsCount = baseUnits.length(); i < baseUnitsCount; i++) {
baseUnits[i]->dimensionality *= singleUnit.dimensionality;
// TODO: Deal with SI-prefix
result.append(*baseUnits[i], status);
if (U_FAILURE(status)) {
return result;
@ -411,53 +450,70 @@ MeasureUnit U_I18N_API extractCompoundBaseUnit(const MeasureUnit &source,
return result;
}
UnitsConvertibilityState U_I18N_API checkConvertibility(const MeasureUnit &source,
const MeasureUnit &target,
const ConversionRates &conversionRates,
UErrorCode &status) {
if (source.getComplexity(status) == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
target.getComplexity(status) == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
/**
* Determine the convertibility between `source` and `target`.
* For example:
* `meter` and `foot` are `CONVERTIBLE`.
* `meter-per-second` and `second-per-meter` are `RECIPROCAL`.
* `meter` and `pound` are `UNCONVERTIBLE`.
*
* NOTE:
* Only works with SINGLE and COMPOUND units. If one of the units is a
* MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity.
*/
Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source,
const MeasureUnitImpl &target,
const ConversionRates &conversionRates,
UErrorCode &status) {
if (source.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
target.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
status = U_INTERNAL_PROGRAM_ERROR;
return UNCONVERTIBLE;
}
auto sourceBaseUnit = extractCompoundBaseUnit(source, conversionRates, status);
auto targetBaseUnit = extractCompoundBaseUnit(target, conversionRates, status);
MeasureUnitImpl sourceBaseUnit = extractCompoundBaseUnit(source, conversionRates, status);
MeasureUnitImpl targetBaseUnit = extractCompoundBaseUnit(target, conversionRates, status);
if (U_FAILURE(status)) return UNCONVERTIBLE;
if (sourceBaseUnit == targetBaseUnit) return CONVERTIBLE;
if (sourceBaseUnit == targetBaseUnit.reciprocal(status)) return RECIPROCAL;
MaybeStackVector<UnitIndexAndDimension> convertible;
MaybeStackVector<UnitIndexAndDimension> reciprocal;
auto sourceSimplified = sourceBaseUnit.simplify(status);
auto targetSimplified = targetBaseUnit.simplify(status);
mergeUnitsAndDimensions(convertible, sourceBaseUnit, 1);
mergeUnitsAndDimensions(reciprocal, sourceBaseUnit, 1);
if (sourceSimplified == targetSimplified) return CONVERTIBLE;
if (sourceSimplified == targetSimplified.reciprocal(status)) return RECIPROCAL;
mergeUnitsAndDimensions(convertible, targetBaseUnit, -1);
mergeUnitsAndDimensions(reciprocal, targetBaseUnit, 1);
if (checkAllDimensionsAreZeros(convertible)) {
return CONVERTIBLE;
}
if (checkAllDimensionsAreZeros(reciprocal)) {
return RECIPROCAL;
}
return UNCONVERTIBLE;
}
UnitConverter::UnitConverter(MeasureUnit source, MeasureUnit target, const ConversionRates &ratesInfo,
UErrorCode &status) {
if (source.getComplexity(status) == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
target.getComplexity(status) == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
UnitConverter::UnitConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target,
const ConversionRates &ratesInfo, UErrorCode &status)
: conversionRate_(source.copy(status), target.copy(status)) {
if (source.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
target.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
status = U_INTERNAL_PROGRAM_ERROR;
return;
}
UnitsConvertibilityState unitsState = checkConvertibility(source, target, ratesInfo, status);
Convertibility unitsState = extractConvertibility(source, target, ratesInfo, status);
if (U_FAILURE(status)) return;
if (unitsState == UnitsConvertibilityState::UNCONVERTIBLE) {
if (unitsState == Convertibility::UNCONVERTIBLE) {
status = U_INTERNAL_PROGRAM_ERROR;
return;
}
conversionRate_.source = source;
conversionRate_.target = target;
loadConversionRate(conversionRate_, source, target, unitsState, ratesInfo, status);
loadConversionRate(conversionRate_, conversionRate_.source, conversionRate_.target, unitsState,
ratesInfo, status);
}
double UnitConverter::convert(double inputValue) const {

View file

@ -7,9 +7,12 @@
#ifndef __UNITCONVERTER_H__
#define __UNITCONVERTER_H__
#include "unicode/measunit.h"
#include "cmemory.h"
#include "measunit_impl.h"
#include "unicode/errorcode.h"
#include "unicode/stringpiece.h"
#include "unicode/uobject.h"
#include "unitconverter.h"
#include "unitsdata.h"
U_NAMESPACE_BEGIN
@ -65,25 +68,28 @@ void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Sign
/**
* Represents the conversion rate between `source` and `target`.
*/
struct ConversionRate {
MeasureUnit source;
MeasureUnit target;
struct ConversionRate : public UMemory {
const MeasureUnitImpl source;
const MeasureUnitImpl target;
double factorNum = 1;
double factorDen = 1;
double sourceOffset = 0;
double targetOffset = 0;
bool reciprocal = false;
ConversionRate(MeasureUnitImpl &&source, MeasureUnitImpl &&target)
: source(std::move(source)), target(std::move(target)) {}
};
enum U_I18N_API UnitsConvertibilityState {
enum U_I18N_API Convertibility {
RECIPROCAL,
CONVERTIBLE,
UNCONVERTIBLE,
};
MeasureUnit U_I18N_API extractCompoundBaseUnit(const MeasureUnit &source,
const ConversionRates &conversionRates,
UErrorCode &status);
MeasureUnitImpl U_I18N_API extractCompoundBaseUnit(const MeasureUnitImpl &source,
const ConversionRates &conversionRates,
UErrorCode &status);
/**
* Check if the convertibility between `source` and `target`.
@ -96,10 +102,10 @@ MeasureUnit U_I18N_API extractCompoundBaseUnit(const MeasureUnit &source,
* Only works with SINGLE and COMPOUND units. If one of the units is a
* MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity.
*/
UnitsConvertibilityState U_I18N_API checkConvertibility(const MeasureUnit &source,
const MeasureUnit &target,
const ConversionRates &conversionRates,
UErrorCode &status);
Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source,
const MeasureUnitImpl &target,
const ConversionRates &conversionRates,
UErrorCode &status);
/**
* Converts from a source `MeasureUnit` to a target `MeasureUnit`.
@ -120,8 +126,8 @@ class U_I18N_API UnitConverter : public UMemory {
* @param target represents the target unit.
* @param status
*/
UnitConverter(MeasureUnit source, MeasureUnit target, const ConversionRates &ratesInfo,
UErrorCode &status);
UnitConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target,
const ConversionRates &ratesInfo, UErrorCode &status);
/**
* Convert a value in the source unit to another value in the target unit.

View file

@ -7,8 +7,11 @@
#include "charstr.h"
#include "cmemory.h"
#include "cstring.h"
#include "measunit_impl.h"
#include "number_decimalquantity.h"
#include "resource.h"
#include "unicode/measure.h"
#include "unitconverter.h"
#include "unitsdata.h"
#include "unitsrouter.h"
@ -22,7 +25,9 @@ UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece
ConversionRates conversionRates(status);
UnitPreferences prefs(status);
MeasureUnit baseUnit = extractCompoundBaseUnit(inputUnit, conversionRates, status);
MeasureUnitImpl inputUnitImpl = MeasureUnitImpl::forMeasureUnitMaybeCopy(inputUnit, status);
MeasureUnit baseUnit =
(extractCompoundBaseUnit(inputUnitImpl, conversionRates, status)).build(status);
CharString category = getUnitCategory(baseUnit.getIdentifier(), status);
const UnitPreference *const *unitPreferences;
@ -32,7 +37,8 @@ UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece
for (int i = 0; i < preferencesCount; ++i) {
const auto &preference = *unitPreferences[i];
MeasureUnit complexTargetUnit = MeasureUnit::forIdentifier(preference.unit.data(), status);
MeasureUnitImpl complexTargetUnitImpl =
MeasureUnitImpl::forIdentifier(preference.unit.data(), status);
if (U_FAILURE(status)) {
return;
}
@ -49,9 +55,11 @@ UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece
return;
}
outputUnits_.emplaceBackAndCheckErrorCode(status, complexTargetUnit);
converterPreferences_.emplaceBackAndCheckErrorCode(
status, inputUnit, complexTargetUnit, preference.geq, precision, conversionRates, status);
outputUnits_.emplaceBackAndCheckErrorCode(status,
complexTargetUnitImpl.copy(status).build(status));
converterPreferences_.emplaceBackAndCheckErrorCode(status, inputUnitImpl, complexTargetUnitImpl,
preference.geq, std::move(precision),
conversionRates, status);
if (U_FAILURE(status)) {
return;

View file

@ -11,6 +11,7 @@
#include "cmemory.h"
#include "complexunitsconverter.h"
#include "measunit_impl.h"
#include "unicode/measunit.h"
#include "unicode/stringpiece.h"
#include "unicode/uobject.h"
@ -46,14 +47,16 @@ struct ConverterPreference : UMemory {
UnicodeString precision;
// In case there is no limit, the limit will be -inf.
ConverterPreference(MeasureUnit source, MeasureUnit complexTarget, UnicodeString precision,
const ConversionRates &ratesInfo, UErrorCode &status)
ConverterPreference(const MeasureUnitImpl &source, const MeasureUnitImpl &complexTarget,
UnicodeString precision, const ConversionRates &ratesInfo, UErrorCode &status)
: ConverterPreference(source, complexTarget, std::numeric_limits<double>::lowest(), precision,
ratesInfo, status) {}
ConverterPreference(MeasureUnit source, MeasureUnit complexTarget, double limit,
UnicodeString precision, const ConversionRates &ratesInfo, UErrorCode &status)
: converter(source, complexTarget, ratesInfo, status), limit(limit), precision(precision) {}
ConverterPreference(const MeasureUnitImpl &source, const MeasureUnitImpl &complexTarget,
double limit, UnicodeString precision, const ConversionRates &ratesInfo,
UErrorCode &status)
: converter(source, complexTarget, ratesInfo, status), limit(limit),
precision(std::move(precision)) {}
};
/**

View file

@ -121,26 +121,27 @@ void UnitsTest::testConversionCapability() {
struct TestCase {
const char *const source;
const char *const target;
const UnitsConvertibilityState expectedState;
const Convertibility expectedState;
} testCases[]{
{"meter", "foot", CONVERTIBLE}, //
{"kilometer", "foot", CONVERTIBLE}, //
{"hectare", "square-foot", CONVERTIBLE}, //
{"kilometer-per-second", "second-per-meter", RECIPROCAL}, //
{"square-meter", "square-foot", CONVERTIBLE}, //
{"kilometer-per-second", "foot-per-second", CONVERTIBLE}, //
{"square-hectare", "pow4-foot", CONVERTIBLE}, //
{"square-kilometer-per-second", "second-per-square-meter", RECIPROCAL}, //
{"meter", "foot", CONVERTIBLE}, //
{"kilometer", "foot", CONVERTIBLE}, //
{"hectare", "square-foot", CONVERTIBLE}, //
{"kilometer-per-second", "second-per-meter", RECIPROCAL}, //
{"square-meter", "square-foot", CONVERTIBLE}, //
{"kilometer-per-second", "foot-per-second", CONVERTIBLE}, //
{"square-hectare", "pow4-foot", CONVERTIBLE}, //
{"square-kilometer-per-second", "second-per-square-meter", RECIPROCAL}, //
{"cubic-kilometer-per-second-meter", "second-per-square-meter", RECIPROCAL}, //
};
for (const auto &testCase : testCases) {
UErrorCode status = U_ZERO_ERROR;
MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
MeasureUnitImpl source = MeasureUnitImpl::forIdentifier(testCase.source, status);
MeasureUnitImpl target = MeasureUnitImpl::forIdentifier(testCase.target, status);
ConversionRates conversionRates(status);
auto convertibility = checkConvertibility(source, target, conversionRates, status);
auto convertibility = extractConvertibility(source, target, conversionRates, status);
assertEquals(UnicodeString("Conversion Capability: ") + testCase.source + " to " +
testCase.target,
@ -171,8 +172,8 @@ void UnitsTest::testSiPrefixes() {
for (const auto &testCase : testCases) {
UErrorCode status = U_ZERO_ERROR;
MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
MeasureUnitImpl source = MeasureUnitImpl::forIdentifier(testCase.source, status);
MeasureUnitImpl target = MeasureUnitImpl::forIdentifier(testCase.target, status);
ConversionRates conversionRates(status);
UnitConverter converter(source, target, conversionRates, status);
@ -206,8 +207,8 @@ void UnitsTest::testMass() {
for (const auto &testCase : testCases) {
UErrorCode status = U_ZERO_ERROR;
MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
MeasureUnitImpl source = MeasureUnitImpl::forIdentifier(testCase.source, status);
MeasureUnitImpl target = MeasureUnitImpl::forIdentifier(testCase.target, status);
ConversionRates conversionRates(status);
UnitConverter converter(source, target, conversionRates, status);
@ -240,8 +241,8 @@ void UnitsTest::testTemperature() {
for (const auto &testCase : testCases) {
UErrorCode status = U_ZERO_ERROR;
MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
MeasureUnitImpl source = MeasureUnitImpl::forIdentifier(testCase.source, status);
MeasureUnitImpl target = MeasureUnitImpl::forIdentifier(testCase.target, status);
ConversionRates conversionRates(status);
UnitConverter converter(source, target, conversionRates, status);
@ -278,8 +279,8 @@ void UnitsTest::testArea() {
for (const auto &testCase : testCases) {
UErrorCode status = U_ZERO_ERROR;
MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
MeasureUnitImpl source = MeasureUnitImpl::forIdentifier(testCase.source, status);
MeasureUnitImpl target = MeasureUnitImpl::forIdentifier(testCase.target, status);
ConversionRates conversionRates(status);
UnitConverter converter(source, target, conversionRates, status);
@ -356,12 +357,12 @@ void unitsTestDataLineFn(void *context, char *fields[][2], int32_t fieldCount, U
return;
}
MeasureUnit sourceUnit = MeasureUnit::forIdentifier(x, status);
MeasureUnitImpl sourceUnit = MeasureUnitImpl::forIdentifier(x, status);
if (status.errIfFailureAndReset("forIdentifier(\"%.*s\")", x.length(), x.data())) {
return;
}
MeasureUnit targetUnit = MeasureUnit::forIdentifier(y, status);
MeasureUnitImpl targetUnit = MeasureUnitImpl::forIdentifier(y, status);
if (status.errIfFailureAndReset("forIdentifier(\"%.*s\")", y.length(), y.data())) {
return;
}
@ -373,16 +374,16 @@ void unitsTestDataLineFn(void *context, char *fields[][2], int32_t fieldCount, U
expected, commentConversionFormula.length(), commentConversionFormula.data());
// Convertibility:
auto convertibility = checkConvertibility(sourceUnit, targetUnit, *ctx->conversionRates, status);
if (status.errIfFailureAndReset("checkConvertibility(<%s>, <%s>, ...)", sourceUnit.getIdentifier(),
targetUnit.getIdentifier())) {
auto convertibility = extractConvertibility(sourceUnit, targetUnit, *ctx->conversionRates, status);
if (status.errIfFailureAndReset("extractConvertibility(<%s>, <%s>, ...)",
sourceUnit.identifier.data(), targetUnit.identifier.data())) {
return;
}
CharString msg;
msg.append("convertible: ", status)
.append(sourceUnit.getIdentifier(), status)
.append(sourceUnit.identifier.data(), status)
.append(" -> ", status)
.append(targetUnit.getIdentifier(), status);
.append(targetUnit.identifier.data(), status);
if (status.errIfFailureAndReset("msg construction")) {
return;
}
@ -391,7 +392,7 @@ void unitsTestDataLineFn(void *context, char *fields[][2], int32_t fieldCount, U
// Conversion:
UnitConverter converter(sourceUnit, targetUnit, *ctx->conversionRates, status);
if (status.errIfFailureAndReset("constructor: UnitConverter(<%s>, <%s>, status)",
sourceUnit.getIdentifier(), targetUnit.getIdentifier())) {
sourceUnit.identifier.data(), targetUnit.identifier.data())) {
return;
}
double got = converter.convert(1000);
@ -598,6 +599,7 @@ void unitPreferencesTestDataLineFn(void *context, char *fields[][2], int32_t fie
if (U_FAILURE(status)) {
return;
}
UnitsRouter router(inputMeasureUnit, region, usage, status);
if (status.errIfFailureAndReset("UnitsRouter(<%s>, \"%.*s\", \"%.*s\", status)",
inputMeasureUnit.getIdentifier(), region.length(), region.data(),