blob: 3c593b380b04b5e89d11c02147d3cc4598aab341 [file] [log] [blame]
// Copyright (c) 2023, 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
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 {
final int _i;
/// The maximum positive value attainable by an [Int64], namely
/// 9,223,372,036,854,775,807.
static const Int64 MAX_VALUE = Int64(9223372036854775807);
/// The minimum positive value attainable by an [Int64], namely
/// -9,223,372,036,854,775,808.
static const Int64 MIN_VALUE = Int64(-9223372036854775808);
/// 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);
const Int64([int value = 0]) : _i = value;
/// Constructs an [Int64] from a pair of 32-bit integers having the value
/// [:((high & 0xffffffff) << 32) | (low & 0xffffffff):].
factory Int64.fromInts(int high, int low) =>
Int64((high << 32) | (low & 0xFFFFFFFF));
/// Constructs an [Int64] from the first 8 bytes in [bytes], as little endian.
factory Int64.fromBytes(List<int> bytes) => Int64(((bytes[7] & 0xFF) << 56) |
((bytes[6] & 0xFF) << 48) |
((bytes[5] & 0xFF) << 40) |
((bytes[4] & 0xFF) << 32) |
((bytes[3] & 0xFF) << 24) |
((bytes[2] & 0xFF) << 16) |
((bytes[1] & 0xFF) << 8) |
(bytes[0] & 0xFF));
/// Constructs an [Int64] from the first 8 bytes in [bytes], as big endian.
factory Int64.fromBytesBigEndian(List<int> bytes) => Int64((bytes[7] & 0xFF) |
((bytes[6] & 0xFF) << 8) |
((bytes[5] & 0xFF) << 16) |
((bytes[4] & 0xFF) << 24) |
((bytes[3] & 0xFF) << 32) |
((bytes[2] & 0xFF) << 40) |
((bytes[1] & 0xFF) << 48) |
((bytes[0] & 0xFF) << 56));
/// 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) => _parseRadix(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) => _parseRadix(source, 10, false);
/// 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) =>
_parseRadix(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) =>
_parseRadix(source, u.validateRadix(radix), false);
/// 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) => _parseRadix(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) => _parseRadix(source, 16, false);
static Int64? _parseRadix(String s, int radix, bool throwOnError) {
var charIdx = 0;
var negative = false;
if (s.startsWith('-')) {
negative = true;
charIdx++;
}
if (charIdx >= s.length) {
if (!throwOnError) return null;
throw FormatException('No digits', s, charIdx);
}
var i = 0;
for (; charIdx < s.length; charIdx++) {
final c = s.codeUnitAt(charIdx);
final digit = u.decodeDigit(c);
if (digit < radix) {
i = (i * radix) + digit;
} else {
if (!throwOnError) return null;
throw FormatException('Not radix digit', s, charIdx);
}
}
if (negative) {
return Int64(-i);
}
return Int64(i);
}
static int _promote(Object value) {
if (value is Int64) {
return value._i;
} 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) => Int64(_i + _promote(other));
@override
Int64 operator -(Object other) => Int64(_i - _promote(other));
@override
Int64 operator -() => Int64(-_i);
@override
Int64 operator *(Object other) => Int64(_i * _promote(other));
@override
Int64 operator %(Object other) => Int64(_i % _promote(other));
@override
Int64 operator ~/(Object other) => Int64(_i ~/ _promote(other));
@override
Int64 remainder(Object other) => Int64(_i.remainder(_promote(other)));
@override
Int64 operator &(Object other) => Int64(_i & _promote(other));
@override
Int64 operator |(Object other) => Int64(_i | _promote(other));
@override
Int64 operator ^(Object other) => Int64(_i ^ _promote(other));
@override
Int64 operator ~() => Int64(~_i);
@override
Int64 operator <<(int shiftAmount) => Int64(_i << shiftAmount);
@override
Int64 operator >>(int shiftAmount) => Int64(_i >> shiftAmount);
@override
Int64 shiftRightUnsigned(int shiftAmount) => Int64(_i >>> shiftAmount);
@override
int compareTo(Object other) => _i.compareTo(_promote(other));
@override
bool operator <(Object other) => _i < _promote(other);
@override
bool operator <=(Object other) => _i <= _promote(other);
@override
bool operator >(Object other) => _i > _promote(other);
@override
bool operator >=(Object other) => _i >= _promote(other);
@override
bool get isEven => _i.isEven;
@override
bool get isMaxValue => this == MAX_VALUE;
@override
bool get isMinValue => this == MIN_VALUE;
@override
bool get isNegative => _i < 0;
@override
bool get isOdd => _i.isOdd;
@override
bool get isZero => _i == 0;
@override
int get bitLength => _i.bitLength;
/// Returns a hash code based on the numeric value.
@override
int get hashCode => _i.hashCode;
/// Returns whether 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 _i == other._i;
} else if (other is int) {
return _i == other;
} else if (other is Int32) {
return _i == other.toInt();
} else {
return false;
}
}
@override
Int64 abs() => Int64(_i.abs());
@override
Int64 clamp(Object lowerLimit, Object upperLimit) =>
Int64(_i.clamp(_promote(lowerLimit), _promote(upperLimit)));
@override
int numberOfLeadingZeros() => _i < 0 ? 0 : (64 - _i.bitLength);
@override
int numberOfTrailingZeros() {
if (_i == 0) return 64;
var lsb = _i & (_i ^ (_i - 1));
if (lsb < 0) return 63;
return lsb.bitLength - 1;
}
@override
Int64 toSigned(int width) {
if (width < 1 || width > 64) {
throw RangeError.range(width, 1, 64);
}
return Int64(_i.toSigned(width));
}
@override
Int64 toUnsigned(int width) {
if (width < 0 || width > 64) {
throw RangeError.range(width, 0, 64);
}
return Int64(_i.toUnsigned(width));
}
@override
List<int> toBytes() {
final result = List<int>.filled(8, 0);
result[7] = (_i >> 56) & 0xff;
result[6] = (_i >> 48) & 0xff;
result[5] = (_i >> 40) & 0xff;
result[4] = (_i >> 32) & 0xff;
result[3] = (_i >> 24) & 0xff;
result[2] = (_i >> 16) & 0xff;
result[1] = (_i >> 8) & 0xff;
result[0] = _i & 0xff;
return result;
}
@override
double toDouble() => _i.toDouble();
@override
int toInt() => _i;
@override
Int32 toInt32() => Int32(_i);
@override
Int64 toInt64() => this;
@override
String toString() => _i.toString();
@override
String toHexString() => toRadixStringUnsigned(16).toUpperCase();
@override
String toRadixString(int radix) => _i.toRadixString(radix);
String toRadixStringUnsigned(int radix) => _toRadixStringUnsigned(_i, radix);
String toStringUnsigned() => _toRadixStringUnsigned(_i, 10);
static String _toRadixStringUnsigned(int value, int radix) {
if (radix < 2 || radix > 36) {
throw ArgumentError('toStringRadixUnsigned radix must be >= 2 and <= 36'
' (found $radix)');
}
if (value == 0) {
return '0';
}
// Split value into two 32-bit unsigned digits (v1, v0).
final v1 = value >>> 32;
var v0 = value.toUnsigned(32);
// Long division by a single digit: (q1, q0) = (v1, v0) ~/ radix, remainder r0.
final q1 = v1 ~/ radix;
final r1 = v1.remainder(radix);
v0 += r1 << 32;
final q0 = v0 ~/ radix;
final r0 = v0.remainder(radix); // lowest digit.
final otherDigits = (q1 << 32) + q0;
return '${otherDigits.toRadixString(radix)}${r0.toRadixString(radix)}';
}
}