0ef66e0f5c
A field/function annotated with this pragma must be guaranteed to not return `null` at runtime. Make use of this non-nullable annotation in the VM's type propagator. Annotates the "_TypedListView._typedData" field to ensure the VM knows it returns a non-nullable _TypedListView. Furthermore annotates methods on the integer implementation. Those particular methods are recognized methods with a "dynamic" return type. This caused the type propagator to use CompileType::Dynamic() as result type. Since a previous CL started to only utilize the annotated type if it is better than "dynamic" more integer operations got handled in-line, though with null-checks. Annotating those methods to return non-null improves the in-line handling of integer operations. This improves dart-aot On arm7hf: SHA256: +5%, SHA: +6%, JsonObjectRoundTrip: +7%, ... On arm8: SHA1: +28%, MD5: +25%, SHA256: +15%, TypedData.Int16ListViewBench: +18.5%, StringInterpolation: +18%, ... Issue https://github.com/dart-lang/sdk/issues/31954 Issue https://github.com/dart-lang/sdk/issues/35154 Change-Id: Ia4263a37241a36c9dc35e8a48893297effa6f4b2 Reviewed-on: https://dart-review.googlesource.com/c/84421 Commit-Queue: Martin Kustermann <kustermann@google.com> Reviewed-by: Vyacheslav Egorov <vegorov@google.com> Reviewed-by: Alexander Markov <alexmarkov@google.com>
696 lines
22 KiB
Dart
696 lines
22 KiB
Dart
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
|
|
// 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.
|
|
|
|
// part of "core_patch.dart";
|
|
|
|
abstract class _IntegerImplementation implements int {
|
|
@pragma("vm:non-nullable-result-type")
|
|
num operator +(num other) => other._addFromInteger(this);
|
|
@pragma("vm:non-nullable-result-type")
|
|
num operator -(num other) => other._subFromInteger(this);
|
|
@pragma("vm:non-nullable-result-type")
|
|
num operator *(num other) => other._mulFromInteger(this);
|
|
|
|
@pragma("vm:non-nullable-result-type")
|
|
int operator ~/(num other) {
|
|
if ((other is int) && (other == 0)) {
|
|
throw const IntegerDivisionByZeroException();
|
|
}
|
|
return other._truncDivFromInteger(this);
|
|
}
|
|
|
|
double operator /(num other) {
|
|
return this.toDouble() / other.toDouble();
|
|
}
|
|
|
|
@pragma("vm:non-nullable-result-type")
|
|
num operator %(num other) {
|
|
if ((other is int) && (other == 0)) {
|
|
throw const IntegerDivisionByZeroException();
|
|
}
|
|
return other._moduloFromInteger(this);
|
|
}
|
|
|
|
@pragma("vm:non-nullable-result-type")
|
|
int operator -() {
|
|
return 0 - this;
|
|
}
|
|
|
|
@pragma("vm:non-nullable-result-type")
|
|
int operator &(int other) => other._bitAndFromInteger(this);
|
|
@pragma("vm:non-nullable-result-type")
|
|
int operator |(int other) => other._bitOrFromInteger(this);
|
|
@pragma("vm:non-nullable-result-type")
|
|
int operator ^(int other) => other._bitXorFromInteger(this);
|
|
|
|
num remainder(num other) {
|
|
return other._remainderFromInteger(this);
|
|
}
|
|
|
|
@pragma("vm:non-nullable-result-type")
|
|
int _bitAndFromSmi(_Smi other) native "Integer_bitAndFromInteger";
|
|
@pragma("vm:non-nullable-result-type")
|
|
int _bitAndFromInteger(int other) native "Integer_bitAndFromInteger";
|
|
@pragma("vm:non-nullable-result-type")
|
|
int _bitOrFromInteger(int other) native "Integer_bitOrFromInteger";
|
|
@pragma("vm:non-nullable-result-type")
|
|
int _bitXorFromInteger(int other) native "Integer_bitXorFromInteger";
|
|
@pragma("vm:non-nullable-result-type")
|
|
int _shrFromInteger(int other) native "Integer_shrFromInteger";
|
|
@pragma("vm:non-nullable-result-type")
|
|
int _shlFromInteger(int other) native "Integer_shlFromInteger";
|
|
@pragma("vm:non-nullable-result-type")
|
|
int _addFromInteger(int other) native "Integer_addFromInteger";
|
|
@pragma("vm:non-nullable-result-type")
|
|
int _subFromInteger(int other) native "Integer_subFromInteger";
|
|
@pragma("vm:non-nullable-result-type")
|
|
int _mulFromInteger(int other) native "Integer_mulFromInteger";
|
|
@pragma("vm:non-nullable-result-type")
|
|
int _truncDivFromInteger(int other) native "Integer_truncDivFromInteger";
|
|
@pragma("vm:non-nullable-result-type")
|
|
int _moduloFromInteger(int other) native "Integer_moduloFromInteger";
|
|
int _remainderFromInteger(int other) {
|
|
return other - (other ~/ this) * this;
|
|
}
|
|
|
|
@pragma("vm:non-nullable-result-type")
|
|
int operator >>(int other) => other._shrFromInteger(this);
|
|
@pragma("vm:non-nullable-result-type")
|
|
int operator <<(int other) => other._shlFromInteger(this);
|
|
|
|
@pragma("vm:exact-result-type", bool)
|
|
bool operator <(num other) {
|
|
return other > this;
|
|
}
|
|
|
|
@pragma("vm:exact-result-type", bool)
|
|
bool operator >(num other) {
|
|
return other._greaterThanFromInteger(this);
|
|
}
|
|
|
|
@pragma("vm:exact-result-type", bool)
|
|
bool operator >=(num other) {
|
|
return (this == other) || (this > other);
|
|
}
|
|
|
|
@pragma("vm:exact-result-type", bool)
|
|
bool operator <=(num other) {
|
|
return (this == other) || (this < other);
|
|
}
|
|
|
|
@pragma("vm:exact-result-type", bool)
|
|
bool _greaterThanFromInteger(int other)
|
|
native "Integer_greaterThanFromInteger";
|
|
|
|
@pragma("vm:exact-result-type", bool)
|
|
bool operator ==(Object other) {
|
|
if (other is num) {
|
|
return other._equalToInteger(this);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@pragma("vm:exact-result-type", bool)
|
|
bool _equalToInteger(int other) native "Integer_equalToInteger";
|
|
int abs() {
|
|
return this < 0 ? -this : this;
|
|
}
|
|
|
|
int get sign {
|
|
return (this > 0) ? 1 : (this < 0) ? -1 : 0;
|
|
}
|
|
|
|
bool get isEven => ((this & 1) == 0);
|
|
bool get isOdd => !isEven;
|
|
bool get isNaN => false;
|
|
bool get isNegative => this < 0;
|
|
bool get isInfinite => false;
|
|
bool get isFinite => true;
|
|
|
|
int toUnsigned(int width) {
|
|
return this & ((1 << width) - 1);
|
|
}
|
|
|
|
int toSigned(int width) {
|
|
// The value of binary number weights each bit by a power of two. The
|
|
// twos-complement value weights the sign bit negatively. We compute the
|
|
// value of the negative weighting by isolating the sign bit with the
|
|
// correct power of two weighting and subtracting it from the value of the
|
|
// lower bits.
|
|
int signMask = 1 << (width - 1);
|
|
return (this & (signMask - 1)) - (this & signMask);
|
|
}
|
|
|
|
int compareTo(num other) {
|
|
const int EQUAL = 0, LESS = -1, GREATER = 1;
|
|
if (other is double) {
|
|
const int MAX_EXACT_INT_TO_DOUBLE = 9007199254740992; // 2^53.
|
|
const int MIN_EXACT_INT_TO_DOUBLE = -MAX_EXACT_INT_TO_DOUBLE;
|
|
const bool limitIntsTo64Bits = ((1 << 64) == 0);
|
|
if (limitIntsTo64Bits) {
|
|
// With integers limited to 64 bits, double.toInt() clamps
|
|
// double value to fit into the MIN_INT64..MAX_INT64 range.
|
|
// Check if the double value is outside of this range.
|
|
// This check handles +/-infinity as well.
|
|
const double minInt64AsDouble = -9223372036854775808.0;
|
|
// MAX_INT64 is not precisely representable in doubles, so
|
|
// check against (MAX_INT64 + 1).
|
|
const double maxInt64Plus1AsDouble = 9223372036854775808.0;
|
|
if (other < minInt64AsDouble) {
|
|
return GREATER;
|
|
} else if (other >= maxInt64Plus1AsDouble) {
|
|
return LESS;
|
|
}
|
|
} else {
|
|
if (other.isInfinite) {
|
|
return other.isNegative ? GREATER : LESS;
|
|
}
|
|
}
|
|
if (other.isNaN) {
|
|
return LESS;
|
|
}
|
|
if (MIN_EXACT_INT_TO_DOUBLE <= this && this <= MAX_EXACT_INT_TO_DOUBLE) {
|
|
// Let the double implementation deal with -0.0.
|
|
return -(other.compareTo(this.toDouble()));
|
|
} else {
|
|
// If abs(other) > MAX_EXACT_INT_TO_DOUBLE, then other has an integer
|
|
// value (no bits below the decimal point).
|
|
other = other.toInt();
|
|
}
|
|
}
|
|
if (this < other) {
|
|
return LESS;
|
|
} else if (this > other) {
|
|
return GREATER;
|
|
} else {
|
|
return EQUAL;
|
|
}
|
|
}
|
|
|
|
int round() {
|
|
return this;
|
|
}
|
|
|
|
int floor() {
|
|
return this;
|
|
}
|
|
|
|
int ceil() {
|
|
return this;
|
|
}
|
|
|
|
int truncate() {
|
|
return this;
|
|
}
|
|
|
|
double roundToDouble() {
|
|
return this.toDouble();
|
|
}
|
|
|
|
double floorToDouble() {
|
|
return this.toDouble();
|
|
}
|
|
|
|
double ceilToDouble() {
|
|
return this.toDouble();
|
|
}
|
|
|
|
double truncateToDouble() {
|
|
return this.toDouble();
|
|
}
|
|
|
|
num clamp(num lowerLimit, num upperLimit) {
|
|
if (lowerLimit is! num) {
|
|
throw new ArgumentError.value(lowerLimit, "lowerLimit", "not a number");
|
|
}
|
|
if (upperLimit is! num) {
|
|
throw new ArgumentError.value(upperLimit, "upperLimit", "not a number");
|
|
}
|
|
|
|
// Special case for integers.
|
|
if (lowerLimit is int && upperLimit is int && lowerLimit <= upperLimit) {
|
|
if (this < lowerLimit) return lowerLimit;
|
|
if (this > upperLimit) return upperLimit;
|
|
return this;
|
|
}
|
|
// Generic case involving doubles, and invalid integer ranges.
|
|
if (lowerLimit.compareTo(upperLimit) > 0) {
|
|
throw new ArgumentError(lowerLimit);
|
|
}
|
|
if (lowerLimit.isNaN) return lowerLimit;
|
|
// Note that we don't need to care for -0.0 for the lower limit.
|
|
if (this < lowerLimit) return lowerLimit;
|
|
if (this.compareTo(upperLimit) > 0) return upperLimit;
|
|
return this;
|
|
}
|
|
|
|
int toInt() {
|
|
return this;
|
|
}
|
|
|
|
@pragma("vm:exact-result-type", _Double)
|
|
double toDouble() {
|
|
return new _Double.fromInteger(this);
|
|
}
|
|
|
|
String toStringAsFixed(int fractionDigits) {
|
|
return this.toDouble().toStringAsFixed(fractionDigits);
|
|
}
|
|
|
|
String toStringAsExponential([int fractionDigits]) {
|
|
return this.toDouble().toStringAsExponential(fractionDigits);
|
|
}
|
|
|
|
String toStringAsPrecision(int precision) {
|
|
return this.toDouble().toStringAsPrecision(precision);
|
|
}
|
|
|
|
static const _digits = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
|
|
String toRadixString(int radix) {
|
|
if (radix < 2 || 36 < radix) {
|
|
throw new RangeError.range(radix, 2, 36, "radix");
|
|
}
|
|
if (radix & (radix - 1) == 0) {
|
|
return _toPow2String(radix);
|
|
}
|
|
if (radix == 10) return this.toString();
|
|
final bool isNegative = this < 0;
|
|
int value = isNegative ? -this : this;
|
|
if (value < 0) {
|
|
// With integers limited to 64 bits, the value
|
|
// MIN_INT64 = -0x8000000000000000 overflows at negation:
|
|
// -MIN_INT64 == MIN_INT64, so it requires special handling.
|
|
return _minInt64ToRadixString(radix);
|
|
}
|
|
List temp = new List();
|
|
do {
|
|
int digit = value % radix;
|
|
value ~/= radix;
|
|
temp.add(_digits.codeUnitAt(digit));
|
|
} while (value > 0);
|
|
if (isNegative) temp.add(0x2d); // '-'.
|
|
|
|
_OneByteString string = _OneByteString._allocate(temp.length);
|
|
for (int i = 0, j = temp.length; j > 0; i++) {
|
|
string._setAt(i, temp[--j]);
|
|
}
|
|
return string;
|
|
}
|
|
|
|
String _toPow2String(int radix) {
|
|
int value = this;
|
|
if (value == 0) return "0";
|
|
assert(radix & (radix - 1) == 0);
|
|
var negative = value < 0;
|
|
var bitsPerDigit = radix.bitLength - 1;
|
|
var length = 0;
|
|
if (negative) {
|
|
value = -value;
|
|
length = 1;
|
|
if (value < 0) {
|
|
// With integers limited to 64 bits, the value
|
|
// MIN_INT64 = -0x8000000000000000 overflows at negation:
|
|
// -MIN_INT64 == MIN_INT64, so it requires special handling.
|
|
return _minInt64ToRadixString(radix);
|
|
}
|
|
}
|
|
// Integer division, rounding up, to find number of _digits.
|
|
length += (value.bitLength + bitsPerDigit - 1) ~/ bitsPerDigit;
|
|
_OneByteString string = _OneByteString._allocate(length);
|
|
string._setAt(0, 0x2d); // '-'. Is overwritten if not negative.
|
|
var mask = radix - 1;
|
|
do {
|
|
string._setAt(--length, _digits.codeUnitAt(value & mask));
|
|
value >>= bitsPerDigit;
|
|
} while (value > 0);
|
|
return string;
|
|
}
|
|
|
|
/// Converts negative value to radix string.
|
|
/// This method is only used to handle corner case of
|
|
/// MIN_INT64 = -0x8000000000000000.
|
|
String _minInt64ToRadixString(int radix) {
|
|
List temp = new List();
|
|
int value = this;
|
|
assert(value < 0);
|
|
do {
|
|
int digit = -value.remainder(radix);
|
|
value ~/= radix;
|
|
temp.add(_digits.codeUnitAt(digit));
|
|
} while (value != 0);
|
|
temp.add(0x2d); // '-'.
|
|
|
|
_OneByteString string = _OneByteString._allocate(temp.length);
|
|
for (int i = 0, j = temp.length; j > 0; i++) {
|
|
string._setAt(i, temp[--j]);
|
|
}
|
|
return string;
|
|
}
|
|
|
|
// Returns pow(this, e) % m.
|
|
int modPow(int e, int m) {
|
|
if (e is! int) {
|
|
throw new ArgumentError.value(e, "exponent", "not an integer");
|
|
}
|
|
if (m is! int) {
|
|
throw new ArgumentError.value(m, "modulus", "not an integer");
|
|
}
|
|
if (e < 0) throw new RangeError.range(e, 0, null, "exponent");
|
|
if (m <= 0) throw new RangeError.range(m, 1, null, "modulus");
|
|
if (e == 0) return 1;
|
|
int b = this;
|
|
if (b < 0 || b > m) {
|
|
b %= m;
|
|
}
|
|
int r = 1;
|
|
while (e > 0) {
|
|
if (e.isOdd) {
|
|
r = (r * b) % m;
|
|
}
|
|
e >>= 1;
|
|
b = (b * b) % m;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
// If inv is false, returns gcd(x, y).
|
|
// If inv is true and gcd(x, y) = 1, returns d, so that c*x + d*y = 1.
|
|
// If inv is true and gcd(x, y) != 1, throws Exception("Not coprime").
|
|
static int _binaryGcd(int x, int y, bool inv) {
|
|
int s = 0;
|
|
if (!inv) {
|
|
while (x.isEven && y.isEven) {
|
|
x >>= 1;
|
|
y >>= 1;
|
|
s++;
|
|
}
|
|
if (y.isOdd) {
|
|
var t = x;
|
|
x = y;
|
|
y = t;
|
|
}
|
|
}
|
|
final bool ac = x.isEven;
|
|
int u = x;
|
|
int v = y;
|
|
int a = 1, b = 0, c = 0, d = 1;
|
|
do {
|
|
while (u.isEven) {
|
|
u >>= 1;
|
|
if (ac) {
|
|
if (!a.isEven || !b.isEven) {
|
|
a += y;
|
|
b -= x;
|
|
}
|
|
a >>= 1;
|
|
} else if (!b.isEven) {
|
|
b -= x;
|
|
}
|
|
b >>= 1;
|
|
}
|
|
while (v.isEven) {
|
|
v >>= 1;
|
|
if (ac) {
|
|
if (!c.isEven || !d.isEven) {
|
|
c += y;
|
|
d -= x;
|
|
}
|
|
c >>= 1;
|
|
} else if (!d.isEven) {
|
|
d -= x;
|
|
}
|
|
d >>= 1;
|
|
}
|
|
if (u >= v) {
|
|
u -= v;
|
|
if (ac) a -= c;
|
|
b -= d;
|
|
} else {
|
|
v -= u;
|
|
if (ac) c -= a;
|
|
d -= b;
|
|
}
|
|
} while (u != 0);
|
|
if (!inv) return v << s;
|
|
if (v != 1) {
|
|
throw new Exception("Not coprime");
|
|
}
|
|
if (d < 0) {
|
|
d += x;
|
|
if (d < 0) d += x;
|
|
} else if (d > x) {
|
|
d -= x;
|
|
if (d > x) d -= x;
|
|
}
|
|
return d;
|
|
}
|
|
|
|
// Returns 1/this % m, with m > 0.
|
|
int modInverse(int m) {
|
|
if (m is! int) {
|
|
throw new ArgumentError.value(m, "modulus", "not an integer");
|
|
}
|
|
if (m <= 0) throw new RangeError.range(m, 1, null, "modulus");
|
|
if (m == 1) return 0;
|
|
int t = this;
|
|
if ((t < 0) || (t >= m)) t %= m;
|
|
if (t == 1) return 1;
|
|
if ((t == 0) || (t.isEven && m.isEven)) {
|
|
throw new Exception("Not coprime");
|
|
}
|
|
return _binaryGcd(m, t, true);
|
|
}
|
|
|
|
// Returns gcd of abs(this) and abs(other).
|
|
int gcd(int other) {
|
|
if (other is! int) {
|
|
throw new ArgumentError.value(other, "other", "not an integer");
|
|
}
|
|
int x = this.abs();
|
|
int y = other.abs();
|
|
if (x == 0) return y;
|
|
if (y == 0) return x;
|
|
if ((x == 1) || (y == 1)) return 1;
|
|
return _binaryGcd(x, y, false);
|
|
}
|
|
}
|
|
|
|
@pragma("vm:entry-point")
|
|
class _Smi extends _IntegerImplementation {
|
|
factory _Smi._uninstantiable() {
|
|
throw new UnsupportedError("_Smi can only be allocated by the VM");
|
|
}
|
|
int get hashCode => this;
|
|
int get _identityHashCode => this;
|
|
@pragma("vm:exact-result-type", "dart:core#_Smi")
|
|
int operator ~() native "Smi_bitNegate";
|
|
@pragma("vm:exact-result-type", "dart:core#_Smi")
|
|
int get bitLength native "Smi_bitLength";
|
|
|
|
int operator &(int other) => other._bitAndFromSmi(this);
|
|
|
|
@pragma("vm:exact-result-type", "dart:core#_Smi")
|
|
int _bitAndFromSmi(_Smi other) native "Smi_bitAndFromSmi";
|
|
|
|
/**
|
|
* The digits of '00', '01', ... '99' as a single array.
|
|
*
|
|
* Get the digits of `n`, with `0 <= n < 100`, as
|
|
* `_digitTable[n * 2]` and `_digitTable[n * 2 + 1]`.
|
|
*/
|
|
static const _digitTable = const [
|
|
0x30, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, //
|
|
0x30, 0x34, 0x30, 0x35, 0x30, 0x36, 0x30, 0x37, //
|
|
0x30, 0x38, 0x30, 0x39, 0x31, 0x30, 0x31, 0x31, //
|
|
0x31, 0x32, 0x31, 0x33, 0x31, 0x34, 0x31, 0x35, //
|
|
0x31, 0x36, 0x31, 0x37, 0x31, 0x38, 0x31, 0x39, //
|
|
0x32, 0x30, 0x32, 0x31, 0x32, 0x32, 0x32, 0x33, //
|
|
0x32, 0x34, 0x32, 0x35, 0x32, 0x36, 0x32, 0x37, //
|
|
0x32, 0x38, 0x32, 0x39, 0x33, 0x30, 0x33, 0x31, //
|
|
0x33, 0x32, 0x33, 0x33, 0x33, 0x34, 0x33, 0x35, //
|
|
0x33, 0x36, 0x33, 0x37, 0x33, 0x38, 0x33, 0x39, //
|
|
0x34, 0x30, 0x34, 0x31, 0x34, 0x32, 0x34, 0x33, //
|
|
0x34, 0x34, 0x34, 0x35, 0x34, 0x36, 0x34, 0x37, //
|
|
0x34, 0x38, 0x34, 0x39, 0x35, 0x30, 0x35, 0x31, //
|
|
0x35, 0x32, 0x35, 0x33, 0x35, 0x34, 0x35, 0x35, //
|
|
0x35, 0x36, 0x35, 0x37, 0x35, 0x38, 0x35, 0x39, //
|
|
0x36, 0x30, 0x36, 0x31, 0x36, 0x32, 0x36, 0x33, //
|
|
0x36, 0x34, 0x36, 0x35, 0x36, 0x36, 0x36, 0x37, //
|
|
0x36, 0x38, 0x36, 0x39, 0x37, 0x30, 0x37, 0x31, //
|
|
0x37, 0x32, 0x37, 0x33, 0x37, 0x34, 0x37, 0x35, //
|
|
0x37, 0x36, 0x37, 0x37, 0x37, 0x38, 0x37, 0x39, //
|
|
0x38, 0x30, 0x38, 0x31, 0x38, 0x32, 0x38, 0x33, //
|
|
0x38, 0x34, 0x38, 0x35, 0x38, 0x36, 0x38, 0x37, //
|
|
0x38, 0x38, 0x38, 0x39, 0x39, 0x30, 0x39, 0x31, //
|
|
0x39, 0x32, 0x39, 0x33, 0x39, 0x34, 0x39, 0x35, //
|
|
0x39, 0x36, 0x39, 0x37, 0x39, 0x38, 0x39, 0x39, //
|
|
];
|
|
|
|
/**
|
|
* Result of int.toString for -99, -98, ..., 98, 99.
|
|
*/
|
|
static const _smallLookupTable = const [
|
|
"-99", "-98", "-97", "-96", "-95", "-94", "-93", "-92", "-91", "-90", //
|
|
"-89", "-88", "-87", "-86", "-85", "-84", "-83", "-82", "-81", "-80", //
|
|
"-79", "-78", "-77", "-76", "-75", "-74", "-73", "-72", "-71", "-70", //
|
|
"-69", "-68", "-67", "-66", "-65", "-64", "-63", "-62", "-61", "-60", //
|
|
"-59", "-58", "-57", "-56", "-55", "-54", "-53", "-52", "-51", "-50", //
|
|
"-49", "-48", "-47", "-46", "-45", "-44", "-43", "-42", "-41", "-40", //
|
|
"-39", "-38", "-37", "-36", "-35", "-34", "-33", "-32", "-31", "-30", //
|
|
"-29", "-28", "-27", "-26", "-25", "-24", "-23", "-22", "-21", "-20", //
|
|
"-19", "-18", "-17", "-16", "-15", "-14", "-13", "-12", "-11", "-10", //
|
|
"-9", "-8", "-7", "-6", "-5", "-4", "-3", "-2", "-1", "0", //
|
|
"1", "2", "3", "4", "5", "6", "7", "8", "9", "10", //
|
|
"11", "12", "13", "14", "15", "16", "17", "18", "19", "20", //
|
|
"21", "22", "23", "24", "25", "26", "27", "28", "29", "30", //
|
|
"31", "32", "33", "34", "35", "36", "37", "38", "39", "40", //
|
|
"41", "42", "43", "44", "45", "46", "47", "48", "49", "50", //
|
|
"51", "52", "53", "54", "55", "56", "57", "58", "59", "60", //
|
|
"61", "62", "63", "64", "65", "66", "67", "68", "69", "70", //
|
|
"71", "72", "73", "74", "75", "76", "77", "78", "79", "80", //
|
|
"81", "82", "83", "84", "85", "86", "87", "88", "89", "90", //
|
|
"91", "92", "93", "94", "95", "96", "97", "98", "99" //
|
|
];
|
|
|
|
// Powers of 10 above 1000000 are indistinguishable by eye.
|
|
static const int _POW_10_7 = 10000000;
|
|
static const int _POW_10_8 = 100000000;
|
|
static const int _POW_10_9 = 1000000000;
|
|
|
|
// Find the number of decimal digits in a positive smi.
|
|
// Never called with numbers < 100. These are handled before calling.
|
|
static int _positiveBase10Length(var smi) {
|
|
// A positive smi has length <= 19 if 63-bit, <=10 if 31-bit.
|
|
// Avoid comparing a 31-bit smi to a non-smi.
|
|
if (smi < 1000) return 3;
|
|
if (smi < 10000) return 4;
|
|
if (smi < _POW_10_7) {
|
|
if (smi < 100000) return 5;
|
|
if (smi < 1000000) return 6;
|
|
return 7;
|
|
}
|
|
if (smi < _POW_10_8) return 8;
|
|
if (smi < _POW_10_9) return 9;
|
|
smi = smi ~/ _POW_10_9;
|
|
// Handle numbers < 100 before calling recursively.
|
|
if (smi < 10) return 10;
|
|
if (smi < 100) return 11;
|
|
return 9 + _positiveBase10Length(smi);
|
|
}
|
|
|
|
String toString() {
|
|
if (this < 100 && this > -100) return _smallLookupTable[this + 99];
|
|
if (this < 0) return _negativeToString(this);
|
|
// Inspired by Andrei Alexandrescu: "Three Optimization Tips for C++"
|
|
// Avoid expensive remainder operation by doing it on more than
|
|
// one digit at a time.
|
|
const int DIGIT_ZERO = 0x30;
|
|
int length = _positiveBase10Length(this);
|
|
_OneByteString result = _OneByteString._allocate(length);
|
|
int index = length - 1;
|
|
var smi = this;
|
|
do {
|
|
// Two digits at a time.
|
|
var twoDigits = smi.remainder(100);
|
|
smi = smi ~/ 100;
|
|
int digitIndex = twoDigits * 2;
|
|
result._setAt(index, _digitTable[digitIndex + 1]);
|
|
result._setAt(index - 1, _digitTable[digitIndex]);
|
|
index -= 2;
|
|
} while (smi >= 100);
|
|
if (smi < 10) {
|
|
// Character code for '0'.
|
|
result._setAt(index, DIGIT_ZERO + smi);
|
|
} else {
|
|
// No remainder for this case.
|
|
int digitIndex = smi * 2;
|
|
result._setAt(index, _digitTable[digitIndex + 1]);
|
|
result._setAt(index - 1, _digitTable[digitIndex]);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Find the number of decimal digits in a negative smi.
|
|
// Never called with numbers > -100. These are handled before calling.
|
|
static int _negativeBase10Length(var negSmi) {
|
|
// A negative smi has length <= 19 if 63-bit, <=10 if 31-bit.
|
|
// Avoid comparing a 31-bit smi to a non-smi.
|
|
if (negSmi > -1000) return 3;
|
|
if (negSmi > -10000) return 4;
|
|
if (negSmi > -_POW_10_7) {
|
|
if (negSmi > -100000) return 5;
|
|
if (negSmi > -1000000) return 6;
|
|
return 7;
|
|
}
|
|
if (negSmi > -_POW_10_8) return 8;
|
|
if (negSmi > -_POW_10_9) return 9;
|
|
negSmi = negSmi ~/ _POW_10_9;
|
|
// Handle numbers > -100 before calling recursively.
|
|
if (negSmi > -10) return 10;
|
|
if (negSmi > -100) return 11;
|
|
return 9 + _negativeBase10Length(negSmi);
|
|
}
|
|
|
|
// Convert a negative smi to a string.
|
|
// Doesn't negate the smi to avoid negating the most negative smi, which
|
|
// would become a non-smi.
|
|
static String _negativeToString(int negSmi) {
|
|
// Character code for '-'
|
|
const int MINUS_SIGN = 0x2d;
|
|
// Character code for '0'.
|
|
const int DIGIT_ZERO = 0x30;
|
|
if (negSmi > -10) {
|
|
return _OneByteString._allocate(2)
|
|
.._setAt(0, MINUS_SIGN)
|
|
.._setAt(1, DIGIT_ZERO - negSmi);
|
|
}
|
|
if (negSmi > -100) {
|
|
int digitIndex = 2 * -negSmi;
|
|
return _OneByteString._allocate(3)
|
|
.._setAt(0, MINUS_SIGN)
|
|
.._setAt(1, _digitTable[digitIndex])
|
|
.._setAt(2, _digitTable[digitIndex + 1]);
|
|
}
|
|
// Number of digits, not including minus.
|
|
int digitCount = _negativeBase10Length(negSmi);
|
|
_OneByteString result = _OneByteString._allocate(digitCount + 1);
|
|
result._setAt(0, MINUS_SIGN); // '-'.
|
|
int index = digitCount;
|
|
do {
|
|
var twoDigits = negSmi.remainder(100);
|
|
negSmi = negSmi ~/ 100;
|
|
int digitIndex = -twoDigits * 2;
|
|
result._setAt(index, _digitTable[digitIndex + 1]);
|
|
result._setAt(index - 1, _digitTable[digitIndex]);
|
|
index -= 2;
|
|
} while (negSmi <= -100);
|
|
if (negSmi > -10) {
|
|
result._setAt(index, DIGIT_ZERO - negSmi);
|
|
} else {
|
|
// No remainder necessary for this case.
|
|
int digitIndex = -negSmi * 2;
|
|
result._setAt(index, _digitTable[digitIndex + 1]);
|
|
result._setAt(index - 1, _digitTable[digitIndex]);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
// Represents integers that cannot be represented by Smi but fit into 64bits.
|
|
@pragma("vm:entry-point")
|
|
class _Mint extends _IntegerImplementation {
|
|
factory _Mint._uninstantiable() {
|
|
throw new UnsupportedError("_Mint can only be allocated by the VM");
|
|
}
|
|
int get hashCode => this;
|
|
int get _identityHashCode => this;
|
|
@pragma("vm:non-nullable-result-type")
|
|
int operator ~() native "Mint_bitNegate";
|
|
@pragma("vm:exact-result-type", "dart:core#_Smi")
|
|
int get bitLength native "Mint_bitLength";
|
|
|
|
int _bitAndFromSmi(_Smi other) => _bitAndFromInteger(other);
|
|
}
|