template<typename T, int32_t stackCapacity = 8>
class MemoryPool : public UMemory {
public:
- MemoryPool() : count(0), pool() {}
+ MemoryPool() : fCount(0), fPool() {}
~MemoryPool() {
- for (int32_t i = 0; i < count; ++i) {
- delete pool[i];
+ for (int32_t i = 0; i < fCount; ++i) {
+ delete fPool[i];
}
}
MemoryPool(const MemoryPool&) = delete;
MemoryPool& operator=(const MemoryPool&) = delete;
- MemoryPool(MemoryPool&& other) U_NOEXCEPT : count(other.count),
- pool(std::move(other.pool)) {
- other.count = 0;
+ MemoryPool(MemoryPool&& other) U_NOEXCEPT : fCount(other.fCount),
+ fPool(std::move(other.fPool)) {
+ other.fCount = 0;
}
MemoryPool& operator=(MemoryPool&& other) U_NOEXCEPT {
- count = other.count;
- pool = std::move(other.pool);
- other.count = 0;
+ fCount = other.fCount;
+ fPool = std::move(other.fPool);
+ other.fCount = 0;
return *this;
}
*/
template<typename... Args>
T* create(Args&&... args) {
- int32_t capacity = pool.getCapacity();
- if (count == capacity &&
- pool.resize(capacity == stackCapacity ? 4 * capacity : 2 * capacity,
- capacity) == nullptr) {
+ int32_t capacity = fPool.getCapacity();
+ if (fCount == capacity &&
+ fPool.resize(capacity == stackCapacity ? 4 * capacity : 2 * capacity,
+ capacity) == nullptr) {
return nullptr;
}
- return pool[count++] = new T(std::forward<Args>(args)...);
+ return fPool[fCount++] = new T(std::forward<Args>(args)...);
}
/**
* @return Number of elements that have been allocated.
*/
- int32_t size() const {
- return count;
+ int32_t count() const {
+ return fCount;
+ }
+
+protected:
+ int32_t fCount;
+ MaybeStackArray<T*, stackCapacity> fPool;
+};
+
+/**
+ * An internal Vector-like implementation based on MemoryPool.
+ *
+ * To append an item to the vector, use emplaceBack.
+ *
+ * MaybeStackVector<MyType> vector;
+ * MyType* element = vector.emplaceBack();
+ * if (!element) {
+ * status = U_MEMORY_ALLOCATION_ERROR;
+ * }
+ * // do stuff with element
+ *
+ * To loop over the vector, use a for loop with indices:
+ *
+ * for (int32_t i = 0; i < vector.length(); i++) {
+ * MyType* element = vector[i];
+ * }
+ */
+template<typename T, int32_t stackCapacity = 8>
+class MaybeStackVector : protected MemoryPool<T, stackCapacity> {
+public:
+ using MemoryPool<T, stackCapacity>::MemoryPool;
+ using MemoryPool<T, stackCapacity>::operator=;
+
+ template<typename... Args>
+ T* emplaceBack(Args&&... args) {
+ return this->create(args...);
+ }
+
+ int32_t length() const {
+ return this->count();
}
/**
* @return reference to the array item
*/
T *operator[](ptrdiff_t i) const {
- return pool[i];
+ return this->fPool[i];
}
-
-private:
- int32_t count;
- MaybeStackArray<T*, stackCapacity> pool;
};
+
U_NAMESPACE_END
#endif /* __cplusplus */
#include "ucln_in.h"
#include "umutex.h"
#include "unicode/errorcode.h"
+#include "unicode/localpointer.h"
#include "unicode/measunit.h"
#include "unicode/ucharstrie.h"
#include "unicode/ucharstriebuilder.h"
class CompoundUnit {
public:
+ typedef MaybeStackVector<PowerUnit, 3> PowerUnitList;
+
void append(PowerUnit&& powerUnit, UErrorCode& status) {
if (powerUnit.power >= 0) {
appendImpl(numerator, std::move(powerUnit), status);
}
void appendTo(CharString& builder, UErrorCode& status) {
- if (numerator.size() == 0) {
+ if (numerator.length() == 0) {
builder.append("one", status);
} else {
- appendToImpl(numerator, numerator.size(), builder, status);
+ appendToImpl(numerator, numerator.length(), builder, status);
}
- if (denominator.size() > 0) {
+ if (denominator.length() > 0) {
builder.append("-per-", status);
- appendToImpl(denominator, denominator.size(), builder, status);
+ appendToImpl(denominator, denominator.length(), builder, status);
}
}
+ const PowerUnitList& getNumeratorUnits() {
+ return numerator;
+ }
+
+ const PowerUnitList& getDenominatorUnits() {
+ return denominator;
+ }
+
private:
- typedef MemoryPool<PowerUnit, 3> PowerUnitList;
PowerUnitList numerator;
PowerUnitList denominator;
void appendImpl(PowerUnitList& unitList, PowerUnit&& powerUnit, UErrorCode& status) {
// Check that the same simple unit doesn't already exist
- for (int32_t i = 0; i < unitList.size(); i++) {
+ for (int32_t i = 0; i < unitList.length(); i++) {
PowerUnit* candidate = unitList[i];
if (candidate->simpleUnitIndex == powerUnit.simpleUnitIndex
&& candidate->siPrefix == powerUnit.siPrefix) {
}
}
// Add a new unit
- PowerUnit* destination = unitList.create();
+ PowerUnit* destination = unitList.emplaceBack();
if (!destination) {
status = U_MEMORY_ALLOCATION_ERROR;
return;
goto fail;
}
fAfterPer = true;
+ result.power = -1;
break;
case COMPOUND_PART_TIMES:
if (state > 0) {
goto fail;
}
- result.power = token.getPower();
- if (fAfterPer) {
- result.power *= -1;
- }
+ result.power *= token.getPower();
previ = fIndex;
state = 1;
break;
return MeasureUnit(builder.cloneData(status));
}
+LocalArray<MeasureUnit> MeasureUnit::getSimpleUnits(UErrorCode& status) const {
+ const char* id = getIdentifier();
+ CompoundUnit compoundUnit = UnitIdentifierParser::from(id, status).getOnlyCompoundUnit(status);
+ if (U_FAILURE(status)) {
+ return LocalArray<MeasureUnit>::withLength(nullptr, 0);
+ }
+
+ const CompoundUnit::PowerUnitList& numerator = compoundUnit.getNumeratorUnits();
+ const CompoundUnit::PowerUnitList& denominator = compoundUnit.getDenominatorUnits();
+ int32_t count = numerator.length() + denominator.length();
+ MeasureUnit* arr = new MeasureUnit[count];
+
+ CharString builder;
+ int32_t i = 0;
+ for (int32_t j = 0; j < numerator.length(); j++) {
+ numerator[j]->appendTo(builder.clear(), status);
+ arr[i++] = MeasureUnit(builder.cloneData(status));
+ }
+ for (int32_t j = 0; j < denominator.length(); j++) {
+ builder.clear().append("one-per-", status);
+ denominator[j]->appendTo(builder, status);
+ arr[i++] = MeasureUnit(builder.cloneData(status));
+ }
+
+ return LocalArray<MeasureUnit>::withLength(arr, count);
+}
+
U_NAMESPACE_END
NumberFormat::EAlignmentFields field,
int32_t start,
int32_t end);
- void verifyUnitParts(
+ void verifyPowerUnit(
const MeasureUnit& unit,
UMeasureSIPrefix siPrefix,
int8_t power,
const char* identifier);
- void verifyUnitIdentifierOnly(
+ void verifyCompoundUnit(
const MeasureUnit& unit,
- const char* identifier);
+ const char* identifier,
+ const char** subIdentifiers,
+ int32_t subIdentifierCount);
};
void MeasureFormatTest::runIndexedTest(
MeasureUnit centimeter2 = meter.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status);
MeasureUnit cubicDecimeter = cubicMeter.withSIPrefix(UMEASURE_SI_PREFIX_DECI, status);
- verifyUnitParts(kilometer, UMEASURE_SI_PREFIX_KILO, 1, "kilometer");
- verifyUnitParts(meter, UMEASURE_SI_PREFIX_ONE, 1, "meter");
- verifyUnitParts(centimeter1, UMEASURE_SI_PREFIX_CENTI, 1, "centimeter");
- verifyUnitParts(centimeter2, UMEASURE_SI_PREFIX_CENTI, 1, "centimeter");
- verifyUnitParts(cubicDecimeter, UMEASURE_SI_PREFIX_DECI, 3, "cubic-decimeter");
+ verifyPowerUnit(kilometer, UMEASURE_SI_PREFIX_KILO, 1, "kilometer");
+ verifyPowerUnit(meter, UMEASURE_SI_PREFIX_ONE, 1, "meter");
+ verifyPowerUnit(centimeter1, UMEASURE_SI_PREFIX_CENTI, 1, "centimeter");
+ verifyPowerUnit(centimeter2, UMEASURE_SI_PREFIX_CENTI, 1, "centimeter");
+ verifyPowerUnit(cubicDecimeter, UMEASURE_SI_PREFIX_DECI, 3, "cubic-decimeter");
assertTrue("centimeter equality", centimeter1 == centimeter2);
assertTrue("kilometer inequality", centimeter1 != kilometer);
MeasureUnit quarticKilometer = kilometer.withPower(4, status);
MeasureUnit overQuarticKilometer1 = kilometer.withPower(-4, status);
- verifyUnitParts(squareMeter, UMEASURE_SI_PREFIX_ONE, 2, "square-meter");
- verifyUnitParts(overCubicCentimeter, UMEASURE_SI_PREFIX_CENTI, -3, "one-per-cubic-centimeter");
- verifyUnitParts(quarticKilometer, UMEASURE_SI_PREFIX_KILO, 4, "p4-kilometer");
- verifyUnitParts(overQuarticKilometer1, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
+ verifyPowerUnit(squareMeter, UMEASURE_SI_PREFIX_ONE, 2, "square-meter");
+ verifyPowerUnit(overCubicCentimeter, UMEASURE_SI_PREFIX_CENTI, -3, "one-per-cubic-centimeter");
+ verifyPowerUnit(quarticKilometer, UMEASURE_SI_PREFIX_KILO, 4, "p4-kilometer");
+ verifyPowerUnit(overQuarticKilometer1, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
assertTrue("power inequality", quarticKilometer != overQuarticKilometer1);
.product(kilometer, status)
.reciprocal(status);
- verifyUnitParts(overQuarticKilometer2, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
- verifyUnitParts(overQuarticKilometer3, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
+ verifyPowerUnit(overQuarticKilometer2, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
+ verifyPowerUnit(overQuarticKilometer3, UMEASURE_SI_PREFIX_KILO, -4, "one-per-p4-kilometer");
assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer2);
assertTrue("reciprocal equality", overQuarticKilometer1 == overQuarticKilometer3);
MeasureUnit centimeterSecond1 = meter.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status).product(kiloSquareSecond, status);
MeasureUnit secondCubicMeter = kiloSquareSecond.product(meter.withPower(3, status), status);
MeasureUnit secondCentimeter = kiloSquareSecond.product(meter.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status), status);
+ MeasureUnit secondCentimeterPerKilometer = secondCentimeter.product(kilometer.reciprocal(status), status);
+
+ verifyPowerUnit(kiloSquareSecond, UMEASURE_SI_PREFIX_KILO, 2, "square-kilosecond");
+ const char* meterSecondSub[] = {"meter", "square-kilosecond"};
+ verifyCompoundUnit(meterSecond, "meter-square-kilosecond",
+ meterSecondSub, UPRV_LENGTHOF(meterSecondSub));
+ const char* cubicMeterSecond1Sub[] = {"cubic-meter", "square-kilosecond"};
+ verifyCompoundUnit(cubicMeterSecond1, "cubic-meter-square-kilosecond",
+ cubicMeterSecond1Sub, UPRV_LENGTHOF(cubicMeterSecond1Sub));
+ const char* centimeterSecond1Sub[] = {"centimeter", "square-kilosecond"};
+ verifyCompoundUnit(centimeterSecond1, "centimeter-square-kilosecond",
+ centimeterSecond1Sub, UPRV_LENGTHOF(centimeterSecond1Sub));
+ const char* secondCubicMeterSub[] = {"square-kilosecond", "cubic-meter"};
+ verifyCompoundUnit(secondCubicMeter, "square-kilosecond-cubic-meter",
+ secondCubicMeterSub, UPRV_LENGTHOF(secondCubicMeterSub));
+ const char* secondCentimeterSub[] = {"square-kilosecond", "centimeter"};
+ verifyCompoundUnit(secondCentimeter, "square-kilosecond-centimeter",
+ secondCentimeterSub, UPRV_LENGTHOF(secondCentimeterSub));
+ const char* secondCentimeterPerKilometerSub[] = {"square-kilosecond", "centimeter", "one-per-kilometer"};
+ verifyCompoundUnit(secondCentimeterPerKilometer, "square-kilosecond-centimeter-per-kilometer",
+ secondCentimeterPerKilometerSub, UPRV_LENGTHOF(secondCentimeterPerKilometerSub));
- verifyUnitParts(kiloSquareSecond, UMEASURE_SI_PREFIX_KILO, 2, "square-kilosecond");
- verifyUnitIdentifierOnly(meterSecond, "meter-square-kilosecond");
- verifyUnitIdentifierOnly(cubicMeterSecond1, "cubic-meter-square-kilosecond");
- verifyUnitIdentifierOnly(centimeterSecond1, "centimeter-square-kilosecond");
- verifyUnitIdentifierOnly(secondCubicMeter, "square-kilosecond-cubic-meter");
- verifyUnitIdentifierOnly(secondCentimeter, "square-kilosecond-centimeter");
+ assertTrue("order matters inequality", cubicMeterSecond1 != secondCubicMeter);
+ assertTrue("additional simple units inequality", secondCubicMeter != secondCentimeter);
// Don't allow get/set power or SI prefix on compound units
status.errIfFailureAndReset();
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
meterSecond.withSIPrefix(UMEASURE_SI_PREFIX_CENTI, status);
status.expectErrorAndReset(U_ILLEGAL_ARGUMENT_ERROR);
-
- assertTrue("order matters inequality", cubicMeterSecond1 != secondCubicMeter);
- assertTrue("additional simple units inequality", secondCubicMeter != secondCentimeter);
}
}
}
-void MeasureFormatTest::verifyUnitParts(
+void MeasureFormatTest::verifyPowerUnit(
const MeasureUnit& unit,
UMeasureSIPrefix siPrefix,
int8_t power,
const char* identifier) {
- IcuTestErrorCode status(*this, "verifyUnitParts");
+ IcuTestErrorCode status(*this, "verifyPowerUnit");
UnicodeString uid(identifier, -1, US_INV);
assertEquals(uid + ": SI prefix",
siPrefix,
status.errIfFailureAndReset("%s: Constructor", identifier);
}
-void MeasureFormatTest::verifyUnitIdentifierOnly(
+void MeasureFormatTest::verifyCompoundUnit(
const MeasureUnit& unit,
- const char* identifier) {
- IcuTestErrorCode status(*this, "verifyUnitIdentifierOnly");
+ const char* identifier,
+ const char** subIdentifiers,
+ int32_t subIdentifierCount) {
+ IcuTestErrorCode status(*this, "verifyCompoundUnit");
UnicodeString uid(identifier, -1, US_INV);
assertEquals(uid + ": Identifier",
identifier,
assertTrue(uid + ": Constructor",
unit == MeasureUnit::forIdentifier(identifier, status));
status.errIfFailureAndReset("%s: Constructor", identifier);
+
+ LocalArray<MeasureUnit> subUnits = unit.getSimpleUnits(status);
+ assertEquals(uid + ": Length", subIdentifierCount, subUnits.length());
+ for (int32_t i = 0;; i++) {
+ if (i >= subIdentifierCount || i >= subUnits.length()) break;
+ assertEquals(uid + ": Sub-unit #" + Int64ToUnicodeString(i),
+ subIdentifiers[i],
+ subUnits[i].getIdentifier());
+ }
}
extern IntlTest *createMeasureFormatTest() {