File: units_router.cpp

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (159 lines) | stat: -rw-r--r-- 6,073 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// © 2020 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html

#include <_foundation_unicode/utypes.h>

#if !UCONFIG_NO_FORMATTING

#include "charstr.h"
#include "cmemory.h"
#include "cstring.h"
#include "measunit_impl.h"
#include "number_decimalquantity.h"
#include "number_roundingutils.h"
#include "resource.h"
#include <_foundation_unicode/measure.h>
#include "units_data.h"
#include "units_router.h"
#include <cmath>

U_NAMESPACE_BEGIN
namespace units {

#if APPLE_ICU_CHANGES
// rdar://
// The following is needed for Apple embedded (here we use
// 16 significant digits, 18 would be 2.22044604925031308E-16;
// value found by running a macOS tool to print DBL_EPSILON
#ifndef DBL_EPSILON
#define DBL_EPSILON (2.220446049250313E-16)
#endif
#endif  // APPLE_ICU_CHANGES

using number::Precision;
using number::impl::parseIncrementOption;

Precision UnitsRouter::parseSkeletonToPrecision(icu::UnicodeString precisionSkeleton,
                                                UErrorCode &status) {
    if (U_FAILURE(status)) {
        // As a member of UsagePrefsHandler, which is a friend of Precision, we
        // get access to the default constructor.
        return {};
    }
    constexpr int32_t kSkelPrefixLen = 20;
    if (!precisionSkeleton.startsWith(UNICODE_STRING_SIMPLE("precision-increment/"))) {
        status = U_INVALID_FORMAT_ERROR;
        return {};
    }
    U_ASSERT(precisionSkeleton[kSkelPrefixLen - 1] == u'/');
    StringSegment segment(precisionSkeleton, false);
    segment.adjustOffset(kSkelPrefixLen);
    Precision result;
    parseIncrementOption(segment, result, status);
    return result;
}

UnitsRouter::UnitsRouter(StringPiece inputUnitIdentifier, const Locale &locale, StringPiece usage,
                         UErrorCode &status) {
    this->init(MeasureUnit::forIdentifier(inputUnitIdentifier, status), locale, usage, status);
}

UnitsRouter::UnitsRouter(const MeasureUnit &inputUnit, const Locale &locale, StringPiece usage,
                         UErrorCode &status) {
    this->init(std::move(inputUnit), locale, usage, status);
}

void UnitsRouter::init(const MeasureUnit &inputUnit, const Locale &locale, StringPiece usage,
                       UErrorCode &status) {

    if (U_FAILURE(status)) {
        return;
    }

    // TODO: do we want to pass in ConversionRates and UnitPreferences instead
    // of loading in each UnitsRouter instance? (Or make global?)
    ConversionRates conversionRates(status);
    UnitPreferences prefs(status);

    MeasureUnitImpl inputUnitImpl = MeasureUnitImpl::forMeasureUnitMaybeCopy(inputUnit, status);
    MeasureUnitImpl baseUnitImpl =
        (extractCompoundBaseUnit(inputUnitImpl, conversionRates, status));
    CharString category = getUnitQuantity(baseUnitImpl, status);
    if (U_FAILURE(status)) {
        return;
    }

    const MaybeStackVector<UnitPreference> unitPrefs =
        prefs.getPreferencesFor(category.toStringPiece(), usage, locale, status);
    for (int32_t i = 0, n = unitPrefs.length(); i < n; ++i) {
        U_ASSERT(unitPrefs[i] != nullptr);
        const auto preference = unitPrefs[i];

        MeasureUnitImpl complexTargetUnitImpl =
            MeasureUnitImpl::forIdentifier(preference->unit.data(), status);
        if (U_FAILURE(status)) {
            return;
        }

        UnicodeString precision = preference->skeleton;

        // For now, we only have "precision-increment" in Units Preferences skeleton.
        // Therefore, we check if the skeleton starts with "precision-increment" and force the program to
        // fail otherwise.
        // NOTE:
        //  It is allowed to have an empty precision.
        if (!precision.isEmpty() && !precision.startsWith(u"precision-increment", 19)) {
            status = U_INTERNAL_PROGRAM_ERROR;
            return;
        }

        outputUnits_.emplaceBackAndCheckErrorCode(status,
                                                  complexTargetUnitImpl.copy(status).build(status));
        converterPreferences_.emplaceBackAndCheckErrorCode(status, inputUnitImpl, complexTargetUnitImpl,
                                                           preference->geq, std::move(precision),
                                                           conversionRates, status);

        if (U_FAILURE(status)) {
            return;
        }
    }
}

RouteResult UnitsRouter::route(double quantity, icu::number::impl::RoundingImpl *rounder, UErrorCode &status) const {
    // Find the matching preference
    const ConverterPreference *converterPreference = nullptr;
    for (int32_t i = 0, n = converterPreferences_.length(); i < n; i++) {
        converterPreference = converterPreferences_[i];
        if (converterPreference->converter.greaterThanOrEqual(std::abs(quantity) * (1 + DBL_EPSILON),
                                                              converterPreference->limit)) {
            break;
        }
    }
    U_ASSERT(converterPreference != nullptr);

    // Set up the rounder for this preference's precision
    if (rounder != nullptr && rounder->fPrecision.isBogus()) {
        if (converterPreference->precision.length() > 0) {
            rounder->fPrecision = parseSkeletonToPrecision(converterPreference->precision, status);
        } else {
            // We use the same rounding mode as COMPACT notation: known to be a
            // human-friendly rounding mode: integers, but add a decimal digit
            // as needed to ensure we have at least 2 significant digits.
            rounder->fPrecision = Precision::integer().withMinDigits(2);
        }
    }

    return RouteResult(converterPreference->converter.convert(quantity, rounder, status),
                       converterPreference->targetUnit.copy(status));
}

const MaybeStackVector<MeasureUnit> *UnitsRouter::getOutputUnits() const {
    // TODO: consider pulling this from converterPreferences_ and dropping
    // outputUnits_?
    return &outputUnits_;
}

} // namespace units
U_NAMESPACE_END

#endif /* #if !UCONFIG_NO_FORMATTING */