tor-browser

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

number_utils.cpp (9360B)


      1 // © 2018 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 
      4 #include "unicode/utypes.h"
      5 
      6 #if !UCONFIG_NO_FORMATTING
      7 
      8 // Allow implicit conversion from char16_t* to UnicodeString for this file:
      9 // Helpful in toString methods and elsewhere.
     10 #define UNISTR_FROM_STRING_EXPLICIT
     11 
     12 #include <stdlib.h>
     13 #include <cmath>
     14 #include "number_decnum.h"
     15 #include "number_types.h"
     16 #include "number_utils.h"
     17 #include "charstr.h"
     18 #include "decContext.h"
     19 #include "decNumber.h"
     20 #ifdef JS_HAS_INTL_API
     21 #include "double-conversion/double-conversion.h"
     22 #else
     23 #include "double-conversion.h"
     24 #endif
     25 #include "fphdlimp.h"
     26 #include "uresimp.h"
     27 #include "ureslocs.h"
     28 
     29 using namespace icu;
     30 using namespace icu::number;
     31 using namespace icu::number::impl;
     32 
     33 #ifdef JS_HAS_INTL_API
     34 using double_conversion::DoubleToStringConverter;
     35 #else
     36 using icu::double_conversion::DoubleToStringConverter;
     37 #endif
     38 
     39 
     40 namespace {
     41 
     42 const char16_t*
     43 doGetPattern(UResourceBundle* res, const char* nsName, const char* patternKey, UErrorCode& publicStatus,
     44             UErrorCode& localStatus) {
     45    // Construct the path into the resource bundle
     46    CharString key;
     47    key.append("NumberElements/", publicStatus);
     48    key.append(nsName, publicStatus);
     49    key.append("/patterns/", publicStatus);
     50    key.append(patternKey, publicStatus);
     51    if (U_FAILURE(publicStatus)) {
     52        return u"";
     53    }
     54    return ures_getStringByKeyWithFallback(res, key.data(), nullptr, &localStatus);
     55 }
     56 
     57 }
     58 
     59 
     60 const char16_t* utils::getPatternForStyle(const Locale& locale, const char* nsName, CldrPatternStyle style,
     61                                          UErrorCode& status) {
     62    const char* patternKey;
     63    switch (style) {
     64        case CLDR_PATTERN_STYLE_DECIMAL:
     65            patternKey = "decimalFormat";
     66            break;
     67        case CLDR_PATTERN_STYLE_CURRENCY:
     68            patternKey = "currencyFormat";
     69            break;
     70        case CLDR_PATTERN_STYLE_ACCOUNTING:
     71            patternKey = "accountingFormat";
     72            break;
     73        case CLDR_PATTERN_STYLE_PERCENT:
     74            patternKey = "percentFormat";
     75            break;
     76        case CLDR_PATTERN_STYLE_SCIENTIFIC:
     77            patternKey = "scientificFormat";
     78            break;
     79        default:
     80            patternKey = "decimalFormat"; // silence compiler error
     81            UPRV_UNREACHABLE_EXIT;
     82    }
     83    LocalUResourceBundlePointer res(ures_open(nullptr, locale.getName(), &status));
     84    if (U_FAILURE(status)) { return u""; }
     85 
     86    // Attempt to get the pattern with the native numbering system.
     87    UErrorCode localStatus = U_ZERO_ERROR;
     88    const char16_t* pattern;
     89    pattern = doGetPattern(res.getAlias(), nsName, patternKey, status, localStatus);
     90    if (U_FAILURE(status)) { return u""; }
     91 
     92    // Fall back to latn if native numbering system does not have the right pattern
     93    if (U_FAILURE(localStatus) && uprv_strcmp("latn", nsName) != 0) {
     94        localStatus = U_ZERO_ERROR;
     95        pattern = doGetPattern(res.getAlias(), "latn", patternKey, status, localStatus);
     96        if (U_FAILURE(status)) { return u""; }
     97    }
     98 
     99    return pattern;
    100 }
    101 
    102 
    103 DecNum::DecNum() {
    104    uprv_decContextDefault(&fContext, DEC_INIT_BASE);
    105    uprv_decContextSetRounding(&fContext, DEC_ROUND_HALF_EVEN);
    106    fContext.traps = 0; // no traps, thank you (what does this even mean?)
    107 }
    108 
    109 DecNum::DecNum(const DecNum& other, UErrorCode& status)
    110        : fContext(other.fContext) {
    111    // Allocate memory for the new DecNum.
    112    U_ASSERT(fContext.digits == other.fData.getCapacity());
    113    if (fContext.digits > kDefaultDigits) {
    114        void* p = fData.resize(fContext.digits, 0);
    115        if (p == nullptr) {
    116            status = U_MEMORY_ALLOCATION_ERROR;
    117            return;
    118        }
    119    }
    120 
    121    // Copy the data from the old DecNum to the new one.
    122    uprv_memcpy(fData.getAlias(), other.fData.getAlias(), sizeof(decNumber));
    123    uprv_memcpy(fData.getArrayStart(),
    124            other.fData.getArrayStart(),
    125            other.fData.getArrayLimit() - other.fData.getArrayStart());
    126 }
    127 
    128 void DecNum::setTo(StringPiece str, UErrorCode& status) {
    129    // We need NUL-terminated for decNumber; CharString guarantees this, but not StringPiece.
    130    CharString cstr(str, status);
    131    if (U_FAILURE(status)) { return; }
    132    _setTo(cstr.data(), str.length(), status);
    133 }
    134 
    135 void DecNum::setTo(const char* str, UErrorCode& status) {
    136    _setTo(str, static_cast<int32_t>(uprv_strlen(str)), status);
    137 }
    138 
    139 void DecNum::setTo(double d, UErrorCode& status) {
    140    // Need to check for NaN and Infinity before going into DoubleToStringConverter
    141    if (std::isnan(d) != 0 || std::isfinite(d) == 0) {
    142        status = U_UNSUPPORTED_ERROR;
    143        return;
    144    }
    145 
    146    // First convert from double to string, then string to DecNum.
    147    // Allocate enough room for: all digits, "E-324", and NUL-terminator.
    148    char buffer[DoubleToStringConverter::kBase10MaximalLength + 6];
    149    bool sign; // unused; always positive
    150    int32_t length;
    151    int32_t point;
    152    DoubleToStringConverter::DoubleToAscii(
    153            d,
    154            DoubleToStringConverter::DtoaMode::SHORTEST,
    155            0,
    156            buffer,
    157            sizeof(buffer),
    158            &sign,
    159            &length,
    160            &point
    161    );
    162 
    163    // Read initial result as a string.
    164    _setTo(buffer, length, status);
    165 
    166    // Set exponent and bitmask. Note that DoubleToStringConverter does not do negatives.
    167    fData.getAlias()->exponent += point - length;
    168    fData.getAlias()->bits |= static_cast<uint8_t>(std::signbit(d) ? DECNEG : 0);
    169 }
    170 
    171 void DecNum::_setTo(const char* str, int32_t maxDigits, UErrorCode& status) {
    172    if (maxDigits > kDefaultDigits) {
    173        fData.resize(maxDigits, 0);
    174        fContext.digits = maxDigits;
    175    } else {
    176        fContext.digits = kDefaultDigits;
    177    }
    178 
    179    static_assert(DECDPUN == 1, "Assumes that DECDPUN is set to 1");
    180    uprv_decNumberFromString(fData.getAlias(), str, &fContext);
    181 
    182    // Check for invalid syntax and set the corresponding error code.
    183    if ((fContext.status & DEC_Conversion_syntax) != 0) {
    184        status = U_DECIMAL_NUMBER_SYNTAX_ERROR;
    185        return;
    186    } else if (fContext.status != 0) {
    187        // Not a syntax error, but some other error, like an exponent that is too large.
    188        status = U_UNSUPPORTED_ERROR;
    189        return;
    190    }
    191 }
    192 
    193 void
    194 DecNum::setTo(const uint8_t* bcd, int32_t length, int32_t scale, bool isNegative, UErrorCode& status) {
    195    if (length > kDefaultDigits) {
    196        fData.resize(length, 0);
    197        fContext.digits = length;
    198    } else {
    199        fContext.digits = kDefaultDigits;
    200    }
    201 
    202    // "digits is of type int32_t, and must have a value in the range 1 through 999,999,999."
    203    if (length < 1 || length > 999999999) {
    204        // Too large for decNumber
    205        status = U_UNSUPPORTED_ERROR;
    206        return;
    207    }
    208    // "The exponent field holds the exponent of the number. Its range is limited by the requirement that
    209    // "the range of the adjusted exponent of the number be balanced and fit within a whole number of
    210    // "decimal digits (in this implementation, be –999,999,999 through +999,999,999). The adjusted
    211    // "exponent is the exponent that would result if the number were expressed with a single digit before
    212    // "the decimal point, and is therefore given by exponent+digits-1."
    213    if (scale > 999999999 - length + 1 || scale < -999999999 - length + 1) {
    214        // Too large for decNumber
    215        status = U_UNSUPPORTED_ERROR;
    216        return;
    217    }
    218 
    219    fData.getAlias()->digits = length;
    220    fData.getAlias()->exponent = scale;
    221    fData.getAlias()->bits = static_cast<uint8_t>(isNegative ? DECNEG : 0);
    222    uprv_decNumberSetBCD(fData, bcd, static_cast<uint32_t>(length));
    223    if (fContext.status != 0) {
    224        // Some error occurred while constructing the decNumber.
    225        status = U_INTERNAL_PROGRAM_ERROR;
    226    }
    227 }
    228 
    229 void DecNum::normalize() {
    230    uprv_decNumberReduce(fData, fData, &fContext);
    231 }
    232 
    233 void DecNum::multiplyBy(const DecNum& rhs, UErrorCode& status) {
    234    uprv_decNumberMultiply(fData, fData, rhs.fData, &fContext);
    235    if (fContext.status != 0) {
    236        status = U_INTERNAL_PROGRAM_ERROR;
    237    }
    238 }
    239 
    240 void DecNum::divideBy(const DecNum& rhs, UErrorCode& status) {
    241    uprv_decNumberDivide(fData, fData, rhs.fData, &fContext);
    242    if ((fContext.status & DEC_Inexact) != 0) {
    243        // Ignore.
    244    } else if (fContext.status != 0) {
    245        status = U_INTERNAL_PROGRAM_ERROR;
    246    }
    247 }
    248 
    249 bool DecNum::isNegative() const {
    250    return decNumberIsNegative(fData.getAlias());
    251 }
    252 
    253 bool DecNum::isZero() const {
    254    return decNumberIsZero(fData.getAlias());
    255 }
    256 
    257 bool DecNum::isSpecial() const {
    258    return decNumberIsSpecial(fData.getAlias());
    259 }
    260 
    261 bool DecNum::isInfinity() const {
    262    return decNumberIsInfinite(fData.getAlias());
    263 }
    264 
    265 bool DecNum::isNaN() const {
    266    return decNumberIsNaN(fData.getAlias());
    267 }
    268 
    269 void DecNum::toString(ByteSink& output, UErrorCode& status) const {
    270    if (U_FAILURE(status)) {
    271        return;
    272    }
    273    // "string must be at least dn->digits+14 characters long"
    274    int32_t minCapacity = fData.getAlias()->digits + 14;
    275    MaybeStackArray<char, 30> buffer(minCapacity, status);
    276    if (U_FAILURE(status)) {
    277        return;
    278    }
    279    uprv_decNumberToString(fData, buffer.getAlias());
    280    output.Append(buffer.getAlias(), static_cast<int32_t>(uprv_strlen(buffer.getAlias())));
    281 }
    282 
    283 #endif /* #if !UCONFIG_NO_FORMATTING */