tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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