| /* |
| * Copyright (C) 2017 Caio Lima <ticaiolima@gmail.com> |
| * Copyright (C) 2017 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
| * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * Parts of the implementation below: |
| * |
| * Copyright 2017 the V8 project authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| * |
| * |
| * Copyright (c) 2014 the Dart project authors. Please see the AUTHORS file [1] |
| * for details. All rights reserved. Use of this source code is governed by a |
| * BSD-style license that can be found in the LICENSE file [2]. |
| * |
| * [1] https://github.com/dart-lang/sdk/blob/master/AUTHORS |
| * [2] https://github.com/dart-lang/sdk/blob/master/LICENSE |
| * |
| * Copyright 2009 The Go Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style |
| * license that can be found in the LICENSE file [3]. |
| * |
| * [3] https://golang.org/LICENSE |
| */ |
| |
| #include "config.h" |
| #include "JSBigInt.h" |
| |
| #include "BigIntObject.h" |
| #include "CatchScope.h" |
| #include "JSCInlines.h" |
| #include "MathCommon.h" |
| #include "ParseInt.h" |
| #include <algorithm> |
| #include <wtf/text/StringVector.h> |
| |
| #define STATIC_ASSERT(cond) static_assert(cond, "JSBigInt assumes " #cond) |
| |
| namespace JSC { |
| |
| const ClassInfo JSBigInt::s_info = |
| { "JSBigInt", nullptr, nullptr, nullptr, CREATE_METHOD_TABLE(JSBigInt) }; |
| |
| void JSBigInt::visitChildren(JSCell* cell, SlotVisitor& visitor) |
| { |
| JSBigInt* thisObject = jsCast<JSBigInt*>(cell); |
| ASSERT_GC_OBJECT_INHERITS(thisObject, info()); |
| Base::visitChildren(thisObject, visitor); |
| } |
| |
| JSBigInt::JSBigInt(VM& vm, Structure* structure, int length) |
| : Base(vm, structure) |
| , m_length(length) |
| { } |
| |
| void JSBigInt::initialize(InitializationType initType) |
| { |
| setSign(false); |
| if (initType == InitializationType::WithZero) |
| memset(dataStorage(), 0, length() * sizeof(Digit)); |
| } |
| |
| Structure* JSBigInt::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) |
| { |
| return Structure::create(vm, globalObject, prototype, TypeInfo(BigIntType, StructureFlags), info()); |
| } |
| |
| JSBigInt* JSBigInt::createZero(VM& vm) |
| { |
| JSBigInt* zeroBigInt = createWithLength(vm, 0); |
| zeroBigInt->setSign(false); |
| return zeroBigInt; |
| } |
| |
| size_t JSBigInt::allocationSize(int length) |
| { |
| size_t sizeWithPadding = WTF::roundUpToMultipleOf<sizeof(size_t)>(sizeof(JSBigInt)); |
| return sizeWithPadding + length * sizeof(Digit); |
| } |
| |
| JSBigInt* JSBigInt::createWithLength(VM& vm, int length) |
| { |
| JSBigInt* bigInt = new (NotNull, allocateCell<JSBigInt>(vm.heap, allocationSize(length))) JSBigInt(vm, vm.bigIntStructure.get(), length); |
| bigInt->finishCreation(vm); |
| return bigInt; |
| } |
| |
| void JSBigInt::finishCreation(VM& vm) |
| { |
| Base::finishCreation(vm); |
| } |
| |
| |
| JSBigInt* JSBigInt::createFrom(VM& vm, int32_t value) |
| { |
| if (!value) |
| return createZero(vm); |
| |
| JSBigInt* bigInt = createWithLength(vm, 1); |
| |
| if (value < 0) { |
| bigInt->setDigit(0, static_cast<Digit>(-1 * static_cast<int64_t>(value))); |
| bigInt->setSign(true); |
| } else { |
| bigInt->setDigit(0, static_cast<Digit>(value)); |
| bigInt->setSign(false); |
| } |
| |
| return bigInt; |
| } |
| |
| JSBigInt* JSBigInt::createFrom(VM& vm, uint32_t value) |
| { |
| if (!value) |
| return createZero(vm); |
| |
| JSBigInt* bigInt = createWithLength(vm, 1); |
| bigInt->setDigit(0, static_cast<Digit>(value)); |
| bigInt->setSign(false); |
| return bigInt; |
| } |
| |
| JSBigInt* JSBigInt::createFrom(VM& vm, int64_t value) |
| { |
| if (!value) |
| return createZero(vm); |
| |
| if (sizeof(Digit) == 8) { |
| JSBigInt* bigInt = createWithLength(vm, 1); |
| |
| if (value < 0) { |
| bigInt->setDigit(0, static_cast<Digit>(static_cast<uint64_t>(-(value + 1)) + 1)); |
| bigInt->setSign(true); |
| } else { |
| bigInt->setDigit(0, static_cast<Digit>(value)); |
| bigInt->setSign(false); |
| } |
| |
| return bigInt; |
| } |
| |
| JSBigInt* bigInt = createWithLength(vm, 2); |
| |
| uint64_t tempValue; |
| bool sign = false; |
| if (value < 0) { |
| tempValue = static_cast<uint64_t>(-(value + 1)) + 1; |
| sign = true; |
| } else |
| tempValue = value; |
| |
| Digit lowBits = static_cast<Digit>(tempValue & 0xffffffff); |
| Digit highBits = static_cast<Digit>((tempValue >> 32) & 0xffffffff); |
| |
| bigInt->setDigit(0, lowBits); |
| bigInt->setDigit(1, highBits); |
| bigInt->setSign(sign); |
| |
| return bigInt; |
| } |
| |
| JSBigInt* JSBigInt::createFrom(VM& vm, bool value) |
| { |
| if (!value) |
| return createZero(vm); |
| |
| JSBigInt* bigInt = createWithLength(vm, 1); |
| bigInt->setDigit(0, static_cast<Digit>(value)); |
| bigInt->setSign(false); |
| return bigInt; |
| } |
| |
| JSValue JSBigInt::toPrimitive(ExecState*, PreferredPrimitiveType) const |
| { |
| return const_cast<JSBigInt*>(this); |
| } |
| |
| std::optional<uint8_t> JSBigInt::singleDigitValueForString() |
| { |
| if (isZero()) |
| return 0; |
| |
| if (length() == 1 && !sign()) { |
| Digit rDigit = digit(0); |
| if (rDigit <= 9) |
| return static_cast<uint8_t>(rDigit); |
| } |
| return { }; |
| } |
| |
| JSBigInt* JSBigInt::parseInt(ExecState* state, StringView s) |
| { |
| if (s.is8Bit()) |
| return parseInt(state, s.characters8(), s.length()); |
| return parseInt(state, s.characters16(), s.length()); |
| } |
| |
| JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, StringView s, uint8_t radix) |
| { |
| if (s.is8Bit()) |
| return parseInt(state, vm, s.characters8(), s.length(), 0, radix, false); |
| return parseInt(state, vm, s.characters16(), s.length(), 0, radix, false); |
| } |
| |
| String JSBigInt::toString(ExecState& state, int radix) |
| { |
| if (this->isZero()) |
| return state.vm().smallStrings.singleCharacterStringRep('0'); |
| |
| return toStringGeneric(state, this, radix); |
| } |
| |
| bool JSBigInt::isZero() |
| { |
| ASSERT(length() || !sign()); |
| return length() == 0; |
| } |
| |
| // Multiplies {this} with {factor} and adds {summand} to the result. |
| void JSBigInt::inplaceMultiplyAdd(uintptr_t factor, uintptr_t summand) |
| { |
| STATIC_ASSERT(sizeof(factor) == sizeof(Digit)); |
| STATIC_ASSERT(sizeof(summand) == sizeof(Digit)); |
| |
| internalMultiplyAdd(this, factor, summand, length(), this); |
| } |
| |
| #if USE(JSVALUE32_64) |
| #define HAVE_TWO_DIGIT 1 |
| typedef uint64_t TwoDigit; |
| #elif HAVE(INT128_T) |
| #define HAVE_TWO_DIGIT 1 |
| typedef __uint128_t TwoDigit; |
| #else |
| #define HAVE_TWO_DIGIT 0 |
| #endif |
| |
| // {carry} must point to an initialized Digit and will either be incremented |
| // by one or left alone. |
| JSBigInt::Digit JSBigInt::digitAdd(Digit a, Digit b, Digit& carry) |
| { |
| #if HAVE_TWO_DIGIT |
| TwoDigit result = static_cast<TwoDigit>(a) + static_cast<TwoDigit>(b); |
| carry += result >> digitBits; |
| |
| return static_cast<Digit>(result); |
| #else |
| Digit result = a + b; |
| |
| if (result < a) |
| carry += 1; |
| |
| return result; |
| #endif |
| } |
| |
| // {borrow} must point to an initialized Digit and will either be incremented |
| // by one or left alone. |
| JSBigInt::Digit JSBigInt::digitSub(Digit a, Digit b, Digit& borrow) |
| { |
| #if HAVE_TWO_DIGIT |
| TwoDigit result = static_cast<TwoDigit>(a) - static_cast<TwoDigit>(b); |
| borrow += (result >> digitBits) & 1; |
| |
| return static_cast<Digit>(result); |
| #else |
| Digit result = a - b; |
| if (result > a) |
| borrow += 1; |
| |
| return static_cast<Digit>(result); |
| #endif |
| } |
| |
| // Returns the low half of the result. High half is in {high}. |
| JSBigInt::Digit JSBigInt::digitMul(Digit a, Digit b, Digit& high) |
| { |
| #if HAVE_TWO_DIGIT |
| TwoDigit result = static_cast<TwoDigit>(a) * static_cast<TwoDigit>(b); |
| high = result >> digitBits; |
| |
| return static_cast<Digit>(result); |
| #else |
| // Multiply in half-pointer-sized chunks. |
| // For inputs [AH AL]*[BH BL], the result is: |
| // |
| // [AL*BL] // rLow |
| // + [AL*BH] // rMid1 |
| // + [AH*BL] // rMid2 |
| // + [AH*BH] // rHigh |
| // = [R4 R3 R2 R1] // high = [R4 R3], low = [R2 R1] |
| // |
| // Where of course we must be careful with carries between the columns. |
| Digit aLow = a & halfDigitMask; |
| Digit aHigh = a >> halfDigitBits; |
| Digit bLow = b & halfDigitMask; |
| Digit bHigh = b >> halfDigitBits; |
| |
| Digit rLow = aLow * bLow; |
| Digit rMid1 = aLow * bHigh; |
| Digit rMid2 = aHigh * bLow; |
| Digit rHigh = aHigh * bHigh; |
| |
| Digit carry = 0; |
| Digit low = digitAdd(rLow, rMid1 << halfDigitBits, carry); |
| low = digitAdd(low, rMid2 << halfDigitBits, carry); |
| |
| high = (rMid1 >> halfDigitBits) + (rMid2 >> halfDigitBits) + rHigh + carry; |
| |
| return low; |
| #endif |
| } |
| |
| // Raises {base} to the power of {exponent}. Does not check for overflow. |
| JSBigInt::Digit JSBigInt::digitPow(Digit base, Digit exponent) |
| { |
| Digit result = 1ull; |
| while (exponent > 0) { |
| if (exponent & 1) |
| result *= base; |
| |
| exponent >>= 1; |
| base *= base; |
| } |
| |
| return result; |
| } |
| |
| // Returns the quotient. |
| // quotient = (high << digitBits + low - remainder) / divisor |
| JSBigInt::Digit JSBigInt::digitDiv(Digit high, Digit low, Digit divisor, Digit& remainder) |
| { |
| ASSERT(high < divisor); |
| #if CPU(X86_64) && COMPILER(GCC_OR_CLANG) |
| Digit quotient; |
| Digit rem; |
| __asm__("divq %[divisor]" |
| // Outputs: {quotient} will be in rax, {rem} in rdx. |
| : "=a"(quotient), "=d"(rem) |
| // Inputs: put {high} into rdx, {low} into rax, and {divisor} into |
| // any register or stack slot. |
| : "d"(high), "a"(low), [divisor] "rm"(divisor)); |
| remainder = rem; |
| return quotient; |
| #elif CPU(X86) && COMPILER(GCC_OR_CLANG) |
| Digit quotient; |
| Digit rem; |
| __asm__("divl %[divisor]" |
| // Outputs: {quotient} will be in eax, {rem} in edx. |
| : "=a"(quotient), "=d"(rem) |
| // Inputs: put {high} into edx, {low} into eax, and {divisor} into |
| // any register or stack slot. |
| : "d"(high), "a"(low), [divisor] "rm"(divisor)); |
| remainder = rem; |
| return quotient; |
| #else |
| static const Digit kHalfDigitBase = 1ull << halfDigitBits; |
| // Adapted from Warren, Hacker's Delight, p. 152. |
| #if USE(JSVALUE64) |
| int s = clz64(divisor); |
| #else |
| int s = clz32(divisor); |
| #endif |
| divisor <<= s; |
| |
| Digit vn1 = divisor >> halfDigitBits; |
| Digit vn0 = divisor & halfDigitMask; |
| |
| // {s} can be 0. "low >> digitBits == low" on x86, so we "&" it with |
| // {s_zero_mask} which is 0 if s == 0 and all 1-bits otherwise. |
| STATIC_ASSERT(sizeof(intptr_t) == sizeof(Digit)); |
| Digit sZeroMask = static_cast<Digit>(static_cast<intptr_t>(-s) >> (digitBits - 1)); |
| Digit un32 = (high << s) | ((low >> (digitBits - s)) & sZeroMask); |
| Digit un10 = low << s; |
| Digit un1 = un10 >> halfDigitBits; |
| Digit un0 = un10 & halfDigitMask; |
| Digit q1 = un32 / vn1; |
| Digit rhat = un32 - q1 * vn1; |
| |
| while (q1 >= kHalfDigitBase || q1 * vn0 > rhat * kHalfDigitBase + un1) { |
| q1--; |
| rhat += vn1; |
| if (rhat >= kHalfDigitBase) |
| break; |
| } |
| |
| Digit un21 = un32 * kHalfDigitBase + un1 - q1 * divisor; |
| Digit q0 = un21 / vn1; |
| rhat = un21 - q0 * vn1; |
| |
| while (q0 >= kHalfDigitBase || q0 * vn0 > rhat * kHalfDigitBase + un0) { |
| q0--; |
| rhat += vn1; |
| if (rhat >= kHalfDigitBase) |
| break; |
| } |
| |
| remainder = (un21 * kHalfDigitBase + un0 - q0 * divisor) >> s; |
| return q1 * kHalfDigitBase + q0; |
| #endif |
| } |
| |
| // Multiplies {source} with {factor} and adds {summand} to the result. |
| // {result} and {source} may be the same BigInt for inplace modification. |
| void JSBigInt::internalMultiplyAdd(JSBigInt* source, Digit factor, Digit summand, int n, JSBigInt* result) |
| { |
| ASSERT(source->length() >= n); |
| ASSERT(result->length() >= n); |
| |
| Digit carry = summand; |
| Digit high = 0; |
| for (int i = 0; i < n; i++) { |
| Digit current = source->digit(i); |
| Digit newCarry = 0; |
| |
| // Compute this round's multiplication. |
| Digit newHigh = 0; |
| current = digitMul(current, factor, newHigh); |
| |
| // Add last round's carryovers. |
| current = digitAdd(current, high, newCarry); |
| current = digitAdd(current, carry, newCarry); |
| |
| // Store result and prepare for next round. |
| result->setDigit(i, current); |
| carry = newCarry; |
| high = newHigh; |
| } |
| |
| if (result->length() > n) { |
| result->setDigit(n++, carry + high); |
| |
| // Current callers don't pass in such large results, but let's be robust. |
| while (n < result->length()) |
| result->setDigit(n++, 0); |
| } else |
| ASSERT(!(carry + high)); |
| } |
| |
| bool JSBigInt::equals(JSBigInt* x, JSBigInt* y) |
| { |
| if (x->sign() != y->sign()) |
| return false; |
| |
| if (x->length() != y->length()) |
| return false; |
| |
| for (int i = 0; i < x->length(); i++) { |
| if (x->digit(i) != y->digit(i)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Divides {x} by {divisor}, returning the result in {quotient} and {remainder}. |
| // Mathematically, the contract is: |
| // quotient = (x - remainder) / divisor, with 0 <= remainder < divisor. |
| // If {quotient} is an empty handle, an appropriately sized BigInt will be |
| // allocated for it; otherwise the caller must ensure that it is big enough. |
| // {quotient} can be the same as {x} for an in-place division. {quotient} can |
| // also be nullptr if the caller is only interested in the remainder. |
| void JSBigInt::absoluteDivSmall(ExecState& state, JSBigInt* x, Digit divisor, JSBigInt** quotient, Digit& remainder) |
| { |
| ASSERT(divisor); |
| |
| VM& vm = state.vm(); |
| ASSERT(!x->isZero()); |
| remainder = 0; |
| if (divisor == 1) { |
| if (quotient != nullptr) |
| *quotient = x; |
| return; |
| } |
| |
| int length = x->length(); |
| if (quotient != nullptr) { |
| if (*quotient == nullptr) |
| *quotient = JSBigInt::createWithLength(vm, length); |
| |
| for (int i = length - 1; i >= 0; i--) { |
| Digit q = digitDiv(remainder, x->digit(i), divisor, remainder); |
| (*quotient)->setDigit(i, q); |
| } |
| } else { |
| for (int i = length - 1; i >= 0; i--) |
| digitDiv(remainder, x->digit(i), divisor, remainder); |
| } |
| } |
| |
| // Lookup table for the maximum number of bits required per character of a |
| // base-N string representation of a number. To increase accuracy, the array |
| // value is the actual value multiplied by 32. To generate this table: |
| // for (var i = 0; i <= 36; i++) { print(Math.ceil(Math.log2(i) * 32) + ","); } |
| constexpr uint8_t maxBitsPerCharTable[] = { |
| 0, 0, 32, 51, 64, 75, 83, 90, 96, // 0..8 |
| 102, 107, 111, 115, 119, 122, 126, 128, // 9..16 |
| 131, 134, 136, 139, 141, 143, 145, 147, // 17..24 |
| 149, 151, 153, 154, 156, 158, 159, 160, // 25..32 |
| 162, 163, 165, 166, // 33..36 |
| }; |
| |
| static const int bitsPerCharTableShift = 5; |
| static const size_t bitsPerCharTableMultiplier = 1u << bitsPerCharTableShift; |
| |
| // Compute (an overapproximation of) the length of the resulting string: |
| // Divide bit length of the BigInt by bits representable per character. |
| uint64_t JSBigInt::calculateMaximumCharactersRequired(int length, int radix, Digit lastDigit, bool sign) |
| { |
| int leadingZeros; |
| if (sizeof(lastDigit) == 8) |
| leadingZeros = clz64(lastDigit); |
| else |
| leadingZeros = clz32(lastDigit); |
| |
| size_t bitLength = length * digitBits - leadingZeros; |
| |
| // Maximum number of bits we can represent with one character. We'll use this |
| // to find an appropriate chunk size below. |
| uint8_t maxBitsPerChar = maxBitsPerCharTable[radix]; |
| |
| // For estimating result length, we have to be pessimistic and work with |
| // the minimum number of bits one character can represent. |
| uint8_t minBitsPerChar = maxBitsPerChar - 1; |
| |
| // Perform the following computation with uint64_t to avoid overflows. |
| uint64_t maximumCharactersRequired = bitLength; |
| maximumCharactersRequired *= bitsPerCharTableMultiplier; |
| |
| // Round up. |
| maximumCharactersRequired += minBitsPerChar - 1; |
| maximumCharactersRequired /= minBitsPerChar; |
| maximumCharactersRequired += sign; |
| |
| return maximumCharactersRequired; |
| } |
| |
| String JSBigInt::toStringGeneric(ExecState& state, JSBigInt* x, int radix) |
| { |
| // FIXME: [JSC] Revisit usage of StringVector into JSBigInt::toString |
| // https://bugs.webkit.org/show_bug.cgi?id=18067 |
| StringVector<LChar> resultString; |
| |
| ASSERT(radix >= 2 && radix <= 36); |
| ASSERT(!x->isZero()); |
| |
| int length = x->length(); |
| bool sign = x->sign(); |
| |
| uint8_t maxBitsPerChar = maxBitsPerCharTable[radix]; |
| uint64_t maximumCharactersRequired = calculateMaximumCharactersRequired(length, radix, x->digit(length - 1), sign); |
| |
| if (maximumCharactersRequired > JSString::MaxLength) { |
| auto scope = DECLARE_THROW_SCOPE(state.vm()); |
| throwOutOfMemoryError(&state, scope); |
| return String(); |
| } |
| |
| Digit lastDigit; |
| if (length == 1) |
| lastDigit = x->digit(0); |
| else { |
| int chunkChars = digitBits * bitsPerCharTableMultiplier / maxBitsPerChar; |
| Digit chunkDivisor = digitPow(radix, chunkChars); |
| |
| // By construction of chunkChars, there can't have been overflow. |
| ASSERT(chunkDivisor); |
| int nonZeroDigit = length - 1; |
| ASSERT(x->digit(nonZeroDigit)); |
| |
| // {rest} holds the part of the BigInt that we haven't looked at yet. |
| // Not to be confused with "remainder"! |
| JSBigInt* rest = nullptr; |
| |
| // In the first round, divide the input, allocating a new BigInt for |
| // the result == rest; from then on divide the rest in-place. |
| JSBigInt** dividend = &x; |
| do { |
| Digit chunk; |
| absoluteDivSmall(state, *dividend, chunkDivisor, &rest, chunk); |
| ASSERT(rest); |
| |
| dividend = &rest; |
| for (int i = 0; i < chunkChars; i++) { |
| resultString.append(radixDigits[chunk % radix]); |
| chunk /= radix; |
| } |
| ASSERT(!chunk); |
| |
| if (!rest->digit(nonZeroDigit)) |
| nonZeroDigit--; |
| |
| // We can never clear more than one digit per iteration, because |
| // chunkDivisor is smaller than max digit value. |
| ASSERT(rest->digit(nonZeroDigit)); |
| } while (nonZeroDigit > 0); |
| |
| lastDigit = rest->digit(0); |
| } |
| |
| do { |
| resultString.append(radixDigits[lastDigit % radix]); |
| lastDigit /= radix; |
| } while (lastDigit > 0); |
| ASSERT(resultString.size()); |
| ASSERT(resultString.size() <= static_cast<size_t>(maximumCharactersRequired)); |
| |
| // Remove leading zeroes. |
| int newSizeNoLeadingZeroes = resultString.size(); |
| while (newSizeNoLeadingZeroes > 1 && resultString[newSizeNoLeadingZeroes - 1] == '0') |
| newSizeNoLeadingZeroes--; |
| |
| resultString.shrink(newSizeNoLeadingZeroes); |
| |
| if (sign) |
| resultString.append('-'); |
| |
| std::reverse(resultString.begin(), resultString.end()); |
| |
| return StringImpl::adopt(WTFMove(resultString)); |
| } |
| |
| JSBigInt* JSBigInt::rightTrim(VM& vm) |
| { |
| if (isZero()) |
| return this; |
| |
| int nonZeroIndex = m_length - 1; |
| while (nonZeroIndex >= 0 && !digit(nonZeroIndex)) |
| nonZeroIndex--; |
| |
| if (nonZeroIndex == m_length - 1) |
| return this; |
| |
| int newLength = nonZeroIndex + 1; |
| JSBigInt* trimmedBigInt = createWithLength(vm, newLength); |
| RELEASE_ASSERT(trimmedBigInt); |
| std::copy(dataStorage(), dataStorage() + newLength, trimmedBigInt->dataStorage()); |
| |
| trimmedBigInt->setSign(this->sign()); |
| |
| return trimmedBigInt; |
| } |
| |
| JSBigInt* JSBigInt::allocateFor(ExecState* state, VM& vm, int radix, int charcount) |
| { |
| ASSERT(2 <= radix && radix <= 36); |
| ASSERT(charcount >= 0); |
| |
| size_t bitsPerChar = maxBitsPerCharTable[radix]; |
| size_t chars = charcount; |
| const int roundup = bitsPerCharTableMultiplier - 1; |
| if (chars <= (std::numeric_limits<size_t>::max() - roundup) / bitsPerChar) { |
| size_t bitsMin = bitsPerChar * chars; |
| |
| // Divide by 32 (see table), rounding up. |
| bitsMin = (bitsMin + roundup) >> bitsPerCharTableShift; |
| if (bitsMin <= static_cast<size_t>(maxInt)) { |
| // Divide by kDigitsBits, rounding up. |
| int length = (static_cast<int>(bitsMin) + digitBits - 1) / digitBits; |
| if (length <= maxLength) { |
| JSBigInt* result = JSBigInt::createWithLength(vm, length); |
| return result; |
| } |
| } |
| } |
| |
| if (state) { |
| auto scope = DECLARE_THROW_SCOPE(vm); |
| throwOutOfMemoryError(state, scope); |
| } |
| return nullptr; |
| } |
| |
| size_t JSBigInt::estimatedSize(JSCell* cell) |
| { |
| return Base::estimatedSize(cell) + jsCast<JSBigInt*>(cell)->m_length * sizeof(Digit); |
| } |
| |
| double JSBigInt::toNumber(ExecState* state) const |
| { |
| VM& vm = state->vm(); |
| auto scope = DECLARE_THROW_SCOPE(vm); |
| throwTypeError(state, scope, ASCIILiteral("Convertion from 'BigInt' to 'number' is not allowed.")); |
| return 0.0; |
| } |
| |
| bool JSBigInt::getPrimitiveNumber(ExecState* state, double& number, JSValue& result) const |
| { |
| result = this; |
| number = toNumber(state); |
| return true; |
| } |
| |
| size_t JSBigInt::offsetOfData() |
| { |
| return WTF::roundUpToMultipleOf<sizeof(Digit)>(sizeof(JSBigInt)); |
| } |
| |
| template <typename CharType> |
| JSBigInt* JSBigInt::parseInt(ExecState* state, CharType* data, int length) |
| { |
| VM& vm = state->vm(); |
| |
| int p = 0; |
| while (p < length && isStrWhiteSpace(data[p])) |
| ++p; |
| |
| // Check Radix from frist characters |
| if (static_cast<unsigned>(p) + 1 < static_cast<unsigned>(length) && data[p] == '0') { |
| if (isASCIIAlphaCaselessEqual(data[p + 1], 'b')) |
| return parseInt(state, vm, data, length, p + 2, 2, false); |
| |
| if (isASCIIAlphaCaselessEqual(data[p + 1], 'x')) |
| return parseInt(state, vm, data, length, p + 2, 16, false); |
| |
| if (isASCIIAlphaCaselessEqual(data[p + 1], 'o')) |
| return parseInt(state, vm, data, length, p + 2, 8, false); |
| } |
| |
| bool sign = false; |
| if (p < length) { |
| if (data[p] == '+') |
| ++p; |
| else if (data[p] == '-') { |
| sign = true; |
| ++p; |
| } |
| } |
| |
| JSBigInt* result = parseInt(state, vm, data, length, p, 10); |
| |
| if (result && !result->isZero()) |
| result->setSign(sign); |
| |
| return result; |
| } |
| |
| template <typename CharType> |
| JSBigInt* JSBigInt::parseInt(ExecState* state, VM& vm, CharType* data, int length, int startIndex, int radix, bool allowEmptyString) |
| { |
| ASSERT(length >= 0); |
| int p = startIndex; |
| |
| auto scope = DECLARE_THROW_SCOPE(vm); |
| |
| if (!allowEmptyString && startIndex == length) { |
| ASSERT(state); |
| throwVMError(state, scope, createSyntaxError(state, "Failed to parse String to BigInt")); |
| return nullptr; |
| } |
| |
| // Skipping leading zeros |
| while (p < length && data[p] == '0') |
| ++p; |
| |
| int endIndex = length - 1; |
| // Removing trailing spaces |
| while (endIndex >= p && isStrWhiteSpace(data[endIndex])) |
| --endIndex; |
| |
| length = endIndex + 1; |
| |
| if (p == length) |
| return createZero(vm); |
| |
| int limit0 = '0' + (radix < 10 ? radix : 10); |
| int limita = 'a' + (radix - 10); |
| int limitA = 'A' + (radix - 10); |
| |
| JSBigInt* result = allocateFor(state, vm, radix, length - p); |
| RETURN_IF_EXCEPTION(scope, nullptr); |
| |
| result->initialize(InitializationType::WithZero); |
| |
| for (int i = p; i < length; i++, p++) { |
| uint32_t digit; |
| if (data[i] >= '0' && data[i] < limit0) |
| digit = data[i] - '0'; |
| else if (data[i] >= 'a' && data[i] < limita) |
| digit = data[i] - 'a' + 10; |
| else if (data[i] >= 'A' && data[i] < limitA) |
| digit = data[i] - 'A' + 10; |
| else |
| break; |
| |
| result->inplaceMultiplyAdd(static_cast<Digit>(radix), static_cast<Digit>(digit)); |
| } |
| |
| if (p == length) |
| return result->rightTrim(vm); |
| |
| ASSERT(state); |
| throwVMError(state, scope, createSyntaxError(state, "Failed to parse String to BigInt")); |
| |
| return nullptr; |
| } |
| |
| JSBigInt::Digit* JSBigInt::dataStorage() |
| { |
| return reinterpret_cast<Digit*>(reinterpret_cast<char*>(this) + offsetOfData()); |
| } |
| |
| JSBigInt::Digit JSBigInt::digit(int n) |
| { |
| ASSERT(n >= 0 && n < length()); |
| return dataStorage()[n]; |
| } |
| |
| void JSBigInt::setDigit(int n, Digit value) |
| { |
| ASSERT(n >= 0 && n < length()); |
| dataStorage()[n] = value; |
| } |
| |
| JSObject* JSBigInt::toObject(ExecState* exec, JSGlobalObject* globalObject) const |
| { |
| return BigIntObject::create(exec->vm(), globalObject, const_cast<JSBigInt*>(this)); |
| } |
| |
| } // namespace JSC |