• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2017 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "xfa/fgas/crt/cfgas_decimal.h"
8 
9 #include <algorithm>
10 #include <limits>
11 #include <utility>
12 
13 #include "core/fxcrt/fx_extension.h"
14 
15 #define FXMATH_DECIMAL_SCALELIMIT 0x1c
16 #define FXMATH_DECIMAL_RSHIFT32BIT(x) ((x) >> 0x10 >> 0x10)
17 #define FXMATH_DECIMAL_LSHIFT32BIT(x) ((x) << 0x10 << 0x10)
18 
19 namespace {
20 
decimal_helper_div10(uint64_t & phi,uint64_t & pmid,uint64_t & plo)21 inline uint8_t decimal_helper_div10(uint64_t& phi,
22                                     uint64_t& pmid,
23                                     uint64_t& plo) {
24   uint8_t retVal;
25   pmid += FXMATH_DECIMAL_LSHIFT32BIT(phi % 0xA);
26   phi /= 0xA;
27   plo += FXMATH_DECIMAL_LSHIFT32BIT(pmid % 0xA);
28   pmid /= 0xA;
29   retVal = plo % 0xA;
30   plo /= 0xA;
31   return retVal;
32 }
33 
decimal_helper_div10_any(uint64_t nums[],uint8_t numcount)34 inline uint8_t decimal_helper_div10_any(uint64_t nums[], uint8_t numcount) {
35   uint8_t retVal = 0;
36   for (int i = numcount - 1; i > 0; i--) {
37     nums[i - 1] += FXMATH_DECIMAL_LSHIFT32BIT(nums[i] % 0xA);
38     nums[i] /= 0xA;
39   }
40   if (numcount) {
41     retVal = nums[0] % 0xA;
42     nums[0] /= 0xA;
43   }
44   return retVal;
45 }
46 
decimal_helper_mul10(uint64_t & phi,uint64_t & pmid,uint64_t & plo)47 inline void decimal_helper_mul10(uint64_t& phi, uint64_t& pmid, uint64_t& plo) {
48   plo *= 0xA;
49   pmid = pmid * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(plo);
50   plo = (uint32_t)plo;
51   phi = phi * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(pmid);
52   pmid = (uint32_t)pmid;
53 }
54 
decimal_helper_mul10_any(uint64_t nums[],uint8_t numcount)55 inline void decimal_helper_mul10_any(uint64_t nums[], uint8_t numcount) {
56   nums[0] *= 0xA;
57   for (int i = 1; i < numcount; i++) {
58     nums[i] = nums[i] * 0xA + FXMATH_DECIMAL_RSHIFT32BIT(nums[i - 1]);
59     nums[i - 1] = (uint32_t)nums[i - 1];
60   }
61 }
62 
decimal_helper_normalize(uint64_t & phi,uint64_t & pmid,uint64_t & plo)63 inline void decimal_helper_normalize(uint64_t& phi,
64                                      uint64_t& pmid,
65                                      uint64_t& plo) {
66   phi += FXMATH_DECIMAL_RSHIFT32BIT(pmid);
67   pmid = (uint32_t)pmid;
68   pmid += FXMATH_DECIMAL_RSHIFT32BIT(plo);
69   plo = (uint32_t)plo;
70   phi += FXMATH_DECIMAL_RSHIFT32BIT(pmid);
71   pmid = (uint32_t)pmid;
72 }
73 
decimal_helper_normalize_any(uint64_t nums[],uint8_t len)74 inline void decimal_helper_normalize_any(uint64_t nums[], uint8_t len) {
75   for (int i = len - 2; i > 0; i--) {
76     nums[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(nums[i]);
77     nums[i] = (uint32_t)nums[i];
78   }
79   for (int i = 0; i < len - 1; i++) {
80     nums[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(nums[i]);
81     nums[i] = (uint32_t)nums[i];
82   }
83 }
84 
decimal_helper_raw_compare_any(uint64_t a[],uint8_t al,uint64_t b[],uint8_t bl)85 inline int8_t decimal_helper_raw_compare_any(uint64_t a[],
86                                              uint8_t al,
87                                              uint64_t b[],
88                                              uint8_t bl) {
89   int8_t retVal = 0;
90   for (int i = std::max(al - 1, bl - 1); i >= 0; i--) {
91     uint64_t l = (i >= al ? 0 : a[i]), r = (i >= bl ? 0 : b[i]);
92     retVal += (l > r ? 1 : (l < r ? -1 : 0));
93     if (retVal)
94       return retVal;
95   }
96   return retVal;
97 }
98 
decimal_helper_dec_any(uint64_t a[],uint8_t al)99 inline void decimal_helper_dec_any(uint64_t a[], uint8_t al) {
100   for (int i = 0; i < al; i++) {
101     if (a[i]--)
102       return;
103   }
104 }
105 
decimal_helper_inc_any(uint64_t a[],uint8_t al)106 inline void decimal_helper_inc_any(uint64_t a[], uint8_t al) {
107   for (int i = 0; i < al; i++) {
108     a[i]++;
109     if ((uint32_t)a[i] == a[i])
110       return;
111     a[i] = 0;
112   }
113 }
114 
decimal_helper_raw_mul(uint64_t a[],uint8_t al,uint64_t b[],uint8_t bl,uint64_t c[],uint8_t cl)115 inline void decimal_helper_raw_mul(uint64_t a[],
116                                    uint8_t al,
117                                    uint64_t b[],
118                                    uint8_t bl,
119                                    uint64_t c[],
120                                    uint8_t cl) {
121   ASSERT(al + bl <= cl);
122   for (int i = 0; i < cl; i++)
123     c[i] = 0;
124 
125   for (int i = 0; i < al; i++) {
126     for (int j = 0; j < bl; j++) {
127       uint64_t m = (uint64_t)a[i] * b[j];
128       c[i + j] += (uint32_t)m;
129       c[i + j + 1] += FXMATH_DECIMAL_RSHIFT32BIT(m);
130     }
131   }
132   for (int i = 0; i < cl - 1; i++) {
133     c[i + 1] += FXMATH_DECIMAL_RSHIFT32BIT(c[i]);
134     c[i] = (uint32_t)c[i];
135   }
136   for (int i = 0; i < cl; i++)
137     c[i] = (uint32_t)c[i];
138 }
139 
decimal_helper_raw_div(uint64_t a[],uint8_t al,uint64_t b[],uint8_t bl,uint64_t c[],uint8_t cl)140 inline void decimal_helper_raw_div(uint64_t a[],
141                                    uint8_t al,
142                                    uint64_t b[],
143                                    uint8_t bl,
144                                    uint64_t c[],
145                                    uint8_t cl) {
146   for (int i = 0; i < cl; i++)
147     c[i] = 0;
148 
149   uint64_t left[16] = {0};
150   uint64_t right[16] = {0};
151   left[0] = 0;
152   for (int i = 0; i < al; i++)
153     right[i] = a[i];
154 
155   uint64_t tmp[16];
156   while (decimal_helper_raw_compare_any(left, al, right, al) <= 0) {
157     uint64_t cur[16];
158     for (int i = 0; i < al; i++)
159       cur[i] = left[i] + right[i];
160 
161     for (int i = al - 1; i >= 0; i--) {
162       if (i)
163         cur[i - 1] += FXMATH_DECIMAL_LSHIFT32BIT(cur[i] % 2);
164       cur[i] /= 2;
165     }
166 
167     decimal_helper_raw_mul(cur, al, b, bl, tmp, 16);
168     switch (decimal_helper_raw_compare_any(tmp, 16, a, al)) {
169       case -1:
170         for (int i = 0; i < 16; i++)
171           left[i] = cur[i];
172 
173         left[0]++;
174         decimal_helper_normalize_any(left, al);
175         break;
176       case 1:
177         for (int i = 0; i < 16; i++)
178           right[i] = cur[i];
179         decimal_helper_dec_any(right, al);
180         break;
181       case 0:
182         for (int i = 0; i < std::min(al, cl); i++)
183           c[i] = cur[i];
184         return;
185     }
186   }
187   for (int i = 0; i < std::min(al, cl); i++)
188     c[i] = left[i];
189 }
190 
decimal_helper_outofrange(uint64_t a[],uint8_t al,uint8_t goal)191 inline bool decimal_helper_outofrange(uint64_t a[], uint8_t al, uint8_t goal) {
192   for (int i = goal; i < al; i++) {
193     if (a[i])
194       return true;
195   }
196   return false;
197 }
198 
decimal_helper_shrinkintorange(uint64_t a[],uint8_t al,uint8_t goal,uint8_t & scale)199 inline void decimal_helper_shrinkintorange(uint64_t a[],
200                                            uint8_t al,
201                                            uint8_t goal,
202                                            uint8_t& scale) {
203   bool bRoundUp = false;
204   while (scale != 0 && (scale > FXMATH_DECIMAL_SCALELIMIT ||
205                         decimal_helper_outofrange(a, al, goal))) {
206     bRoundUp = decimal_helper_div10_any(a, al) >= 5;
207     scale--;
208   }
209   if (bRoundUp) {
210     decimal_helper_normalize_any(a, goal);
211     decimal_helper_inc_any(a, goal);
212   }
213 }
214 
decimal_helper_truncate(uint64_t & phi,uint64_t & pmid,uint64_t & plo,uint8_t & scale,uint8_t minscale=0)215 inline void decimal_helper_truncate(uint64_t& phi,
216                                     uint64_t& pmid,
217                                     uint64_t& plo,
218                                     uint8_t& scale,
219                                     uint8_t minscale = 0) {
220   while (scale > minscale) {
221     uint64_t thi = phi, tmid = pmid, tlo = plo;
222     if (decimal_helper_div10(thi, tmid, tlo) != 0)
223       break;
224 
225     phi = thi;
226     pmid = tmid;
227     plo = tlo;
228     scale--;
229   }
230 }
231 
232 }  // namespace
233 
234 CFGAS_Decimal::CFGAS_Decimal() = default;
235 
CFGAS_Decimal(uint64_t val)236 CFGAS_Decimal::CFGAS_Decimal(uint64_t val)
237     : m_uMid(static_cast<uint32_t>(FXMATH_DECIMAL_RSHIFT32BIT(val))),
238       m_uLo(static_cast<uint32_t>(val)) {}
239 
CFGAS_Decimal(uint32_t val)240 CFGAS_Decimal::CFGAS_Decimal(uint32_t val)
241     : m_uLo(static_cast<uint32_t>(val)) {}
242 
CFGAS_Decimal(uint32_t lo,uint32_t mid,uint32_t hi,bool neg,uint8_t scale)243 CFGAS_Decimal::CFGAS_Decimal(uint32_t lo,
244                              uint32_t mid,
245                              uint32_t hi,
246                              bool neg,
247                              uint8_t scale)
248     : m_uHi(hi),
249       m_uMid(mid),
250       m_uLo(lo),
251       m_bNeg(neg && IsNotZero()),
252       m_uScale(scale > FXMATH_DECIMAL_SCALELIMIT ? 0 : scale) {}
253 
CFGAS_Decimal(int32_t val)254 CFGAS_Decimal::CFGAS_Decimal(int32_t val) {
255   if (val >= 0) {
256     *this = CFGAS_Decimal(static_cast<uint32_t>(val));
257   } else if (val == std::numeric_limits<int32_t>::min()) {
258     *this = CFGAS_Decimal(static_cast<uint32_t>(val));
259     SetNegate();
260   } else {
261     *this = CFGAS_Decimal(static_cast<uint32_t>(-val));
262     SetNegate();
263   }
264 }
265 
CFGAS_Decimal(float val,uint8_t scale)266 CFGAS_Decimal::CFGAS_Decimal(float val, uint8_t scale) {
267   float newval = fabs(val);
268   float divisor = powf(2.0, 64.0f);
269   uint64_t bottom64 = static_cast<uint64_t>(fmodf(newval, divisor));
270   uint64_t top64 = static_cast<uint64_t>(newval / divisor);
271   uint64_t plo = bottom64 & 0xFFFFFFFF;
272   uint64_t pmid = bottom64 >> 32;
273   uint64_t phi = top64 & 0xFFFFFFFF;
274 
275   newval = fmodf(newval, 1.0f);
276   for (uint8_t iter = 0; iter < scale; iter++) {
277     decimal_helper_mul10(phi, pmid, plo);
278     newval *= 10;
279     plo += static_cast<uint64_t>(newval);
280     newval = fmodf(newval, 1.0f);
281   }
282 
283   plo += FXSYS_roundf(newval);
284   decimal_helper_normalize(phi, pmid, plo);
285   m_uHi = static_cast<uint32_t>(phi);
286   m_uMid = static_cast<uint32_t>(pmid);
287   m_uLo = static_cast<uint32_t>(plo);
288   m_bNeg = val < 0 && IsNotZero();
289   m_uScale = scale;
290 }
291 
CFGAS_Decimal(WideStringView strObj)292 CFGAS_Decimal::CFGAS_Decimal(WideStringView strObj) {
293   const wchar_t* str = strObj.unterminated_c_str();
294   const wchar_t* strBound = str + strObj.GetLength();
295   bool pointmet = false;
296   bool negmet = false;
297   uint8_t scale = 0;
298   m_uHi = 0;
299   m_uMid = 0;
300   m_uLo = 0;
301   while (str != strBound && *str == ' ')
302     str++;
303   if (str != strBound && *str == '-') {
304     negmet = 1;
305     str++;
306   } else if (str != strBound && *str == '+') {
307     str++;
308   }
309 
310   while (str != strBound && (FXSYS_IsDecimalDigit(*str) || *str == '.') &&
311          scale < FXMATH_DECIMAL_SCALELIMIT) {
312     if (*str == '.') {
313       if (!pointmet)
314         pointmet = 1;
315     } else {
316       m_uHi = m_uHi * 0xA + FXMATH_DECIMAL_RSHIFT32BIT((uint64_t)m_uMid * 0xA);
317       m_uMid = m_uMid * 0xA + FXMATH_DECIMAL_RSHIFT32BIT((uint64_t)m_uLo * 0xA);
318       m_uLo = m_uLo * 0xA + (*str - '0');
319       if (pointmet)
320         scale++;
321     }
322     str++;
323   }
324   m_bNeg = negmet && IsNotZero();
325   m_uScale = scale;
326 }
327 
ToWideString() const328 WideString CFGAS_Decimal::ToWideString() const {
329   WideString retString;
330   WideString tmpbuf;
331   uint64_t phi = m_uHi;
332   uint64_t pmid = m_uMid;
333   uint64_t plo = m_uLo;
334   while (phi || pmid || plo)
335     tmpbuf += decimal_helper_div10(phi, pmid, plo) + '0';
336 
337   uint8_t outputlen = (uint8_t)tmpbuf.GetLength();
338   uint8_t scale = m_uScale;
339   while (scale >= outputlen) {
340     tmpbuf += '0';
341     outputlen++;
342   }
343   if (m_bNeg && IsNotZero())
344     retString += '-';
345 
346   for (uint8_t idx = 0; idx < outputlen; idx++) {
347     if (idx == (outputlen - scale) && scale != 0)
348       retString += '.';
349     retString += tmpbuf[outputlen - 1 - idx];
350   }
351   return retString;
352 }
353 
ToFloat() const354 float CFGAS_Decimal::ToFloat() const {
355   return static_cast<float>(ToDouble());
356 }
357 
ToDouble() const358 double CFGAS_Decimal::ToDouble() const {
359   double pow = (double)(1 << 16) * (1 << 16);
360   double base = static_cast<double>(m_uHi) * pow * pow +
361                 static_cast<double>(m_uMid) * pow + static_cast<double>(m_uLo);
362   return (m_bNeg ? -1 : 1) * base * powf(10.0f, -m_uScale);
363 }
364 
SetScale(uint8_t newscale)365 void CFGAS_Decimal::SetScale(uint8_t newscale) {
366   uint8_t oldscale = m_uScale;
367   if (oldscale == newscale)
368     return;
369 
370   uint64_t phi = m_uHi;
371   uint64_t pmid = m_uMid;
372   uint64_t plo = m_uLo;
373   if (newscale > oldscale) {
374     for (uint8_t iter = 0; iter < newscale - oldscale; iter++)
375       decimal_helper_mul10(phi, pmid, plo);
376 
377     m_uHi = static_cast<uint32_t>(phi);
378     m_uMid = static_cast<uint32_t>(pmid);
379     m_uLo = static_cast<uint32_t>(plo);
380     m_bNeg = m_bNeg && IsNotZero();
381     m_uScale = newscale;
382   } else {
383     uint64_t point5_hi = 0;
384     uint64_t point5_mid = 0;
385     uint64_t point5_lo = 5;
386     for (uint8_t iter = 0; iter < oldscale - newscale - 1; iter++)
387       decimal_helper_mul10(point5_hi, point5_mid, point5_lo);
388 
389     phi += point5_hi;
390     pmid += point5_mid;
391     plo += point5_lo;
392     decimal_helper_normalize(phi, pmid, plo);
393     for (uint8_t iter = 0; iter < oldscale - newscale; iter++)
394       decimal_helper_div10(phi, pmid, plo);
395   }
396   m_uHi = static_cast<uint32_t>(phi);
397   m_uMid = static_cast<uint32_t>(pmid);
398   m_uLo = static_cast<uint32_t>(plo);
399   m_bNeg = m_bNeg && IsNotZero();
400   m_uScale = newscale;
401 }
402 
SetNegate()403 void CFGAS_Decimal::SetNegate() {
404   if (IsNotZero())
405     m_bNeg = !m_bNeg;
406 }
407 
operator *(const CFGAS_Decimal & val) const408 CFGAS_Decimal CFGAS_Decimal::operator*(const CFGAS_Decimal& val) const {
409   uint64_t a[3] = {m_uLo, m_uMid, m_uHi},
410            b[3] = {val.m_uLo, val.m_uMid, val.m_uHi};
411   uint64_t c[6];
412   decimal_helper_raw_mul(a, 3, b, 3, c, 6);
413   bool neg = m_bNeg ^ val.m_bNeg;
414   uint8_t scale = m_uScale + val.m_uScale;
415   decimal_helper_shrinkintorange(c, 6, 3, scale);
416   return CFGAS_Decimal(static_cast<uint32_t>(c[0]), static_cast<uint32_t>(c[1]),
417                        static_cast<uint32_t>(c[2]), neg, scale);
418 }
419 
operator /(const CFGAS_Decimal & val) const420 CFGAS_Decimal CFGAS_Decimal::operator/(const CFGAS_Decimal& val) const {
421   if (!val.IsNotZero())
422     return CFGAS_Decimal();
423 
424   bool neg = m_bNeg ^ val.m_bNeg;
425   uint64_t a[7] = {m_uLo, m_uMid, m_uHi},
426            b[3] = {val.m_uLo, val.m_uMid, val.m_uHi}, c[7] = {0};
427   uint8_t scale = 0;
428   if (m_uScale < val.m_uScale) {
429     for (int i = val.m_uScale - m_uScale; i > 0; i--)
430       decimal_helper_mul10_any(a, 7);
431   } else {
432     scale = m_uScale - val.m_uScale;
433   }
434 
435   uint8_t minscale = scale;
436   if (!IsNotZero())
437     return CFGAS_Decimal(0, 0, 0, 0, minscale);
438 
439   while (!a[6]) {
440     decimal_helper_mul10_any(a, 7);
441     scale++;
442   }
443 
444   decimal_helper_div10_any(a, 7);
445   scale--;
446   decimal_helper_raw_div(a, 6, b, 3, c, 7);
447   decimal_helper_shrinkintorange(c, 6, 3, scale);
448   decimal_helper_truncate(c[2], c[1], c[0], scale, minscale);
449   return CFGAS_Decimal(static_cast<uint32_t>(c[0]), static_cast<uint32_t>(c[1]),
450                        static_cast<uint32_t>(c[2]), neg, scale);
451 }
452