"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "source/i18n/measunit_extra.cpp" between
icu4c-67rc-src.tgz and icu4c-67_1-src.tgz

About: ICU (International Components for Unicode) is a set of C/C++ and Java libraries providing Unicode and Globalization support for software applications on a wide variety of platforms.

measunit_extra.cpp  (icu4c-67rc-src.tgz):measunit_extra.cpp  (icu4c-67_1-src.tgz)
skipping to change at line 15 skipping to change at line 15
// Separate .o file so that it can be removed for modularity. // Separate .o file so that it can be removed for modularity.
#include "unicode/utypes.h" #include "unicode/utypes.h"
#if !UCONFIG_NO_FORMATTING #if !UCONFIG_NO_FORMATTING
// Allow implicit conversion from char16_t* to UnicodeString for this file: // Allow implicit conversion from char16_t* to UnicodeString for this file:
// Helpful in toString methods and elsewhere. // Helpful in toString methods and elsewhere.
#define UNISTR_FROM_STRING_EXPLICIT #define UNISTR_FROM_STRING_EXPLICIT
#include <cstdlib>
#include "cstring.h" #include "cstring.h"
#include "measunit_impl.h" #include "measunit_impl.h"
#include "uarrsort.h" #include "uarrsort.h"
#include "uassert.h" #include "uassert.h"
#include "ucln_in.h" #include "ucln_in.h"
#include "umutex.h" #include "umutex.h"
#include "unicode/errorcode.h" #include "unicode/errorcode.h"
#include "unicode/localpointer.h" #include "unicode/localpointer.h"
#include "unicode/measunit.h" #include "unicode/measunit.h"
#include "unicode/ucharstrie.h" #include "unicode/ucharstrie.h"
skipping to change at line 36 skipping to change at line 37
#include "cstr.h" #include "cstr.h"
U_NAMESPACE_BEGIN U_NAMESPACE_BEGIN
namespace { namespace {
// TODO: Propose a new error code for this? // TODO: Propose a new error code for this?
constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR; constexpr UErrorCode kUnitIdentifierSyntaxError = U_ILLEGAL_ARGUMENT_ERROR;
// This is to ensure we only insert positive integers into the trie // Trie value offset for SI Prefixes. This is big enough to ensure we only
// insert positive integers into the trie.
constexpr int32_t kSIPrefixOffset = 64; constexpr int32_t kSIPrefixOffset = 64;
// Trie value offset for compound parts, e.g. "-per-", "-", "-and-".
constexpr int32_t kCompoundPartOffset = 128; constexpr int32_t kCompoundPartOffset = 128;
enum CompoundPart { enum CompoundPart {
// Represents "-per-"
COMPOUND_PART_PER = kCompoundPartOffset, COMPOUND_PART_PER = kCompoundPartOffset,
// Represents "-"
COMPOUND_PART_TIMES, COMPOUND_PART_TIMES,
COMPOUND_PART_PLUS, // Represents "-and-"
COMPOUND_PART_AND,
}; };
// Trie value offset for "per-".
constexpr int32_t kInitialCompoundPartOffset = 192;
enum InitialCompoundPart {
// Represents "per-", the only compound part that can appear at the start of
// an identifier.
INITIAL_COMPOUND_PART_PER = kInitialCompoundPartOffset,
};
// Trie value offset for powers like "square-", "cubic-", "p2-" etc.
constexpr int32_t kPowerPartOffset = 256; constexpr int32_t kPowerPartOffset = 256;
enum PowerPart { enum PowerPart {
POWER_PART_P2 = kPowerPartOffset + 2, POWER_PART_P2 = kPowerPartOffset + 2,
POWER_PART_P3, POWER_PART_P3,
POWER_PART_P4, POWER_PART_P4,
POWER_PART_P5, POWER_PART_P5,
POWER_PART_P6, POWER_PART_P6,
POWER_PART_P7, POWER_PART_P7,
POWER_PART_P8, POWER_PART_P8,
POWER_PART_P9, POWER_PART_P9,
POWER_PART_P10, POWER_PART_P10,
POWER_PART_P11, POWER_PART_P11,
POWER_PART_P12, POWER_PART_P12,
POWER_PART_P13, POWER_PART_P13,
POWER_PART_P14, POWER_PART_P14,
POWER_PART_P15, POWER_PART_P15,
}; };
// Trie value offset for simple units, e.g. "gram", "nautical-mile",
// "fluid-ounce-imperial".
constexpr int32_t kSimpleUnitOffset = 512; constexpr int32_t kSimpleUnitOffset = 512;
const struct SIPrefixStrings { const struct SIPrefixStrings {
const char* const string; const char* const string;
UMeasureSIPrefix value; UMeasureSIPrefix value;
} gSIPrefixStrings[] = { } gSIPrefixStrings[] = {
{ "yotta", UMEASURE_SI_PREFIX_YOTTA }, { "yotta", UMEASURE_SI_PREFIX_YOTTA },
{ "zetta", UMEASURE_SI_PREFIX_ZETTA }, { "zetta", UMEASURE_SI_PREFIX_ZETTA },
{ "exa", UMEASURE_SI_PREFIX_EXA }, { "exa", UMEASURE_SI_PREFIX_EXA },
{ "peta", UMEASURE_SI_PREFIX_PETA }, { "peta", UMEASURE_SI_PREFIX_PETA },
skipping to change at line 96 skipping to change at line 114
{ "nano", UMEASURE_SI_PREFIX_NANO }, { "nano", UMEASURE_SI_PREFIX_NANO },
{ "pico", UMEASURE_SI_PREFIX_PICO }, { "pico", UMEASURE_SI_PREFIX_PICO },
{ "femto", UMEASURE_SI_PREFIX_FEMTO }, { "femto", UMEASURE_SI_PREFIX_FEMTO },
{ "atto", UMEASURE_SI_PREFIX_ATTO }, { "atto", UMEASURE_SI_PREFIX_ATTO },
{ "zepto", UMEASURE_SI_PREFIX_ZEPTO }, { "zepto", UMEASURE_SI_PREFIX_ZEPTO },
{ "yocto", UMEASURE_SI_PREFIX_YOCTO }, { "yocto", UMEASURE_SI_PREFIX_YOCTO },
}; };
// TODO(ICU-21059): Get this list from data // TODO(ICU-21059): Get this list from data
const char16_t* const gSimpleUnits[] = { const char16_t* const gSimpleUnits[] = {
u"one", // note: expected to be index 0
u"candela", u"candela",
u"carat", u"carat",
u"gram", u"gram",
u"ounce", u"ounce",
u"ounce-troy", u"ounce-troy",
u"pound", u"pound",
u"kilogram", u"kilogram",
u"stone", u"stone",
u"ton", u"ton",
u"metric-ton", u"metric-ton",
skipping to change at line 228 skipping to change at line 245
// Add SI prefixes // Add SI prefixes
for (const auto& siPrefixInfo : gSIPrefixStrings) { for (const auto& siPrefixInfo : gSIPrefixStrings) {
UnicodeString uSIPrefix(siPrefixInfo.string, -1, US_INV); UnicodeString uSIPrefix(siPrefixInfo.string, -1, US_INV);
b.add(uSIPrefix, siPrefixInfo.value + kSIPrefixOffset, status); b.add(uSIPrefix, siPrefixInfo.value + kSIPrefixOffset, status);
} }
if (U_FAILURE(status)) { return; } if (U_FAILURE(status)) { return; }
// Add syntax parts (compound, power prefixes) // Add syntax parts (compound, power prefixes)
b.add(u"-per-", COMPOUND_PART_PER, status); b.add(u"-per-", COMPOUND_PART_PER, status);
b.add(u"-", COMPOUND_PART_TIMES, status); b.add(u"-", COMPOUND_PART_TIMES, status);
b.add(u"-and-", COMPOUND_PART_PLUS, status); b.add(u"-and-", COMPOUND_PART_AND, status);
b.add(u"per-", INITIAL_COMPOUND_PART_PER, status);
b.add(u"square-", POWER_PART_P2, status); b.add(u"square-", POWER_PART_P2, status);
b.add(u"cubic-", POWER_PART_P3, status); b.add(u"cubic-", POWER_PART_P3, status);
b.add(u"p2-", POWER_PART_P2, status); b.add(u"p2-", POWER_PART_P2, status);
b.add(u"p3-", POWER_PART_P3, status); b.add(u"p3-", POWER_PART_P3, status);
b.add(u"p4-", POWER_PART_P4, status); b.add(u"p4-", POWER_PART_P4, status);
b.add(u"p5-", POWER_PART_P5, status); b.add(u"p5-", POWER_PART_P5, status);
b.add(u"p6-", POWER_PART_P6, status); b.add(u"p6-", POWER_PART_P6, status);
b.add(u"p7-", POWER_PART_P7, status); b.add(u"p7-", POWER_PART_P7, status);
b.add(u"p8-", POWER_PART_P8, status); b.add(u"p8-", POWER_PART_P8, status);
b.add(u"p9-", POWER_PART_P9, status); b.add(u"p9-", POWER_PART_P9, status);
skipping to change at line 272 skipping to change at line 290
uprv_memcpy(kSerializedUnitExtrasStemTrie, result.getBuffer(), numBytes); uprv_memcpy(kSerializedUnitExtrasStemTrie, result.getBuffer(), numBytes);
} }
class Token { class Token {
public: public:
Token(int32_t match) : fMatch(match) {} Token(int32_t match) : fMatch(match) {}
enum Type { enum Type {
TYPE_UNDEFINED, TYPE_UNDEFINED,
TYPE_SI_PREFIX, TYPE_SI_PREFIX,
// Token type for "-per-", "-", and "-and-".
TYPE_COMPOUND_PART, TYPE_COMPOUND_PART,
// Token type for "per-".
TYPE_INITIAL_COMPOUND_PART,
TYPE_POWER_PART, TYPE_POWER_PART,
TYPE_ONE,
TYPE_SIMPLE_UNIT, TYPE_SIMPLE_UNIT,
}; };
// Calling getType() is invalid, resulting in an assertion failure, if Token
// value isn't positive.
Type getType() const { Type getType() const {
if (fMatch <= 0) { U_ASSERT(fMatch > 0);
UPRV_UNREACHABLE;
}
if (fMatch < kCompoundPartOffset) { if (fMatch < kCompoundPartOffset) {
return TYPE_SI_PREFIX; return TYPE_SI_PREFIX;
} }
if (fMatch < kPowerPartOffset) { if (fMatch < kInitialCompoundPartOffset) {
return TYPE_COMPOUND_PART; return TYPE_COMPOUND_PART;
} }
if (fMatch < kPowerPartOffset) {
return TYPE_INITIAL_COMPOUND_PART;
}
if (fMatch < kSimpleUnitOffset) { if (fMatch < kSimpleUnitOffset) {
return TYPE_POWER_PART; return TYPE_POWER_PART;
} }
if (fMatch == kSimpleUnitOffset) {
return TYPE_ONE;
}
return TYPE_SIMPLE_UNIT; return TYPE_SIMPLE_UNIT;
} }
UMeasureSIPrefix getSIPrefix() const { UMeasureSIPrefix getSIPrefix() const {
U_ASSERT(getType() == TYPE_SI_PREFIX); U_ASSERT(getType() == TYPE_SI_PREFIX);
return static_cast<UMeasureSIPrefix>(fMatch - kSIPrefixOffset); return static_cast<UMeasureSIPrefix>(fMatch - kSIPrefixOffset);
} }
// Valid only for tokens with type TYPE_COMPOUND_PART.
int32_t getMatch() const { int32_t getMatch() const {
U_ASSERT(getType() == TYPE_COMPOUND_PART); U_ASSERT(getType() == TYPE_COMPOUND_PART);
return fMatch; return fMatch;
} }
int32_t getInitialCompoundPart() const {
// Even if there is only one InitialCompoundPart value, we have this
// function for the simplicity of code consistency.
U_ASSERT(getType() == TYPE_INITIAL_COMPOUND_PART);
// Defensive: if this assert fails, code using this function also needs
// to change.
U_ASSERT(fMatch == INITIAL_COMPOUND_PART_PER);
return fMatch;
}
int8_t getPower() const { int8_t getPower() const {
U_ASSERT(getType() == TYPE_POWER_PART); U_ASSERT(getType() == TYPE_POWER_PART);
return static_cast<int8_t>(fMatch - kPowerPartOffset); return static_cast<int8_t>(fMatch - kPowerPartOffset);
} }
int32_t getSimpleUnitIndex() const { int32_t getSimpleUnitIndex() const {
U_ASSERT(getType() == TYPE_SIMPLE_UNIT); U_ASSERT(getType() == TYPE_SIMPLE_UNIT);
return fMatch - kSimpleUnitOffset; return fMatch - kSimpleUnitOffset;
} }
private: private:
int32_t fMatch; int32_t fMatch;
}; };
class Parser { class Parser {
public: public:
/**
* Factory function for parsing the given identifier.
*
* @param source The identifier to parse. This function does not make a copy
* of source: the underlying string that source points at, must outlive the
* parser.
* @param status ICU error code.
*/
static Parser from(StringPiece source, UErrorCode& status) { static Parser from(StringPiece source, UErrorCode& status) {
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
return Parser(); return Parser();
} }
umtx_initOnce(gUnitExtrasInitOnce, &initUnitExtras, status); umtx_initOnce(gUnitExtrasInitOnce, &initUnitExtras, status);
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
return Parser(); return Parser();
} }
return Parser(source); return Parser(source);
} }
MeasureUnitImpl parse(UErrorCode& status) { MeasureUnitImpl parse(UErrorCode& status) {
MeasureUnitImpl result; MeasureUnitImpl result;
parseImpl(result, status); parseImpl(result, status);
return result; return result;
} }
private: private:
// Tracks parser progress: the offset into fSource.
int32_t fIndex = 0; int32_t fIndex = 0;
// Since we're not owning this memory, whatever is passed to the constructor
// should live longer than this Parser - and the parser shouldn't return any
// references to that string.
StringPiece fSource; StringPiece fSource;
UCharsTrie fTrie; UCharsTrie fTrie;
// Set to true when we've seen a "-per-" or a "per-", after which all units
// are in the denominator. Until we find an "-and-", at which point the
// identifier is invalid pending TODO(CLDR-13700).
bool fAfterPer = false; bool fAfterPer = false;
Parser() : fSource(""), fTrie(u"") {} Parser() : fSource(""), fTrie(u"") {}
Parser(StringPiece source) Parser(StringPiece source)
: fSource(source), fTrie(kSerializedUnitExtrasStemTrie) {} : fSource(source), fTrie(kSerializedUnitExtrasStemTrie) {}
inline bool hasNext() const { inline bool hasNext() const {
return fIndex < fSource.length(); return fIndex < fSource.length();
} }
// Returns the next Token parsed from fSource, advancing fIndex to the end
// of that token in fSource. In case of U_FAILURE(status), the token
// returned will cause an abort if getType() is called on it.
Token nextToken(UErrorCode& status) { Token nextToken(UErrorCode& status) {
fTrie.reset(); fTrie.reset();
int32_t match = -1; int32_t match = -1;
// Saves the position in the fSource string for the end of the most
// recent matching token.
int32_t previ = -1; int32_t previ = -1;
do { // Find the longest token that matches a value in the trie:
while (fIndex < fSource.length()) {
auto result = fTrie.next(fSource.data()[fIndex++]); auto result = fTrie.next(fSource.data()[fIndex++]);
if (result == USTRINGTRIE_NO_MATCH) { if (result == USTRINGTRIE_NO_MATCH) {
break; break;
} else if (result == USTRINGTRIE_NO_VALUE) { } else if (result == USTRINGTRIE_NO_VALUE) {
continue; continue;
} }
U_ASSERT(USTRINGTRIE_HAS_VALUE(result)); U_ASSERT(USTRINGTRIE_HAS_VALUE(result));
match = fTrie.getValue(); match = fTrie.getValue();
previ = fIndex; previ = fIndex;
if (result == USTRINGTRIE_FINAL_VALUE) { if (result == USTRINGTRIE_FINAL_VALUE) {
break; break;
} }
U_ASSERT(result == USTRINGTRIE_INTERMEDIATE_VALUE); U_ASSERT(result == USTRINGTRIE_INTERMEDIATE_VALUE);
// continue; // continue;
} while (fIndex < fSource.length()); }
if (match < 0) { if (match < 0) {
status = kUnitIdentifierSyntaxError; status = kUnitIdentifierSyntaxError;
} else { } else {
fIndex = previ; fIndex = previ;
} }
return Token(match); return Token(match);
} }
void nextSingleUnit(SingleUnitImpl& result, bool& sawPlus, UErrorCode& statu /**
s) { * Returns the next "single unit" via result.
sawPlus = false; *
* If a "-per-" was parsed, the result will have appropriate negative
* dimensionality.
*
* Returns an error if we parse both compound units and "-and-", since mixed
* compound units are not yet supported - TODO(CLDR-13700).
*
* @param result Will be overwritten by the result, if status shows success.
* @param sawAnd If an "-and-" was parsed prior to finding the "single
* unit", sawAnd is set to true. If not, it is left as is.
* @param status ICU error code.
*/
void nextSingleUnit(SingleUnitImpl& result, bool& sawAnd, UErrorCode& status
) {
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
return; return;
} }
if (!hasNext()) {
// probably "one"
return;
}
// state: // state:
// 0 = no tokens seen yet (will accept power, SI prefix, or simple unit) // 0 = no tokens seen yet (will accept power, SI prefix, or simple unit)
// 1 = power token seen (will not accept another power token) // 1 = power token seen (will not accept another power token)
// 2 = SI prefix token seen (will not accept a power or SI prefix token) // 2 = SI prefix token seen (will not accept a power or SI prefix token)
int32_t state = 0; int32_t state = 0;
int32_t previ = fIndex;
// Maybe read a compound part bool atStart = fIndex == 0;
if (fIndex != 0) { Token token = nextToken(status);
Token token = nextToken(status); if (U_FAILURE(status)) { return; }
if (U_FAILURE(status)) {
return; if (atStart) {
// Identifiers optionally start with "per-".
if (token.getType() == Token::TYPE_INITIAL_COMPOUND_PART) {
U_ASSERT(token.getInitialCompoundPart() == INITIAL_COMPOUND_PART
_PER);
fAfterPer = true;
result.dimensionality = -1;
token = nextToken(status);
if (U_FAILURE(status)) { return; }
} }
} else {
// All other SingleUnit's are separated from previous SingleUnit's
// via a compound part:
if (token.getType() != Token::TYPE_COMPOUND_PART) { if (token.getType() != Token::TYPE_COMPOUND_PART) {
status = kUnitIdentifierSyntaxError; status = kUnitIdentifierSyntaxError;
return; return;
} }
switch (token.getMatch()) { switch (token.getMatch()) {
case COMPOUND_PART_PER: case COMPOUND_PART_PER:
if (fAfterPer) { if (sawAnd) {
status = kUnitIdentifierSyntaxError; // Mixed compound units not yet supported,
return; // TODO(CLDR-13700).
} status = kUnitIdentifierSyntaxError;
fAfterPer = true; return;
result.dimensionality = -1; }
break; fAfterPer = true;
result.dimensionality = -1;
break;
case COMPOUND_PART_TIMES: case COMPOUND_PART_TIMES:
break; if (fAfterPer) {
result.dimensionality = -1;
}
break;
case COMPOUND_PART_PLUS: case COMPOUND_PART_AND:
sawPlus = true; if (fAfterPer) {
fAfterPer = false; // Can't start with "-and-", and mixed compound units
break; // not yet supported, TODO(CLDR-13700).
status = kUnitIdentifierSyntaxError;
return;
}
sawAnd = true;
break;
} }
previ = fIndex;
}
// Read a unit token = nextToken(status);
while (hasNext()) { if (U_FAILURE(status)) { return; }
Token token = nextToken(status); }
if (U_FAILURE(status)) {
return;
}
// Read tokens until we have a complete SingleUnit or we reach the end.
while (true) {
switch (token.getType()) { switch (token.getType()) {
case Token::TYPE_POWER_PART: case Token::TYPE_POWER_PART:
if (state > 0) { if (state > 0) {
status = kUnitIdentifierSyntaxError; status = kUnitIdentifierSyntaxError;
return; return;
} }
result.dimensionality *= token.getPower(); result.dimensionality *= token.getPower();
previ = fIndex;
state = 1; state = 1;
break; break;
case Token::TYPE_SI_PREFIX: case Token::TYPE_SI_PREFIX:
if (state > 1) { if (state > 1) {
status = kUnitIdentifierSyntaxError; status = kUnitIdentifierSyntaxError;
return; return;
} }
result.siPrefix = token.getSIPrefix(); result.siPrefix = token.getSIPrefix();
previ = fIndex;
state = 2; state = 2;
break; break;
case Token::TYPE_ONE:
// Skip "one" and go to the next unit
return nextSingleUnit(result, sawPlus, status);
case Token::TYPE_SIMPLE_UNIT: case Token::TYPE_SIMPLE_UNIT:
result.index = token.getSimpleUnitIndex(); result.index = token.getSimpleUnitIndex();
result.identifier = fSource.substr(previ, fIndex - previ);
return; return;
default: default:
status = kUnitIdentifierSyntaxError; status = kUnitIdentifierSyntaxError;
return; return;
} }
}
// We ran out of tokens before finding a complete single unit. if (!hasNext()) {
status = kUnitIdentifierSyntaxError; // We ran out of tokens before finding a complete single unit.
status = kUnitIdentifierSyntaxError;
return;
}
token = nextToken(status);
if (U_FAILURE(status)) {
return;
}
}
} }
/// @param result is modified, not overridden. Caller must pass in a
/// default-constructed (empty) MeasureUnitImpl instance.
void parseImpl(MeasureUnitImpl& result, UErrorCode& status) { void parseImpl(MeasureUnitImpl& result, UErrorCode& status) {
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
return; return;
} }
if (fSource.empty()) {
// The dimenionless unit: nothing to parse. leave result as is.
return;
}
int32_t unitNum = 0; int32_t unitNum = 0;
while (hasNext()) { while (hasNext()) {
bool sawPlus; bool sawAnd = false;
SingleUnitImpl singleUnit; SingleUnitImpl singleUnit;
nextSingleUnit(singleUnit, sawPlus, status); nextSingleUnit(singleUnit, sawAnd, status);
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
return; return;
} }
if (singleUnit.index == 0) { U_ASSERT(!singleUnit.isDimensionless());
continue;
}
bool added = result.append(singleUnit, status); bool added = result.append(singleUnit, status);
if (sawPlus && !added) { if (sawAnd && !added) {
// Two similar units are not allowed in a mixed unit // Two similar units are not allowed in a mixed unit
status = kUnitIdentifierSyntaxError; status = kUnitIdentifierSyntaxError;
return; return;
} }
if ((++unitNum) >= 2) { if ((++unitNum) >= 2) {
UMeasureUnitComplexity complexity = sawPlus // nextSingleUnit fails appropriately for "per" and "and" in the
? UMEASURE_UNIT_MIXED // same identifier. It doesn't fail for other compound units
: UMEASURE_UNIT_COMPOUND; // (COMPOUND_PART_TIMES). Consequently we take care of that
// here.
UMeasureUnitComplexity complexity =
sawAnd ? UMEASURE_UNIT_MIXED : UMEASURE_UNIT_COMPOUND;
if (unitNum == 2) { if (unitNum == 2) {
U_ASSERT(result.complexity == UMEASURE_UNIT_SINGLE); U_ASSERT(result.complexity == UMEASURE_UNIT_SINGLE);
result.complexity = complexity; result.complexity = complexity;
} else if (result.complexity != complexity) { } else if (result.complexity != complexity) {
// Can't have mixed compound units // Can't have mixed compound units
status = kUnitIdentifierSyntaxError; status = kUnitIdentifierSyntaxError;
return; return;
} }
} }
} }
skipping to change at line 528 skipping to change at line 614
int32_t U_CALLCONV int32_t U_CALLCONV
compareSingleUnits(const void* /*context*/, const void* left, const void* right) { compareSingleUnits(const void* /*context*/, const void* left, const void* right) {
auto realLeft = static_cast<const SingleUnitImpl* const*>(left); auto realLeft = static_cast<const SingleUnitImpl* const*>(left);
auto realRight = static_cast<const SingleUnitImpl* const*>(right); auto realRight = static_cast<const SingleUnitImpl* const*>(right);
return (*realLeft)->compareTo(**realRight); return (*realLeft)->compareTo(**realRight);
} }
/** /**
* Generate the identifier string for a single unit in place. * Generate the identifier string for a single unit in place.
*
* Does not support the dimensionless SingleUnitImpl: calling serializeSingle
* with the dimensionless unit results in an U_INTERNAL_PROGRAM_ERROR.
*
* @param first If singleUnit is part of a compound unit, and not its first
* single unit, set this to false. Otherwise: set to true.
*/ */
void serializeSingle(const SingleUnitImpl& singleUnit, bool first, CharString& o utput, UErrorCode& status) { void serializeSingle(const SingleUnitImpl& singleUnit, bool first, CharString& o utput, UErrorCode& status) {
if (first && singleUnit.dimensionality < 0) { if (first && singleUnit.dimensionality < 0) {
output.append("one-per-", status); // Essentially the "unary per". For compound units with a numerator, the
// caller takes care of the "binary per".
output.append("per-", status);
} }
if (singleUnit.index == 0) { if (singleUnit.isDimensionless()) {
// Don't propagate SI prefixes and powers on one status = U_INTERNAL_PROGRAM_ERROR;
output.append("one", status);
return; return;
} }
int8_t posPower = std::abs(singleUnit.dimensionality); int8_t posPower = std::abs(singleUnit.dimensionality);
if (posPower == 0) { if (posPower == 0) {
status = U_INTERNAL_PROGRAM_ERROR; status = U_INTERNAL_PROGRAM_ERROR;
} else if (posPower == 1) { } else if (posPower == 1) {
// no-op // no-op
} else if (posPower == 2) { } else if (posPower == 2) {
output.append("square-", status); output.append("square-", status);
} else if (posPower == 3) { } else if (posPower == 3) {
skipping to change at line 575 skipping to change at line 668
if (siPrefixInfo.value == singleUnit.siPrefix) { if (siPrefixInfo.value == singleUnit.siPrefix) {
output.append(siPrefixInfo.string, status); output.append(siPrefixInfo.string, status);
break; break;
} }
} }
} }
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
return; return;
} }
output.append(singleUnit.identifier, status); output.appendInvariantChars(gSimpleUnits[singleUnit.index], status);
} }
/** /**
* Normalize a MeasureUnitImpl and generate the identifier string in place. * Normalize a MeasureUnitImpl and generate the identifier string in place.
*/ */
void serialize(MeasureUnitImpl& impl, UErrorCode& status) { void serialize(MeasureUnitImpl& impl, UErrorCode& status) {
if (U_FAILURE(status)) { if (U_FAILURE(status)) {
return; return;
} }
U_ASSERT(impl.identifier.isEmpty()); U_ASSERT(impl.identifier.isEmpty());
if (impl.units.length() == 0) { if (impl.units.length() == 0) {
impl.identifier.append("one", status); // Dimensionless, constructed by the default constructor: no appending
// to impl.identifier, we wish it to contain the zero-length string.
return; return;
} }
if (impl.complexity == UMEASURE_UNIT_COMPOUND) { if (impl.complexity == UMEASURE_UNIT_COMPOUND) {
// Note: don't sort a MIXED unit // Note: don't sort a MIXED unit
uprv_sortArray( uprv_sortArray(
impl.units.getAlias(), impl.units.getAlias(),
impl.units.length(), impl.units.length(),
sizeof(impl.units[0]), sizeof(impl.units[0]),
compareSingleUnits, compareSingleUnits,
nullptr, nullptr,
skipping to change at line 626 skipping to change at line 720
impl.identifier.append("-per-", status); impl.identifier.append("-per-", status);
} else { } else {
impl.identifier.append('-', status); impl.identifier.append('-', status);
} }
serializeSingle(curr, false, impl.identifier, status); serializeSingle(curr, false, impl.identifier, status);
} }
} }
} }
/** @return true if a new item was added */ /**
* Appends a SingleUnitImpl to a MeasureUnitImpl.
*
* @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 appendImpl(MeasureUnitImpl& impl, const SingleUnitImpl& unit, UErrorCode& s tatus) { bool appendImpl(MeasureUnitImpl& impl, const SingleUnitImpl& unit, UErrorCode& s tatus) {
if (unit.isDimensionless()) {
// We don't append dimensionless units.
return false;
}
// Find a similar unit that already exists, to attempt to coalesce // Find a similar unit that already exists, to attempt to coalesce
SingleUnitImpl* oldUnit = nullptr; SingleUnitImpl* oldUnit = nullptr;
for (int32_t i = 0; i < impl.units.length(); i++) { for (int32_t i = 0; i < impl.units.length(); i++) {
auto* candidate = impl.units[i]; auto* candidate = impl.units[i];
if (candidate->isCompatibleWith(unit)) { if (candidate->isCompatibleWith(unit)) {
oldUnit = candidate; oldUnit = candidate;
} }
} }
if (oldUnit) { if (oldUnit) {
// Both dimensionalities will be positive, or both will be negative, by
// virtue of isCompatibleWith().
oldUnit->dimensionality += unit.dimensionality; oldUnit->dimensionality += unit.dimensionality;
} else { } else {
SingleUnitImpl* destination = impl.units.emplaceBack(); SingleUnitImpl* destination = impl.units.emplaceBack();
if (!destination) { if (!destination) {
status = U_MEMORY_ALLOCATION_ERROR; status = U_MEMORY_ALLOCATION_ERROR;
return false; return false;
} }
*destination = unit; *destination = unit;
} }
return (oldUnit == nullptr); return (oldUnit == nullptr);
skipping to change at line 733 skipping to change at line 838
return SingleUnitImpl::forMeasureUnit(*this, status).siPrefix; return SingleUnitImpl::forMeasureUnit(*this, status).siPrefix;
} }
MeasureUnit MeasureUnit::withSIPrefix(UMeasureSIPrefix prefix, UErrorCode& statu s) const { MeasureUnit MeasureUnit::withSIPrefix(UMeasureSIPrefix prefix, UErrorCode& statu s) const {
SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status); SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
singleUnit.siPrefix = prefix; singleUnit.siPrefix = prefix;
return singleUnit.build(status); return singleUnit.build(status);
} }
int32_t MeasureUnit::getDimensionality(UErrorCode& status) const { int32_t MeasureUnit::getDimensionality(UErrorCode& status) const {
return SingleUnitImpl::forMeasureUnit(*this, status).dimensionality; SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
if (U_FAILURE(status)) { return 0; }
if (singleUnit.isDimensionless()) {
return 0;
}
return singleUnit.dimensionality;
} }
MeasureUnit MeasureUnit::withDimensionality(int32_t dimensionality, UErrorCode& status) const { MeasureUnit MeasureUnit::withDimensionality(int32_t dimensionality, UErrorCode& status) const {
SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status); SingleUnitImpl singleUnit = SingleUnitImpl::forMeasureUnit(*this, status);
singleUnit.dimensionality = dimensionality; singleUnit.dimensionality = dimensionality;
return singleUnit.build(status); return singleUnit.build(status);
} }
MeasureUnit MeasureUnit::reciprocal(UErrorCode& status) const { MeasureUnit MeasureUnit::reciprocal(UErrorCode& status) const {
MeasureUnitImpl impl = MeasureUnitImpl::forMeasureUnitMaybeCopy(*this, statu s); MeasureUnitImpl impl = MeasureUnitImpl::forMeasureUnitMaybeCopy(*this, statu s);
 End of changes. 62 change blocks. 
77 lines changed or deleted 188 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)