Commit da13b897 authored by Jakob Kummerow's avatar Jakob Kummerow Committed by Commit Bot

[bigint] Implement toString(radix) for any radix

Power-of-two radixes were supported already; this adds all others
(with 2 <= radix <= 36).

Bonus: fix digit_div fallback path for divisors with no leading zeros.

Bug: v8:6791
Change-Id: Id472667f057ad13338e0d8257a899490490e6f8f
Reviewed-on: https://chromium-review.googlesource.com/693316
Commit-Queue: Jakob Kummerow <jkummerow@chromium.org>
Reviewed-by: 's avatarDaniel Ehrenberg <littledan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#48323}
parent 8fc10b5a
...@@ -77,6 +77,10 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT ...@@ -77,6 +77,10 @@ void HeapObject::HeapObjectPrint(std::ostream& os) { // NOLINT
HeapNumber::cast(this)->HeapNumberPrint(os); HeapNumber::cast(this)->HeapNumberPrint(os);
os << ">\n"; os << ">\n";
break; break;
case BIGINT_TYPE:
BigInt::cast(this)->BigIntPrint(os);
os << "\n";
break;
case FIXED_DOUBLE_ARRAY_TYPE: case FIXED_DOUBLE_ARRAY_TYPE:
FixedDoubleArray::cast(this)->FixedDoubleArrayPrint(os); FixedDoubleArray::cast(this)->FixedDoubleArrayPrint(os);
break; break;
......
...@@ -238,9 +238,14 @@ Handle<BigInt> BigInt::BitwiseOr(Handle<BigInt> x, Handle<BigInt> y) { ...@@ -238,9 +238,14 @@ Handle<BigInt> BigInt::BitwiseOr(Handle<BigInt> x, Handle<BigInt> y) {
} }
MaybeHandle<String> BigInt::ToString(Handle<BigInt> bigint, int radix) { MaybeHandle<String> BigInt::ToString(Handle<BigInt> bigint, int radix) {
// TODO(jkummerow): Support non-power-of-two radixes. Isolate* isolate = bigint->GetIsolate();
if (!base::bits::IsPowerOfTwo(radix)) radix = 16; if (bigint->is_zero()) {
return ToStringBasePowerOfTwo(bigint, radix); return isolate->factory()->NewStringFromStaticChars("0");
}
if (base::bits::IsPowerOfTwo(radix)) {
return ToStringBasePowerOfTwo(bigint, radix);
}
return ToStringGeneric(bigint, radix);
} }
void BigInt::Initialize(int length, bool zero_initialize) { void BigInt::Initialize(int length, bool zero_initialize) {
...@@ -927,7 +932,7 @@ Handle<BigInt> BigInt::Copy(Handle<BigInt> source) { ...@@ -927,7 +932,7 @@ Handle<BigInt> BigInt::Copy(Handle<BigInt> source) {
// base-N string representation of a number. To increase accuracy, the array // base-N string representation of a number. To increase accuracy, the array
// value is the actual value multiplied by 32. To generate this table: // 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) + ","); } // for (var i = 0; i <= 36; i++) { print(Math.ceil(Math.log2(i) * 32) + ","); }
uint8_t kMaxBitsPerChar[] = { constexpr uint8_t kMaxBitsPerChar[] = {
0, 0, 32, 51, 64, 75, 83, 90, 96, // 0..8 0, 0, 32, 51, 64, 75, 83, 90, 96, // 0..8
102, 107, 111, 115, 119, 122, 126, 128, // 9..16 102, 107, 111, 115, 119, 122, 126, 128, // 9..16
131, 134, 136, 139, 141, 143, 145, 147, // 17..24 131, 134, 136, 139, 141, 143, 145, 147, // 17..24
...@@ -942,25 +947,16 @@ MaybeHandle<BigInt> BigInt::AllocateFor(Isolate* isolate, int radix, ...@@ -942,25 +947,16 @@ MaybeHandle<BigInt> BigInt::AllocateFor(Isolate* isolate, int radix,
int charcount) { int charcount) {
DCHECK(2 <= radix && radix <= 36); DCHECK(2 <= radix && radix <= 36);
DCHECK(charcount >= 0); DCHECK(charcount >= 0);
size_t bits_min;
size_t bits_per_char = kMaxBitsPerChar[radix]; size_t bits_per_char = kMaxBitsPerChar[radix];
size_t chars = static_cast<size_t>(charcount); size_t chars = static_cast<size_t>(charcount);
const int roundup = kBitsPerCharTableMultiplier - 1; const int roundup = kBitsPerCharTableMultiplier - 1;
if (chars <= 1000000) { if ((std::numeric_limits<size_t>::max() - roundup) / bits_per_char < chars) {
// More precise path: multiply first, then divide. THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntTooBig),
bits_min = bits_per_char * chars; BigInt);
// Divide by 32 (see table), rounding up.
bits_min = (bits_min + roundup) >> kBitsPerCharTableShift;
} else {
// Overflow avoidance path: divide first, then multiply.
// The addition can't overflow because of the int -> size_t cast.
bits_min = ((chars + roundup) >> kBitsPerCharTableShift) * bits_per_char;
// Check if overflow happened.
if (bits_min < chars) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntTooBig),
BigInt);
}
} }
size_t bits_min = bits_per_char * chars;
// Divide by 32 (see table), rounding up.
bits_min = (bits_min + roundup) >> kBitsPerCharTableShift;
if (bits_min > static_cast<size_t>(kMaxInt)) { if (bits_min > static_cast<size_t>(kMaxInt)) {
THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntTooBig), THROW_NEW_ERROR(isolate, NewRangeError(MessageTemplate::kBigIntTooBig),
BigInt); BigInt);
...@@ -991,16 +987,13 @@ void BigInt::RightTrim() { ...@@ -991,16 +987,13 @@ void BigInt::RightTrim() {
static const char kConversionChars[] = "0123456789abcdefghijklmnopqrstuvwxyz"; static const char kConversionChars[] = "0123456789abcdefghijklmnopqrstuvwxyz";
// TODO(jkummerow): Add more tests for this when we have a way to construct
// multi-digit BigInts.
MaybeHandle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x, MaybeHandle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x,
int radix) { int radix) {
STATIC_ASSERT(base::bits::IsPowerOfTwo(kDigitBits)); STATIC_ASSERT(base::bits::IsPowerOfTwo(kDigitBits));
DCHECK(base::bits::IsPowerOfTwo(radix)); DCHECK(base::bits::IsPowerOfTwo(radix));
DCHECK(radix >= 2 && radix <= 32); DCHECK(radix >= 2 && radix <= 32);
DCHECK(!x->is_zero());
Isolate* isolate = x->GetIsolate(); Isolate* isolate = x->GetIsolate();
// TODO(jkummerow): check in caller?
if (x->is_zero()) return isolate->factory()->NewStringFromStaticChars("0");
const int length = x->length(); const int length = x->length();
const bool sign = x->sign(); const bool sign = x->sign();
...@@ -1022,6 +1015,7 @@ MaybeHandle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x, ...@@ -1022,6 +1015,7 @@ MaybeHandle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x,
isolate->factory() isolate->factory()
->NewRawOneByteString(static_cast<int>(chars_required)) ->NewRawOneByteString(static_cast<int>(chars_required))
.ToHandleChecked(); .ToHandleChecked();
DisallowHeapAllocation no_gc;
uint8_t* buffer = result->GetChars(); uint8_t* buffer = result->GetChars();
// Print the number into the string, starting from the last position. // Print the number into the string, starting from the last position.
int pos = static_cast<int>(chars_required - 1); int pos = static_cast<int>(chars_required - 1);
...@@ -1055,6 +1049,127 @@ MaybeHandle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x, ...@@ -1055,6 +1049,127 @@ MaybeHandle<String> BigInt::ToStringBasePowerOfTwo(Handle<BigInt> x,
return result; return result;
} }
MaybeHandle<String> BigInt::ToStringGeneric(Handle<BigInt> x, int radix) {
DCHECK(radix >= 2 && radix <= 36);
DCHECK(!x->is_zero());
Heap* heap = x->GetHeap();
Isolate* isolate = heap->isolate();
const int length = x->length();
const bool sign = x->sign();
// Compute (an overapproximation of) the length of the resulting string:
// Divide bit length of the BigInt by bits representable per character.
const size_t bit_length =
length * kDigitBits - base::bits::CountLeadingZeros(x->digit(length - 1));
// Maximum number of bits we can represent with one character. We'll use this
// to find an appropriate chunk size below.
const uint8_t max_bits_per_char = kMaxBitsPerChar[radix];
// For estimating result length, we have to be pessimistic and work with
// the minimum number of bits one character can represent.
const uint8_t min_bits_per_char = max_bits_per_char - 1;
// Perform the following computation with uint64_t to avoid overflows.
uint64_t chars_required = bit_length;
chars_required *= kBitsPerCharTableMultiplier;
chars_required += min_bits_per_char - 1; // Round up.
chars_required /= min_bits_per_char;
chars_required += sign;
if (chars_required > String::kMaxLength) {
THROW_NEW_ERROR(isolate, NewInvalidStringLengthError(), String);
}
Handle<SeqOneByteString> result =
isolate->factory()
->NewRawOneByteString(static_cast<int>(chars_required))
.ToHandleChecked();
#if DEBUG
// Zap the string first.
{
DisallowHeapAllocation no_gc;
uint8_t* chars = result->GetChars();
for (int i = 0; i < static_cast<int>(chars_required); i++) chars[i] = '?';
}
#endif
// We assemble the result string in reverse order, and then reverse it.
// TODO(jkummerow): Consider building the string from the right, and
// left-shifting it if the length estimate was too large.
int pos = 0;
digit_t last_digit;
if (length == 1) {
last_digit = x->digit(0);
} else {
int chunk_chars =
kDigitBits * kBitsPerCharTableMultiplier / max_bits_per_char;
digit_t chunk_divisor = digit_pow(radix, chunk_chars);
// By construction of chunk_chars, there can't have been overflow.
DCHECK(chunk_divisor != 0);
int nonzero_digit = length - 1;
DCHECK(x->digit(nonzero_digit) != 0);
// {rest} holds the part of the BigInt that we haven't looked at yet.
// Not to be confused with "remainder"!
Handle<BigInt> rest;
// In the first round, divide the input, allocating a new BigInt for
// the result == rest; from then on divide the rest in-place.
Handle<BigInt>* dividend = &x;
do {
digit_t chunk;
AbsoluteDivSmall(*dividend, chunk_divisor, &rest, &chunk);
DCHECK(!rest.is_null());
dividend = &rest;
DisallowHeapAllocation no_gc;
uint8_t* chars = result->GetChars();
for (int i = 0; i < chunk_chars; i++) {
chars[pos++] = kConversionChars[chunk % radix];
chunk /= radix;
}
DCHECK(chunk == 0);
if (rest->digit(nonzero_digit) == 0) nonzero_digit--;
// We can never clear more than one digit per iteration, because
// chunk_divisor is smaller than max digit value.
DCHECK(rest->digit(nonzero_digit) > 0);
} while (nonzero_digit > 0);
last_digit = rest->digit(0);
}
DisallowHeapAllocation no_gc;
uint8_t* chars = result->GetChars();
do {
chars[pos++] = kConversionChars[last_digit % radix];
last_digit /= radix;
} while (last_digit > 0);
DCHECK(pos >= 1);
DCHECK(pos <= static_cast<int>(chars_required));
// Remove leading zeroes.
while (pos > 1 && chars[pos - 1] == '0') pos--;
if (sign) chars[pos++] = '-';
// Trim any over-allocation (which can happen due to conservative estimates).
if (pos < static_cast<int>(chars_required)) {
result->synchronized_set_length(pos);
int string_size =
SeqOneByteString::SizeFor(static_cast<int>(chars_required));
int needed_size = SeqOneByteString::SizeFor(pos);
if (needed_size < string_size) {
Address new_end = result->address() + needed_size;
heap->CreateFillerObjectAt(new_end, (string_size - needed_size),
ClearRecordedSlots::kNo);
}
}
// Reverse the string.
for (int i = 0, j = pos - 1; i < j; i++, j--) {
uint8_t tmp = chars[i];
chars[i] = chars[j];
chars[j] = tmp;
}
#if DEBUG
// Verify that all characters have been written.
DCHECK(result->length() == pos);
for (int i = 0; i < pos; i++) DCHECK(chars[i] != '?');
#endif
return result;
}
// Digit arithmetic helpers. // Digit arithmetic helpers.
#if V8_TARGET_ARCH_32_BIT #if V8_TARGET_ARCH_32_BIT
...@@ -1152,7 +1267,12 @@ BigInt::digit_t BigInt::digit_div(digit_t high, digit_t low, digit_t divisor, ...@@ -1152,7 +1267,12 @@ BigInt::digit_t BigInt::digit_div(digit_t high, digit_t low, digit_t divisor,
digit_t vn1 = divisor >> kHalfDigitBits; digit_t vn1 = divisor >> kHalfDigitBits;
digit_t vn0 = divisor & kHalfDigitMask; digit_t vn0 = divisor & kHalfDigitMask;
digit_t un32 = (high << s) | (low >> (kDigitBits - s)); // {s} can be 0. "low >> kDigitBits == 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_t));
digit_t s_zero_mask =
static_cast<digit_t>(static_cast<intptr_t>(-s) >> (kDigitBits - 1));
digit_t un32 = (high << s) | ((low >> (kDigitBits - s)) & s_zero_mask);
digit_t un10 = low << s; digit_t un10 = low << s;
digit_t un1 = un10 >> kHalfDigitBits; digit_t un1 = un10 >> kHalfDigitBits;
digit_t un0 = un10 & kHalfDigitMask; digit_t un0 = un10 & kHalfDigitMask;
...@@ -1180,6 +1300,19 @@ BigInt::digit_t BigInt::digit_div(digit_t high, digit_t low, digit_t divisor, ...@@ -1180,6 +1300,19 @@ BigInt::digit_t BigInt::digit_div(digit_t high, digit_t low, digit_t divisor,
#endif #endif
} }
// Raises {base} to the power of {exponent}. Does not check for overflow.
BigInt::digit_t BigInt::digit_pow(digit_t base, digit_t exponent) {
digit_t result = 1ull;
while (exponent > 0) {
if (exponent & 1) {
result *= base;
}
exponent >>= 1;
base *= base;
}
return result;
}
#undef HAVE_TWODIGIT_T #undef HAVE_TWODIGIT_T
#ifdef OBJECT_PRINT #ifdef OBJECT_PRINT
......
...@@ -157,6 +157,7 @@ class BigInt : public HeapObject { ...@@ -157,6 +157,7 @@ class BigInt : public HeapObject {
static MaybeHandle<String> ToStringBasePowerOfTwo(Handle<BigInt> x, static MaybeHandle<String> ToStringBasePowerOfTwo(Handle<BigInt> x,
int radix); int radix);
static MaybeHandle<String> ToStringGeneric(Handle<BigInt> x, int radix);
// Digit arithmetic helpers. // Digit arithmetic helpers.
static inline digit_t digit_add(digit_t a, digit_t b, digit_t* carry); static inline digit_t digit_add(digit_t a, digit_t b, digit_t* carry);
...@@ -164,6 +165,7 @@ class BigInt : public HeapObject { ...@@ -164,6 +165,7 @@ class BigInt : public HeapObject {
static inline digit_t digit_mul(digit_t a, digit_t b, digit_t* high); static inline digit_t digit_mul(digit_t a, digit_t b, digit_t* high);
static inline digit_t digit_div(digit_t high, digit_t low, digit_t divisor, static inline digit_t digit_div(digit_t high, digit_t low, digit_t divisor,
digit_t* remainder); digit_t* remainder);
static digit_t digit_pow(digit_t base, digit_t exponent);
static inline bool digit_ismax(digit_t x) { static inline bool digit_ismax(digit_t x) {
return static_cast<digit_t>(~x) == 0; return static_cast<digit_t>(~x) == 0;
} }
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment