| Index: runtime/vm/object.cc
|
| ===================================================================
|
| --- runtime/vm/object.cc (revision 40060)
|
| +++ runtime/vm/object.cc (working copy)
|
| @@ -8,7 +8,6 @@
|
| #include "platform/assert.h"
|
| #include "vm/assembler.h"
|
| #include "vm/cpu.h"
|
| -#include "vm/bigint_operations.h"
|
| #include "vm/bit_vector.h"
|
| #include "vm/bootstrap.h"
|
| #include "vm/class_finalizer.h"
|
| @@ -3863,6 +3862,11 @@
|
| }
|
|
|
|
|
| +RawFunction* Class::LookupFactoryAllowPrivate(const String& name) const {
|
| + return LookupFunctionAllowPrivate(name, kFactory);
|
| +}
|
| +
|
| +
|
| RawFunction* Class::LookupFunction(const String& name) const {
|
| return LookupFunction(name, kAny);
|
| }
|
| @@ -11750,26 +11754,6 @@
|
| }
|
|
|
|
|
| -bool ICData::AllReceiversAreNumbers() const {
|
| - if (NumberOfChecks() == 0) return false;
|
| - Class& cls = Class::Handle();
|
| - const intptr_t len = NumberOfChecks();
|
| - for (intptr_t i = 0; i < len; i++) {
|
| - if (IsUsedAt(i)) {
|
| - cls = Function::Handle(GetTargetAt(i)).Owner();
|
| - const intptr_t cid = cls.id();
|
| - if ((cid != kSmiCid) &&
|
| - (cid != kMintCid) &&
|
| - (cid != kBigintCid) &&
|
| - (cid != kDoubleCid)) {
|
| - return false;
|
| - }
|
| - }
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -
|
| bool ICData::HasReceiverClassId(intptr_t class_id) const {
|
| ASSERT(NumArgsTested() > 0);
|
| const intptr_t len = NumberOfChecks();
|
| @@ -13286,7 +13270,7 @@
|
|
|
| bool Instance::CheckAndCanonicalizeFields(const char** error_str) const {
|
| const Class& cls = Class::Handle(this->clazz());
|
| - if ((cls.id() >= kNumPredefinedCids)) {
|
| + if (cls.id() >= kNumPredefinedCids) {
|
| // Iterate over all fields, canonicalize numbers and strings, expect all
|
| // other instances to be canonical otherwise report error (return false).
|
| Object& obj = Object::Handle();
|
| @@ -15503,9 +15487,10 @@
|
| ASSERT(str.IsOneByteString());
|
| int64_t value;
|
| if (!OS::StringToInt64(str.ToCString(), &value)) {
|
| - const Bigint& big = Bigint::Handle(Bigint::New(str, space));
|
| - ASSERT(!BigintOperations::FitsIntoSmi(big));
|
| - ASSERT(!BigintOperations::FitsIntoInt64(big));
|
| + const Bigint& big = Bigint::Handle(
|
| + Bigint::NewFromCString(str.ToCString(), space));
|
| + ASSERT(!big.FitsIntoSmi());
|
| + ASSERT(!big.FitsIntoInt64());
|
| if (FLAG_throw_on_javascript_int_overflow) {
|
| ThrowJavascriptIntegerOverflow(big);
|
| }
|
| @@ -15525,8 +15510,8 @@
|
| int64_t value;
|
| if (!OS::StringToInt64(str.ToCString(), &value)) {
|
| const Bigint& big = Bigint::Handle(Bigint::NewCanonical(str));
|
| - ASSERT(!BigintOperations::FitsIntoSmi(big));
|
| - ASSERT(!BigintOperations::FitsIntoInt64(big));
|
| + ASSERT(!big.FitsIntoSmi());
|
| + ASSERT(!big.FitsIntoInt64());
|
| return big.raw();
|
| }
|
| if (Smi::IsValid(value)) {
|
| @@ -15563,11 +15548,10 @@
|
| RawInteger* Integer::NewFromUint64(uint64_t value, Heap::Space space) {
|
| if (value > static_cast<uint64_t>(Mint::kMaxValue)) {
|
| if (FLAG_throw_on_javascript_int_overflow) {
|
| - const Integer &i =
|
| - Integer::Handle(BigintOperations::NewFromUint64(value, space));
|
| + const Integer &i = Integer::Handle(Bigint::NewFromUint64(value, space));
|
| ThrowJavascriptIntegerOverflow(i);
|
| }
|
| - return BigintOperations::NewFromUint64(value, space);
|
| + return Bigint::NewFromUint64(value, space);
|
| } else {
|
| return Integer::New(value, space);
|
| }
|
| @@ -15574,26 +15558,58 @@
|
| }
|
|
|
|
|
| +bool Integer::Equals(const Instance& other) const {
|
| + // Integer is an abstract class.
|
| + UNREACHABLE();
|
| + return false;
|
| +}
|
| +
|
| +
|
| +bool Integer::IsZero() const {
|
| + // Integer is an abstract class.
|
| + UNREACHABLE();
|
| + return false;
|
| +}
|
| +
|
| +
|
| +bool Integer::IsNegative() const {
|
| + // Integer is an abstract class.
|
| + UNREACHABLE();
|
| + return false;
|
| +}
|
| +
|
| +
|
| double Integer::AsDoubleValue() const {
|
| - UNIMPLEMENTED();
|
| + // Integer is an abstract class.
|
| + UNREACHABLE();
|
| return 0.0;
|
| }
|
|
|
|
|
| int64_t Integer::AsInt64Value() const {
|
| - UNIMPLEMENTED();
|
| + // Integer is an abstract class.
|
| + UNREACHABLE();
|
| return 0;
|
| }
|
|
|
|
|
| uint32_t Integer::AsTruncatedUint32Value() const {
|
| - UNIMPLEMENTED();
|
| + // Integer is an abstract class.
|
| + UNREACHABLE();
|
| return 0;
|
| }
|
|
|
|
|
| +bool Integer::FitsIntoSmi() const {
|
| + // Integer is an abstract class.
|
| + UNREACHABLE();
|
| + return false;
|
| +}
|
| +
|
| +
|
| int Integer::CompareWith(const Integer& other) const {
|
| - UNIMPLEMENTED();
|
| + // Integer is an abstract class.
|
| + UNREACHABLE();
|
| return 0;
|
| }
|
|
|
| @@ -15610,11 +15626,8 @@
|
| mint ^= raw();
|
| value = mint.value();
|
| } else {
|
| - ASSERT(IsBigint());
|
| - Bigint& big_value = Bigint::Handle();
|
| - big_value ^= raw();
|
| - if (BigintOperations::FitsIntoInt64(big_value)) {
|
| - value = BigintOperations::ToInt64(big_value);
|
| + if (Bigint::Cast(*this).FitsIntoInt64()) {
|
| + value = AsInt64Value();
|
| }
|
| }
|
| return !IsJavascriptInt(value);
|
| @@ -15636,16 +15649,14 @@
|
| return raw();
|
| }
|
| }
|
| - ASSERT(IsBigint());
|
| - Bigint& big_value = Bigint::Handle();
|
| - big_value ^= raw();
|
| - if (BigintOperations::FitsIntoSmi(big_value)) {
|
| - return BigintOperations::ToSmi(big_value);
|
| - } else if (BigintOperations::FitsIntoInt64(big_value)) {
|
| - return Mint::New(BigintOperations::ToInt64(big_value));
|
| - } else {
|
| - return big_value.raw();
|
| + if (Bigint::Cast(*this).FitsIntoInt64()) {
|
| + const int64_t value = AsInt64Value();
|
| + if (Smi::IsValid(value)) {
|
| + return Smi::New(value);
|
| + }
|
| + return Mint::New(value);
|
| }
|
| + return raw();
|
| }
|
|
|
|
|
| @@ -15698,28 +15709,37 @@
|
| UNIMPLEMENTED();
|
| }
|
| }
|
| - // In 32-bit mode, the result of any operation (except multiplication) between
|
| - // two 63-bit signed integers will fit in a 64-bit signed result.
|
| - // For the multiplication result to fit, the sum of the highest bits of the
|
| - // absolute values of the operands must be smaller than 62.
|
| - // In 64-bit mode, 63-bit signed integers are Smis, already processed above.
|
| - if ((Smi::kBits < 32) && !IsBigint() && !other.IsBigint()) {
|
| + if (!IsBigint() && !other.IsBigint()) {
|
| const int64_t left_value = AsInt64Value();
|
| const int64_t right_value = other.AsInt64Value();
|
| - if (operation == Token::kMUL) {
|
| - if ((Utils::HighestBit(left_value) +
|
| - Utils::HighestBit(right_value)) < 62) {
|
| - return Integer::New(left_value * right_value);
|
| + switch (operation) {
|
| + case Token::kADD: {
|
| + if (((left_value < 0) != (right_value < 0)) ||
|
| + ((left_value + right_value) < 0) == (left_value < 0)) {
|
| + return Integer::New(left_value + right_value);
|
| + }
|
| + break;
|
| }
|
| - // Perform a Bigint multiplication below.
|
| - } else if (Utils::IsInt(63, left_value) && Utils::IsInt(63, right_value)) {
|
| - switch (operation) {
|
| - case Token::kADD:
|
| - return Integer::New(left_value + right_value);
|
| - case Token::kSUB:
|
| - return Integer::New(left_value - right_value);
|
| - case Token::kTRUNCDIV:
|
| - return Integer::New(left_value / right_value);
|
| + case Token::kSUB: {
|
| + if (((left_value < 0) == (right_value < 0)) ||
|
| + ((left_value - right_value) < 0) == (left_value < 0)) {
|
| + return Integer::New(left_value - right_value);
|
| + }
|
| + break;
|
| + }
|
| + case Token::kMUL: {
|
| + if ((Utils::HighestBit(left_value) +
|
| + Utils::HighestBit(right_value)) < 62) {
|
| + return Integer::New(left_value * right_value);
|
| + }
|
| + break;
|
| + }
|
| + case Token::kTRUNCDIV: {
|
| + if ((left_value != Mint::kMinValue) || (right_value != -1)) {
|
| + return Integer::New(left_value / right_value);
|
| + }
|
| + break;
|
| + }
|
| case Token::kMOD: {
|
| const int64_t remainder = left_value % right_value;
|
| if (remainder < 0) {
|
| @@ -15733,14 +15753,9 @@
|
| }
|
| default:
|
| UNIMPLEMENTED();
|
| - }
|
| }
|
| }
|
| - const Bigint& left_big = Bigint::Handle(AsBigint());
|
| - const Bigint& right_big = Bigint::Handle(other.AsBigint());
|
| - const Bigint& result =
|
| - Bigint::Handle(left_big.BigArithmeticOp(operation, right_big));
|
| - return Integer::Handle(result.AsValidInteger()).raw();
|
| + return Integer::null(); // Notify caller that a bigint operation is required.
|
| }
|
|
|
|
|
| @@ -15782,21 +15797,8 @@
|
| default:
|
| UNIMPLEMENTED();
|
| }
|
| - } else {
|
| - Bigint& op1 = Bigint::Handle(AsBigint());
|
| - Bigint& op2 = Bigint::Handle(other.AsBigint());
|
| - switch (kind) {
|
| - case Token::kBIT_AND:
|
| - return BigintOperations::BitAnd(op1, op2);
|
| - case Token::kBIT_OR:
|
| - return BigintOperations::BitOr(op1, op2);
|
| - case Token::kBIT_XOR:
|
| - return BigintOperations::BitXor(op1, op2);
|
| - default:
|
| - UNIMPLEMENTED();
|
| - }
|
| }
|
| - return Integer::null();
|
| + return Integer::null(); // Notify caller that a bigint operation is required.
|
| }
|
|
|
|
|
| @@ -15817,9 +15819,7 @@
|
| int cnt = Utils::HighestBit(left_value);
|
| if ((cnt + right_value) >= Smi::kBits) {
|
| if ((cnt + right_value) >= Mint::kBits) {
|
| - return BigintOperations::ShiftLeft(
|
| - Bigint::Handle(BigintOperations::NewFromSmi(*this)),
|
| - right_value);
|
| + return Bigint::NewFromShiftedInt64(left_value, right_value);
|
| } else {
|
| int64_t left_64 = left_value;
|
| return Integer::New(left_64 << right_value, Heap::kNew, silent);
|
| @@ -15866,22 +15866,6 @@
|
| }
|
|
|
|
|
| -static bool FitsIntoSmi(const Integer& integer) {
|
| - if (integer.IsSmi()) {
|
| - return true;
|
| - }
|
| - if (integer.IsMint()) {
|
| - int64_t mint_value = integer.AsInt64Value();
|
| - return Smi::IsValid(mint_value);
|
| - }
|
| - if (integer.IsBigint()) {
|
| - return BigintOperations::FitsIntoSmi(Bigint::Cast(integer));
|
| - }
|
| - UNREACHABLE();
|
| - return false;
|
| -}
|
| -
|
| -
|
| int Smi::CompareWith(const Integer& other) const {
|
| if (other.IsSmi()) {
|
| const Smi& other_smi = Smi::Cast(other);
|
| @@ -15893,7 +15877,7 @@
|
| return 0;
|
| }
|
| }
|
| - ASSERT(!FitsIntoSmi(other));
|
| + ASSERT(!other.FitsIntoSmi());
|
| if (other.IsMint() || other.IsBigint()) {
|
| if (this->IsNegative() == other.IsNegative()) {
|
| return this->IsNegative() ? 1 : -1;
|
| @@ -16008,8 +15992,13 @@
|
| }
|
|
|
|
|
| +bool Mint::FitsIntoSmi() const {
|
| + return Smi::IsValid(AsInt64Value());
|
| +}
|
| +
|
| +
|
| int Mint::CompareWith(const Integer& other) const {
|
| - ASSERT(!FitsIntoSmi(*this));
|
| + ASSERT(!FitsIntoSmi());
|
| if (other.IsMint() || other.IsSmi()) {
|
| int64_t a = AsInt64Value();
|
| int64_t b = other.AsInt64Value();
|
| @@ -16021,15 +16010,12 @@
|
| return 0;
|
| }
|
| }
|
| - if (other.IsBigint()) {
|
| - ASSERT(!BigintOperations::FitsIntoInt64(Bigint::Cast(other)));
|
| - if (this->IsNegative() == other.IsNegative()) {
|
| - return this->IsNegative() ? 1 : -1;
|
| - }
|
| - return this->IsNegative() ? -1 : 1;
|
| + ASSERT(other.IsBigint());
|
| + ASSERT(!Bigint::Cast(other).FitsIntoInt64());
|
| + if (this->IsNegative() == other.IsNegative()) {
|
| + return this->IsNegative() ? 1 : -1;
|
| }
|
| - UNREACHABLE();
|
| - return 0;
|
| + return this->IsNegative() ? -1 : 1;
|
| }
|
|
|
|
|
| @@ -16188,46 +16174,53 @@
|
| }
|
|
|
|
|
| -RawBigint* Integer::AsBigint() const {
|
| - ASSERT(!IsNull());
|
| - if (IsSmi()) {
|
| - Smi& smi = Smi::Handle();
|
| - smi ^= raw();
|
| - return BigintOperations::NewFromSmi(smi);
|
| - } else if (IsMint()) {
|
| - Mint& mint = Mint::Handle();
|
| - mint ^= raw();
|
| - return BigintOperations::NewFromInt64(mint.value());
|
| - } else {
|
| - ASSERT(IsBigint());
|
| - Bigint& big = Bigint::Handle();
|
| - big ^= raw();
|
| - ASSERT(!BigintOperations::FitsIntoSmi(big));
|
| - return big.raw();
|
| - }
|
| +void Bigint::set_neg(const Bool& value) const {
|
| + StorePointer(&raw_ptr()->neg_, value.raw());
|
| }
|
|
|
|
|
| -RawBigint* Bigint::BigArithmeticOp(Token::Kind operation,
|
| - const Bigint& other) const {
|
| - switch (operation) {
|
| - case Token::kADD:
|
| - return BigintOperations::Add(*this, other);
|
| - case Token::kSUB:
|
| - return BigintOperations::Subtract(*this, other);
|
| - case Token::kMUL:
|
| - return BigintOperations::Multiply(*this, other);
|
| - case Token::kTRUNCDIV:
|
| - return BigintOperations::Divide(*this, other);
|
| - case Token::kMOD:
|
| - return BigintOperations::Modulo(*this, other);
|
| - default:
|
| - UNIMPLEMENTED();
|
| - return Bigint::null();
|
| - }
|
| +void Bigint::set_used(const Smi& value) const {
|
| + raw_ptr()->used_ = value.raw();
|
| }
|
|
|
|
|
| +void Bigint::set_digits(const TypedData& value) const {
|
| + StorePointer(&raw_ptr()->digits_, value.raw());
|
| +}
|
| +
|
| +
|
| +bool Bigint::Neg() const {
|
| + return Bool::Handle(neg()).value();
|
| +}
|
| +
|
| +
|
| +void Bigint::SetNeg(bool value) const {
|
| + set_neg(Bool::Get(value));
|
| +}
|
| +
|
| +
|
| +intptr_t Bigint::Used() const {
|
| + return Smi::Value(used());
|
| +}
|
| +
|
| +
|
| +void Bigint::SetUsed(intptr_t value) const {
|
| + set_used(Smi::Handle(Smi::New(value)));
|
| +}
|
| +
|
| +
|
| +uint32_t Bigint::DigitAt(intptr_t index) const {
|
| + const TypedData& typed_data = TypedData::Handle(digits());
|
| + return typed_data.GetUint32(index << 2);
|
| +}
|
| +
|
| +
|
| +void Bigint::SetDigitAt(intptr_t index, uint32_t value) const {
|
| + const TypedData& typed_data = TypedData::Handle(digits());
|
| + typed_data.SetUint32(index << 2, value);
|
| +}
|
| +
|
| +
|
| bool Bigint::Equals(const Instance& other) const {
|
| if (this->raw() == other.raw()) {
|
| // Both handles point to the same raw instance.
|
| @@ -16240,17 +16233,17 @@
|
|
|
| const Bigint& other_bgi = Bigint::Cast(other);
|
|
|
| - if (this->IsNegative() != other_bgi.IsNegative()) {
|
| + if (this->Neg() != other_bgi.Neg()) {
|
| return false;
|
| }
|
|
|
| - intptr_t len = this->Length();
|
| - if (len != other_bgi.Length()) {
|
| + const intptr_t used = this->Used();
|
| + if (used != other_bgi.Used()) {
|
| return false;
|
| }
|
|
|
| - for (intptr_t i = 0; i < len; i++) {
|
| - if (this->GetChunkAt(i) != other_bgi.GetChunkAt(i)) {
|
| + for (intptr_t i = 0; i < used; i++) {
|
| + if (this->DigitAt(i) != other_bgi.DigitAt(i)) {
|
| return false;
|
| }
|
| }
|
| @@ -16258,18 +16251,165 @@
|
| }
|
|
|
|
|
| -RawBigint* Bigint::New(const String& str, Heap::Space space) {
|
| - const Bigint& result = Bigint::Handle(
|
| - BigintOperations::NewFromCString(str.ToCString(), space));
|
| - ASSERT(!BigintOperations::FitsIntoInt64(result));
|
| +bool Bigint::CheckAndCanonicalizeFields(const char** error_str) const {
|
| + // Bool field neg should always be canonical.
|
| + ASSERT(Bool::Handle(neg()).IsCanonical());
|
| + // Smi field used is canonical by definition.
|
| + if (Used() > 0) {
|
| + // Canonicalize TypedData field digits.
|
| + TypedData& digits_ = TypedData::Handle(digits());
|
| + digits_ ^= digits_.CheckAndCanonicalize(NULL);
|
| + ASSERT(!digits_.IsNull());
|
| + set_digits(digits_);
|
| + } else {
|
| + ASSERT(digits() == TypedData::null());
|
| + }
|
| + return true;
|
| +}
|
| +
|
| +
|
| +RawBigint* Bigint::New(Heap::Space space) {
|
| + ASSERT(Isolate::Current()->object_store()->bigint_class() != Class::null());
|
| + Bigint& result = Bigint::Handle();
|
| + {
|
| + RawObject* raw = Object::Allocate(Bigint::kClassId,
|
| + Bigint::InstanceSize(),
|
| + space);
|
| + NoGCScope no_gc;
|
| + result ^= raw;
|
| + }
|
| + result.set_neg(Bool::Get(false));
|
| + result.set_used(Smi::Handle(Smi::New(0)));
|
| return result.raw();
|
| }
|
|
|
|
|
| +RawBigint* Bigint::NewFromInt64(int64_t value, Heap::Space space) {
|
| + const Bigint& result = Bigint::Handle(New(space));
|
| + result.EnsureLength(2, space);
|
| + result.SetUsed(2);
|
| + if (value < 0) {
|
| + result.SetNeg(true);
|
| + value = -value; // No concern about overflow, since sign is captured.
|
| + }
|
| + result.SetDigitAt(0, static_cast<uint32_t>(value));
|
| + result.SetDigitAt(1, static_cast<uint32_t>(value >> 32)); // value >= 0.
|
| + result.Clamp();
|
| + return result.raw();
|
| +}
|
| +
|
| +
|
| +RawBigint* Bigint::NewFromUint64(uint64_t value, Heap::Space space) {
|
| + const Bigint& result = Bigint::Handle(New(space));
|
| + result.EnsureLength(2, space);
|
| + result.SetUsed(2);
|
| + result.SetDigitAt(0, static_cast<uint32_t>(value));
|
| + result.SetDigitAt(1, static_cast<uint32_t>(value >> 32));
|
| + result.Clamp();
|
| + return result.raw();
|
| +}
|
| +
|
| +
|
| +RawBigint* Bigint::NewFromShiftedInt64(int64_t value, intptr_t shift,
|
| + Heap::Space space) {
|
| + ASSERT(kBitsPerDigit == 32);
|
| + ASSERT(shift >= 0);
|
| + const Bigint& result = Bigint::Handle(New(space));
|
| + const intptr_t digit_shift = shift / kBitsPerDigit;
|
| + const intptr_t bit_shift = shift % kBitsPerDigit;
|
| + result.EnsureLength(3 + digit_shift, space);
|
| + result.SetUsed(3 + digit_shift);
|
| + uint64_t abs_value;
|
| + if (value < 0) {
|
| + result.SetNeg(true);
|
| + abs_value = -value; // No concern about overflow, since sign is captured.
|
| + } else {
|
| + abs_value = value;
|
| + }
|
| + for (intptr_t i = 0; i < digit_shift; i++) {
|
| + result.SetDigitAt(i, 0);
|
| + }
|
| + result.SetDigitAt(0 + digit_shift,
|
| + static_cast<uint32_t>(abs_value << bit_shift));
|
| + result.SetDigitAt(1 + digit_shift,
|
| + static_cast<uint32_t>(abs_value >> (32 - bit_shift)));
|
| + result.SetDigitAt(2 + digit_shift,
|
| + (bit_shift == 0) ? 0
|
| + : static_cast<uint32_t>(abs_value >> (64 - bit_shift)));
|
| + result.Clamp();
|
| + return result.raw();
|
| +}
|
| +
|
| +
|
| +void Bigint::EnsureLength(intptr_t length, Heap::Space space) const {
|
| + ASSERT(length >= 0);
|
| + TypedData& old_digits = TypedData::Handle(digits());
|
| + if ((length > 0) && (old_digits.IsNull() || (length > old_digits.Length()))) {
|
| + TypedData& new_digits = TypedData::Handle(
|
| + TypedData::New(kTypedDataUint32ArrayCid, length + kExtraDigits, space));
|
| + if (!old_digits.IsNull()) {
|
| + TypedData::Copy(new_digits, TypedData::data_offset(),
|
| + old_digits, TypedData::data_offset(),
|
| + old_digits.LengthInBytes());
|
| + }
|
| + set_digits(new_digits);
|
| + }
|
| +}
|
| +
|
| +
|
| +void Bigint::Clamp() const {
|
| + intptr_t used = Used();
|
| + while ((used > 0) && (DigitAt(used - 1) == 0)) {
|
| + --used;
|
| + }
|
| + SetUsed(used);
|
| +}
|
| +
|
| +
|
| +bool Bigint::IsClamped() const {
|
| + intptr_t used = Used();
|
| + return (used == 0) || (DigitAt(used - 1) > 0);
|
| +}
|
| +
|
| +
|
| +RawBigint* Bigint::NewFromCString(const char* str, Heap::Space space) {
|
| + ASSERT(str != NULL);
|
| + if (str[0] == '\0') {
|
| + return NewFromInt64(0, space);
|
| + }
|
| +
|
| + // If the string starts with '-' recursively restart the whole operation
|
| + // without the character and then toggle the sign.
|
| + // This allows multiple leading '-' (which will cancel each other out), but
|
| + // we have added an assert, to make sure that the returned result of the
|
| + // recursive call is not negative.
|
| + // We don't catch leading '-'s for zero. Ex: "--0", or "---".
|
| + if (str[0] == '-') {
|
| + const Bigint& result = Bigint::Handle(NewFromCString(&str[1], space));
|
| + result.SetNeg(!result.Neg()); // Toggle sign.
|
| + ASSERT(result.IsZero() || result.IsNegative());
|
| + ASSERT(result.IsClamped());
|
| + return result.raw();
|
| + }
|
| +
|
| + // No overflow check needed since overflowing str_length implies that we take
|
| + // the branch to NewFromDecCString() which contains a check itself.
|
| + const intptr_t str_length = strlen(str);
|
| + if ((str_length > 2) &&
|
| + (str[0] == '0') &&
|
| + ((str[1] == 'x') || (str[1] == 'X'))) {
|
| + const Bigint& result = Bigint::Handle(NewFromHexCString(&str[2], space));
|
| + ASSERT(result.IsClamped());
|
| + return result.raw();
|
| + } else {
|
| + return NewFromDecCString(str, space);
|
| + }
|
| +}
|
| +
|
| +
|
| RawBigint* Bigint::NewCanonical(const String& str) {
|
| const Bigint& value = Bigint::Handle(
|
| - BigintOperations::NewFromCString(str.ToCString(), Heap::kOld));
|
| - ASSERT(!BigintOperations::FitsIntoInt64(value));
|
| + Bigint::NewFromCString(str.ToCString(), Heap::kOld));
|
| const Class& cls =
|
| Class::Handle(Isolate::Current()->object_store()->bigint_class());
|
| const Array& constants = Array::Handle(cls.constants());
|
| @@ -16296,58 +16436,500 @@
|
| }
|
|
|
|
|
| +RawBigint* Bigint::NewFromHexCString(const char* str, Heap::Space space) {
|
| + // TODO(regis): Do we need to check for max length?
|
| + // If the string starts with '-' recursively restart the whole operation
|
| + // without the character and then toggle the sign.
|
| + // This allows multiple leading '-' (which will cancel each other out), but
|
| + // we have added an assert, to make sure that the returned result of the
|
| + // recursive call is not negative.
|
| + // We don't catch leading '-'s for zero. Ex: "--0", or "---".
|
| + if (str[0] == '-') {
|
| + const Bigint& result = Bigint::Handle(NewFromHexCString(&str[1], space));
|
| + result.SetNeg(!result.Neg()); // Toggle sign.
|
| + ASSERT(result.IsZero() || result.IsNegative());
|
| + ASSERT(result.IsClamped());
|
| + return result.raw();
|
| + }
|
| + const Bigint& result = Bigint::Handle(New(space));
|
| + const int kBitsPerHexDigit = 4;
|
| + const int kHexDigitsPerDigit = 8;
|
| + const int kBitsPerDigit = kBitsPerHexDigit * kHexDigitsPerDigit;
|
| + intptr_t hex_i = strlen(str); // Terminating byte excluded.
|
| + result.EnsureLength((hex_i + kHexDigitsPerDigit - 1) / kHexDigitsPerDigit,
|
| + space);
|
| + intptr_t used_ = 0;
|
| + uint32_t digit = 0;
|
| + intptr_t bit_i = 0;
|
| + while (--hex_i >= 0) {
|
| + digit += Utils::HexDigitToInt(str[hex_i]) << bit_i;
|
| + bit_i += kBitsPerHexDigit;
|
| + if (bit_i == kBitsPerDigit) {
|
| + bit_i = 0;
|
| + result.SetDigitAt(used_++, digit);
|
| + digit = 0;
|
| + }
|
| + }
|
| + if (bit_i != 0) {
|
| + result.SetDigitAt(used_++, digit);
|
| + }
|
| + result.SetUsed(used_);
|
| + result.Clamp();
|
| + return result.raw();
|
| +}
|
| +
|
| +
|
| +RawBigint* Bigint::NewFromDecCString(const char* str, Heap::Space space) {
|
| + // Read 9 digits a time. 10^9 < 2^32.
|
| + const int kDecDigitsPerIteration = 9;
|
| + const uint32_t kTenMultiplier = 1000000000;
|
| + ASSERT(kBitsPerDigit == 32);
|
| +
|
| + const intptr_t str_length = strlen(str);
|
| + if (str_length < 0) { // TODO(regis): Pick a smaller limit.
|
| + FATAL("Fatal error in Bigint::NewFromDecCString: string too long");
|
| + }
|
| + intptr_t str_pos = 0;
|
| +
|
| + Bigint& result = Bigint::Handle(Bigint::New(space));
|
| + // One decimal digit takes log2(10) bits, i.e. ~3.32192809489 bits.
|
| + // That is a theoretical limit for large numbers.
|
| + // The extra digits allocated take care of variations (kExtraDigits).
|
| + const int64_t kLog10Dividend = 33219281;
|
| + const int64_t kLog10Divisor = 10000000;
|
| + result.EnsureLength((kLog10Dividend * str_length) /
|
| + (kLog10Divisor * kBitsPerDigit), space);
|
| +
|
| + // Read first digit separately. This avoids a multiplication and addition.
|
| + // The first digit might also not have kDecDigitsPerIteration decimal digits.
|
| + const intptr_t lsdigit_length = str_length % kDecDigitsPerIteration;
|
| + uint32_t digit = 0;
|
| + for (intptr_t i = 0; i < lsdigit_length; i++) {
|
| + char c = str[str_pos++];
|
| + ASSERT(('0' <= c) && (c <= '9'));
|
| + digit = digit * 10 + c - '0';
|
| + }
|
| + result.SetDigitAt(0, digit);
|
| + intptr_t used = 1;
|
| +
|
| + // Read kDecDigitsPerIteration at a time, and store it in 'digit'.
|
| + // Then multiply the temporary result by 10^kDecDigitsPerIteration and add
|
| + // 'digit' to the new result.
|
| + while (str_pos < str_length - 1) {
|
| + digit = 0;
|
| + for (intptr_t i = 0; i < kDecDigitsPerIteration; i++) {
|
| + char c = str[str_pos++];
|
| + ASSERT(('0' <= c) && (c <= '9'));
|
| + digit = digit * 10 + c - '0';
|
| + }
|
| + // Multiply result with kTenMultiplier and add digit.
|
| + for (intptr_t i = 0; i < used; i++) {
|
| + uint64_t product =
|
| + (static_cast<uint64_t>(result.DigitAt(i)) * kTenMultiplier) + digit;
|
| + result.SetDigitAt(i, static_cast<uint32_t>(product & kDigitMask));
|
| + digit = static_cast<uint32_t>(product >> kBitsPerDigit);
|
| + }
|
| + result.SetDigitAt(used++, digit);
|
| + }
|
| + result.SetUsed(used);
|
| + result.Clamp();
|
| + return result.raw();
|
| +}
|
| +
|
| +
|
| +static double Uint64ToDouble(uint64_t x) {
|
| +#if _WIN64
|
| + // For static_cast<double>(x) MSVC x64 generates
|
| + //
|
| + // cvtsi2sd xmm0, rax
|
| + // test rax, rax
|
| + // jns done
|
| + // addsd xmm0, static_cast<double>(2^64)
|
| + // done:
|
| + //
|
| + // while GCC -m64 generates
|
| + //
|
| + // test rax, rax
|
| + // js negative
|
| + // cvtsi2sd xmm0, rax
|
| + // jmp done
|
| + // negative:
|
| + // mov rdx, rax
|
| + // shr rdx, 1
|
| + // and eax, 0x1
|
| + // or rdx, rax
|
| + // cvtsi2sd xmm0, rdx
|
| + // addsd xmm0, xmm0
|
| + // done:
|
| + //
|
| + // which results in a different rounding.
|
| + //
|
| + // For consistency between platforms fallback to GCC style converstion
|
| + // on Win64.
|
| + //
|
| + const int64_t y = static_cast<int64_t>(x);
|
| + if (y > 0) {
|
| + return static_cast<double>(y);
|
| + } else {
|
| + const double half = static_cast<double>(
|
| + static_cast<int64_t>(x >> 1) | (y & 1));
|
| + return half + half;
|
| + }
|
| +#else
|
| + return static_cast<double>(x);
|
| +#endif
|
| +}
|
| +
|
| +
|
| double Bigint::AsDoubleValue() const {
|
| - return Double::Handle(BigintOperations::ToDouble(*this)).value();
|
| + ASSERT(kBitsPerDigit == 32);
|
| + ASSERT(IsClamped());
|
| + const intptr_t used = Used();
|
| + if (used == 0) {
|
| + return 0.0;
|
| + }
|
| + if (used <= 2) {
|
| + const uint64_t digit1 = (used > 1) ? DigitAt(1) : 0;
|
| + const uint64_t abs_value = (digit1 << 32) + DigitAt(0);
|
| + const double abs_double_value = Uint64ToDouble(abs_value);
|
| + return Neg() ? -abs_double_value : abs_double_value;
|
| + }
|
| +
|
| + static const int kPhysicalSignificandSize = 52;
|
| + // The significand size has an additional hidden bit.
|
| + static const int kSignificandSize = kPhysicalSignificandSize + 1;
|
| + static const int kExponentBias = 0x3FF + kPhysicalSignificandSize;
|
| + static const int kMaxExponent = 0x7FF - kExponentBias;
|
| + static const uint64_t kOne64 = 1;
|
| + static const uint64_t kInfinityBits =
|
| + DART_2PART_UINT64_C(0x7FF00000, 00000000);
|
| +
|
| + // A double is composed of an exponent e and a significand s. Its value equals
|
| + // s * 2^e. The significand has 53 bits of which the first one must always be
|
| + // 1 (at least for then numbers we are working with here) and is therefore
|
| + // omitted. The physical size of the significand is thus 52 bits.
|
| + // The exponent has 11 bits and is biased by 0x3FF + 52. For example an
|
| + // exponent e = 10 is written as 0x3FF + 52 + 10 (in the 11 bits that are
|
| + // reserved for the exponent).
|
| + // When converting the given bignum to a double we have to pay attention to
|
| + // the rounding. In particular we have to decide which double to pick if an
|
| + // input lies exactly between two doubles. As usual with double operations
|
| + // we pick the double with an even significand in such cases.
|
| + //
|
| + // General approach of this algorithm: Get 54 bits (one more than the
|
| + // significand size) of the bigint. If the last bit is then 1, then (without
|
| + // knowledge of the remaining bits) we could have a half-way number.
|
| + // If the second-to-last bit is odd then we know that we have to round up:
|
| + // if the remaining bits are not zero then the input lies closer to the higher
|
| + // double. If the remaining bits are zero then we have a half-way case and
|
| + // we need to round up too (rounding to the even double).
|
| + // If the second-to-last bit is even then we need to look at the remaining
|
| + // bits to determine if any of them is not zero. If that's the case then the
|
| + // number lies closer to the next-higher double. Otherwise we round the
|
| + // half-way case down to even.
|
| +
|
| + if (((used - 1) * kBitsPerDigit) > (kMaxExponent + kSignificandSize)) {
|
| + // Does not fit into a double.
|
| + const double infinity = bit_cast<double>(kInfinityBits);
|
| + return Neg() ? -infinity : infinity;
|
| + }
|
| +
|
| + intptr_t digit_index = used - 1;
|
| + // In order to round correctly we need to look at half-way cases. Therefore we
|
| + // get kSignificandSize + 1 bits. If the last bit is 1 then we have to look
|
| + // at the remaining bits to know if we have to round up.
|
| + int needed_bits = kSignificandSize + 1;
|
| + ASSERT((kBitsPerDigit < needed_bits) && (2 * kBitsPerDigit >= needed_bits));
|
| + bool discarded_bits_were_zero = true;
|
| +
|
| + const uint32_t firstDigit = DigitAt(digit_index--);
|
| + ASSERT(firstDigit > 0);
|
| + uint64_t twice_significand_floor = firstDigit;
|
| + intptr_t twice_significant_exponent = (digit_index + 1) * kBitsPerDigit;
|
| + needed_bits -= Utils::HighestBit(firstDigit) + 1;
|
| +
|
| + if (needed_bits >= kBitsPerDigit) {
|
| + twice_significand_floor <<= kBitsPerDigit;
|
| + twice_significand_floor |= DigitAt(digit_index--);
|
| + twice_significant_exponent -= kBitsPerDigit;
|
| + needed_bits -= kBitsPerDigit;
|
| + }
|
| + if (needed_bits > 0) {
|
| + ASSERT(needed_bits <= kBitsPerDigit);
|
| + uint32_t digit = DigitAt(digit_index--);
|
| + int discarded_bits_count = kBitsPerDigit - needed_bits;
|
| + twice_significand_floor <<= needed_bits;
|
| + twice_significand_floor |= digit >> discarded_bits_count;
|
| + twice_significant_exponent -= needed_bits;
|
| + uint64_t discarded_bits_mask = (kOne64 << discarded_bits_count) - 1;
|
| + discarded_bits_were_zero = ((digit & discarded_bits_mask) == 0);
|
| + }
|
| + ASSERT((twice_significand_floor >> kSignificandSize) == 1);
|
| +
|
| + // We might need to round up the significand later.
|
| + uint64_t significand = twice_significand_floor >> 1;
|
| + const intptr_t exponent = twice_significant_exponent + 1;
|
| +
|
| + if (exponent >= kMaxExponent) {
|
| + // Infinity.
|
| + // Does not fit into a double.
|
| + const double infinity = bit_cast<double>(kInfinityBits);
|
| + return Neg() ? -infinity : infinity;
|
| + }
|
| +
|
| + if ((twice_significand_floor & 1) == 1) {
|
| + bool round_up = false;
|
| +
|
| + if ((significand & 1) != 0 || !discarded_bits_were_zero) {
|
| + // Even if the remaining bits are zero we still need to round up since we
|
| + // want to round to even for half-way cases.
|
| + round_up = true;
|
| + } else {
|
| + // Could be a half-way case. See if the remaining bits are non-zero.
|
| + for (intptr_t i = 0; i <= digit_index; i++) {
|
| + if (DigitAt(i) != 0) {
|
| + round_up = true;
|
| + break;
|
| + }
|
| + }
|
| + }
|
| +
|
| + if (round_up) {
|
| + significand++;
|
| + // It might be that we just went from 53 bits to 54 bits.
|
| + // Example: After adding 1 to 1FFF..FF (with 53 bits set to 1) we have
|
| + // 2000..00 (= 2 ^ 54). When adding the exponent and significand together
|
| + // this will increase the exponent by 1 which is exactly what we want.
|
| + }
|
| + }
|
| +
|
| + ASSERT(((significand >> (kSignificandSize - 1)) == 1) ||
|
| + (significand == (kOne64 << kSignificandSize)));
|
| + // The significand still has the hidden bit. We simply decrement the biased
|
| + // exponent by one instead of playing around with the significand.
|
| + const uint64_t biased_exponent = exponent + kExponentBias - 1;
|
| + // Note that we must use the plus operator instead of bit-or.
|
| + const uint64_t double_bits =
|
| + (biased_exponent << kPhysicalSignificandSize) + significand;
|
| +
|
| + const double value = bit_cast<double>(double_bits);
|
| + return Neg() ? -value : value;
|
| }
|
|
|
|
|
| -int64_t Bigint::AsInt64Value() const {
|
| - if (!BigintOperations::FitsIntoInt64(*this)) {
|
| - UNREACHABLE();
|
| +bool Bigint::FitsIntoSmi() const {
|
| + return FitsIntoInt64() && Smi::IsValid(AsInt64Value());
|
| +}
|
| +
|
| +
|
| +bool Bigint::FitsIntoInt64() const {
|
| + ASSERT(Bigint::kBitsPerDigit == 32);
|
| + const intptr_t used = Used();
|
| + if (used < 2) return true;
|
| + if (used > 2) return false;
|
| + const uint64_t digit1 = DigitAt(1);
|
| + const uint64_t value = (digit1 << 32) + DigitAt(0);
|
| + uint64_t limit = Mint::kMaxValue;
|
| + if (Neg()) {
|
| + limit++;
|
| }
|
| - return BigintOperations::ToInt64(*this);
|
| + return value <= limit;
|
| }
|
|
|
|
|
| +int64_t Bigint::AsInt64Value() const {
|
| + ASSERT(FitsIntoInt64());
|
| + const intptr_t used = Used();
|
| + if (used == 0) return 0;
|
| + const int64_t digit1 = (used > 1) ? DigitAt(1) : 0;
|
| + const int64_t value = (digit1 << 32) + DigitAt(0);
|
| + return Neg() ? -value : value;
|
| +}
|
| +
|
| +
|
| +bool Bigint::FitsIntoUint64() const {
|
| + ASSERT(Bigint::kBitsPerDigit == 32);
|
| + return !Neg() && (Used() <= 2);
|
| +}
|
| +
|
| +
|
| +uint64_t Bigint::AsUint64Value() const {
|
| + ASSERT(FitsIntoUint64());
|
| + const intptr_t used = Used();
|
| + if (used == 0) return 0;
|
| + const uint64_t digit1 = (used > 1) ? DigitAt(1) : 0;
|
| + return (digit1 << 32) + DigitAt(0);
|
| +}
|
| +
|
| +
|
| uint32_t Bigint::AsTruncatedUint32Value() const {
|
| - return BigintOperations::TruncateToUint32(*this);
|
| + // Note: the previous implementation of Bigint returned the absolute value
|
| + // truncated to 32 bits, which is not consistent with Smi and Mint behavior.
|
| + ASSERT(Bigint::kBitsPerDigit == 32);
|
| + const intptr_t used = Used();
|
| + if (used == 0) return 0;
|
| + const uint32_t digit0 = DigitAt(0);
|
| + return Neg() ? -digit0 : digit0;
|
| }
|
|
|
|
|
| // For positive values: Smi < Mint < Bigint.
|
| int Bigint::CompareWith(const Integer& other) const {
|
| - ASSERT(!FitsIntoSmi(*this));
|
| - ASSERT(!BigintOperations::FitsIntoInt64(*this));
|
| - if (other.IsBigint()) {
|
| - return BigintOperations::Compare(*this, Bigint::Cast(other));
|
| + ASSERT(!FitsIntoSmi());
|
| + ASSERT(!FitsIntoInt64());
|
| + if (other.IsBigint() && (IsNegative() == other.IsNegative())) {
|
| + const Bigint& other_bgi = Bigint::Cast(other);
|
| + int64_t result = Used() - other_bgi.Used();
|
| + if (result == 0) {
|
| + for (intptr_t i = Used(); --i >= 0; ) {
|
| + result = DigitAt(i);
|
| + result -= other_bgi.DigitAt(i);
|
| + if (result != 0) break;
|
| + }
|
| + }
|
| + if (IsNegative()) {
|
| + result = -result;
|
| + }
|
| + return result > 0 ? 1 : result < 0 ? -1 : 0;
|
| }
|
| - if (this->IsNegative() == other.IsNegative()) {
|
| - return this->IsNegative() ? -1 : 1;
|
| - }
|
| return this->IsNegative() ? -1 : 1;
|
| }
|
|
|
|
|
| -RawBigint* Bigint::Allocate(intptr_t length, Heap::Space space) {
|
| - if (length < 0 || length > kMaxElements) {
|
| - // This should be caught before we reach here.
|
| - FATAL1("Fatal error in Bigint::Allocate: invalid length %" Pd "\n", length);
|
| +const char* Bigint::ToDecCString(uword (*allocator)(intptr_t size)) const {
|
| + // log10(2) ~= 0.30102999566398114.
|
| + const intptr_t kLog2Dividend = 30103;
|
| + const intptr_t kLog2Divisor = 100000;
|
| + intptr_t used = Used();
|
| + const intptr_t kMaxUsed =
|
| + kIntptrMax / kBitsPerDigit / kLog2Dividend * kLog2Divisor;
|
| + if (used > kMaxUsed) {
|
| + // Throw out of memory exception.
|
| + Isolate* isolate = Isolate::Current();
|
| + const Instance& exception =
|
| + Instance::Handle(isolate->object_store()->out_of_memory());
|
| + Exceptions::Throw(isolate, exception);
|
| + UNREACHABLE();
|
| }
|
| - ASSERT(Isolate::Current()->object_store()->bigint_class() != Class::null());
|
| - Bigint& result = Bigint::Handle();
|
| - {
|
| - RawObject* raw = Object::Allocate(Bigint::kClassId,
|
| - Bigint::InstanceSize(length),
|
| - space);
|
| - NoGCScope no_gc;
|
| - result ^= raw;
|
| - result.raw_ptr()->allocated_length_ = length; // Chunk length allocated.
|
| - result.raw_ptr()->signed_length_ = length; // Chunk length in use.
|
| + const int64_t bit_len = used * kBitsPerDigit;
|
| + const int64_t dec_len = (bit_len * kLog2Dividend / kLog2Divisor) + 1;
|
| + // Add one byte for the minus sign and for the trailing \0 character.
|
| + const int64_t len = (Neg() ? 1 : 0) + dec_len + 1;
|
| + char* chars = reinterpret_cast<char*>(allocator(len));
|
| + intptr_t pos = 0;
|
| + const intptr_t kDivisor = 100000000;
|
| + const intptr_t kDigits = 8;
|
| + ASSERT(pow(10.0, kDigits) == kDivisor);
|
| + ASSERT(kDivisor < kDigitBase);
|
| + ASSERT(Smi::IsValid(kDivisor));
|
| + // Allocate a copy of the digits.
|
| + const TypedData& rest_digits = TypedData::Handle(
|
| + TypedData::New(kTypedDataUint32ArrayCid, used));
|
| + for (intptr_t i = 0; i < used; i++) {
|
| + rest_digits.SetUint32(i << 2, DigitAt(i));
|
| }
|
| - return result.raw();
|
| + if (used == 0) {
|
| + chars[pos++] = '0';
|
| + }
|
| + while (used > 0) {
|
| + uint32_t remainder = 0;
|
| + for (intptr_t i = used - 1; i >= 0; i--) {
|
| + uint64_t dividend = (static_cast<uint64_t>(remainder) << kBitsPerDigit) +
|
| + rest_digits.GetUint32(i << 2);
|
| + uint32_t quotient = static_cast<uint32_t>(dividend / kDivisor);
|
| + remainder = static_cast<uint32_t>(
|
| + dividend - static_cast<uint64_t>(quotient) * kDivisor);
|
| + rest_digits.SetUint32(i << 2, quotient);
|
| + }
|
| + // Clamp rest_digits.
|
| + while ((used > 0) && (rest_digits.GetUint32((used - 1) << 2) == 0)) {
|
| + used--;
|
| + }
|
| + for (intptr_t i = 0; i < kDigits; i++) {
|
| + chars[pos++] = '0' + (remainder % 10);
|
| + remainder /= 10;
|
| + }
|
| + ASSERT(remainder == 0);
|
| + }
|
| + // Remove leading zeros.
|
| + while ((pos > 1) && (chars[pos - 1] == '0')) {
|
| + pos--;
|
| + }
|
| + if (Neg()) {
|
| + chars[pos++] = '-';
|
| + }
|
| + // Reverse the string.
|
| + intptr_t i = 0;
|
| + intptr_t j = pos - 1;
|
| + while (i < j) {
|
| + char tmp = chars[i];
|
| + chars[i] = chars[j];
|
| + chars[j] = tmp;
|
| + i++;
|
| + j--;
|
| + }
|
| + chars[pos] = '\0';
|
| + return chars;
|
| }
|
|
|
|
|
| +const char* Bigint::ToHexCString(uword (*allocator)(intptr_t size)) const {
|
| + NoGCScope no_gc; // TODO(regis): Is this necessary?
|
| +
|
| + const intptr_t used = Used();
|
| + if (used == 0) {
|
| + const char* zero = "0x0";
|
| + const size_t len = strlen(zero) + 1;
|
| + char* chars = reinterpret_cast<char*>(allocator(len));
|
| + strncpy(chars, zero, len);
|
| + return chars;
|
| + }
|
| + const int kBitsPerHexDigit = 4;
|
| + const int kHexDigitsPerDigit = 8;
|
| + const intptr_t kMaxUsed = (kIntptrMax - 4) / kHexDigitsPerDigit;
|
| + if (used > kMaxUsed) {
|
| + // Throw out of memory exception.
|
| + Isolate* isolate = Isolate::Current();
|
| + const Instance& exception =
|
| + Instance::Handle(isolate->object_store()->out_of_memory());
|
| + Exceptions::Throw(isolate, exception);
|
| + UNREACHABLE();
|
| + }
|
| + intptr_t hex_len = (used - 1) * kHexDigitsPerDigit;
|
| + // The most significant digit may use fewer than kHexDigitsPerDigit digits.
|
| + uint32_t digit = DigitAt(used - 1);
|
| + ASSERT(digit != 0); // Value must be clamped.
|
| + while (digit != 0) {
|
| + hex_len++;
|
| + digit >>= kBitsPerHexDigit;
|
| + }
|
| + // Add bytes for '0x', for the minus sign, and for the trailing \0 character.
|
| + const int32_t len = (Neg() ? 1 : 0) + 2 + hex_len + 1;
|
| + char* chars = reinterpret_cast<char*>(allocator(len));
|
| + intptr_t pos = len;
|
| + chars[--pos] = '\0';
|
| + for (intptr_t i = 0; i < (used - 1); i++) {
|
| + digit = DigitAt(i);
|
| + for (intptr_t j = 0; j < kHexDigitsPerDigit; j++) {
|
| + chars[--pos] = Utils::IntToHexDigit(digit & 0xf);
|
| + digit >>= kBitsPerHexDigit;
|
| + }
|
| + }
|
| + digit = DigitAt(used - 1);
|
| + while (digit != 0) {
|
| + chars[--pos] = Utils::IntToHexDigit(digit & 0xf);
|
| + digit >>= kBitsPerHexDigit;
|
| + }
|
| + chars[--pos] = 'x';
|
| + chars[--pos] = '0';
|
| + if (Neg()) {
|
| + chars[--pos] = '-';
|
| + }
|
| + ASSERT(pos == 0);
|
| + return chars;
|
| +}
|
| +
|
| +
|
| static uword BigintAllocator(intptr_t size) {
|
| Zone* zone = Isolate::Current()->current_zone();
|
| return zone->AllocUnsafe(size);
|
| @@ -16355,7 +16937,7 @@
|
|
|
|
|
| const char* Bigint::ToCString() const {
|
| - return BigintOperations::ToDecimalCString(*this, &BigintAllocator);
|
| + return ToDecCString(&BigintAllocator);
|
| }
|
|
|
|
|
| @@ -18512,6 +19094,7 @@
|
| if (hash_code.IsSmi()) {
|
| // May waste some bits on 64-bit, to ensure consistency with non-Smi case.
|
| return static_cast<uword>(Smi::Cast(hash_code).Value() & 0xFFFFFFFF);
|
| + // TODO(regis): Same as Smi::AsTruncatedUint32Value(), simplify?
|
| } else if (hash_code.IsInteger()) {
|
| return static_cast<uword>(
|
| Integer::Cast(hash_code).AsTruncatedUint32Value());
|
| @@ -18954,6 +19537,32 @@
|
| };
|
|
|
|
|
| +bool TypedData::CanonicalizeEquals(const Instance& other) const {
|
| + if (this->raw() == other.raw()) {
|
| + // Both handles point to the same raw instance.
|
| + return true;
|
| + }
|
| +
|
| + if (!other.IsTypedData() || other.IsNull()) {
|
| + return false;
|
| + }
|
| +
|
| + const TypedData& other_typed_data = TypedData::Cast(other);
|
| +
|
| + if (this->ElementType() != other_typed_data.ElementType()) {
|
| + return false;
|
| + }
|
| +
|
| + const intptr_t len = this->LengthInBytes();
|
| + if (len != other_typed_data.LengthInBytes()) {
|
| + return false;
|
| + }
|
| +
|
| + return (len == 0) ||
|
| + (memcmp(DataAddr(0), other_typed_data.DataAddr(0), len) == 0);
|
| +}
|
| +
|
| +
|
| RawTypedData* TypedData::New(intptr_t class_id,
|
| intptr_t len,
|
| Heap::Space space) {
|
| @@ -18962,7 +19571,7 @@
|
| }
|
| TypedData& result = TypedData::Handle();
|
| {
|
| - intptr_t lengthInBytes = len * ElementSizeInBytes(class_id);
|
| + const intptr_t lengthInBytes = len * ElementSizeInBytes(class_id);
|
| RawObject* raw = Object::Allocate(class_id,
|
| TypedData::InstanceSize(lengthInBytes),
|
| space);
|
|
|