blob: 5ff622ad1b27fa6057bf1226f566687a3e26bbf0 [file] [log] [blame] [edit]
// 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.
// ignore_for_file: constant_identifier_names
// Many locals are declared as `int` or `double`. We keep local variable types
// because the types are critical to the efficiency of many operations.
//
// ignore_for_file: omit_local_variable_types
import 'dart:typed_data';
import 'int32.dart';
import 'intx.dart';
import 'utilities.dart' as u;
/// An immutable 64-bit signed integer, in the range [-2^63, 2^63 - 1].
///
/// Arithmetic operations may overflow in order to maintain this range.
class Int64 implements IntX {
/// The 64-bit value.
final int _value;
// Minimal 64-bit value, -2^63.
static const int _minValue = -9223372036854775808;
// Maximal 64-bit value, 2^63 - 1.
static const int _maxValue = 9223372036854775807;
/// The maximum positive value attainable by an [Int64], namely
/// 9,223,372,036,854,775,807.
static const Int64 MAX_VALUE = Int64._(_maxValue);
/// The minimum positive value attainable by an [Int64], namely
/// -9,223,372,036,854,775,808.
static const Int64 MIN_VALUE = Int64._(_minValue);
/// An [Int64] constant equal to 0.
static const Int64 ZERO = Int64._(0);
/// An [Int64] constant equal to 1.
static const Int64 ONE = Int64._(1);
/// An [Int64] constant equal to 2.
static const Int64 TWO = Int64._(2);
/// Constructs an [Int64] with a given value.
const Int64._(this._value);
/// Parses [source] in a given [radix] between 2 and 36.
///
/// Returns an [Int64] with the numerical value of [source].
/// If the numerical value of [source] does not fit
/// in a signed 64 bit integer,
/// the numerical value is truncated to the lowest 64 bits
/// of the value's binary representation,
/// interpreted as a 64-bit two's complement integer.
///
/// The [source] string must contain a sequence of base-[radix]
/// digits (using letters from `a` to `z` as digits with values 10 through
/// 25 for radixes above 10), possibly prefixed by a `-` sign.
///
/// Throws a [FormatException] if the input is not recognized as a valid
/// integer numeral.
static Int64 parseRadix(String source, int radix) =>
Int64._(_parse(source, u.validateRadix(radix), true)!);
/// Parses [source] in a given [radix] between 2 and 36.
///
/// Returns an [Int64] with the numerical value of [source].
/// If the numerical value of [source] does not fit
/// in a signed 64 bit integer,
/// the numerical value is truncated to the lowest 64 bits
/// of the value's binary representation,
/// interpreted as a 64-bit two's complement integer.
///
/// The [source] string must contain a sequence of base-[radix]
/// digits (using letters from `a` to `z` as digits with values 10 through
/// 25 for radixes above 10), possibly prefixed by a `-` sign.
///
/// Returns `null` if the input is not recognized as a valid
/// integer numeral.
static Int64? tryParseRadix(String source, int radix) {
var value = _parse(source, u.validateRadix(radix), false);
return value == null ? null : Int64._(value);
}
/// Parses [source] as a decimal numeral.
///
/// Returns an [Int64] with the numerical value of [source].
/// If the numerical value of [source] does not fit
/// in a signed 64 bit integer,
/// the numerical value is truncated to the lowest 64 bits
/// of the value's binary representation,
/// interpreted as a 64-bit two's complement integer.
///
/// The [source] string must contain a sequence of digits (`0`-`9`),
/// possibly prefixed by a `-` sign.
///
/// Throws a [FormatException] if the input is not a valid
/// decimal integer numeral.
static Int64 parseInt(String source) => Int64._(_parse(source, 10, true)!);
/// Parses [source] as a decimal numeral.
///
/// Returns an [Int64] with the numerical value of [source].
/// If the numerical value of [source] does not fit
/// in a signed 64 bit integer,
/// the numerical value is truncated to the lowest 64 bits
/// of the value's binary representation,
/// interpreted as a 64-bit two's complement integer.
///
/// The [source] string must contain a sequence of digits (`0`-`9`),
/// possibly prefixed by a `-` sign.
///
/// Returns `null` if the input is not a valid
/// decimal integer numeral.
static Int64? tryParseInt(String source) {
var value = _parse(source, 10, false);
return value == null ? null : Int64._(value);
}
/// Parses [source] as a hexadecimal numeral.
///
/// Returns an [Int64] with the numerical value of [source].
/// If the numerical value of [source] does not fit
/// in a signed 64 bit integer,
/// the numerical value is truncated to the lowest 64 bits
/// of the value's binary representation,
/// interpreted as a 64-bit two's complement integer.
///
/// The [source] string must contain a sequence of hexadecimal
/// digits (`0`-`9`, `a`-`f` or `A`-`F`), possibly prefixed by a `-` sign.
///
/// Throws a [FormatException] if the input is not a valid
/// hexadecimal integer numeral.
static Int64 parseHex(String source) => Int64._(_parse(source, 16, true)!);
/// Parses [source] as a hexadecimal numeral.
///
/// Returns an [Int64] with the numerical value of [source].
/// If the numerical value of [source] does not fit
/// in a signed 64 bit integer,
/// the numerical value is truncated to the lowest 64 bits
/// of the value's binary representation,
/// interpreted as a 64-bit two's complement integer.
///
/// The [source] string must contain a sequence of hexadecimal
/// digits (`0`-`9`, `a`-`f` or `A`-`F`), possibly prefixed by a `-` sign.
///
/// Returns `null` if the input is not a valid
/// hexadecimal integer numeral.
static Int64? tryParseHex(String source) {
var value = _parse(source, 16, false);
return value == null ? null : Int64._(value);
}
/// Parses [source] as a base [radix] number.
///
/// Unlike `int.parse`, it allows over-long numerals, and truncates
/// the result.
static int? _parse(String source, int radix, bool throwOnError) {
// Try using `int.parse` first. If it works, we're set.
var fastResult = int.tryParse(source, radix: radix);
if (fastResult != null) return fastResult;
// If not, the input is either invalid, or too long for `int.parse`,
// and we need to truncate the value.
var negative = false;
var index = 0;
if (source.startsWith('-')) {
negative = true;
index = 1;
}
if (index == source.length) {
if (throwOnError) {
throw FormatException("No digits", source, index);
}
return null;
}
var result = 0;
while (index < source.length) {
var char = source.codeUnitAt(index);
var digit = u.decodeDigit(char);
if (digit < radix) {
result = result * radix + digit;
index++;
} else {
if (throwOnError) {
throw FormatException("Not a radix $radix digit", source, index);
}
return null;
}
}
return negative ? -result : result;
}
//
// Public constructors
//
/// Constructs an [Int64] with a given [int] value; zero by default.
factory Int64([int value = 0]) => Int64._(value);
factory Int64.fromBytes(List<int> bytes) {
var result = 0;
for (var i = 7; i >= 0; i--) {
result = (result << 8) | (bytes[i] & 0xFF);
}
return Int64._(result);
}
factory Int64.fromBytesBigEndian(List<int> bytes) {
var result = 0;
for (var i = 0; i < 8; i++) {
result = (result << 8) | (bytes[i] & 0xFF);
}
return Int64._(result);
}
/// Constructs an [Int64] from a pair of 32-bit integers having the value
/// [:((top & 0xffffffff) << 32) | (bottom & 0xffffffff):].
factory Int64.fromInts(int top, int bottom) => Int64._(top << 32 | bottom);
// Returns the [Int64] representation of the specified value. Throws
// [ArgumentError] for non-integer arguments.
static int _promote(value) {
if (value is Int64) {
return value._value;
} else if (value is int) {
return value;
} else if (value is Int32) {
return value.toInt();
}
throw ArgumentError.value(value, 'other', 'not an int, Int32 or Int64');
}
@override
Int64 operator +(Object other) {
int o = _promote(other);
return Int64._(_value + o);
}
@override
Int64 operator -(Object other) {
int o = _promote(other);
return Int64._(_value - o);
}
@override
Int64 operator -() => Int64._(-_value);
@override
Int64 operator *(Object other) {
int o = _promote(other);
return Int64._(_value * o);
}
@override
Int64 operator %(Object other) {
int o = _promote(other);
return Int64._(_value % o);
}
@override
Int64 operator ~/(Object other) {
int o = _promote(other);
return Int64._(_value ~/ o);
}
@override
Int64 remainder(Object other) {
int o = _promote(other);
return Int64._(_value.remainder(o));
}
@override
Int64 operator &(Object other) {
int o = _promote(other);
return Int64._(_value & o);
}
@override
Int64 operator |(Object other) {
int o = _promote(other);
return Int64._(_value | o);
}
@override
Int64 operator ^(Object other) {
int o = _promote(other);
return Int64._(_value ^ o);
}
@override
Int64 operator ~() => Int64._(~_value);
@override
Int64 operator <<(int n) => Int64._(_value << n);
@override
Int64 operator >>(int n) => Int64._(_value >> n);
@override
Int64 shiftRightUnsigned(int n) => Int64._(_value >>> n);
/// Returns [:true:] if this [Int64] has the same numeric value as the
/// given object. The argument may be an [int] or an [IntX].
@override
bool operator ==(Object other) {
if (other is Int64) {
return _value == other._value;
} else if (other is int) {
return _value == other;
} else if (other is Int32) {
return _value == other.toInt();
}
return false;
}
@override
int compareTo(Object other) => _value.compareTo(_promote(other));
@override
bool operator <(Object other) => _value < _promote(other);
@override
bool operator <=(Object other) => _value <= _promote(other);
@override
bool operator >(Object other) => _value > _promote(other);
@override
bool operator >=(Object other) => _value >= _promote(other);
@override
bool get isEven => _value.isEven;
@override
bool get isMaxValue => _value == _maxValue;
@override
bool get isMinValue => _value == _minValue;
@override
bool get isNegative => _value < 0;
@override
bool get isOdd => _value.isOdd;
@override
bool get isZero => _value == 0;
@override
int get bitLength => _value.bitLength;
/// Returns a hash code based on all the bits of this [Int64].
@override
int get hashCode => _value.hashCode;
@override
Int64 abs() => _value < 0 ? Int64._(-_value) : this;
@override
Int64 clamp(Object lowerLimit, Object upperLimit) {
if (lowerLimit is Int64) {
if (_value < lowerLimit._value) return lowerLimit;
} else {
int lower = _promote(lowerLimit);
if (_value < lower) return Int64._(lower);
}
if (upperLimit is Int64) {
if (_value > upperLimit._value) return upperLimit;
} else {
int upper = _promote(upperLimit);
if (_value > upper) return Int64._(upper);
}
return this;
}
/// Returns the number of leading zeros in this [Int64] as an [int]
/// between 0 and 64.
@override
int numberOfLeadingZeros() => _value < 0 ? 0 : 64 - _value.bitLength;
/// Returns the number of trailing zeros in this [Int64] as an [int]
/// between 0 and 64.
@override
int numberOfTrailingZeros() {
var value = _value;
if (value == 0) return 64;
// Set every bit up to and including the lowest set bit, clear the rest.
value ^= value - 1;
// Count of bits is number of trailing zeros plus one.
if (value >= 0 && value < 0x100000000) {
// u.bitCount only works up to 32-bit values.
return u.bitCount(value) - 1;
}
return 31 + u.bitCount(value >>> 32);
}
@override
Int64 toSigned(int width) {
if (width < 1 || width > 64) throw RangeError.range(width, 1, 64);
return Int64._(_value.toSigned(width));
}
@override
Int64 toUnsigned(int width) {
if (width < 0 || width > 64) throw RangeError.range(width, 0, 64);
return Int64._(_value.toUnsigned(width));
}
@override
List<int> toBytes() {
var result = List<int>.filled(8, 0);
var value = _value;
for (var i = 0; i < 8 && value != 0; i++) {
result[i] = value & 0xFF;
value >>= 8;
}
return result;
}
@override
double toDouble() => _value.toDouble();
@override
int toInt() => _value;
/// Returns an [Int32] containing the low 32 bits of this [Int64].
@override
Int32 toInt32() => Int32(_value & 0xFFFFFFFF);
/// Returns `this`.
@override
Int64 toInt64() => this;
/// Returns the value of this [Int64] as a decimal [String].
@override
String toString() => _value.toString();
@override
String toHexString() {
if (_value >= 0) return _value.toRadixString(16).toUpperCase();
return _toRadixStringUnsignedPow2(16, _value);
}
/// Returns the digits of `this` when interpreted as an unsigned 64-bit value.
String toStringUnsigned() {
if (_value >= 0) return _value.toString();
return _toRadixStringUnsigned(10, _value);
}
String toRadixStringUnsigned(int radix) {
if (_value >= 0) {
var result = _value.toRadixString(radix);
if (radix > 10) result = result.toUpperCase();
return result;
}
return _toRadixStringUnsigned(u.validateRadix(radix), _value);
}
@override
String toRadixString(int radix) =>
_value.toRadixString(u.validateRadix(radix));
// Reusable buffer.
static final Uint8List _buffer = Uint8List(64);
static String _toRadixStringUnsignedPow2(int radix, int value) {
var buffer = _buffer;
assert(value < 0);
var bits = u.bitCount(radix - 1);
var mask = radix - 1;
var index = 64;
while (value != 0) {
buffer[--index] = u.radixDigits.codeUnitAt(value & mask);
value >>>= bits;
}
return String.fromCharCodes(Uint8List.sublistView(buffer, index));
}
static String _toRadixStringUnsigned(int radix, int value) {
if (u.isPowerOf2(radix)) {
// 16 is likely to be the most common radix for unsigned,
// so use a faster approach, which works for all powers of 2.
return _toRadixStringUnsignedPow2(radix, value);
}
// Consider adding a special case for radix 10, if it's commonly used.
assert(value < 0);
// Remove sign bit, since it affects numerical value.
// Correct for missing 2^63 after first division.
value &= 0x7FFFFFFFFFFFFFFF;
var buffer = _buffer;
var index = 64;
var digit = value.remainder(radix);
value ~/= radix;
// Correct for missing 2^63.
// Table is (2^63 ~/ radix, 2^63 % radix) pairs, starting from radix 3.
value += _toUnsignedTable[radix * 2 - 6];
digit += _toUnsignedTable[radix * 2 - 5];
if (digit >= radix) {
digit -= radix;
value += 1;
}
buffer[--index] = u.radixDigits.codeUnitAt(digit);
do {
var digit = value.remainder(radix);
value ~/= radix;
buffer[--index] = u.radixDigits.codeUnitAt(digit);
} while (value != 0);
return String.fromCharCodes(Uint8List.sublistView(buffer, index));
}
/// Table of 2^63 ~/ radix and 2^63 % radix for radix 3-36.
///
/// Omits powers of two, since they use a simpler algorthm.
static const List<int> _toUnsignedTable = [
3074457345618258602, 2, // 3
0, 0, // 4
1844674407370955161, 3, // 5
1537228672809129301, 2, // 6
1317624576693539401, 1, // 7
0, 0, // 8
1024819115206086200, 8, // 9
922337203685477580, 8, // 10
838488366986797800, 8, // 11
768614336404564650, 8, // 12
709490156681136600, 8, // 13
658812288346769700, 8, // 14
614891469123651720, 8, // 15
0, 0, // 16
542551296285575047, 9, // 17
512409557603043100, 8, // 18
485440633518672410, 18, // 19
461168601842738790, 8, // 20
439208192231179800, 8, // 21
419244183493398900, 8, // 22
401016175515425035, 3, // 23
384307168202282325, 8, // 24
368934881474191032, 8, // 25
354745078340568300, 8, // 26
341606371735362066, 26, // 27
329406144173384850, 8, // 28
318047311615681924, 12, // 29
307445734561825860, 8, // 30
297528130221121800, 8, // 31
0, 0, // 32
279496122328932600, 8, // 33
271275648142787523, 26, // 34
263524915338707880, 8, // 35
256204778801521550, 8, // 36
];
String toDebugString() => 'Int64[$_value]';
}