// 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. // TODO(srdjan): fix limitations. // - shift amount must be a Smi. class _IntegerImplementation { factory _IntegerImplementation._uninstantiable() { throw new UnsupportedError( "_IntegerImplementation can only be allocated by the VM"); } Type get runtimeType => int; num operator +(num other) { return other._addFromInteger(this); } num operator -(num other) { return other._subFromInteger(this); } num operator *(num other) { return other._mulFromInteger(this); } num operator ~/(num other) { if ((other is int) && (other == 0)) { throw const IntegerDivisionByZeroException(); } return other._truncDivFromInteger(this); } num operator /(num other) { return this.toDouble() / other.toDouble(); } num operator %(num other) { if ((other is int) && (other == 0)) { throw const IntegerDivisionByZeroException(); } return other._moduloFromInteger(this); } int operator -() { return 0 - this; } int operator &(int other) { return other._bitAndFromInteger(this); } int operator |(int other) { return other._bitOrFromInteger(this); } int operator ^(int other) { return other._bitXorFromInteger(this); } num remainder(num other) { return other._remainderFromInteger(this); } int _bitAndFromInteger(int other) native "Integer_bitAndFromInteger"; int _bitOrFromInteger(int other) native "Integer_bitOrFromInteger"; int _bitXorFromInteger(int other) native "Integer_bitXorFromInteger"; int _addFromInteger(int other) native "Integer_addFromInteger"; int _subFromInteger(int other) native "Integer_subFromInteger"; int _mulFromInteger(int other) native "Integer_mulFromInteger"; int _truncDivFromInteger(int other) native "Integer_truncDivFromInteger"; int _moduloFromInteger(int other) native "Integer_moduloFromInteger"; int _remainderFromInteger(int other) { return other - (other ~/ this) * this; } int operator >>(int other) { return other._shrFromInt(this); } int operator <<(int other) { return other._shlFromInt(this); } bool operator <(num other) { return other > this; } bool operator >(num other) { return other._greaterThanFromInteger(this); } bool operator >=(num other) { return (this == other) || (this > other); } bool operator <=(num other) { return (this == other) || (this < other); } bool _greaterThanFromInteger(int other) native "Integer_greaterThanFromInteger"; bool operator ==(other) { if (other is num) { return other._equalToInteger(this); } return false; } 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) { final int EQUAL = 0, LESS = -1, GREATER = 1; if (other is double) { // TODO(floitsch): the following locals should be 'const'. int MAX_EXACT_INT_TO_DOUBLE = 9007199254740992; // 2^53. int MIN_EXACT_INT_TO_DOUBLE = -MAX_EXACT_INT_TO_DOUBLE; double d = other; if (d.isInfinite) { return d == double.NEGATIVE_INFINITY ? GREATER : LESS; } if (d.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 -(d.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 = d.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(lowerLimit); if (upperLimit is! num) throw new ArgumentError(upperLimit); // Special case for integers. if (lowerLimit is int && upperLimit is int) { if (lowerLimit > upperLimit) { throw new ArgumentError(lowerLimit); } if (this < lowerLimit) return lowerLimit; if (this > upperLimit) return upperLimit; return this; } // Generic case involving doubles. 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; } 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 is! int || radix < 2 || radix > 36) { throw new ArgumentError(radix); } if (radix & (radix - 1) == 0) { return _toPow2String(this, radix); } if (radix == 10) return this.toString(); final bool isNegative = this < 0; int value = isNegative ? -this : this; 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; } static String _toPow2String(value, radix) { 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; } // 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; } _leftShiftWithMask32(count, mask) native "Integer_leftShiftWithMask32"; } class _Smi extends _IntegerImplementation implements int { factory _Smi._uninstantiable() { throw new UnsupportedError( "_Smi can only be allocated by the VM"); } int get _identityHashCode { return this; } int operator ~() native "Smi_bitNegate"; int get bitLength native "Smi_bitLength"; int _shrFromInt(int other) native "Smi_shrFromInt"; int _shlFromInt(int other) native "Smi_shlFromInt"; String toString() { if (this == 0) return "0"; var reversed = _toStringBuffer; var negative = false; var val = this; int index = 0; if (this < 0) { negative = true; // Handle the first digit as negative to avoid negating the minimum // smi, for which the negation is not a smi. int digit = -(val.remainder(10)); reversed[index++] = digit + 0x30; val = -(val ~/ 10); } while (val > 0) { int digit = val % 10; val = val ~/ 10; reversed[index++] = (digit + 0x30); } if (negative) reversed[index++] = 0x2D; // '-'. _OneByteString string = _OneByteString._allocate(index); for (int i = 0, j = index; i < index; i++) { string._setAt(i, reversed[--j]); } return string; } } // Reusable buffer used by smi.toString. final List _toStringBuffer = new Uint8List(20); // Represents integers that cannot be represented by Smi but fit into 64bits. class _Mint extends _IntegerImplementation implements int { factory _Mint._uninstantiable() { throw new UnsupportedError( "_Mint can only be allocated by the VM"); } int get _identityHashCode { return this; } int operator ~() native "Mint_bitNegate"; int get bitLength native "Mint_bitLength"; // Shift by mint exceeds range that can be handled by the VM. int _shrFromInt(int other) { if (other < 0) { return -1; } else { return 0; } } int _shlFromInt(int other) native "Mint_shlFromInt"; } // A number that can be represented as Smi or Mint will never be represented as // Bigint. class _Bigint extends _IntegerImplementation implements int { factory _Bigint._uninstantiable() { throw new UnsupportedError( "_Bigint can only be allocated by the VM"); } int get _identityHashCode { return this; } int operator ~() native "Bigint_bitNegate"; int get bitLength native "Bigint_bitLength"; // Shift by bigint exceeds range that can be handled by the VM. int _shrFromInt(int other) { if (other < 0) { return -1; } else { return 0; } } int _shlFromInt(int other) native "Bigint_shlFromInt"; int pow(int exponent) { throw "Bigint.pow not implemented"; } }