Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/intl/components/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 15 kB image not shown  

Quelle  NumberFormatterSkeleton.cpp   Sprache: C

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "NumberFormatterSkeleton.h"
#include "NumberFormat.h"

#include "MeasureUnitGenerated.h"

#include "mozilla/RangedPtr.h"

#include <algorithm>
#include <limits>

#include "unicode/unumberrangeformatter.h"

namespace mozilla::intl {

NumberFormatterSkeleton::NumberFormatterSkeleton(
    const NumberFormatOptions& options) {
  if (options.mCurrency.isSome()) {
    if (!currency(options.mCurrency->first) ||
        !currencyDisplay(options.mCurrency->second)) {
      return;
    }
  } else if (options.mUnit.isSome()) {
    if (!unit(options.mUnit->first) || !unitDisplay(options.mUnit->second)) {
      return;
    }
  } else if (options.mPercent) {
    if (!percent()) {
      return;
    }
  }

  if (options.mRoundingIncrement != 1) {
    auto fd = options.mFractionDigits.valueOr(std::pair{0, 0});
    if (!roundingIncrement(options.mRoundingIncrement, fd.first, fd.second,
                           options.mStripTrailingZero)) {
      return;
    }
  } else if (options.mRoundingPriority ==
             NumberFormatOptions::RoundingPriority::Auto) {
    if (options.mFractionDigits.isSome()) {
      if (!fractionDigits(options.mFractionDigits->first,
                          options.mFractionDigits->second,
                          options.mStripTrailingZero)) {
        return;
      }
    }

    if (options.mSignificantDigits.isSome()) {
      if (!significantDigits(options.mSignificantDigits->first,
                             options.mSignificantDigits->second,
                             options.mStripTrailingZero)) {
        return;
      }
    }
  } else {
    MOZ_ASSERT(options.mFractionDigits);
    MOZ_ASSERT(options.mSignificantDigits);

    bool relaxed = options.mRoundingPriority ==
                   NumberFormatOptions::RoundingPriority::MorePrecision;
    if (!fractionWithSignificantDigits(options.mFractionDigits->first,
                                       options.mFractionDigits->second,
                                       options.mSignificantDigits->first,
                                       options.mSignificantDigits->second,
                                       relaxed, options.mStripTrailingZero)) {
      return;
    }
  }

  if (options.mMinIntegerDigits.isSome()) {
    if (!minIntegerDigits(*options.mMinIntegerDigits)) {
      return;
    }
  }

  if (!grouping(options.mGrouping)) {
    return;
  }

  if (!notation(options.mNotation)) {
    return;
  }

  if (!signDisplay(options.mSignDisplay)) {
    return;
  }

  if (!roundingMode(options.mRoundingMode)) {
    return;
  }

  mValidSkeleton = true;
}

bool NumberFormatterSkeleton::currency(std::string_view currency) {
  MOZ_ASSERT(currency.size() == 3,
             "IsWellFormedCurrencyCode permits only length-3 strings");

  char16_t currencyChars[] = {static_cast<char16_t>(currency[0]),
                              static_cast<char16_t>(currency[1]),
                              static_cast<char16_t>(currency[2]), '\0'};
  return append(u"currency/") && append(currencyChars) && append(' ');
}

bool NumberFormatterSkeleton::currencyDisplay(
    NumberFormatOptions::CurrencyDisplay display) {
  switch (display) {
    case NumberFormatOptions::CurrencyDisplay::Code:
      return appendToken(u"unit-width-iso-code");
    case NumberFormatOptions::CurrencyDisplay::Name:
      return appendToken(u"unit-width-full-name");
    case NumberFormatOptions::CurrencyDisplay::Symbol:
      // Default, no additional tokens needed.
      return true;
    case NumberFormatOptions::CurrencyDisplay::NarrowSymbol:
      return appendToken(u"unit-width-narrow");
  }
  MOZ_ASSERT_UNREACHABLE("unexpected currency display type");
  return false;
}

static const SimpleMeasureUnit& FindSimpleMeasureUnit(std::string_view name) {
  const auto* measureUnit = std::lower_bound(
      std::begin(simpleMeasureUnits), std::end(simpleMeasureUnits), name,
      [](const auto& measureUnit, std::string_view name) {
        return name.compare(measureUnit.name) > 0;
      });
  MOZ_ASSERT(measureUnit != std::end(simpleMeasureUnits),
             "unexpected unit identifier: unit not found");
  MOZ_ASSERT(measureUnit->name == name,
             "unexpected unit identifier: wrong unit found");
  return *measureUnit;
}

static constexpr size_t MaxUnitLength() {
  size_t length = 0;
  for (const auto& unit : simpleMeasureUnits) {
    length = std::max(length, std::char_traits<char>::length(unit.name));
  }
  return length * 2 + std::char_traits<char>::length("-per-");
}

bool NumberFormatterSkeleton::unit(std::string_view unit) {
  MOZ_RELEASE_ASSERT(unit.length() <= MaxUnitLength());

  auto appendUnit = [this](const SimpleMeasureUnit& unit) {
    return append(unit.type, strlen(unit.type)) && append('-') &&
           append(unit.name, strlen(unit.name));
  };

  // |unit| can be a compound unit identifier, separated by "-per-".
  static constexpr char separator[] = "-per-";
  size_t separator_len = strlen(separator);
  size_t offset = unit.find(separator);
  if (offset != std::string_view::npos) {
    const auto& numerator = FindSimpleMeasureUnit(unit.substr(0, offset));
    const auto& denominator = FindSimpleMeasureUnit(
        std::string_view(unit.data() + offset + separator_len,
                         unit.length() - offset - separator_len));
    return append(u"measure-unit/") && appendUnit(numerator) && append(' ') &&
           append(u"per-measure-unit/") && appendUnit(denominator) &&
           append(' ');
  }

  const auto& simple = FindSimpleMeasureUnit(unit);
  return append(u"measure-unit/") && appendUnit(simple) && append(' ');
}

bool NumberFormatterSkeleton::unitDisplay(
    NumberFormatOptions::UnitDisplay display) {
  switch (display) {
    case NumberFormatOptions::UnitDisplay::Short:
      return appendToken(u"unit-width-short");
    case NumberFormatOptions::UnitDisplay::Narrow:
      return appendToken(u"unit-width-narrow");
    case NumberFormatOptions::UnitDisplay::Long:
      return appendToken(u"unit-width-full-name");
  }
  MOZ_ASSERT_UNREACHABLE("unexpected unit display type");
  return false;
}

bool NumberFormatterSkeleton::percent() {
  return appendToken(u"percent scale/100");
}

bool NumberFormatterSkeleton::fractionDigits(uint32_t min, uint32_t max,
                                             bool stripTrailingZero) {
  // Note: |min| can be zero here.
  MOZ_ASSERT(min <= max);
  if (!append('.') || !appendN('0', min) || !appendN('#', max - min)) {
    return false;
  }
  if (stripTrailingZero) {
    if (!append(u"/w")) {
      return false;
    }
  }
  return append(' ');
}

bool NumberFormatterSkeleton::fractionWithSignificantDigits(
    uint32_t mnfd, uint32_t mxfd, uint32_t mnsd, uint32_t mxsd, bool relaxed,
    bool stripTrailingZero) {
  // Note: |mnfd| can be zero here.
  MOZ_ASSERT(mnfd <= mxfd);
  MOZ_ASSERT(mnsd > 0);
  MOZ_ASSERT(mnsd <= mxsd);

  if (!append('.') || !appendN('0', mnfd) || !appendN('#', mxfd - mnfd)) {
    return false;
  }
  if (!append('/') || !appendN('@', mnsd) || !appendN('#', mxsd - mnsd)) {
    return false;
  }
  if (!append(relaxed ? 'r' : 's')) {
    return false;
  }
  if (stripTrailingZero) {
    if (!append(u"/w")) {
      return false;
    }
  }
  return append(' ');
}

bool NumberFormatterSkeleton::minIntegerDigits(uint32_t min) {
  MOZ_ASSERT(min > 0);
  return append(u"integer-width/+") && appendN('0', min) && append(' ');
}

bool NumberFormatterSkeleton::significantDigits(uint32_t min, uint32_t max,
                                                bool stripTrailingZero) {
  MOZ_ASSERT(min > 0);
  MOZ_ASSERT(min <= max);
  if (!appendN('@', min) || !appendN('#', max - min)) {
    return false;
  }
  if (stripTrailingZero) {
    if (!append(u"/w")) {
      return false;
    }
  }
  return append(' ');
}

bool NumberFormatterSkeleton::grouping(NumberFormatOptions::Grouping grouping) {
  switch (grouping) {
    case NumberFormatOptions::Grouping::Auto:
      // Default, no additional tokens needed.
      return true;
    case NumberFormatOptions::Grouping::Always:
      return appendToken(u"group-on-aligned");
    case NumberFormatOptions::Grouping::Min2:
      return appendToken(u"group-min2");
    case NumberFormatOptions::Grouping::Never:
      return appendToken(u"group-off");
  }
  MOZ_ASSERT_UNREACHABLE("unexpected grouping mode");
  return false;
}

bool NumberFormatterSkeleton::notation(NumberFormatOptions::Notation style) {
  switch (style) {
    case NumberFormatOptions::Notation::Standard:
      // Default, no additional tokens needed.
      return true;
    case NumberFormatOptions::Notation::Scientific:
      return appendToken(u"scientific");
    case NumberFormatOptions::Notation::Engineering:
      return appendToken(u"engineering");
    case NumberFormatOptions::Notation::CompactShort:
      return appendToken(u"compact-short");
    case NumberFormatOptions::Notation::CompactLong:
      return appendToken(u"compact-long");
  }
  MOZ_ASSERT_UNREACHABLE("unexpected notation style");
  return false;
}

bool NumberFormatterSkeleton::signDisplay(
    NumberFormatOptions::SignDisplay display) {
  switch (display) {
    case NumberFormatOptions::SignDisplay::Auto:
      // Default, no additional tokens needed.
      return true;
    case NumberFormatOptions::SignDisplay::Always:
      return appendToken(u"sign-always");
    case NumberFormatOptions::SignDisplay::Never:
      return appendToken(u"sign-never");
    case NumberFormatOptions::SignDisplay::ExceptZero:
      return appendToken(u"sign-except-zero");
    case NumberFormatOptions::SignDisplay::Negative:
      return appendToken(u"sign-negative");
    case NumberFormatOptions::SignDisplay::Accounting:
      return appendToken(u"sign-accounting");
    case NumberFormatOptions::SignDisplay::AccountingAlways:
      return appendToken(u"sign-accounting-always");
    case NumberFormatOptions::SignDisplay::AccountingExceptZero:
      return appendToken(u"sign-accounting-except-zero");
    case NumberFormatOptions::SignDisplay::AccountingNegative:
      return appendToken(u"sign-accounting-negative");
  }
  MOZ_ASSERT_UNREACHABLE("unexpected sign display type");
  return false;
}

bool NumberFormatterSkeleton::roundingIncrement(uint32_t increment,
                                                uint32_t mnfd, uint32_t mxfd,
                                                bool stripTrailingZero) {
  // Note: |mnfd| can be zero here.
  MOZ_ASSERT(mnfd <= mxfd);
  MOZ_ASSERT(increment > 1);

  // Limit |mxfd| to 100.
  constexpr size_t maxFracDigits = 100;
  MOZ_RELEASE_ASSERT(mxfd <= maxFracDigits);

  static constexpr char digits[] = "0123456789";

  // We need enough space to print any uint32_t, which is possibly shifted by
  // |mxfd| decimal places. And additionally we need to reserve space for "0.".
  static_assert(std::numeric_limits<uint32_t>::digits10 + 1 < maxFracDigits);
  constexpr size_t maxLength = maxFracDigits + 2;

  char chars[maxLength];
  RangedPtr<char> ptr(chars + maxLength, chars, maxLength);
  const RangedPtr<char> end = ptr;

  // Convert to a signed integer, so we don't have to worry about underflows.
  int32_t maxFrac = int32_t(mxfd);

  // Write |increment| from back to front.
  while (increment != 0) {
    *--ptr = digits[increment % 10];
    increment /= 10;
    maxFrac -= 1;

    if (maxFrac == 0) {
      *--ptr = '.';
    }
  }

  // Write any remaining zeros from |mxfd| and prepend '0' if we last wrote the
  // decimal point.
  while (maxFrac >= 0) {
    MOZ_ASSERT_IF(maxFrac == 0, *ptr == '.');

    *--ptr = '0';
    maxFrac -= 1;

    if (maxFrac == 0) {
      *--ptr = '.';
    }
  }

  MOZ_ASSERT(ptr < end, "At least one character is written.");
  MOZ_ASSERT(*ptr != '.'"First character is a digit.");

  if (!append(u"precision-increment/") || !append(ptr.get(), end - ptr)) {
    return false;
  }
  if (stripTrailingZero) {
    if (!append(u"/w")) {
      return false;
    }
  }
  return append(' ');
}

bool NumberFormatterSkeleton::roundingMode(
    NumberFormatOptions::RoundingMode rounding) {
  switch (rounding) {
    case NumberFormatOptions::RoundingMode::Ceil:
      return appendToken(u"rounding-mode-ceiling");
    case NumberFormatOptions::RoundingMode::Floor:
      return appendToken(u"rounding-mode-floor");
    case NumberFormatOptions::RoundingMode::Expand:
      return appendToken(u"rounding-mode-up");
    case NumberFormatOptions::RoundingMode::Trunc:
      return appendToken(u"rounding-mode-down");
    case NumberFormatOptions::RoundingMode::HalfCeil:
      return appendToken(u"rounding-mode-half-ceiling");
    case NumberFormatOptions::RoundingMode::HalfFloor:
      return appendToken(u"rounding-mode-half-floor");
    case NumberFormatOptions::RoundingMode::HalfExpand:
      return appendToken(u"rounding-mode-half-up");
    case NumberFormatOptions::RoundingMode::HalfTrunc:
      return appendToken(u"rounding-mode-half-down");
    case NumberFormatOptions::RoundingMode::HalfEven:
      return appendToken(u"rounding-mode-half-even");
    case NumberFormatOptions::RoundingMode::HalfOdd:
      return appendToken(u"rounding-mode-half-odd");
  }
  MOZ_ASSERT_UNREACHABLE("unexpected rounding mode");
  return false;
}

UNumberFormatter* NumberFormatterSkeleton::toFormatter(
    std::string_view locale) {
  if (!mValidSkeleton) {
    return nullptr;
  }

  UErrorCode status = U_ZERO_ERROR;
  UNumberFormatter* nf = unumf_openForSkeletonAndLocale(
      mVector.begin(), mVector.length(), AssertNullTerminatedString(locale),
      &status);
  if (U_FAILURE(status)) {
    return nullptr;
  }
  return nf;
}

static UNumberRangeCollapse ToUNumberRangeCollapse(
    NumberRangeFormatOptions::RangeCollapse collapse) {
  using RangeCollapse = NumberRangeFormatOptions::RangeCollapse;
  switch (collapse) {
    case RangeCollapse::Auto:
      return UNUM_RANGE_COLLAPSE_AUTO;
    case RangeCollapse::None:
      return UNUM_RANGE_COLLAPSE_NONE;
    case RangeCollapse::Unit:
      return UNUM_RANGE_COLLAPSE_UNIT;
    case RangeCollapse::All:
      return UNUM_RANGE_COLLAPSE_ALL;
  }
  MOZ_ASSERT_UNREACHABLE("unexpected range collapse");
  return UNUM_RANGE_COLLAPSE_NONE;
}

static UNumberRangeIdentityFallback ToUNumberRangeIdentityFallback(
    NumberRangeFormatOptions::RangeIdentityFallback identity) {
  using RangeIdentityFallback = NumberRangeFormatOptions::RangeIdentityFallback;
  switch (identity) {
    case RangeIdentityFallback::SingleValue:
      return UNUM_IDENTITY_FALLBACK_SINGLE_VALUE;
    case RangeIdentityFallback::ApproximatelyOrSingleValue:
      return UNUM_IDENTITY_FALLBACK_APPROXIMATELY_OR_SINGLE_VALUE;
    case RangeIdentityFallback::Approximately:
      return UNUM_IDENTITY_FALLBACK_APPROXIMATELY;
    case RangeIdentityFallback::Range:
      return UNUM_IDENTITY_FALLBACK_RANGE;
  }
  MOZ_ASSERT_UNREACHABLE("unexpected range identity fallback");
  return UNUM_IDENTITY_FALLBACK_RANGE;
}

UNumberRangeFormatter* NumberFormatterSkeleton::toRangeFormatter(
    std::string_view locale, NumberRangeFormatOptions::RangeCollapse collapse,
    NumberRangeFormatOptions::RangeIdentityFallback identity) {
  if (!mValidSkeleton) {
    return nullptr;
  }

  UParseError* perror = nullptr;
  UErrorCode status = U_ZERO_ERROR;
  UNumberRangeFormatter* nrf =
      unumrf_openForSkeletonWithCollapseAndIdentityFallback(
          mVector.begin(), mVector.length(), ToUNumberRangeCollapse(collapse),
          ToUNumberRangeIdentityFallback(identity),
          AssertNullTerminatedString(locale), perror, &status);
  if (U_FAILURE(status)) {
    return nullptr;
  }
  return nrf;
}

}  // namespace mozilla::intl

Messung V0.5
C=96 H=97 G=96

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.