tor-browser

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

NumberRangeFormat.cpp (6710B)


      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 
      5 #include "mozilla/intl/NumberRangeFormat.h"
      6 
      7 #include "mozilla/Try.h"
      8 #include "mozilla/intl/ICU4CGlue.h"
      9 #include "mozilla/intl/NumberFormat.h"
     10 #include "NumberFormatFields.h"
     11 #include "NumberFormatterSkeleton.h"
     12 #include "ScopedICUObject.h"
     13 
     14 #include "unicode/uformattedvalue.h"
     15 #include "unicode/unumberrangeformatter.h"
     16 #include "unicode/upluralrules.h"
     17 
     18 namespace mozilla::intl {
     19 
     20 /*static*/ Result<UniquePtr<NumberRangeFormat>, ICUError>
     21 NumberRangeFormat::TryCreate(std::string_view aLocale,
     22                             const NumberRangeFormatOptions& aOptions) {
     23  UniquePtr<NumberRangeFormat> nrf = MakeUnique<NumberRangeFormat>();
     24  MOZ_TRY(nrf->initialize(aLocale, aOptions));
     25  return nrf;
     26 }
     27 
     28 NumberRangeFormat::~NumberRangeFormat() {
     29  if (mFormattedNumberRange) {
     30    unumrf_closeResult(mFormattedNumberRange);
     31  }
     32  if (mNumberRangeFormatter) {
     33    unumrf_close(mNumberRangeFormatter);
     34  }
     35 }
     36 
     37 Result<Ok, ICUError> NumberRangeFormat::initialize(
     38    std::string_view aLocale, const NumberRangeFormatOptions& aOptions) {
     39  mFormatForUnit = aOptions.mUnit.isSome();
     40 
     41  NumberFormatterSkeleton skeleton(aOptions);
     42  mNumberRangeFormatter = skeleton.toRangeFormatter(
     43      aLocale, aOptions.mRangeCollapse, aOptions.mRangeIdentityFallback);
     44  if (mNumberRangeFormatter) {
     45    UErrorCode status = U_ZERO_ERROR;
     46    mFormattedNumberRange = unumrf_openResult(&status);
     47    if (U_FAILURE(status)) {
     48      return Err(ToICUError(status));
     49    }
     50    return Ok();
     51  }
     52  return Err(ICUError::InternalError);
     53 }
     54 
     55 Result<int32_t, ICUError> NumberRangeFormat::selectForRange(
     56    double start, double end, char16_t* keyword, int32_t keywordSize,
     57    const UPluralRules* pluralRules) const {
     58  MOZ_ASSERT(keyword);
     59  MOZ_ASSERT(pluralRules);
     60 
     61  MOZ_TRY(format(start, end));
     62 
     63  UErrorCode status = U_ZERO_ERROR;
     64  int32_t utf16KeywordLength = uplrules_selectForRange(
     65      pluralRules, mFormattedNumberRange, keyword, keywordSize, &status);
     66  if (U_FAILURE(status)) {
     67    return Err(ToICUError(status));
     68  }
     69 
     70  return utf16KeywordLength;
     71 }
     72 
     73 bool NumberRangeFormat::formatInternal(double start, double end) const {
     74  // ICU incorrectly formats NaN values with the sign bit set, as if they
     75  // were negative.  Replace all NaNs with a single pattern with sign bit
     76  // unset ("positive", that is) until ICU is fixed.
     77  if (MOZ_UNLIKELY(std::isnan(start))) {
     78    start = SpecificNaN<double>(0, 1);
     79  }
     80  if (MOZ_UNLIKELY(std::isnan(end))) {
     81    end = SpecificNaN<double>(0, 1);
     82  }
     83 
     84  UErrorCode status = U_ZERO_ERROR;
     85  unumrf_formatDoubleRange(mNumberRangeFormatter, start, end,
     86                           mFormattedNumberRange, &status);
     87  return U_SUCCESS(status);
     88 }
     89 
     90 bool NumberRangeFormat::formatInternal(std::string_view start,
     91                                       std::string_view end) const {
     92  UErrorCode status = U_ZERO_ERROR;
     93  unumrf_formatDecimalRange(mNumberRangeFormatter, start.data(), start.size(),
     94                            end.data(), end.size(), mFormattedNumberRange,
     95                            &status);
     96  return U_SUCCESS(status);
     97 }
     98 
     99 Result<std::u16string_view, ICUError> NumberRangeFormat::formatResult() const {
    100  UErrorCode status = U_ZERO_ERROR;
    101 
    102  const UFormattedValue* formattedValue =
    103      unumrf_resultAsValue(mFormattedNumberRange, &status);
    104  if (U_FAILURE(status)) {
    105    return Err(ToICUError(status));
    106  }
    107 
    108  int32_t utf16Length;
    109  const char16_t* utf16Str =
    110      ufmtval_getString(formattedValue, &utf16Length, &status);
    111  if (U_FAILURE(status)) {
    112    return Err(ToICUError(status));
    113  }
    114 
    115  return std::u16string_view(utf16Str, static_cast<size_t>(utf16Length));
    116 }
    117 
    118 Result<std::u16string_view, ICUError> NumberRangeFormat::formatResultToParts(
    119    Maybe<double> start, bool startIsNegative, Maybe<double> end,
    120    bool endIsNegative, NumberPartVector& parts) const {
    121  UErrorCode status = U_ZERO_ERROR;
    122 
    123  const UFormattedValue* formattedValue =
    124      unumrf_resultAsValue(mFormattedNumberRange, &status);
    125  if (U_FAILURE(status)) {
    126    return Err(ToICUError(status));
    127  }
    128 
    129  int32_t utf16Length;
    130  const char16_t* utf16Str =
    131      ufmtval_getString(formattedValue, &utf16Length, &status);
    132  if (U_FAILURE(status)) {
    133    return Err(ToICUError(status));
    134  }
    135 
    136  UConstrainedFieldPosition* fpos = ucfpos_open(&status);
    137  if (U_FAILURE(status)) {
    138    return Err(ToICUError(status));
    139  }
    140  ScopedICUObject<UConstrainedFieldPosition, ucfpos_close> toCloseFpos(fpos);
    141 
    142  Maybe<double> number = start;
    143  bool isNegative = startIsNegative;
    144 
    145  NumberPartSourceMap sourceMap;
    146 
    147  // Vacuum up fields in the overall formatted string.
    148  NumberFormatFields fields;
    149 
    150  while (true) {
    151    bool hasMore = ufmtval_nextPosition(formattedValue, fpos, &status);
    152    if (U_FAILURE(status)) {
    153      return Err(ToICUError(status));
    154    }
    155    if (!hasMore) {
    156      break;
    157    }
    158 
    159    int32_t category = ucfpos_getCategory(fpos, &status);
    160    if (U_FAILURE(status)) {
    161      return Err(ToICUError(status));
    162    }
    163 
    164    int32_t fieldName = ucfpos_getField(fpos, &status);
    165    if (U_FAILURE(status)) {
    166      return Err(ToICUError(status));
    167    }
    168 
    169    int32_t beginIndex, endIndex;
    170    ucfpos_getIndexes(fpos, &beginIndex, &endIndex, &status);
    171    if (U_FAILURE(status)) {
    172      return Err(ToICUError(status));
    173    }
    174 
    175    if (category == UFIELD_CATEGORY_NUMBER_RANGE_SPAN) {
    176      // The special field category UFIELD_CATEGORY_NUMBER_RANGE_SPAN has only
    177      // two allowed values (0 or 1), indicating the begin of the start resp.
    178      // end number.
    179      MOZ_ASSERT(fieldName == 0 || fieldName == 1,
    180                 "span category has unexpected value");
    181 
    182      if (fieldName == 0) {
    183        number = start;
    184        isNegative = startIsNegative;
    185 
    186        sourceMap.start = {uint32_t(beginIndex), uint32_t(endIndex)};
    187      } else {
    188        number = end;
    189        isNegative = endIsNegative;
    190 
    191        sourceMap.end = {uint32_t(beginIndex), uint32_t(endIndex)};
    192      }
    193 
    194      continue;
    195    }
    196 
    197    // Ignore categories other than UFIELD_CATEGORY_NUMBER.
    198    if (category != UFIELD_CATEGORY_NUMBER) {
    199      continue;
    200    }
    201 
    202    Maybe<NumberPartType> partType = GetPartTypeForNumberField(
    203        UNumberFormatFields(fieldName), number, isNegative, mFormatForUnit);
    204    if (!partType || !fields.append(*partType, beginIndex, endIndex)) {
    205      return Err(ToICUError(status));
    206    }
    207  }
    208 
    209  if (!fields.toPartsVector(utf16Length, sourceMap, parts)) {
    210    return Err(ToICUError(status));
    211  }
    212 
    213  return std::u16string_view(utf16Str, static_cast<size_t>(utf16Length));
    214 }
    215 
    216 }  // namespace mozilla::intl