NumberFormat.cpp (4888B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 #include "mozilla/Try.h" 5 #include "mozilla/intl/NumberFormat.h" 6 #include "NumberFormatFields.h" 7 #include "NumberFormatterSkeleton.h" 8 #include "ScopedICUObject.h" 9 10 #include "unicode/unumberformatter.h" 11 #include "unicode/upluralrules.h" 12 13 namespace mozilla::intl { 14 15 /*static*/ Result<UniquePtr<NumberFormat>, ICUError> NumberFormat::TryCreate( 16 std::string_view aLocale, const NumberFormatOptions& aOptions) { 17 UniquePtr<NumberFormat> nf = MakeUnique<NumberFormat>(); 18 Result<Ok, ICUError> result = nf->initialize(aLocale, aOptions); 19 if (result.isOk()) { 20 return nf; 21 } 22 23 return Err(result.unwrapErr()); 24 } 25 26 NumberFormat::~NumberFormat() { 27 if (mFormattedNumber) { 28 unumf_closeResult(mFormattedNumber); 29 } 30 if (mNumberFormatter) { 31 unumf_close(mNumberFormatter); 32 } 33 } 34 35 Result<Ok, ICUError> NumberFormat::initialize( 36 std::string_view aLocale, const NumberFormatOptions& aOptions) { 37 mFormatForUnit = aOptions.mUnit.isSome(); 38 NumberFormatterSkeleton skeleton(aOptions); 39 mNumberFormatter = skeleton.toFormatter(aLocale); 40 if (mNumberFormatter) { 41 UErrorCode status = U_ZERO_ERROR; 42 mFormattedNumber = unumf_openResult(&status); 43 if (U_FAILURE(status)) { 44 return Err(ToICUError(status)); 45 } 46 return Ok(); 47 } 48 return Err(ICUError::InternalError); 49 } 50 51 Result<std::u16string_view, ICUError> NumberFormat::formatToParts( 52 double number, NumberPartVector& parts) const { 53 if (!formatInternal(number)) { 54 return Err(ICUError::InternalError); 55 } 56 57 bool isNegative = !std::isnan(number) && IsNegative(number); 58 59 return FormatResultToParts(mFormattedNumber, Some(number), isNegative, 60 mFormatForUnit, parts); 61 } 62 63 Result<std::u16string_view, ICUError> NumberFormat::formatToParts( 64 int64_t number, NumberPartVector& parts) const { 65 if (!formatInternal(number)) { 66 return Err(ICUError::InternalError); 67 } 68 69 return FormatResultToParts(mFormattedNumber, Nothing(), number < 0, 70 mFormatForUnit, parts); 71 } 72 73 Result<std::u16string_view, ICUError> NumberFormat::formatToParts( 74 std::string_view number, NumberPartVector& parts) const { 75 if (!formatInternal(number)) { 76 return Err(ICUError::InternalError); 77 } 78 79 // Non-finite numbers aren't currently supported here. If we ever need to 80 // support those, the |Maybe<double>| argument must be computed here. 81 MOZ_ASSERT(number != "Infinity"); 82 MOZ_ASSERT(number != "+Infinity"); 83 MOZ_ASSERT(number != "-Infinity"); 84 MOZ_ASSERT(number != "NaN"); 85 86 bool isNegative = !number.empty() && number[0] == '-'; 87 88 return FormatResultToParts(mFormattedNumber, Nothing(), isNegative, 89 mFormatForUnit, parts); 90 } 91 92 Result<int32_t, ICUError> NumberFormat::selectFormatted( 93 double number, char16_t* keyword, int32_t keywordSize, 94 UPluralRules* pluralRules) const { 95 MOZ_ASSERT(keyword && pluralRules); 96 UErrorCode status = U_ZERO_ERROR; 97 98 MOZ_TRY(format(number)); 99 100 int32_t utf16KeywordLength = uplrules_selectFormatted( 101 pluralRules, mFormattedNumber, keyword, keywordSize, &status); 102 103 if (U_FAILURE(status)) { 104 return Err(ToICUError(status)); 105 } 106 107 return utf16KeywordLength; 108 } 109 110 bool NumberFormat::formatInternal(double number) const { 111 // ICU incorrectly formats NaN values with the sign bit set, as if they 112 // were negative. Replace all NaNs with a single pattern with sign bit 113 // unset ("positive", that is) until ICU is fixed. 114 if (MOZ_UNLIKELY(std::isnan(number))) { 115 number = SpecificNaN<double>(0, 1); 116 } 117 118 UErrorCode status = U_ZERO_ERROR; 119 unumf_formatDouble(mNumberFormatter, number, mFormattedNumber, &status); 120 return U_SUCCESS(status); 121 } 122 123 bool NumberFormat::formatInternal(int64_t number) const { 124 UErrorCode status = U_ZERO_ERROR; 125 unumf_formatInt(mNumberFormatter, number, mFormattedNumber, &status); 126 return U_SUCCESS(status); 127 } 128 129 bool NumberFormat::formatInternal(std::string_view number) const { 130 UErrorCode status = U_ZERO_ERROR; 131 unumf_formatDecimal(mNumberFormatter, number.data(), number.size(), 132 mFormattedNumber, &status); 133 return U_SUCCESS(status); 134 } 135 136 Result<std::u16string_view, ICUError> NumberFormat::formatResult() const { 137 UErrorCode status = U_ZERO_ERROR; 138 139 const UFormattedValue* formattedValue = 140 unumf_resultAsValue(mFormattedNumber, &status); 141 if (U_FAILURE(status)) { 142 return Err(ToICUError(status)); 143 } 144 145 int32_t utf16Length; 146 const char16_t* utf16Str = 147 ufmtval_getString(formattedValue, &utf16Length, &status); 148 if (U_FAILURE(status)) { 149 return Err(ToICUError(status)); 150 } 151 152 return std::u16string_view(utf16Str, static_cast<size_t>(utf16Length)); 153 } 154 155 } // namespace mozilla::intl