Bug Summary

File:root/firefox-clang/intl/icu/source/i18n/nfrs.cpp
Warning:line 623, column 63
Division by zero

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name nfrs.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D U_I18N_IMPLEMENTATION -D _LIBCPP_DISABLE_DEPRECATION_WARNINGS -D U_USING_ICU_NAMESPACE=0 -D U_NO_DEFAULT_INCLUDE_UTF_HEADERS=1 -D U_HIDE_OBSOLETE_UTF_OLD_H=1 -D UCONFIG_NO_LEGACY_CONVERSION -D UCONFIG_NO_TRANSLITERATION -D UCONFIG_NO_REGULAR_EXPRESSIONS -D UCONFIG_NO_BREAK_ITERATION -D UCONFIG_NO_IDNA -D UCONFIG_NO_MF2 -D UCONFIG_NO_NORMALIZATION -D UCONFIG_NO_COLLATION -D U_CHARSET_IS_UTF8 -D UNISTR_FROM_CHAR_EXPLICIT=explicit -D UNISTR_FROM_STRING_EXPLICIT=explicit -D U_ENABLE_DYLOAD=0 -D U_DEBUG=1 -I /root/firefox-clang/config/external/icu/i18n -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/config/external/icu/i18n -I /root/firefox-clang/intl/icu/source/common -I /root/firefox-clang/mfbt/double-conversion -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -Wno-comma -Wno-implicit-const-int-float-conversion -Wno-macro-redefined -Wno-microsoft-include -Wno-tautological-unsigned-enum-zero-compare -Wno-unreachable-code-loop-increment -Wno-unreachable-code-return -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ /root/firefox-clang/intl/icu/source/i18n/nfrs.cpp
1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4******************************************************************************
5* Copyright (C) 1997-2015, International Business Machines
6* Corporation and others. All Rights Reserved.
7******************************************************************************
8* file name: nfrs.cpp
9* encoding: UTF-8
10* tab size: 8 (not used)
11* indentation:4
12*
13* Modification history
14* Date Name Comments
15* 10/11/2001 Doug Ported from ICU4J
16*/
17
18#include "nfrs.h"
19
20#if U_HAVE_RBNF1
21
22#include "unicode/uchar.h"
23#include "nfrule.h"
24#include "nfrlist.h"
25#include "patternprops.h"
26#include "putilimp.h"
27
28#ifdef RBNF_DEBUG
29#include "cmemory.h"
30#endif
31
32enum {
33 /** -x */
34 NEGATIVE_RULE_INDEX = 0,
35 /** x.x */
36 IMPROPER_FRACTION_RULE_INDEX = 1,
37 /** 0.x */
38 PROPER_FRACTION_RULE_INDEX = 2,
39 /** x.0 */
40 DEFAULT_RULE_INDEX = 3,
41 /** Inf */
42 INFINITY_RULE_INDEX = 4,
43 /** NaN */
44 NAN_RULE_INDEX = 5,
45 NON_NUMERICAL_RULE_LENGTH = 6
46};
47
48U_NAMESPACE_BEGINnamespace icu_78 {
49
50#if 0
51// euclid's algorithm works with doubles
52// note, doubles only get us up to one quadrillion or so, which
53// isn't as much range as we get with longs. We probably still
54// want either 64-bit math, or BigInteger.
55
56static int64_t
57util_lcm(int64_t x, int64_t y)
58{
59 x.abs();
60 y.abs();
61
62 if (x == 0 || y == 0) {
63 return 0;
64 } else {
65 do {
66 if (x < y) {
67 int64_t t = x; x = y; y = t;
68 }
69 x -= y * (x/y);
70 } while (x != 0);
71
72 return y;
73 }
74}
75
76#else
77/**
78 * Calculates the least common multiple of x and y.
79 */
80static int64_t
81util_lcm(int64_t x, int64_t y)
82{
83 // binary gcd algorithm from Knuth, "The Art of Computer Programming,"
84 // vol. 2, 1st ed., pp. 298-299
85 int64_t x1 = x;
86 int64_t y1 = y;
87
88 int p2 = 0;
89 while ((x1 & 1) == 0 && (y1 & 1) == 0) {
10
Assuming the condition is false
90 ++p2;
91 x1 >>= 1;
92 y1 >>= 1;
93 }
94
95 int64_t t;
96 if ((x1 & 1) == 1) {
11
Taking true branch
97 t = -y1;
98 } else {
99 t = x1;
100 }
101
102 while (t != 0) {
12
Assuming 't' is equal to 0
13
Loop condition is false. Execution continues on line 114
103 while ((t & 1) == 0) {
104 t = t >> 1;
105 }
106 if (t > 0) {
107 x1 = t;
108 } else {
109 y1 = -t;
110 }
111 t = x1 - y1;
112 }
113
114 int64_t gcd = x1 << p2;
115
116 // x * y == gcd(x, y) * lcm(x, y)
117 return x / gcd * y;
14
Returning zero
118}
119#endif
120
121static const char16_t gPercent = 0x0025;
122static const char16_t gColon = 0x003a;
123static const char16_t gSemicolon = 0x003b;
124static const char16_t gLineFeed = 0x000a;
125
126static const char16_t gPercentPercent[] =
127{
128 0x25, 0x25, 0
129}; /* "%%" */
130
131static const char16_t gNoparse[] =
132{
133 0x40, 0x6E, 0x6F, 0x70, 0x61, 0x72, 0x73, 0x65, 0
134}; /* "@noparse" */
135
136NFRuleSet::NFRuleSet(RuleBasedNumberFormat *_owner, UnicodeString* descriptions, int32_t index, UErrorCode& status)
137 : name()
138 , rules(0)
139 , owner(_owner)
140 , fractionRules()
141 , fIsFractionRuleSet(false)
142 , fIsPublic(false)
143 , fIsParseable(true)
144{
145 for (int32_t i = 0; i < NON_NUMERICAL_RULE_LENGTH; ++i) {
146 nonNumericalRules[i] = nullptr;
147 }
148
149 if (U_FAILURE(status)) {
150 return;
151 }
152
153 UnicodeString& description = descriptions[index]; // !!! make sure index is valid
154
155 if (description.isEmpty()) {
156 // throw new IllegalArgumentException("Empty rule set description");
157 status = U_PARSE_ERROR;
158 return;
159 }
160
161 // if the description begins with a rule set name (the rule set
162 // name can be omitted in formatter descriptions that consist
163 // of only one rule set), copy it out into our "name" member
164 // and delete it from the description
165 if (description.charAt(0) == gPercent) {
166 int32_t pos = description.indexOf(gColon);
167 // if there are no name or the name is "%".
168 if (pos < 2) {
169 // throw new IllegalArgumentException("Rule set name doesn't end in colon");
170 status = U_PARSE_ERROR;
171 return;
172 } else {
173 name.setTo(description, 0, pos);
174 while (pos < description.length() && PatternProps::isWhiteSpace(description.charAt(++pos))) {
175 }
176 description.remove(0, pos);
177 }
178 } else {
179 name.setTo(UNICODE_STRING_SIMPLE("%default")icu::UnicodeString(true, u"%default", -1));
180 }
181
182 if (description.isEmpty()) {
183 // throw new IllegalArgumentException("Empty rule set description");
184 status = U_PARSE_ERROR;
185 return;
186 }
187
188 fIsPublic = name.indexOf(gPercentPercent, 2, 0) != 0;
189
190 if (name.endsWith(gNoparse, 8)) {
191 fIsParseable = false;
192 name.truncate(name.length() - 8); // remove the @noparse from the name
193 }
194
195 // all of the other members of NFRuleSet are initialized
196 // by parseRules()
197}
198
199void
200NFRuleSet::parseRules(UnicodeString& description, UErrorCode& status)
201{
202 // start by creating a Vector whose elements are Strings containing
203 // the descriptions of the rules (one rule per element). The rules
204 // are separated by semicolons (there's no escape facility: ALL
205 // semicolons are rule delimiters)
206
207 if (U_FAILURE(status)) {
208 return;
209 }
210
211 // ensure we are starting with an empty rule list
212 rules.deleteAll();
213
214 // dlf - the original code kept a separate description array for no reason,
215 // so I got rid of it. The loop was too complex so I simplified it.
216
217 UnicodeString currentDescription;
218 int32_t oldP = 0;
219 while (oldP < description.length()) {
220 int32_t p = description.indexOf(gSemicolon, oldP);
221 if (p == -1) {
222 p = description.length();
223 }
224 currentDescription.setTo(description, oldP, p - oldP);
225 NFRule::makeRules(currentDescription, this, rules.last(), owner, rules, status);
226 if (U_FAILURE(status)) {
227 return;
228 }
229 oldP = p + 1;
230 }
231
232 // for rules that didn't specify a base value, their base values
233 // were initialized to 0. Make another pass through the list and
234 // set all those rules' base values. We also remove any special
235 // rules from the list and put them into their own member variables
236 int64_t defaultBaseValue = 0;
237
238 // (this isn't a for loop because we might be deleting items from
239 // the vector-- we want to make sure we only increment i when
240 // we _didn't_ delete anything from the vector)
241 int32_t rulesSize = rules.size();
242 for (int32_t i = 0; i < rulesSize; i++) {
243 NFRule* rule = rules[i];
244 int64_t baseValue = rule->getBaseValue();
245
246 if (baseValue == 0) {
247 // if the rule's base value is 0, fill in a default
248 // base value (this will be 1 plus the preceding
249 // rule's base value for regular rule sets, and the
250 // same as the preceding rule's base value in fraction
251 // rule sets)
252 rule->setBaseValue(defaultBaseValue, status);
253 if (U_FAILURE(status)) {
254 return;
255 }
256 }
257 else {
258 // if it's a regular rule that already knows its base value,
259 // check to make sure the rules are in order, and update
260 // the default base value for the next rule
261 if (baseValue < defaultBaseValue) {
262 // throw new IllegalArgumentException("Rules are not in order");
263 status = U_PARSE_ERROR;
264 return;
265 }
266 defaultBaseValue = baseValue;
267 }
268 if (!fIsFractionRuleSet) {
269 ++defaultBaseValue;
270 }
271 }
272}
273
274/**
275 * Set one of the non-numerical rules.
276 * @param rule The rule to set.
277 */
278void NFRuleSet::setNonNumericalRule(NFRule *rule) {
279 switch (rule->getBaseValue()) {
280 case NFRule::kNegativeNumberRule:
281 delete nonNumericalRules[NEGATIVE_RULE_INDEX];
282 nonNumericalRules[NEGATIVE_RULE_INDEX] = rule;
283 return;
284 case NFRule::kImproperFractionRule:
285 setBestFractionRule(IMPROPER_FRACTION_RULE_INDEX, rule, true);
286 return;
287 case NFRule::kProperFractionRule:
288 setBestFractionRule(PROPER_FRACTION_RULE_INDEX, rule, true);
289 return;
290 case NFRule::kDefaultRule:
291 setBestFractionRule(DEFAULT_RULE_INDEX, rule, true);
292 return;
293 case NFRule::kInfinityRule:
294 delete nonNumericalRules[INFINITY_RULE_INDEX];
295 nonNumericalRules[INFINITY_RULE_INDEX] = rule;
296 return;
297 case NFRule::kNaNRule:
298 delete nonNumericalRules[NAN_RULE_INDEX];
299 nonNumericalRules[NAN_RULE_INDEX] = rule;
300 return;
301 case NFRule::kNoBase:
302 case NFRule::kOtherRule:
303 default:
304 // If we do not remember the rule inside the object.
305 // delete it here to prevent memory leak.
306 delete rule;
307 return;
308 }
309}
310
311/**
312 * Determine the best fraction rule to use. Rules matching the decimal point from
313 * DecimalFormatSymbols become the main set of rules to use.
314 * @param originalIndex The index into nonNumericalRules
315 * @param newRule The new rule to consider
316 * @param rememberRule Should the new rule be added to fractionRules.
317 */
318void NFRuleSet::setBestFractionRule(int32_t originalIndex, NFRule *newRule, UBool rememberRule) {
319 if (rememberRule) {
320 fractionRules.add(newRule);
321 }
322 NFRule *bestResult = nonNumericalRules[originalIndex];
323 if (bestResult == nullptr) {
324 nonNumericalRules[originalIndex] = newRule;
325 }
326 else {
327 // We have more than one. Which one is better?
328 const DecimalFormatSymbols *decimalFormatSymbols = owner->getDecimalFormatSymbols();
329 if (decimalFormatSymbols->getSymbol(DecimalFormatSymbols::kDecimalSeparatorSymbol).charAt(0)
330 == newRule->getDecimalPoint())
331 {
332 nonNumericalRules[originalIndex] = newRule;
333 }
334 // else leave it alone
335 }
336}
337
338NFRuleSet::~NFRuleSet()
339{
340 for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) {
341 if (i != IMPROPER_FRACTION_RULE_INDEX
342 && i != PROPER_FRACTION_RULE_INDEX
343 && i != DEFAULT_RULE_INDEX)
344 {
345 delete nonNumericalRules[i];
346 }
347 // else it will be deleted via NFRuleList fractionRules
348 }
349}
350
351static UBool
352util_equalRules(const NFRule* rule1, const NFRule* rule2)
353{
354 if (rule1) {
355 if (rule2) {
356 return *rule1 == *rule2;
357 }
358 } else if (!rule2) {
359 return true;
360 }
361 return false;
362}
363
364bool
365NFRuleSet::operator==(const NFRuleSet& rhs) const
366{
367 if (rules.size() == rhs.rules.size() &&
368 fIsFractionRuleSet == rhs.fIsFractionRuleSet &&
369 name == rhs.name) {
370
371 // ...then compare the non-numerical rule lists...
372 for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) {
373 if (!util_equalRules(nonNumericalRules[i], rhs.nonNumericalRules[i])) {
374 return false;
375 }
376 }
377
378 // ...then compare the rule lists...
379 for (uint32_t i = 0; i < rules.size(); ++i) {
380 if (*rules[i] != *rhs.rules[i]) {
381 return false;
382 }
383 }
384 return true;
385 }
386 return false;
387}
388
389void
390NFRuleSet::setDecimalFormatSymbols(const DecimalFormatSymbols &newSymbols, UErrorCode& status) {
391 for (uint32_t i = 0; i < rules.size(); ++i) {
392 rules[i]->setDecimalFormatSymbols(newSymbols, status);
393 }
394 // Switch the fraction rules to mirror the DecimalFormatSymbols.
395 for (int32_t nonNumericalIdx = IMPROPER_FRACTION_RULE_INDEX; nonNumericalIdx <= DEFAULT_RULE_INDEX; nonNumericalIdx++) {
396 if (nonNumericalRules[nonNumericalIdx]) {
397 for (uint32_t fIdx = 0; fIdx < fractionRules.size(); fIdx++) {
398 NFRule *fractionRule = fractionRules[fIdx];
399 if (nonNumericalRules[nonNumericalIdx]->getBaseValue() == fractionRule->getBaseValue()) {
400 setBestFractionRule(nonNumericalIdx, fractionRule, false);
401 }
402 }
403 }
404 }
405
406 for (uint32_t nnrIdx = 0; nnrIdx < NON_NUMERICAL_RULE_LENGTH; nnrIdx++) {
407 NFRule *rule = nonNumericalRules[nnrIdx];
408 if (rule) {
409 rule->setDecimalFormatSymbols(newSymbols, status);
410 }
411 }
412}
413
414#define RECURSION_LIMIT64 64
415
416void
417NFRuleSet::format(int64_t number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const
418{
419 if (recursionCount >= RECURSION_LIMIT64) {
420 // stop recursion
421 status = U_INVALID_STATE_ERROR;
422 return;
423 }
424 const NFRule *rule = findNormalRule(number);
425 if (rule) { // else error, but can't report it
426 rule->doFormat(number, toAppendTo, pos, ++recursionCount, status);
427 }
428}
429
430void
431NFRuleSet::format(double number, UnicodeString& toAppendTo, int32_t pos, int32_t recursionCount, UErrorCode& status) const
432{
433 if (recursionCount >= RECURSION_LIMIT64) {
1
Assuming 'recursionCount' is < RECURSION_LIMIT
2
Taking false branch
434 // stop recursion
435 status = U_INVALID_STATE_ERROR;
436 return;
437 }
438 const NFRule *rule = findDoubleRule(number);
3
Calling 'NFRuleSet::findDoubleRule'
439 if (rule) { // else error, but can't report it
440 rule->doFormat(number, toAppendTo, pos, ++recursionCount, status);
441 }
442}
443
444const NFRule*
445NFRuleSet::findDoubleRule(double number) const
446{
447 // if this is a fraction rule set, use findFractionRuleSetRule()
448 if (isFractionRuleSet()) {
4
Assuming the condition is true
5
Taking true branch
449 return findFractionRuleSetRule(number);
6
Calling 'NFRuleSet::findFractionRuleSetRule'
450 }
451
452 if (uprv_isNaN(number)) {
453 const NFRule *rule = nonNumericalRules[NAN_RULE_INDEX];
454 if (!rule) {
455 rule = owner->getDefaultNaNRule();
456 }
457 return rule;
458 }
459
460 // if the number is negative, return the negative number rule
461 // (if there isn't a negative-number rule, we pretend it's a
462 // positive number)
463 if (number < 0) {
464 if (nonNumericalRules[NEGATIVE_RULE_INDEX]) {
465 return nonNumericalRules[NEGATIVE_RULE_INDEX];
466 } else {
467 number = -number;
468 }
469 }
470
471 if (uprv_isInfinite(number)) {
472 const NFRule *rule = nonNumericalRules[INFINITY_RULE_INDEX];
473 if (!rule) {
474 rule = owner->getDefaultInfinityRule();
475 }
476 return rule;
477 }
478
479 // if the number isn't an integer, we use one of the fraction rules...
480 if (number != uprv_floor(number)) {
481 // if the number is between 0 and 1, return the proper
482 // fraction rule
483 if (number < 1 && nonNumericalRules[PROPER_FRACTION_RULE_INDEX]) {
484 return nonNumericalRules[PROPER_FRACTION_RULE_INDEX];
485 }
486 // otherwise, return the improper fraction rule
487 else if (nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX]) {
488 return nonNumericalRules[IMPROPER_FRACTION_RULE_INDEX];
489 }
490 }
491
492 // if there's a default rule, use it to format the number
493 if (nonNumericalRules[DEFAULT_RULE_INDEX]) {
494 return nonNumericalRules[DEFAULT_RULE_INDEX];
495 }
496
497 // and if we haven't yet returned a rule, use findNormalRule()
498 // to find the applicable rule
499 int64_t r = util64_fromDouble(number + 0.5);
500 return findNormalRule(r);
501}
502
503const NFRule *
504NFRuleSet::findNormalRule(int64_t number) const
505{
506 // if this is a fraction rule set, use findFractionRuleSetRule()
507 // to find the rule (we should only go into this clause if the
508 // value is 0)
509 if (fIsFractionRuleSet) {
510 return findFractionRuleSetRule(static_cast<double>(number));
511 }
512
513 // if the number is negative, return the negative-number rule
514 // (if there isn't one, pretend the number is positive)
515 if (number < 0) {
516 if (nonNumericalRules[NEGATIVE_RULE_INDEX]) {
517 return nonNumericalRules[NEGATIVE_RULE_INDEX];
518 } else {
519 number = -number;
520 }
521 }
522
523 // we have to repeat the preceding two checks, even though we
524 // do them in findRule(), because the version of format() that
525 // takes a long bypasses findRule() and goes straight to this
526 // function. This function does skip the fraction rules since
527 // we know the value is an integer (it also skips the default
528 // rule, since it's considered a fraction rule. Skipping the
529 // default rule in this function is also how we avoid infinite
530 // recursion)
531
532 // {dlf} unfortunately this fails if there are no rules except
533 // special rules. If there are no rules, use the default rule.
534
535 // binary-search the rule list for the applicable rule
536 // (a rule is used for all values from its base value to
537 // the next rule's base value)
538 int32_t hi = rules.size();
539 if (hi > 0) {
540 int32_t lo = 0;
541
542 while (lo < hi) {
543 int32_t mid = (lo + hi) / 2;
544 if (rules[mid]->getBaseValue() == number) {
545 return rules[mid];
546 }
547 else if (rules[mid]->getBaseValue() > number) {
548 hi = mid;
549 }
550 else {
551 lo = mid + 1;
552 }
553 }
554 if (hi == 0) { // bad rule set, minimum base > 0
555 return nullptr; // want to throw exception here
556 }
557
558 NFRule *result = rules[hi - 1];
559
560 // use shouldRollBack() to see whether we need to invoke the
561 // rollback rule (see shouldRollBack()'s documentation for
562 // an explanation of the rollback rule). If we do, roll back
563 // one rule and return that one instead of the one we'd normally
564 // return
565 if (result->shouldRollBack(number)) {
566 if (hi == 1) { // bad rule set, no prior rule to rollback to from this base
567 return nullptr;
568 }
569 result = rules[hi - 2];
570 }
571 return result;
572 }
573 // else use the default rule
574 return nonNumericalRules[DEFAULT_RULE_INDEX];
575}
576
577/**
578 * If this rule is a fraction rule set, this function is used by
579 * findRule() to select the most appropriate rule for formatting
580 * the number. Basically, the base value of each rule in the rule
581 * set is treated as the denominator of a fraction. Whichever
582 * denominator can produce the fraction closest in value to the
583 * number passed in is the result. If there's a tie, the earlier
584 * one in the list wins. (If there are two rules in a row with the
585 * same base value, the first one is used when the numerator of the
586 * fraction would be 1, and the second rule is used the rest of the
587 * time.
588 * @param number The number being formatted (which will always be
589 * a number between 0 and 1)
590 * @return The rule to use to format this number
591 */
592const NFRule*
593NFRuleSet::findFractionRuleSetRule(double number) const
594{
595 // the obvious way to do this (multiply the value being formatted
596 // by each rule's base value until you get an integral result)
597 // doesn't work because of rounding error. This method is more
598 // accurate
599
600 // find the least common multiple of the rules' base values
601 // and multiply this by the number being formatted. This is
602 // all the precision we need, and we can do all of the rest
603 // of the math using integer arithmetic
604 int64_t leastCommonMultiple = rules[0]->getBaseValue();
605 int64_t numerator;
606 {
607 for (uint32_t i = 1; i < rules.size(); ++i) {
7
Assuming the condition is true
8
Loop condition is true. Entering loop body
17
Assuming the condition is false
18
Loop condition is false. Execution continues on line 610
608 leastCommonMultiple = util_lcm(leastCommonMultiple, rules[i]->getBaseValue());
9
Calling 'util_lcm'
15
Returning from 'util_lcm'
16
The value 0 is assigned to 'leastCommonMultiple'
609 }
610 numerator = util64_fromDouble(number * static_cast<double>(leastCommonMultiple) + 0.5);
611 }
612 // for each rule, do the following...
613 int64_t tempDifference;
614 int64_t difference = util64_fromDouble(uprv_maxMantissa());
615 int32_t winner = 0;
616 for (uint32_t i = 0; i < rules.size(); ++i) {
19
Loop condition is true. Entering loop body
617 // "numerator" is the numerator of the fraction if the
618 // denominator is the LCD. The numerator if the rule's
619 // base value is the denominator is "numerator" times the
620 // base value divided bythe LCD. Here we check to see if
621 // that's an integer, and if not, how close it is to being
622 // an integer.
623 tempDifference = numerator * rules[i]->getBaseValue() % leastCommonMultiple;
20
Division by zero
624
625
626 // normalize the result of the above calculation: we want
627 // the numerator's distance from the CLOSEST multiple
628 // of the LCD
629 if (leastCommonMultiple - tempDifference < tempDifference) {
630 tempDifference = leastCommonMultiple - tempDifference;
631 }
632
633 // if this is as close as we've come, keep track of how close
634 // that is, and the line number of the rule that did it. If
635 // we've scored a direct hit, we don't have to look at any more
636 // rules
637 if (tempDifference < difference) {
638 difference = tempDifference;
639 winner = i;
640 if (difference == 0) {
641 break;
642 }
643 }
644 }
645
646 // if we have two successive rules that both have the winning base
647 // value, then the first one (the one we found above) is used if
648 // the numerator of the fraction is 1 and the second one is used if
649 // the numerator of the fraction is anything else (this lets us
650 // do things like "one third"/"two thirds" without having to define
651 // a whole bunch of extra rule sets)
652 if (static_cast<unsigned>(winner + 1) < rules.size() &&
653 rules[winner + 1]->getBaseValue() == rules[winner]->getBaseValue()) {
654 double n = static_cast<double>(rules[winner]->getBaseValue()) * number;
655 if (n < 0.5 || n >= 2) {
656 ++winner;
657 }
658 }
659
660 // finally, return the winning rule
661 return rules[winner];
662}
663
664/**
665 * Parses a string. Matches the string to be parsed against each
666 * of its rules (with a base value less than upperBound) and returns
667 * the value produced by the rule that matched the most characters
668 * in the source string.
669 * @param text The string to parse
670 * @param parsePosition The initial position is ignored and assumed
671 * to be 0. On exit, this object has been updated to point to the
672 * first character position this rule set didn't consume.
673 * @param upperBound Limits the rules that can be allowed to match.
674 * Only rules whose base values are strictly less than upperBound
675 * are considered.
676 * @return The numerical result of parsing this string. This will
677 * be the matching rule's base value, composed appropriately with
678 * the results of matching any of its substitutions. The object
679 * will be an instance of Long if it's an integral value; otherwise,
680 * it will be an instance of Double. This function always returns
681 * a valid object: If nothing matched the input string at all,
682 * this function returns new Long(0), and the parse position is
683 * left unchanged.
684 */
685#ifdef RBNF_DEBUG
686#include <stdio.h>
687
688static void dumpUS(FILE* f, const UnicodeString& us) {
689 int len = us.length();
690 char* buf = (char *)uprv_malloc((len+1)*sizeof(char)); //new char[len+1];
691 if (buf != nullptr) {
692 us.extract(0, len, buf);
693 buf[len] = 0;
694 fprintf(f, "%s", buf);
695 uprv_free(buf); //delete[] buf;
696 }
697}
698#endif
699
700UBool
701NFRuleSet::parse(const UnicodeString& text, ParsePosition& pos, double upperBound, uint32_t nonNumericalExecutedRuleMask, int32_t recursionCount, Formattable& result) const
702{
703 // try matching each rule in the rule set against the text being
704 // parsed. Whichever one matches the most characters is the one
705 // that determines the value we return.
706
707 result.setLong(0);
708
709 // dump out if we've reached the recursion limit
710 if (recursionCount >= RECURSION_LIMIT64) {
711 // stop recursion
712 return false;
713 }
714
715 // dump out if there's no text to parse
716 if (text.length() == 0) {
717 return 0;
718 }
719
720 ParsePosition highWaterMark;
721 ParsePosition workingPos = pos;
722
723#ifdef RBNF_DEBUG
724 fprintf(stderrstderr, "<nfrs> %x '", this);
725 dumpUS(stderrstderr, name);
726 fprintf(stderrstderr, "' text '");
727 dumpUS(stderrstderr, text);
728 fprintf(stderrstderr, "'\n");
729 fprintf(stderrstderr, " parse negative: %d\n", this, negativeNumberRule != 0);
730#endif
731 // Try each of the negative rules, fraction rules, infinity rules and NaN rules
732 for (int i = 0; i < NON_NUMERICAL_RULE_LENGTH; i++) {
733 if (nonNumericalRules[i] && ((nonNumericalExecutedRuleMask >> i) & 1) == 0) {
734 // Mark this rule as being executed so that we don't try to execute it again.
735 nonNumericalExecutedRuleMask |= 1 << i;
736
737 Formattable tempResult;
738 UBool success = nonNumericalRules[i]->doParse(text, workingPos, 0, upperBound, nonNumericalExecutedRuleMask, recursionCount + 1, tempResult);
739 if (success && (workingPos.getIndex() > highWaterMark.getIndex())) {
740 result = tempResult;
741 highWaterMark = workingPos;
742 }
743 workingPos = pos;
744 }
745 }
746#ifdef RBNF_DEBUG
747 fprintf(stderrstderr, "<nfrs> continue other with text '");
748 dumpUS(stderrstderr, text);
749 fprintf(stderrstderr, "' hwm: %d\n", highWaterMark.getIndex());
750#endif
751
752 // finally, go through the regular rules one at a time. We start
753 // at the end of the list because we want to try matching the most
754 // sigificant rule first (this helps ensure that we parse
755 // "five thousand three hundred six" as
756 // "(five thousand) (three hundred) (six)" rather than
757 // "((five thousand three) hundred) (six)"). Skip rules whose
758 // base values are higher than the upper bound (again, this helps
759 // limit ambiguity by making sure the rules that match a rule's
760 // are less significant than the rule containing the substitutions)/
761 {
762 int64_t ub = util64_fromDouble(upperBound);
763#ifdef RBNF_DEBUG
764 {
765 char ubstr[64];
766 util64_toa(ub, ubstr, 64);
767 char ubstrhex[64];
768 util64_toa(ub, ubstrhex, 64, 16);
769 fprintf(stderrstderr, "ub: %g, i64: %s (%s)\n", upperBound, ubstr, ubstrhex);
770 }
771#endif
772 for (int32_t i = rules.size(); --i >= 0 && highWaterMark.getIndex() < text.length();) {
773 if ((!fIsFractionRuleSet) && (rules[i]->getBaseValue() >= ub)) {
774 continue;
775 }
776 Formattable tempResult;
777 UBool success = rules[i]->doParse(text, workingPos, fIsFractionRuleSet, upperBound, nonNumericalExecutedRuleMask, recursionCount + 1, tempResult);
778 if (success && workingPos.getIndex() > highWaterMark.getIndex()) {
779 result = tempResult;
780 highWaterMark = workingPos;
781 }
782 workingPos = pos;
783 }
784 }
785#ifdef RBNF_DEBUG
786 fprintf(stderrstderr, "<nfrs> exit\n");
787#endif
788 // finally, update the parse position we were passed to point to the
789 // first character we didn't use, and return the result that
790 // corresponds to that string of characters
791 pos = highWaterMark;
792
793 return 1;
794}
795
796void
797NFRuleSet::appendRules(UnicodeString& result) const
798{
799 uint32_t i;
800
801 // the rule set name goes first...
802 result.append(name);
803 result.append(gColon);
804 result.append(gLineFeed);
805
806 // followed by the regular rules...
807 for (i = 0; i < rules.size(); i++) {
808 rules[i]->_appendRuleText(result);
809 result.append(gLineFeed);
810 }
811
812 // followed by the special rules (if they exist)
813 for (i = 0; i < NON_NUMERICAL_RULE_LENGTH; ++i) {
814 NFRule *rule = nonNumericalRules[i];
815 if (nonNumericalRules[i]) {
816 if (rule->getBaseValue() == NFRule::kImproperFractionRule
817 || rule->getBaseValue() == NFRule::kProperFractionRule
818 || rule->getBaseValue() == NFRule::kDefaultRule)
819 {
820 for (uint32_t fIdx = 0; fIdx < fractionRules.size(); fIdx++) {
821 NFRule *fractionRule = fractionRules[fIdx];
822 if (fractionRule->getBaseValue() == rule->getBaseValue()) {
823 fractionRule->_appendRuleText(result);
824 result.append(gLineFeed);
825 }
826 }
827 }
828 else {
829 rule->_appendRuleText(result);
830 result.append(gLineFeed);
831 }
832 }
833 }
834}
835
836// utility functions
837
838int64_t util64_fromDouble(double d) {
839 int64_t result = 0;
840 if (!uprv_isNaN(d)) {
841 double mant = uprv_maxMantissa();
842 if (d < -mant) {
843 d = -mant;
844 } else if (d > mant) {
845 d = mant;
846 }
847 UBool neg = d < 0;
848 if (neg) {
849 d = -d;
850 }
851 result = static_cast<int64_t>(uprv_floor(d));
852 if (neg) {
853 result = -result;
854 }
855 }
856 return result;
857}
858
859uint64_t util64_pow(uint32_t base, uint16_t exponent) {
860 if (base == 0) {
861 return 0;
862 }
863 uint64_t result = 1;
864 uint64_t pow = base;
865 while (true) {
866 if ((exponent & 1) == 1) {
867 result *= pow;
868 }
869 exponent >>= 1;
870 if (exponent == 0) {
871 break;
872 }
873 pow *= pow;
874 }
875 return result;
876}
877
878static const uint8_t asciiDigits[] = {
879 0x30u, 0x31u, 0x32u, 0x33u, 0x34u, 0x35u, 0x36u, 0x37u,
880 0x38u, 0x39u, 0x61u, 0x62u, 0x63u, 0x64u, 0x65u, 0x66u,
881 0x67u, 0x68u, 0x69u, 0x6au, 0x6bu, 0x6cu, 0x6du, 0x6eu,
882 0x6fu, 0x70u, 0x71u, 0x72u, 0x73u, 0x74u, 0x75u, 0x76u,
883 0x77u, 0x78u, 0x79u, 0x7au,
884};
885
886static const char16_t kUMinus = static_cast<char16_t>(0x002d);
887
888#ifdef RBNF_DEBUG
889static const char kMinus = '-';
890
891static const uint8_t digitInfo[] = {
892 0, 0, 0, 0, 0, 0, 0, 0,
893 0, 0, 0, 0, 0, 0, 0, 0,
894 0, 0, 0, 0, 0, 0, 0, 0,
895 0, 0, 0, 0, 0, 0, 0, 0,
896 0, 0, 0, 0, 0, 0, 0, 0,
897 0, 0, 0, 0, 0, 0, 0, 0,
898 0x80u, 0x81u, 0x82u, 0x83u, 0x84u, 0x85u, 0x86u, 0x87u,
899 0x88u, 0x89u, 0, 0, 0, 0, 0, 0,
900 0, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, 0x90u,
901 0x91u, 0x92u, 0x93u, 0x94u, 0x95u, 0x96u, 0x97u, 0x98u,
902 0x99u, 0x9au, 0x9bu, 0x9cu, 0x9du, 0x9eu, 0x9fu, 0xa0u,
903 0xa1u, 0xa2u, 0xa3u, 0, 0, 0, 0, 0,
904 0, 0x8au, 0x8bu, 0x8cu, 0x8du, 0x8eu, 0x8fu, 0x90u,
905 0x91u, 0x92u, 0x93u, 0x94u, 0x95u, 0x96u, 0x97u, 0x98u,
906 0x99u, 0x9au, 0x9bu, 0x9cu, 0x9du, 0x9eu, 0x9fu, 0xa0u,
907 0xa1u, 0xa2u, 0xa3u, 0, 0, 0, 0, 0,
908};
909
910int64_t util64_atoi(const char* str, uint32_t radix)
911{
912 if (radix > 36) {
913 radix = 36;
914 } else if (radix < 2) {
915 radix = 2;
916 }
917 int64_t lradix = radix;
918
919 int neg = 0;
920 if (*str == kMinus) {
921 ++str;
922 neg = 1;
923 }
924 int64_t result = 0;
925 uint8_t b;
926 while ((b = digitInfo[*str++]) && ((b &= 0x7f) < radix)) {
927 result *= lradix;
928 result += (int32_t)b;
929 }
930 if (neg) {
931 result = -result;
932 }
933 return result;
934}
935
936int64_t util64_utoi(const char16_t* str, uint32_t radix)
937{
938 if (radix > 36) {
939 radix = 36;
940 } else if (radix < 2) {
941 radix = 2;
942 }
943 int64_t lradix = radix;
944
945 int neg = 0;
946 if (*str == kUMinus) {
947 ++str;
948 neg = 1;
949 }
950 int64_t result = 0;
951 char16_t c;
952 uint8_t b;
953 while (((c = *str++) < 0x0080) && (b = digitInfo[c]) && ((b &= 0x7f) < radix)) {
954 result *= lradix;
955 result += (int32_t)b;
956 }
957 if (neg) {
958 result = -result;
959 }
960 return result;
961}
962
963uint32_t util64_toa(int64_t w, char* buf, uint32_t len, uint32_t radix, UBool raw)
964{
965 if (radix > 36) {
966 radix = 36;
967 } else if (radix < 2) {
968 radix = 2;
969 }
970 int64_t base = radix;
971
972 char* p = buf;
973 if (len && (w < 0) && (radix == 10) && !raw) {
974 w = -w;
975 *p++ = kMinus;
976 --len;
977 } else if (len && (w == 0)) {
978 *p++ = (char)raw ? 0 : asciiDigits[0];
979 --len;
980 }
981
982 while (len && w != 0) {
983 int64_t n = w / base;
984 int64_t m = n * base;
985 int32_t d = (int32_t)(w-m);
986 *p++ = raw ? (char)d : asciiDigits[d];
987 w = n;
988 --len;
989 }
990 if (len) {
991 *p = 0; // null terminate if room for caller convenience
992 }
993
994 len = p - buf;
995 if (*buf == kMinus) {
996 ++buf;
997 }
998 while (--p > buf) {
999 char c = *p;
1000 *p = *buf;
1001 *buf = c;
1002 ++buf;
1003 }
1004
1005 return len;
1006}
1007#endif
1008
1009uint32_t util64_tou(int64_t w, char16_t* buf, uint32_t len, uint32_t radix, UBool raw)
1010{
1011 if (radix > 36) {
1012 radix = 36;
1013 } else if (radix < 2) {
1014 radix = 2;
1015 }
1016 int64_t base = radix;
1017
1018 char16_t* p = buf;
1019 if (len && (w < 0) && (radix == 10) && !raw) {
1020 w = -w;
1021 *p++ = kUMinus;
1022 --len;
1023 } else if (len && (w == 0)) {
1024 *p++ = static_cast<char16_t>(raw) ? 0 : asciiDigits[0];
1025 --len;
1026 }
1027
1028 while (len && (w != 0)) {
1029 int64_t n = w / base;
1030 int64_t m = n * base;
1031 int32_t d = static_cast<int32_t>(w - m);
1032 *p++ = static_cast<char16_t>(raw ? d : asciiDigits[d]);
1033 w = n;
1034 --len;
1035 }
1036 if (len) {
1037 *p = 0; // null terminate if room for caller convenience
1038 }
1039
1040 len = static_cast<uint32_t>(p - buf);
1041 if (*buf == kUMinus) {
1042 ++buf;
1043 }
1044 while (--p > buf) {
1045 char16_t c = *p;
1046 *p = *buf;
1047 *buf = c;
1048 ++buf;
1049 }
1050
1051 return len;
1052}
1053
1054
1055U_NAMESPACE_END}
1056
1057/* U_HAVE_RBNF */
1058#endif