LCOV - code coverage report
Current view: top level - json/detail/charconv/detail/fast_float - bigint.hpp (source / functions) Coverage Total Hit
Test: coverage_remapped.info Lines: 89.9 % 237 213
Test Date: 2026-02-13 18:42:28 Functions: 100.0 % 36 36

            Line data    Source code
       1              : // Copyright 2020-2023 Daniel Lemire
       2              : // Copyright 2023 Matt Borland
       3              : // Distributed under the Boost Software License, Version 1.0.
       4              : // https://www.boost.org/LICENSE_1_0.txt
       5              : //
       6              : // Derivative of: https://github.com/fastfloat/fast_float
       7              : 
       8              : #ifndef BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_BIGINT_HPP
       9              : #define BOOST_JSON_DETAIL_CHARCONV_DETAIL_FASTFLOAT_BIGINT_HPP
      10              : 
      11              : #include <boost/json/detail/charconv/detail/fast_float/float_common.hpp>
      12              : #include <algorithm>
      13              : #include <cstdint>
      14              : #include <climits>
      15              : #include <cstring>
      16              : 
      17              : namespace boost { namespace json { namespace detail { namespace charconv { namespace detail { namespace fast_float {
      18              : 
      19              : // the limb width: we want efficient multiplication of double the bits in
      20              : // limb, or for 64-bit limbs, at least 64-bit multiplication where we can
      21              : // extract the high and low parts efficiently. this is every 64-bit
      22              : // architecture except for sparc, which emulates 128-bit multiplication.
      23              : // we might have platforms where `CHAR_BIT` is not 8, so let's avoid
      24              : // doing `8 * sizeof(limb)`.
      25              : #if defined(BOOST_JSON_FASTFLOAT_64BIT) && !defined(__sparc)
      26              : #define BOOST_JSON_FASTFLOAT_64BIT_LIMB 1
      27              : typedef uint64_t limb;
      28              : constexpr size_t limb_bits = 64;
      29              : #else
      30              : #define BOOST_JSON_FASTFLOAT_32BIT_LIMB
      31              : typedef uint32_t limb;
      32              : constexpr size_t limb_bits = 32;
      33              : #endif
      34              : 
      35              : typedef span<limb> limb_span;
      36              : 
      37              : // number of bits in a bigint. this needs to be at least the number
      38              : // of bits required to store the largest bigint, which is
      39              : // `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
      40              : // ~3600 bits, so we round to 4000.
      41              : constexpr size_t bigint_bits = 4000;
      42              : constexpr size_t bigint_limbs = bigint_bits / limb_bits;
      43              : 
      44              : // vector-like type that is allocated on the stack. the entire
      45              : // buffer is pre-allocated, and only the length changes.
      46              : template <uint16_t size>
      47              : struct stackvec {
      48              :   limb data[size];
      49              :   // we never need more than 150 limbs
      50              :   uint16_t length{0};
      51              : 
      52        12693 :   stackvec() = default;
      53              :   stackvec(const stackvec &) = delete;
      54              :   stackvec &operator=(const stackvec &) = delete;
      55              :   stackvec(stackvec &&) = delete;
      56              :   stackvec &operator=(stackvec &&other) = delete;
      57              : 
      58              :   // create stack vector from existing limb span.
      59         2024 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) {
      60         2024 :     try_extend(s);
      61         2024 :   }
      62              : 
      63       255428 :   BOOST_JSON_CXX14_CONSTEXPR limb& operator[](size_t index) noexcept {
      64       255428 :     BOOST_ASSERT(index < length);
      65       255428 :     return data[index];
      66              :   }
      67         6198 :   BOOST_JSON_CXX14_CONSTEXPR const limb& operator[](size_t index) const noexcept {
      68         6198 :     BOOST_ASSERT(index < length);
      69         6198 :     return data[index];
      70              :   }
      71              :   // index from the end of the container
      72         9129 :   BOOST_JSON_CXX14_CONSTEXPR const limb& rindex(size_t index) const noexcept {
      73         9129 :     BOOST_ASSERT(index < length);
      74         9129 :     size_t rindex = length - index - 1;
      75         9129 :     return data[rindex];
      76              :   }
      77              : 
      78              :   // set the length, without bounds checking.
      79        28303 :   BOOST_JSON_CXX14_CONSTEXPR void set_len(size_t len) noexcept {
      80        28303 :     length = uint16_t(len);
      81        28303 :   }
      82       259478 :   constexpr size_t len() const noexcept {
      83       259478 :     return length;
      84              :   }
      85         3789 :   constexpr bool is_empty() const noexcept {
      86         3789 :     return length == 0;
      87              :   }
      88        45829 :   constexpr size_t capacity() const noexcept {
      89        45829 :     return size;
      90              :   }
      91              :   // append item to vector, without bounds checking
      92        27233 :   BOOST_JSON_CXX14_CONSTEXPR void push_unchecked(limb value) noexcept {
      93        27233 :     data[length] = value;
      94        27233 :     length++;
      95        27233 :   }
      96              :   // append item to vector, returning if item was added
      97        25622 :   BOOST_JSON_CXX14_CONSTEXPR bool try_push(limb value) noexcept {
      98        25622 :     if (len() < capacity()) {
      99        25622 :       push_unchecked(value);
     100        25622 :       return true;
     101              :     } else {
     102            0 :       return false;
     103              :     }
     104              :   }
     105              :   // add items to the vector, from a span, without bounds checking
     106        10120 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
     107        10120 :     limb* ptr = data + length;
     108        10120 :     std::copy_n(s.ptr, s.len(), ptr);
     109        10120 :     set_len(len() + s.len());
     110        10120 :   }
     111              :   // try to add items to the vector, returning if items were added
     112        10120 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept {
     113        10120 :     if (len() + s.len() <= capacity()) {
     114        10120 :       extend_unchecked(s);
     115        10120 :       return true;
     116              :     } else {
     117            0 :       return false;
     118              :     }
     119              :   }
     120              :   // resize the vector, without bounds checking
     121              :   // if the new size is longer than the vector, assign value to each
     122              :   // appended item.
     123              :   BOOST_JSON_FASTFLOAT_CONSTEXPR20
     124         7673 :   void resize_unchecked(size_t new_len, limb value) noexcept {
     125         7673 :     if (new_len > len()) {
     126         7673 :       size_t count = new_len - len();
     127         7673 :       limb* first = data + len();
     128         7673 :       limb* last = first + count;
     129         7673 :       ::std::fill(first, last, value);
     130         7673 :       set_len(new_len);
     131              :     } else {
     132            0 :       set_len(new_len);
     133              :     }
     134         7673 :   }
     135              :   // try to resize the vector, returning if the vector was resized.
     136         7673 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
     137         7673 :     if (new_len > capacity()) {
     138            0 :       return false;
     139              :     } else {
     140         7673 :       resize_unchecked(new_len, value);
     141         7673 :       return true;
     142              :     }
     143              :   }
     144              :   // check if any limbs are non-zero after the given index.
     145              :   // this needs to be done in reverse order, since the index
     146              :   // is relative to the most significant limbs.
     147         1375 :   BOOST_JSON_CXX14_CONSTEXPR bool nonzero(size_t index) const noexcept {
     148         1375 :     while (index < len()) {
     149         1369 :       if (rindex(index) != 0) {
     150         1369 :         return true;
     151              :       }
     152            0 :       index++;
     153              :     }
     154            6 :     return false;
     155              :   }
     156              :   // normalize the big integer, so most-significant zero limbs are removed.
     157         3635 :   BOOST_JSON_CXX14_CONSTEXPR void normalize() noexcept {
     158         3635 :     while (len() > 0 && rindex(0) == 0) {
     159            0 :       length--;
     160              :     }
     161         3635 :   }
     162              : };
     163              : 
     164              : BOOST_FORCEINLINE BOOST_JSON_CXX14_CONSTEXPR_NO_INLINE
     165              : uint64_t empty_hi64(bool& truncated) noexcept {
     166            0 :   truncated = false;
     167            0 :   return 0;
     168              : }
     169              : 
     170              : BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
     171              : uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept {
     172            0 :   truncated = false;
     173            0 :   int shl = leading_zeroes(r0);
     174            0 :   return r0 << shl;
     175              : }
     176              : 
     177              : BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
     178              : uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
     179         1375 :   int shl = leading_zeroes(r0);
     180         1375 :   if (shl == 0) {
     181           27 :     truncated = r1 != 0;
     182           27 :     return r0;
     183              :   } else {
     184         1348 :     int shr = 64 - shl;
     185         1348 :     truncated = (r1 << shl) != 0;
     186         1348 :     return (r0 << shl) | (r1 >> shr);
     187              :   }
     188              : }
     189              : 
     190              : BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
     191              : uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept {
     192              :   return uint64_hi64(r0, truncated);
     193              : }
     194              : 
     195              : BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
     196              : uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept {
     197              :   uint64_t x0 = r0;
     198              :   uint64_t x1 = r1;
     199              :   return uint64_hi64((x0 << 32) | x1, truncated);
     200              : }
     201              : 
     202              : BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
     203              : uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept {
     204              :   uint64_t x0 = r0;
     205              :   uint64_t x1 = r1;
     206              :   uint64_t x2 = r2;
     207              :   return uint64_hi64(x0, (x1 << 32) | x2, truncated);
     208              : }
     209              : 
     210              : // add two small integers, checking for overflow.
     211              : // we want an efficient operation. for msvc, where
     212              : // we don't have built-in intrinsics, this is still
     213              : // pretty fast.
     214              : BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
     215              : limb scalar_add(limb x, limb y, bool& overflow) noexcept {
     216              :   limb z;
     217              : // gcc and clang
     218              : #if defined(__has_builtin)
     219              :   #if __has_builtin(__builtin_add_overflow)
     220        38490 :     if (!cpp20_and_in_constexpr()) {
     221        38490 :       overflow = __builtin_add_overflow(x, y, &z);
     222        38490 :       return z;
     223              :     }
     224              :   #endif
     225              : #endif
     226              : 
     227              :   // generic, this still optimizes correctly on MSVC.
     228            0 :   z = x + y;
     229            0 :   overflow = z < x;
     230            0 :   return z;
     231              : }
     232              : 
     233              : // multiply two small integers, getting both the high and low bits.
     234              : BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
     235              : limb scalar_mul(limb x, limb y, limb& carry) noexcept {
     236              : #ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
     237              :   #if defined(__SIZEOF_INT128__)
     238              :   // GCC and clang both define it as an extension.
     239        80707 :   __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry);
     240        80707 :   carry = limb(z >> limb_bits);
     241        80707 :   return limb(z);
     242              :   #else
     243              :   // fallback, no native 128-bit integer multiplication with carry.
     244              :   // on msvc, this optimizes identically, somehow.
     245              :   value128 z = full_multiplication(x, y);
     246              :   bool overflow;
     247              :   z.low = scalar_add(z.low, carry, overflow);
     248              :   z.high += uint64_t(overflow);  // cannot overflow
     249              :   carry = z.high;
     250              :   return z.low;
     251              :   #endif
     252              : #else
     253              :   uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry);
     254              :   carry = limb(z >> limb_bits);
     255              :   return limb(z);
     256              : #endif
     257              : }
     258              : 
     259              : // add scalar value to bigint starting from offset.
     260              : // used in grade school multiplication
     261              : template <uint16_t size>
     262              : inline BOOST_JSON_FASTFLOAT_CONSTEXPR20
     263         9437 : bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
     264         9437 :   size_t index = start;
     265         9437 :   limb carry = y;
     266              :   bool overflow;
     267        16116 :   while (carry != 0 && index < vec.len()) {
     268         6679 :     vec[index] = scalar_add(vec[index], carry, overflow);
     269         6679 :     carry = limb(overflow);
     270         6679 :     index += 1;
     271              :   }
     272         9437 :   if (carry != 0) {
     273         2986 :     BOOST_JSON_FASTFLOAT_TRY(vec.try_push(carry));
     274              :   }
     275         9437 :   return true;
     276              : }
     277              : 
     278              : // add scalar value to bigint.
     279              : template <uint16_t size>
     280              : BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
     281              : bool small_add(stackvec<size>& vec, limb y) noexcept {
     282         9437 :   return small_add_from(vec, y, 0);
     283              : }
     284              : 
     285              : // multiply bigint by scalar value.
     286              : template <uint16_t size>
     287              : inline BOOST_JSON_FASTFLOAT_CONSTEXPR20
     288        28029 : bool small_mul(stackvec<size>& vec, limb y) noexcept {
     289        28029 :   limb carry = 0;
     290       108736 :   for (size_t index = 0; index < vec.len(); index++) {
     291       161414 :     vec[index] = scalar_mul(vec[index], y, carry);
     292              :   }
     293        28029 :   if (carry != 0) {
     294        21102 :     BOOST_JSON_FASTFLOAT_TRY(vec.try_push(carry));
     295              :   }
     296        28029 :   return true;
     297              : }
     298              : 
     299              : // add bigint to bigint starting from index.
     300              : // used in grade school multiplication
     301              : template <uint16_t size>
     302              : BOOST_JSON_FASTFLOAT_CONSTEXPR20
     303         8096 : bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
     304              :   // the effective x buffer is from `xstart..x.len()`, so exit early
     305              :   // if we can't get that current range.
     306         8096 :   if (x.len() < start || y.len() > x.len() - start) {
     307         7673 :       BOOST_JSON_FASTFLOAT_TRY(x.try_resize(y.len() + start, 0));
     308              :   }
     309              : 
     310         8096 :   bool carry = false;
     311        34341 :   for (size_t index = 0; index < y.len(); index++) {
     312        26245 :     limb xi = x[index + start];
     313        26245 :     limb yi = y[index];
     314        26245 :     bool c1 = false;
     315        26245 :     bool c2 = false;
     316        26245 :     xi = scalar_add(xi, yi, c1);
     317        26245 :     if (carry) {
     318         5566 :       xi = scalar_add(xi, 1, c2);
     319              :     }
     320        26245 :     x[index + start] = xi;
     321        26245 :     carry = c1 | c2;
     322              :   }
     323              : 
     324              :   // handle overflow
     325         8096 :   if (carry) {
     326            0 :     BOOST_JSON_FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start));
     327              :   }
     328         8096 :   return true;
     329              : }
     330              : 
     331              : // add bigint to bigint.
     332              : template <uint16_t size>
     333              : BOOST_FORCEINLINE BOOST_JSON_FASTFLOAT_CONSTEXPR20
     334              : bool large_add_from(stackvec<size>& x, limb_span y) noexcept {
     335              :   return large_add_from(x, y, 0);
     336              : }
     337              : 
     338              : // grade-school multiplication algorithm
     339              : template <uint16_t size>
     340              : BOOST_JSON_FASTFLOAT_CONSTEXPR20
     341         2024 : bool long_mul(stackvec<size>& x, limb_span y) noexcept {
     342         2024 :   limb_span xs = limb_span(x.data, x.len());
     343         2024 :   stackvec<size> z(xs);
     344         2024 :   limb_span zs = limb_span(z.data, z.len());
     345              : 
     346         2024 :   if (y.len() != 0) {
     347         2024 :     limb y0 = y[0];
     348         2024 :     BOOST_JSON_FASTFLOAT_TRY(small_mul(x, y0));
     349        10120 :     for (size_t index = 1; index < y.len(); index++) {
     350         8096 :       limb yi = y[index];
     351         8096 :       stackvec<size> zi;
     352         8096 :       if (yi != 0) {
     353              :         // re-use the same buffer throughout
     354         8096 :         zi.set_len(0);
     355         8096 :         BOOST_JSON_FASTFLOAT_TRY(zi.try_extend(zs));
     356         8096 :         BOOST_JSON_FASTFLOAT_TRY(small_mul(zi, yi));
     357         8096 :         limb_span zis = limb_span(zi.data, zi.len());
     358         8096 :         BOOST_JSON_FASTFLOAT_TRY(large_add_from(x, zis, index));
     359              :       }
     360              :     }
     361              :   }
     362              : 
     363         2024 :   x.normalize();
     364         2024 :   return true;
     365              : }
     366              : 
     367              : // grade-school multiplication algorithm
     368              : template <uint16_t size>
     369              : BOOST_JSON_FASTFLOAT_CONSTEXPR20
     370         2024 : bool large_mul(stackvec<size>& x, limb_span y) noexcept {
     371         2024 :   if (y.len() == 1) {
     372            0 :     BOOST_JSON_FASTFLOAT_TRY(small_mul(x, y[0]));
     373              :   } else {
     374         2024 :     BOOST_JSON_FASTFLOAT_TRY(long_mul(x, y));
     375              :   }
     376         2024 :   return true;
     377              : }
     378              : 
     379              : template <typename = void>
     380              : struct pow5_tables {
     381              :   static constexpr uint32_t large_step = 135;
     382              :   static constexpr uint64_t small_power_of_5[] = {
     383              :     1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL,
     384              :     1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL,
     385              :     6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL,
     386              :     3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL,
     387              :     2384185791015625UL, 11920928955078125UL, 59604644775390625UL,
     388              :     298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL,
     389              :   };
     390              : #ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
     391              :   constexpr static limb large_power_of_5[] = {
     392              :     1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
     393              :     10482974169319127550UL, 198276706040285095UL};
     394              : #else
     395              :   constexpr static limb large_power_of_5[] = {
     396              :     4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
     397              :     1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
     398              : #endif
     399              : };
     400              : 
     401              : template <typename T>
     402              : constexpr uint32_t pow5_tables<T>::large_step;
     403              : 
     404              : template <typename T>
     405              : constexpr uint64_t pow5_tables<T>::small_power_of_5[];
     406              : 
     407              : template <typename T>
     408              : constexpr limb pow5_tables<T>::large_power_of_5[];
     409              : 
     410              : // big integer type. implements a small subset of big integer
     411              : // arithmetic, using simple algorithms since asymptotically
     412              : // faster algorithms are slower for a small number of limbs.
     413              : // all operations assume the big-integer is normalized.
     414              : struct bigint : pow5_tables<> {
     415              :   // storage of the limbs, in little-endian order.
     416              :   stackvec<bigint_limbs> vec;
     417              : 
     418         2986 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 bigint(): vec() {}
     419              :   bigint(const bigint &) = delete;
     420              :   bigint &operator=(const bigint &) = delete;
     421              :   bigint(bigint &&) = delete;
     422              :   bigint &operator=(bigint &&other) = delete;
     423              : 
     424         1611 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 bigint(uint64_t value): vec() {
     425              : #ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
     426         1611 :     vec.push_unchecked(value);
     427              : #else
     428              :     vec.push_unchecked(uint32_t(value));
     429              :     vec.push_unchecked(uint32_t(value >> 32));
     430              : #endif
     431         1611 :     vec.normalize();
     432         1611 :   }
     433              : 
     434              :   // get the high 64 bits from the vector, and if bits were truncated.
     435              :   // this is to get the significant digits for the float.
     436         1375 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool& truncated) const noexcept {
     437              : #ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
     438         1375 :     if (vec.len() == 0) {
     439            0 :       return empty_hi64(truncated);
     440         1375 :     } else if (vec.len() == 1) {
     441            0 :       return uint64_hi64(vec.rindex(0), truncated);
     442              :     } else {
     443         1375 :       uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated);
     444         1375 :       truncated |= vec.nonzero(2);
     445         1375 :       return result;
     446              :     }
     447              : #else
     448              :     if (vec.len() == 0) {
     449              :       return empty_hi64(truncated);
     450              :     } else if (vec.len() == 1) {
     451              :       return uint32_hi64(vec.rindex(0), truncated);
     452              :     } else if (vec.len() == 2) {
     453              :       return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated);
     454              :     } else {
     455              :       uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated);
     456              :       truncated |= vec.nonzero(3);
     457              :       return result;
     458              :     }
     459              : #endif
     460              :   }
     461              : 
     462              :   // compare two big integers, returning the large value.
     463              :   // assumes both are normalized. if the return value is
     464              :   // negative, other is larger, if the return value is
     465              :   // positive, this is larger, otherwise they are equal.
     466              :   // the limbs are stored in little-endian order, so we
     467              :   // must compare the limbs in ever order.
     468         1611 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 int compare(const bigint& other) const noexcept {
     469         1611 :     if (vec.len() > other.vec.len()) {
     470            0 :       return 1;
     471         1611 :     } else if (vec.len() < other.vec.len()) {
     472            0 :       return -1;
     473              :     } else {
     474         3099 :       for (size_t index = vec.len(); index > 0; index--) {
     475         3099 :         limb xi = vec[index - 1];
     476         3099 :         limb yi = other.vec[index - 1];
     477         3099 :         if (xi > yi) {
     478          767 :           return 1;
     479         2332 :         } else if (xi < yi) {
     480          844 :           return -1;
     481              :         }
     482              :       }
     483            0 :       return 0;
     484              :     }
     485              :   }
     486              : 
     487              :   // shift left each limb n bits, carrying over to the new limb
     488              :   // returns true if we were able to shift all the digits.
     489         2931 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept {
     490              :     // Internally, for each item, we shift left by n, and add the previous
     491              :     // right shifted limb-bits.
     492              :     // For example, we transform (for u8) shifted left 2, to:
     493              :     //      b10100100 b01000010
     494              :     //      b10 b10010001 b00001000
     495         2931 :     BOOST_ASSERT(n != 0);
     496         2931 :     BOOST_ASSERT(n < sizeof(limb) * 8);
     497              : 
     498         2931 :     size_t shl = n;
     499         2931 :     size_t shr = limb_bits - shl;
     500         2931 :     limb prev = 0;
     501        17014 :     for (size_t index = 0; index < vec.len(); index++) {
     502        14083 :       limb xi = vec[index];
     503        14083 :       vec[index] = (xi << shl) | (prev >> shr);
     504        14083 :       prev = xi;
     505              :     }
     506              : 
     507         2931 :     limb carry = prev >> shr;
     508         2931 :     if (carry != 0) {
     509         1534 :       return vec.try_push(carry);
     510              :     }
     511         1397 :     return true;
     512              :   }
     513              : 
     514              :   // move the limbs left by `n` limbs.
     515         2414 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept {
     516         2414 :     BOOST_ASSERT(n != 0);
     517         2414 :     if (n + vec.len() > vec.capacity()) {
     518            0 :       return false;
     519         2414 :     } else if (!vec.is_empty()) {
     520              :       // move limbs
     521         2414 :       limb* dst = vec.data + n;
     522         2414 :       const limb* src = vec.data;
     523         2414 :       std::copy_backward(src, src + vec.len(), dst + vec.len());
     524              :       // fill in empty limbs
     525         2414 :       limb* first = vec.data;
     526         2414 :       limb* last = first + n;
     527         2414 :       ::std::fill(first, last, 0);
     528         2414 :       vec.set_len(n + vec.len());
     529         2414 :       return true;
     530              :     } else {
     531            0 :       return true;
     532              :     }
     533              :   }
     534              : 
     535              :   // move the limbs left by `n` bits.
     536         2984 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept {
     537         2984 :     size_t rem = n % limb_bits;
     538         2984 :     size_t div = n / limb_bits;
     539         2984 :     if (rem != 0) {
     540         2931 :       BOOST_JSON_FASTFLOAT_TRY(shl_bits(rem));
     541              :     }
     542         2984 :     if (div != 0) {
     543         2414 :       BOOST_JSON_FASTFLOAT_TRY(shl_limbs(div));
     544              :     }
     545         2984 :     return true;
     546              :   }
     547              : 
     548              :   // get the number of leading zeros in the bigint.
     549         1375 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept {
     550         1375 :     if (vec.is_empty()) {
     551            0 :       return 0;
     552              :     } else {
     553              : #ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
     554         2750 :       return leading_zeroes(vec.rindex(0));
     555              : #else
     556              :       // no use defining a specialized leading_zeroes for a 32-bit type.
     557              :       uint64_t r0 = vec.rindex(0);
     558              :       return leading_zeroes(r0 << 32);
     559              : #endif
     560              :     }
     561              :   }
     562              : 
     563              :   // get the number of bits in the bigint.
     564         1375 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept {
     565         1375 :     int lz = ctlz();
     566         1375 :     return int(limb_bits * vec.len()) - lz;
     567              :   }
     568              : 
     569         9437 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept {
     570         9437 :     return small_mul(vec, y);
     571              :   }
     572              : 
     573         9437 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept {
     574        18874 :     return small_add(vec, y);
     575              :   }
     576              : 
     577              :   // multiply as if by 2 raised to a power.
     578         2984 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept {
     579         2984 :     return shl(exp);
     580              :   }
     581              : 
     582              :   // multiply as if by 5 raised to a power.
     583         2986 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
     584              :     // multiply by a power of 5
     585         2986 :     constexpr size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
     586         2986 :     limb_span large = limb_span(large_power_of_5, large_length);
     587         5010 :     while (exp >= large_step) {
     588         2024 :       BOOST_JSON_FASTFLOAT_TRY(large_mul(vec, large));
     589         2024 :       exp -= large_step;
     590              :     }
     591              : #ifdef BOOST_JSON_FASTFLOAT_64BIT_LIMB
     592         2986 :     constexpr uint32_t small_step = 27;
     593         2986 :     constexpr limb max_native = 7450580596923828125UL;
     594              : #else
     595              :     constexpr uint32_t small_step = 13;
     596              :     constexpr limb max_native = 1220703125U;
     597              : #endif
     598         8590 :     while (exp >= small_step) {
     599         5604 :       BOOST_JSON_FASTFLOAT_TRY(small_mul(vec, max_native));
     600         5604 :       exp -= small_step;
     601              :     }
     602         2986 :     if (exp != 0) {
     603              :       // Work around clang bug https://godbolt.org/z/zedh7rrhc
     604              :       // This is similar to https://github.com/llvm/llvm-project/issues/47746,
     605              :       // except the workaround described there don't work here
     606         2868 :       BOOST_JSON_FASTFLOAT_TRY(
     607              :         small_mul(vec, limb(((void)small_power_of_5[0], small_power_of_5[exp])))
     608              :       );
     609              :     }
     610              : 
     611         2986 :     return true;
     612              :   }
     613              : 
     614              :   // multiply as if by 10 raised to a power.
     615         1375 :   BOOST_JSON_FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept {
     616         1375 :     BOOST_JSON_FASTFLOAT_TRY(pow5(exp));
     617         1375 :     return pow2(exp);
     618              :   }
     619              : };
     620              : 
     621              : }}}}}} // namespace fast_float
     622              : 
     623              : #endif
        

Generated by: LCOV version 2.3