From: Shane Carr Date: Fri, 23 Mar 2018 10:07:38 +0000 (+0000) Subject: ICU-8610 C++ number skeleton code is building. Testing is next. X-Git-Tag: release-62-rc~200^2~75 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7da9e75441c1c7b68c6750b8a6859da952fece2b;p=icu ICU-8610 C++ number skeleton code is building. Testing is next. X-SVN-Rev: 41144 --- diff --git a/icu4c/source/i18n/Makefile.in b/icu4c/source/i18n/Makefile.in index e249995b47a..0e11817b1a3 100644 --- a/icu4c/source/i18n/Makefile.in +++ b/icu4c/source/i18n/Makefile.in @@ -108,7 +108,7 @@ double-conversion-cached-powers.o double-conversion-diy-fp.o double-conversion-f numparse_stringsegment.o numparse_unisets.o numparse_parsednumber.o \ numparse_impl.o numparse_symbols.o numparse_decimal.o numparse_scientific.o \ numparse_currency.o numparse_affixes.o numparse_compositions.o numparse_validators.o \ -number_mapper.o number_multiplier.o number_currencysymbols.o +number_mapper.o number_multiplier.o number_currencysymbols.o number_skeletons.o ## Header files to install diff --git a/icu4c/source/i18n/number_skeletons.cpp b/icu4c/source/i18n/number_skeletons.cpp index dbb94852285..3efae97f20a 100644 --- a/icu4c/source/i18n/number_skeletons.cpp +++ b/icu4c/source/i18n/number_skeletons.cpp @@ -1,7 +1,6 @@ // © 2018 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html -#include #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT @@ -13,10 +12,11 @@ #include "number_skeletons.h" #include "umutex.h" #include "ucln_in.h" -#include "hash.h" #include "patternprops.h" #include "unicode/ucharstriebuilder.h" #include "number_utils.h" +#include "number_decimalquantity.h" +#include "unicode/numberformatter.h" using namespace icu; using namespace icu::number; @@ -34,6 +34,7 @@ char16_t* kSerializedStemTrie = nullptr; UBool U_CALLCONV cleanupNumberSkeletons() { uprv_free(kSerializedStemTrie); kSerializedStemTrie = nullptr; + return TRUE; } void U_CALLCONV initNumberSkeletons(UErrorCode& status) { @@ -99,7 +100,14 @@ void U_CALLCONV initNumberSkeletons(UErrorCode& status) { } -#define CHECK_NULL(seen, field, status) void; /* for auto-format line wraping */ \ +inline void appendMultiple(UnicodeString& sb, UChar32 cp, int32_t count) { + for (int i = 0; i < count; i++) { + sb.append(cp); + } +} + + +#define CHECK_NULL(seen, field, status) (void)(seen); /* for auto-format line wraping */ \ { \ if ((seen).field) { \ (status) = U_NUMBER_SKELETON_SYNTAX_ERROR; \ @@ -109,6 +117,15 @@ void U_CALLCONV initNumberSkeletons(UErrorCode& status) { } +const char16_t* const kRoundingModeStrings[] = { + u"up", u"down", u"ceiling", u"floor", u"half-up", u"half-down", u"half-even", u"unnecessary"}; + +constexpr int32_t kRoundingModeCount = 8; +static_assert( + sizeof(kRoundingModeStrings) / sizeof(*kRoundingModeStrings) == kRoundingModeCount, + "kRoundingModeCount should be the number of rounding modes"); + + } // anonymous namespace @@ -376,6 +393,7 @@ MacroProps skeleton::parseSkeleton(const UnicodeString& skeletonString, UErrorCo stem = parseOption(stem, segment, macros, status); } segment.resetLength(); + if (U_FAILURE(status)) { return macros; } // Consume the segment: segment.adjustOffset(offset); @@ -439,6 +457,8 @@ skeleton::parseStem(const StringSegment& segment, const UCharsTrie& stemTrie, Se CHECK_NULL(seen, rounder, status); blueprint_helpers::parseDigitsStem(segment, macros, status); return STATE_NULL; + default: + break; } // Now look at the stemsTrie, which is already be pointing at our stem. @@ -706,6 +726,461 @@ void GeneratorHelpers::generateSkeleton(const MacroProps& macros, UnicodeString& } +bool blueprint_helpers::parseExponentWidthOption(const StringSegment& segment, MacroProps& macros, + UErrorCode&) { + if (segment.charAt(0) != u'+') { + return false; + } + int32_t offset = 1; + int32_t minExp = 0; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'e') { + minExp++; + } else { + break; + } + } + if (offset < segment.length()) { + return false; + } + // Use the public APIs to enforce bounds checking + macros.notation = static_cast(macros.notation).withMinExponentDigits(minExp); + return true; +} + +void +blueprint_helpers::generateExponentWidthOption(int32_t minExponentDigits, UnicodeString& sb, UErrorCode&) { + sb.append(u'+'); + appendMultiple(sb, u'e', minExponentDigits); +} + +bool +blueprint_helpers::parseExponentSignOption(const StringSegment& segment, MacroProps& macros, UErrorCode&) { + // Get the sign display type out of the CharsTrie data structure. + UCharsTrie tempStemTrie(kSerializedStemTrie); + UStringTrieResult result = tempStemTrie.next(segment.toUnicodeString().getBuffer(), segment.length()); + if (result != USTRINGTRIE_INTERMEDIATE_VALUE && result != USTRINGTRIE_FINAL_VALUE) { + return false; + } + auto sign = stem_to_object::signDisplay(static_cast(tempStemTrie.getValue())); + if (sign == UNUM_SIGN_COUNT) { + return false; + } + macros.notation = static_cast(macros.notation).withExponentSignDisplay(sign); + return true; +} + +void blueprint_helpers::parseCurrencyOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + if (segment.length() != 3) { + // throw new SkeletonSyntaxException("Invalid currency", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + const UChar* currencyCode = segment.toUnicodeString().getTerminatedBuffer(); + // Check that the currency code is valid: + int32_t numericCode = ucurr_getNumericCode(currencyCode); + if (numericCode == 0) { + // throw new SkeletonSyntaxException("Invalid currency", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + // Slicing is OK + macros.unit = CurrencyUnit(currencyCode, status); // NOLINT +} + +void +blueprint_helpers::generateCurrencyOption(const CurrencyUnit& currency, UnicodeString& sb, UErrorCode&) { + sb.append(currency.getISOCurrency(), -1); +} + +void blueprint_helpers::parseMeasureUnitOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + // NOTE: The category (type) of the unit is guaranteed to be a valid subtag (alphanumeric) + // http://unicode.org/reports/tr35/#Validity_Data + int firstHyphen = 0; + while (firstHyphen < segment.length() && segment.charAt(firstHyphen) != '-') { + firstHyphen++; + } + if (firstHyphen == segment.length()) { + // throw new SkeletonSyntaxException("Invalid measure unit option", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + + // MeasureUnit is in char space; we need to convert. + // Note: the longest type/subtype as of this writing (March 2018) is 24 chars. + static constexpr int32_t CAPACITY = 30; + char type[CAPACITY]; + char subType[CAPACITY]; + const int32_t typeLen = firstHyphen; + const int32_t subTypeLen = segment.length() - firstHyphen - 1; + if (typeLen + 1 > CAPACITY || subTypeLen + 1 > CAPACITY) { + // Type or subtype longer than 30? + // The capacity should be increased if this is a problem with a real CLDR unit. + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + u_UCharsToChars(segment.toUnicodeString().getBuffer(), type, typeLen); + u_UCharsToChars(segment.toUnicodeString().getBuffer() + firstHyphen + 1, subType, subTypeLen); + type[typeLen] = 0; + subType[subTypeLen] = 0; + + // Note: the largest type as of this writing (March 2018) is "volume", which has 24 units. + MeasureUnit units[30]; + UErrorCode localStatus = U_ZERO_ERROR; + int32_t numUnits = MeasureUnit::getAvailable(type, units, 30, localStatus); + if (U_FAILURE(localStatus)) { + // More than 30 units in this type? + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + for (int32_t i = 0; i < numUnits; i++) { + auto& unit = units[i]; + if (uprv_strcmp(subType, unit.getSubtype()) == 0) { + macros.unit = unit; + return; + } + } + + // throw new SkeletonSyntaxException("Unknown measure unit", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; +} + +void blueprint_helpers::generateMeasureUnitOption(const MeasureUnit& measureUnit, UnicodeString& sb, + UErrorCode& status) { + // We need to convert from char* to UChar*... + // See comments in the previous function about the capacity setting. + static constexpr int32_t CAPACITY = 30; + char16_t type16[CAPACITY]; + char16_t subType16[CAPACITY]; + const auto typeLen = static_cast(uprv_strlen(measureUnit.getType())); + const auto subTypeLen = static_cast(uprv_strlen(measureUnit.getSubtype())); + if (typeLen + 1 > CAPACITY || subTypeLen + 1 > CAPACITY) { + // Type or subtype longer than 30? + // The capacity should be increased if this is a problem with a real CLDR unit. + status = U_UNSUPPORTED_ERROR; + return; + } + u_charsToUChars(measureUnit.getType(), type16, typeLen); + u_charsToUChars(measureUnit.getSubtype(), subType16, subTypeLen); + + sb.append(type16, typeLen); + sb.append(u'-'); + sb.append(subType16, subTypeLen); +} + +void blueprint_helpers::parseMeasurePerUnitOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + // A little bit of a hack: safe the current unit (numerator), call the main measure unit + // parsing code, put back the numerator unit, and put the new unit into per-unit. + MeasureUnit numerator = macros.unit; + parseMeasureUnitOption(segment, macros, status); + macros.perUnit = macros.unit; + macros.unit = numerator; +} + +void blueprint_helpers::parseFractionStem(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + U_ASSERT(segment.charAt(0) == u'.'); + int32_t offset = 1; + int32_t minFrac = 0; + int32_t maxFrac; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'0') { + minFrac++; + } else { + break; + } + } + if (offset < segment.length()) { + if (segment.charAt(offset) == u'+') { + maxFrac = -1; + offset++; + } else { + maxFrac = minFrac; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'#') { + maxFrac++; + } else { + break; + } + } + } + } else { + maxFrac = minFrac; + } + if (offset < segment.length()) { + // throw new SkeletonSyntaxException("Invalid fraction stem", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + // Use the public APIs to enforce bounds checking + if (maxFrac == -1) { + macros.rounder = Rounder::minFraction(minFrac); + } else { + macros.rounder = Rounder::minMaxFraction(minFrac, maxFrac); + } +} + +void +blueprint_helpers::generateFractionStem(int32_t minFrac, int32_t maxFrac, UnicodeString& sb, UErrorCode&) { + if (minFrac == 0 && maxFrac == 0) { + sb.append(u"round-integer", -1); + return; + } + sb.append(u'.'); + appendMultiple(sb, u'0', minFrac); + if (maxFrac == -1) { + sb.append(u'+'); + } else { + appendMultiple(sb, u'#', maxFrac - minFrac); + } +} + +void +blueprint_helpers::parseDigitsStem(const StringSegment& segment, MacroProps& macros, UErrorCode& status) { + U_ASSERT(segment.charAt(0) == u'@'); + int offset = 0; + int minSig = 0; + int maxSig; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'@') { + minSig++; + } else { + break; + } + } + if (offset < segment.length()) { + if (segment.charAt(offset) == u'+') { + maxSig = -1; + offset++; + } else { + maxSig = minSig; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'#') { + maxSig++; + } else { + break; + } + } + } + } else { + maxSig = minSig; + } + if (offset < segment.length()) { + // throw new SkeletonSyntaxException("Invalid significant digits stem", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + } + // Use the public APIs to enforce bounds checking + if (maxSig == -1) { + macros.rounder = Rounder::minDigits(minSig); + } else { + macros.rounder = Rounder::minMaxDigits(minSig, maxSig); + } +} + +void +blueprint_helpers::generateDigitsStem(int32_t minSig, int32_t maxSig, UnicodeString& sb, UErrorCode&) { + appendMultiple(sb, u'@', minSig); + if (maxSig == -1) { + sb.append(u'+'); + } else { + appendMultiple(sb, u'#', maxSig - minSig); + } +} + +bool blueprint_helpers::parseFracSigOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + if (segment.charAt(0) != u'@') { + return false; + } + int offset = 0; + int minSig = 0; + int maxSig; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'@') { + minSig++; + } else { + break; + } + } + // For the frac-sig option, there must be minSig or maxSig but not both. + // Valid: @+, @@+, @@@+ + // Valid: @#, @##, @### + // Invalid: @, @@, @@@ + // Invalid: @@#, @@##, @@@# + if (offset < segment.length()) { + if (segment.charAt(offset) == u'+') { + maxSig = -1; + offset++; + } else if (minSig > 1) { + // @@#, @@##, @@@# + // throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return false; + } else { + maxSig = minSig; + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'#') { + maxSig++; + } else { + break; + } + } + } + } else { + // @, @@, @@@ + // throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return false; + } + if (offset < segment.length()) { + // throw new SkeletonSyntaxException("Invalid digits option for fraction rounder", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return false; + } + + auto& oldRounder = static_cast(macros.rounder); + if (maxSig == -1) { + macros.rounder = oldRounder.withMinDigits(minSig); + } else { + macros.rounder = oldRounder.withMaxDigits(maxSig); + } + return true; +} + +void blueprint_helpers::parseIncrementOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + // Utilize DecimalQuantity/decNumber to parse this for us. + static constexpr int32_t CAPACITY = 30; + char buffer[CAPACITY]; + if (segment.length() > CAPACITY) { + // No support for numbers this long; they won't fit in a double anyway. + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + u_UCharsToChars(segment.toUnicodeString().getBuffer(), buffer, segment.length()); + DecimalQuantity dq; + dq.setToDecNumber({buffer, segment.length()}); + double increment = dq.toDouble(); + macros.rounder = Rounder::increment(increment); +} + +void blueprint_helpers::generateIncrementOption(double increment, UnicodeString& sb, UErrorCode&) { + // Utilize DecimalQuantity/double_conversion to format this for us. + DecimalQuantity dq; + dq.setToDouble(increment); + dq.roundToInfinity(); + sb.append(dq.toPlainString()); +} + +bool +blueprint_helpers::parseRoundingModeOption(const StringSegment& segment, MacroProps& macros, UErrorCode&) { + for (int rm = 0; rm < kRoundingModeCount; rm++) { + if (segment == UnicodeString(kRoundingModeStrings[rm], -1)) { + macros.rounder = macros.rounder.withMode(static_cast(rm)); + return true; + } + } + return false; +} + +void blueprint_helpers::generateRoundingModeOption(RoundingMode mode, UnicodeString& sb, UErrorCode&) { + sb.append(kRoundingModeStrings[mode], -1); +} + +void blueprint_helpers::parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + int32_t offset = 0; + int32_t minInt = 0; + int32_t maxInt; + if (segment.charAt(0) == u'+') { + maxInt = -1; + offset++; + } else { + maxInt = 0; + } + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'#') { + maxInt++; + } else { + break; + } + } + if (offset < segment.length()) { + for (; offset < segment.length(); offset++) { + if (segment.charAt(offset) == u'0') { + minInt++; + } else { + break; + } + } + } + if (maxInt != -1) { + maxInt += minInt; + } + if (offset < segment.length()) { + // throw new SkeletonSyntaxException("Invalid integer width stem", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + // Use the public APIs to enforce bounds checking + if (maxInt == -1) { + macros.integerWidth = IntegerWidth::zeroFillTo(minInt); + } else { + macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt); + } +} + +void blueprint_helpers::generateIntegerWidthOption(int32_t minInt, int32_t maxInt, UnicodeString& sb, + UErrorCode&) { + if (maxInt == -1) { + sb.append(u'+'); + } else { + appendMultiple(sb, u'#', maxInt - minInt); + } + appendMultiple(sb, u'0', minInt); +} + +void blueprint_helpers::parseNumberingSystemOption(const StringSegment& segment, MacroProps& macros, + UErrorCode& status) { + // Need to do char <-> UChar conversion... + static constexpr int32_t CAPACITY = 30; + char buffer[CAPACITY]; + if (segment.length() + 1 > CAPACITY) { + // No support for numbers this long; they won't fit in a double anyway. + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + u_UCharsToChars(segment.toUnicodeString().getBuffer(), buffer, segment.length()); + buffer[segment.length()] = 0; + + NumberingSystem* ns = NumberingSystem::createInstanceByName(buffer, status); + if (ns == nullptr) { + // throw new SkeletonSyntaxException("Unknown numbering system", segment); + status = U_NUMBER_SKELETON_SYNTAX_ERROR; + return; + } + macros.symbols.setTo(ns); +} + +void blueprint_helpers::generateNumberingSystemOption(const NumberingSystem& ns, UnicodeString& sb, + UErrorCode& status) { + // Need to do char <-> UChar conversion... + static constexpr int32_t CAPACITY = 30; + char16_t buffer16[CAPACITY]; + const auto len = static_cast(uprv_strlen(ns.getName())); + if (len > CAPACITY) { + // No support for numbers this long; they won't fit in a double anyway. + status = U_UNSUPPORTED_ERROR; + return; + } + u_charsToUChars(ns.getName(), buffer16, len); + sb.append(buffer16, len); +} + + bool GeneratorHelpers::notation(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { if (macros.notation.fType == Notation::NTN_COMPACT) { UNumberCompactStyle style = macros.notation.fUnion.compactStyle; @@ -770,6 +1245,7 @@ bool GeneratorHelpers::perUnit(const MacroProps& macros, UnicodeString& sb, UErr // Per-units are currently expected to be only MeasureUnits. if (unitIsCurrency(macros.perUnit) || unitIsNoUnit(macros.perUnit)) { status = U_UNSUPPORTED_ERROR; + return false; } else { sb.append(u"per-measure-unit/", -1); blueprint_helpers::generateMeasureUnitOption(macros.perUnit, sb, status); @@ -824,10 +1300,12 @@ bool GeneratorHelpers::rounding(const MacroProps& macros, UnicodeString& sb, UEr bool GeneratorHelpers::grouping(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { if (macros.grouper.isBogus() || macros.grouper.fStrategy == UNUM_GROUPING_COUNT) { status = U_UNSUPPORTED_ERROR; + return false; } else if (macros.grouper.fStrategy == UNUM_GROUPING_AUTO) { return false; // Default value } else { enum_to_stem_string::groupingStrategy(macros.grouper.fStrategy, sb); + return true; } } @@ -864,7 +1342,7 @@ bool GeneratorHelpers::symbols(const MacroProps& macros, UnicodeString& sb, UErr } } -bool GeneratorHelpers::unitWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { +bool GeneratorHelpers::unitWidth(const MacroProps& macros, UnicodeString& sb, UErrorCode&) { if (macros.unitWidth == UNUM_UNIT_WIDTH_SHORT || macros.unitWidth == UNUM_UNIT_WIDTH_COUNT) { return false; // Default or Bogus } @@ -872,7 +1350,7 @@ bool GeneratorHelpers::unitWidth(const MacroProps& macros, UnicodeString& sb, UE return true; } -bool GeneratorHelpers::sign(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { +bool GeneratorHelpers::sign(const MacroProps& macros, UnicodeString& sb, UErrorCode&) { if (macros.sign == UNUM_SIGN_AUTO || macros.sign == UNUM_SIGN_COUNT) { return false; // Default or Bogus } @@ -880,7 +1358,7 @@ bool GeneratorHelpers::sign(const MacroProps& macros, UnicodeString& sb, UErrorC return true; } -bool GeneratorHelpers::decimal(const MacroProps& macros, UnicodeString& sb, UErrorCode& status) { +bool GeneratorHelpers::decimal(const MacroProps& macros, UnicodeString& sb, UErrorCode&) { if (macros.decimal == UNUM_DECIMAL_SEPARATOR_AUTO || macros.decimal == UNUM_DECIMAL_SEPARATOR_COUNT) { return false; // Default or Bogus } diff --git a/icu4c/source/i18n/number_skeletons.h b/icu4c/source/i18n/number_skeletons.h index 843f1390af4..e874d6acc33 100644 --- a/icu4c/source/i18n/number_skeletons.h +++ b/icu4c/source/i18n/number_skeletons.h @@ -192,7 +192,7 @@ namespace blueprint_helpers { /** @return Whether we successfully found and parsed an exponent width option. */ bool parseExponentWidthOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); -void generateExponentWidthOption(int32_t xyz, UnicodeString& sb, UErrorCode& status); +void generateExponentWidthOption(int32_t minExponentDigits, UnicodeString& sb, UErrorCode& status); /** @return Whether we successfully found and parsed an exponent sign option. */ bool parseExponentSignOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); @@ -225,7 +225,7 @@ void generateIncrementOption(double increment, UnicodeString& sb, UErrorCode& st /** @return Whether we successfully found and parsed a rounding mode. */ bool parseRoundingModeOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); -void generateRoundingModeOption(RoundingMode, UnicodeString& sb, UErrorCode& status); +void generateRoundingModeOption(RoundingMode mode, UnicodeString& sb, UErrorCode& status); void parseIntegerWidthOption(const StringSegment& segment, MacroProps& macros, UErrorCode& status); diff --git a/icu4c/source/i18n/numparse_stringsegment.cpp b/icu4c/source/i18n/numparse_stringsegment.cpp index e3bdc68c25b..aa8e62beefb 100644 --- a/icu4c/source/i18n/numparse_stringsegment.cpp +++ b/icu4c/source/i18n/numparse_stringsegment.cpp @@ -61,7 +61,8 @@ UChar32 StringSegment::codePointAt(int32_t index) const { } UnicodeString StringSegment::toUnicodeString() const { - return UnicodeString(fStr, fStart, fEnd - fStart); + // Use the readonly-aliasing constructor for efficiency. + return UnicodeString(FALSE, fStr.getBuffer() + fStart, fEnd - fStart); } UChar32 StringSegment::getCodePoint() const { diff --git a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java index dc831d2dd5c..6b8d77c9ac8 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/number/NumberSkeletonImpl.java @@ -884,13 +884,14 @@ class NumberSkeletonImpl { } private static void generateMeasureUnitOption(MeasureUnit unit, StringBuilder sb) { - sb.append(unit.getType() + "-" + unit.getSubtype()); + sb.append(unit.getType()); + sb.append("-"); + sb.append(unit.getSubtype()); } private static void parseMeasurePerUnitOption(StringSegment segment, MacroProps macros) { // A little bit of a hack: safe the current unit (numerator), call the main measure unit - // parsing - // code, put back the numerator unit, and put the new unit into per-unit. + // parsing code, put back the numerator unit, and put the new unit into per-unit. MeasureUnit numerator = macros.unit; parseMeasureUnitOption(segment, macros); macros.perUnit = macros.unit; @@ -1083,8 +1084,7 @@ class NumberSkeletonImpl { } private static void generateRoundingModeOption(RoundingMode mode, StringBuilder sb) { - String option = ROUNDING_MODE_STRINGS[mode.ordinal()]; - sb.append(option); + sb.append(ROUNDING_MODE_STRINGS[mode.ordinal()]); } private static void parseIntegerWidthOption(StringSegment segment, MacroProps macros) {