mirror of https://github.com/dahall/Vanara.git
1048 lines
34 KiB
C#
1048 lines
34 KiB
C#
using System.Buffers.Binary;
|
|
using System.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.Numerics;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Runtime.Serialization;
|
|
|
|
namespace Vanara.PInvoke;
|
|
|
|
/// <summary>Managed instance of the SIZE_T type.</summary>
|
|
[StructLayout(LayoutKind.Sequential), Serializable]
|
|
[TypeConverter(typeof(SizeTTypeConverter))]
|
|
[DebuggerDisplay("{Value}")]
|
|
public struct SizeT : IEquatable<SizeT>, IComparable<SizeT>, IConvertible, IComparable, ISerializable
|
|
#if NET7_0_OR_GREATER
|
|
, IParsable<SizeT>, ISpanParsable<SizeT>, IBinaryInteger<SizeT>, IUnsignedNumber<SizeT>
|
|
#endif
|
|
{
|
|
/// <summary>
|
|
/// Represents the largest possible value of <see cref="SizeT"/>. This property is determined by the maximum bit-size of a pointer.
|
|
/// </summary>
|
|
public static readonly SizeT MaxValue =
|
|
#if NET6_0_OR_GREATER
|
|
nuint.MaxValue;
|
|
#else
|
|
UIntPtr.Size == 8 ? new(ulong.MaxValue) : new(uint.MaxValue);
|
|
#endif
|
|
|
|
/// <summary>Represents the smallest possible value of <see cref="SizeT"/>. This field is constant.</summary>
|
|
public static readonly SizeT MinValue = 0;
|
|
|
|
/// <summary>Represents the zero value of <see cref="SizeT"/>. This field is constant.</summary>
|
|
public static readonly SizeT Zero = default;
|
|
|
|
private nuint val;
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SizeT"/> struct.</summary>
|
|
/// <param name="value">The value.</param>
|
|
public SizeT(uint value) => val = value;
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SizeT"/> struct.</summary>
|
|
/// <param name="value">The value.</param>
|
|
public SizeT(ulong value) => val = new UIntPtr(value);
|
|
|
|
/// <summary>Initializes a new instance of the <see cref="SizeT"/> struct.</summary>
|
|
/// <param name="value">The value.</param>
|
|
public unsafe SizeT(void* value) => val = (nuint)value;
|
|
|
|
private SizeT(SerializationInfo info, StreamingContext context) => val = (nuint)info.GetUInt64("value");
|
|
|
|
/// <inheritdoc/>
|
|
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
|
|
{
|
|
if (info is null) throw new ArgumentNullException(nameof(info));
|
|
info.AddValue("value", (ulong)val);
|
|
}
|
|
|
|
/// <summary>Gets the value.</summary>
|
|
/// <value>The value.</value>
|
|
public ulong Value { get => val; private set => val = new UIntPtr(value); }
|
|
|
|
#if NET7_0_OR_GREATER
|
|
/// <inheritdoc/>
|
|
static SizeT IAdditiveIdentity<SizeT, SizeT>.AdditiveIdentity => Zero;
|
|
|
|
/// <inheritdoc/>
|
|
static SizeT IMultiplicativeIdentity<SizeT, SizeT>.MultiplicativeIdentity => new(1UL);
|
|
|
|
/// <inheritdoc/>
|
|
static SizeT INumberBase<SizeT>.One => new(1UL);
|
|
|
|
/// <inheritdoc/>
|
|
static int INumberBase<SizeT>.Radix => 2;
|
|
|
|
/// <inheritdoc/>
|
|
static SizeT INumberBase<SizeT>.Zero => Zero;
|
|
#endif
|
|
|
|
/// <summary>Performs an implicit conversion from <see cref="SizeT"/> to <see cref="int"/>.</summary>
|
|
/// <param name="value">The value.</param>
|
|
/// <returns>The result of the conversion.</returns>
|
|
public static implicit operator int(SizeT value) => ((IConvertible)value).ToInt32(null);
|
|
|
|
/// <summary>Performs an implicit conversion from <see cref="SizeT"/> to <see cref="long"/>.</summary>
|
|
/// <param name="value">The value.</param>
|
|
/// <returns>The result of the conversion.</returns>
|
|
public static implicit operator long(SizeT value) => ((IConvertible)value).ToInt64(null);
|
|
|
|
/// <summary>Performs an implicit conversion from <see cref="int"/> to <see cref="SizeT"/>.</summary>
|
|
/// <param name="value">The value.</param>
|
|
/// <returns>The result of the conversion.</returns>
|
|
public static implicit operator SizeT(int value) => value >= 0 ? new SizeT((uint)value) : throw new ArgumentOutOfRangeException(nameof(value));
|
|
|
|
/// <summary>Performs an implicit conversion from <see cref="uint"/> to <see cref="SizeT"/>.</summary>
|
|
/// <param name="value">The value.</param>
|
|
/// <returns>The result of the conversion.</returns>
|
|
public static implicit operator SizeT(uint value) => new(value);
|
|
|
|
/// <summary>Performs an implicit conversion from <see cref="long"/> to <see cref="SizeT"/>.</summary>
|
|
/// <param name="value">The value.</param>
|
|
/// <returns>The result of the conversion.</returns>
|
|
public static implicit operator SizeT(long value) => value >= 0 ? new SizeT((ulong)value) : throw new ArgumentOutOfRangeException(nameof(value));
|
|
|
|
/// <summary>Performs an implicit conversion from <see cref="ulong"/> to <see cref="SizeT"/>.</summary>
|
|
/// <param name="value">The value.</param>
|
|
/// <returns>The result of the conversion.</returns>
|
|
public static implicit operator SizeT(ulong value) => new(value);
|
|
|
|
/// <summary>Performs an implicit conversion from <see cref="SizeT"/> to <see cref="uint"/>.</summary>
|
|
/// <param name="value">The value.</param>
|
|
/// <returns>The result of the conversion.</returns>
|
|
public static implicit operator uint(SizeT value) => (uint)value.val;
|
|
|
|
/// <summary>Performs an implicit conversion from <see cref="SizeT"/> to <see cref="ulong"/>.</summary>
|
|
/// <param name="value">The value.</param>
|
|
/// <returns>The result of the conversion.</returns>
|
|
public static implicit operator ulong(SizeT value) => value.val;
|
|
|
|
/// <summary>Subtracts two specified <see cref="SizeT"/> values.</summary>
|
|
/// <param name="s1">The minuend.</param>
|
|
/// <param name="s2">The subtrahend.</param>
|
|
/// <returns>The result of subtracting <paramref name="s2"/> from <paramref name="s1"/>.</returns>
|
|
public static SizeT operator -(SizeT s1, SizeT s2) => s1.Value - s2.Value;
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT operator -(SizeT value) => UIntPtr.Zero - value.val;
|
|
|
|
/// <summary>Decrements the <see cref="SizeT"/> by 1.</summary>
|
|
/// <param name="s1">The value to decrement.</param>
|
|
/// <returns>The value of <paramref name="s1"/> decremented by 1.</returns>
|
|
public static SizeT operator --(SizeT s1) => s1.Value += 1;
|
|
|
|
/// <summary>Indicates whether two <see cref="SizeT"/> instances are not equal.</summary>
|
|
/// <param name="s1">The first integral size to compare.</param>
|
|
/// <param name="s2">The second integral size to compare.</param>
|
|
/// <returns>
|
|
/// <see langword="true"/> if the value of <paramref name="s1"/> is not equal to the value of <paramref name="s2"/>; otherwise, <see langword="false"/>.
|
|
/// </returns>
|
|
public static bool operator !=(SizeT s1, SizeT s2) => !s1.Equals(s2);
|
|
|
|
/// <summary>Returns the remainder resulting from dividing two specified <see cref="SizeT"/> values.</summary>
|
|
/// <param name="s1">The divident.</param>
|
|
/// <param name="s2">The divisor.</param>
|
|
/// <returns>The remainder resulting from dividing <paramref name="s1"/> by <paramref name="s2"/>.</returns>
|
|
public static SizeT operator %(SizeT s1, SizeT s2) => s1.Value % s2.Value;
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT operator &(SizeT left, SizeT right) => left.Value & right.Value;
|
|
|
|
/// <summary>Multiplies two specified <see cref="SizeT"/> values.</summary>
|
|
/// <param name="s1">The first value to multiply.</param>
|
|
/// <param name="s2">The second value to multiply.</param>
|
|
/// <returns>The result of multiplying <paramref name="s1"/> by <paramref name="s2"/>.</returns>
|
|
public static SizeT operator *(SizeT s1, SizeT s2) => s1.Value * s2.Value;
|
|
|
|
/// <summary>Divides two specified <see cref="SizeT"/> values.</summary>
|
|
/// <param name="s1">The divident.</param>
|
|
/// <param name="s2">The divisor.</param>
|
|
/// <returns>The result of dividing <paramref name="s1"/> by <paramref name="s2"/>.</returns>
|
|
public static SizeT operator /(SizeT s1, SizeT s2) => s1.Value / s2.Value;
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT operator ^(SizeT left, SizeT right) => left.Value ^ right.Value;
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT operator ~(SizeT value) => ~value.Value;
|
|
|
|
/// <summary>Adds two specified <see cref="SizeT"/> values.</summary>
|
|
/// <param name="s1">The first value to add.</param>
|
|
/// <param name="s2">The second value to add.</param>
|
|
/// <returns>The result of adding <paramref name="s1"/> and <paramref name="s2"/>.</returns>
|
|
public static SizeT operator +(SizeT s1, SizeT s2) => s1.Value + s2.Value;
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT operator +(SizeT value) => +value.Value;
|
|
|
|
/// <summary>Increments the <see cref="SizeT"/> by 1.</summary>
|
|
/// <param name="s1">The value to increment.</param>
|
|
/// <returns>The value of <paramref name="s1"/> incremented by 1.</returns>
|
|
public static SizeT operator ++(SizeT s1) => s1.Value += 1;
|
|
|
|
/// <summary>Indicates whether a specified <see cref="SizeT"/> is less than another specified <see cref="SizeT"/>.</summary>
|
|
/// <param name="s1">The first integral size to compare.</param>
|
|
/// <param name="s2">The second integral size to compare.</param>
|
|
/// <returns>
|
|
/// <see langword="true"/> if the value of <paramref name="s1"/> is less than the value of <paramref name="s2"/>; otherwise, <see langword="false"/>.
|
|
/// </returns>
|
|
public static bool operator <(SizeT s1, SizeT s2) => s1.CompareTo(s2) < 0;
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT operator <<(SizeT value, int shiftAmount) => value.Value << shiftAmount;
|
|
|
|
/// <summary>Indicates whether a specified <see cref="SizeT"/> is less than or equal to another specified <see cref="SizeT"/>.</summary>
|
|
/// <param name="s1">The first integral size to compare.</param>
|
|
/// <param name="s2">The second integral size to compare.</param>
|
|
/// <returns>
|
|
/// <see langword="true"/> if the value of <paramref name="s1"/> is less than or equal to the value of <paramref name="s2"/>;
|
|
/// otherwise, <see langword="false"/>.
|
|
/// </returns>
|
|
public static bool operator <=(SizeT s1, SizeT s2) => s1.CompareTo(s2) <= 0;
|
|
|
|
/// <summary>Indicates whether two <see cref="SizeT"/> instances are equal.</summary>
|
|
/// <param name="s1">The first integral size to compare.</param>
|
|
/// <param name="s2">The second integral size to compare.</param>
|
|
/// <returns>
|
|
/// <see langword="true"/> if the value of <paramref name="s1"/> is equal to the value of <paramref name="s2"/>; otherwise, <see langword="false"/>.
|
|
/// </returns>
|
|
public static bool operator ==(SizeT s1, SizeT s2) => s1.Equals(s2);
|
|
|
|
/// <summary>Indicates whether a specified <see cref="SizeT"/> is greater than another specified <see cref="SizeT"/>.</summary>
|
|
/// <param name="s1">The first integral size to compare.</param>
|
|
/// <param name="s2">The second integral size to compare.</param>
|
|
/// <returns>
|
|
/// <see langword="true"/> if the value of <paramref name="s1"/> is greater than the value of <paramref name="s2"/>; otherwise, <see langword="false"/>.
|
|
/// </returns>
|
|
public static bool operator >(SizeT s1, SizeT s2) => s1.CompareTo(s2) > 0;
|
|
|
|
/// <summary>Indicates whether a specified <see cref="SizeT"/> is greater than or equal to another specified <see cref="SizeT"/>.</summary>
|
|
/// <param name="s1">The first integral size to compare.</param>
|
|
/// <param name="s2">The second integral size to compare.</param>
|
|
/// <returns>
|
|
/// <see langword="true"/> if the value of <paramref name="s1"/> is greater than or equal to the value of <paramref name="s2"/>;
|
|
/// otherwise, <see langword="false"/>.
|
|
/// </returns>
|
|
public static bool operator >=(SizeT s1, SizeT s2) => s1.CompareTo(s2) >= 0;
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT operator >>(SizeT value, int shiftAmount) => value.Value >> shiftAmount;
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT operator >>>(SizeT value, int shiftAmount) => value.Value >>> shiftAmount;
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT Parse(string s, IFormatProvider? provider = null) => Parse(s, NumberStyles.Any, provider);
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT Parse(string s, NumberStyles style, IFormatProvider? provider) => ulong.Parse(s, style, provider);
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT Parse(ReadOnlySpan<char> s, IFormatProvider? provider) => Parse(s.ToString(), NumberStyles.Any, provider);
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT Parse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider) => Parse(s.ToString(), style, provider);
|
|
|
|
/// <inheritdoc/>
|
|
public static bool TryParse(string? s, IFormatProvider? provider, out SizeT result) => TryParse(s, NumberStyles.Any, provider, out result);
|
|
|
|
/// <inheritdoc/>
|
|
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out SizeT result) => TryParse(s.ToString(), NumberStyles.Any, provider, out result);
|
|
|
|
/// <inheritdoc/>
|
|
public static bool TryParse(string? s, NumberStyles style, IFormatProvider? provider, out SizeT result) { var b = ulong.TryParse(s, style, provider, out var r); result = b ? r : default; return b; }
|
|
|
|
/// <inheritdoc/>
|
|
public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider? provider, out SizeT result) => TryParse(s.ToString(), style, provider, out result);
|
|
|
|
/// <inheritdoc/>
|
|
public int CompareTo(SizeT other) => Value.CompareTo(other.Value);
|
|
|
|
/// <inheritdoc/>
|
|
public override bool Equals(object? obj) => obj is SizeT s ? Equals(s) : Value.Equals(obj);
|
|
|
|
/// <inheritdoc/>
|
|
public bool Equals(SizeT other) => Value.Equals(other.Value);
|
|
|
|
/// <inheritdoc/>
|
|
public override int GetHashCode() => Value.GetHashCode();
|
|
|
|
/// <inheritdoc/>
|
|
public TypeCode GetTypeCode() => Value.GetTypeCode();
|
|
|
|
/// <inheritdoc/>
|
|
public override string ToString() => Value.ToString();
|
|
|
|
/// <inheritdoc/>
|
|
public string ToString(IFormatProvider? provider) => Value.ToString(provider);
|
|
|
|
/// <inheritdoc/>
|
|
public string ToString(string? format, IFormatProvider? formatProvider) => Value.ToString(format, formatProvider);
|
|
|
|
#if NET6_0_OR_GREATER || NETCOREAPP3_1_OR_GREATER
|
|
/// <inheritdoc/>
|
|
public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format, IFormatProvider? provider) =>
|
|
Value.TryFormat(destination, out charsWritten, format, provider);
|
|
#endif
|
|
|
|
#if NET7_0_OR_GREATER
|
|
/// <inheritdoc/>
|
|
static SizeT INumberBase<SizeT>.Abs(SizeT value) => value;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsCanonical(SizeT value) => true;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsComplexNumber(SizeT value) => false;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsEvenInteger(SizeT value) => (value.Value & 1) == 0;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsFinite(SizeT value) => true;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsImaginaryNumber(SizeT value) => false;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsInfinity(SizeT value) => false;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsInteger(SizeT value) => true;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsNaN(SizeT value) => false;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsNegative(SizeT value) => false;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsNegativeInfinity(SizeT value) => false;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsNormal(SizeT value) => value.Value != 0;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsOddInteger(SizeT value) => (value.Value & 1) != 0;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsPositive(SizeT value) => true;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsPositiveInfinity(SizeT value) => false;
|
|
|
|
/// <inheritdoc/>
|
|
static bool IBinaryNumber<SizeT>.IsPow2(SizeT value) => nuint.IsPow2(value.val);
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsRealNumber(SizeT value) => true;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsSubnormal(SizeT value) => false;
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.IsZero(SizeT value) => value.val == nuint.Zero;
|
|
|
|
/// <inheritdoc/>
|
|
static SizeT IBinaryNumber<SizeT>.Log2(SizeT value) => nuint.Log2(value.val);
|
|
|
|
/// <inheritdoc/>
|
|
static SizeT INumberBase<SizeT>.MaxMagnitude(SizeT x, SizeT y) => Math.Max(x.Value, y.Value);
|
|
|
|
/// <inheritdoc/>
|
|
static SizeT INumberBase<SizeT>.MaxMagnitudeNumber(SizeT x, SizeT y) => Math.Max(x.Value, y.Value);
|
|
|
|
/// <inheritdoc/>
|
|
static SizeT INumberBase<SizeT>.MinMagnitude(SizeT x, SizeT y) => Math.Min(x.Value, y.Value);
|
|
|
|
/// <inheritdoc/>
|
|
static SizeT INumberBase<SizeT>.MinMagnitudeNumber(SizeT x, SizeT y) => Math.Min(x.Value, y.Value);
|
|
|
|
/// <inheritdoc/>
|
|
public static SizeT operator |(SizeT left, SizeT right) => left.Value | right.Value;
|
|
|
|
/// <inheritdoc/>
|
|
static SizeT IBinaryInteger<SizeT>.PopCount(SizeT value) => nuint.PopCount(value.val);
|
|
|
|
/// <inheritdoc/>
|
|
static SizeT IBinaryInteger<SizeT>.TrailingZeroCount(SizeT value) => nuint.TrailingZeroCount(value.val);
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.TryConvertFromChecked<TOther>(TOther value, out SizeT result)
|
|
{
|
|
// In order to reduce overall code duplication and improve the inlinabilty of these
|
|
// methods for the corelib types we have `ConvertFrom` handle the same sign and
|
|
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
|
|
// between signed and unsigned types, the one that handles unsigned will also
|
|
// handle `Decimal`.
|
|
//
|
|
// That is, `ConvertFrom` for `ulong` will handle the other unsigned types and
|
|
// `ConvertTo` will handle the signed types
|
|
|
|
if (typeof(TOther) == typeof(byte))
|
|
{
|
|
byte actualValue = (byte)(object)value;
|
|
result = (ulong)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(char))
|
|
{
|
|
char actualValue = (char)(object)value;
|
|
result = (ulong)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(decimal))
|
|
{
|
|
decimal actualValue = (decimal)(object)value;
|
|
result = checked((ulong)actualValue);
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(ushort))
|
|
{
|
|
ushort actualValue = (ushort)(object)value;
|
|
result = (ulong)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(uint))
|
|
{
|
|
uint actualValue = (uint)(object)value;
|
|
result = actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(ulong))
|
|
{
|
|
ulong actualValue = (ulong)(object)value;
|
|
result = actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(UInt128))
|
|
{
|
|
UInt128 actualValue = (UInt128)(object)value;
|
|
result = checked((ulong)actualValue);
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(nuint))
|
|
{
|
|
nuint actualValue = (nuint)(object)value;
|
|
result = actualValue;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
result = default;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.TryConvertFromSaturating<TOther>(TOther value, out SizeT result)
|
|
{
|
|
// In order to reduce overall code duplication and improve the inlinabilty of these
|
|
// methods for the corelib types we have `ConvertFrom` handle the same sign and
|
|
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
|
|
// between signed and unsigned types, the one that handles unsigned will also
|
|
// handle `Decimal`.
|
|
//
|
|
// That is, `ConvertFrom` for `ulong` will handle the other unsigned types and
|
|
// `ConvertTo` will handle the signed types
|
|
|
|
if (typeof(TOther) == typeof(byte))
|
|
{
|
|
byte actualValue = (byte)(object)value;
|
|
result = (uint)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(char))
|
|
{
|
|
char actualValue = (char)(object)value;
|
|
result = (uint)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(decimal))
|
|
{
|
|
decimal actualValue = (decimal)(object)value;
|
|
result = (actualValue >= (ulong)MaxValue) ? MaxValue :
|
|
(actualValue <= (ulong)MinValue) ? MinValue : (ulong)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(ushort))
|
|
{
|
|
ushort actualValue = (ushort)(object)value;
|
|
result = (uint)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(uint))
|
|
{
|
|
uint actualValue = (uint)(object)value;
|
|
result = actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(ulong))
|
|
{
|
|
ulong actualValue = (ulong)(object)value;
|
|
result = actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(UInt128))
|
|
{
|
|
UInt128 actualValue = (UInt128)(object)value;
|
|
result = (actualValue >= (ulong)MaxValue) ? MaxValue : (ulong)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(nuint))
|
|
{
|
|
nuint actualValue = (nuint)(object)value;
|
|
result = actualValue;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
result = default;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.TryConvertFromTruncating<TOther>(TOther value, out SizeT result)
|
|
{
|
|
// In order to reduce overall code duplication and improve the inlinabilty of these
|
|
// methods for the corelib types we have `ConvertFrom` handle the same sign and
|
|
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
|
|
// between signed and unsigned types, the one that handles unsigned will also
|
|
// handle `Decimal`.
|
|
//
|
|
// That is, `ConvertFrom` for `ulong` will handle the other unsigned types and
|
|
// `ConvertTo` will handle the signed types
|
|
|
|
if (typeof(TOther) == typeof(byte))
|
|
{
|
|
byte actualValue = (byte)(object)value;
|
|
result = (uint)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(char))
|
|
{
|
|
char actualValue = (char)(object)value;
|
|
result = (uint)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(decimal))
|
|
{
|
|
decimal actualValue = (decimal)(object)value;
|
|
result = (actualValue >= (ulong)MaxValue) ? MaxValue :
|
|
(actualValue <= (ulong)MinValue) ? MinValue : (ulong)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(ushort))
|
|
{
|
|
ushort actualValue = (ushort)(object)value;
|
|
result = (uint)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(uint))
|
|
{
|
|
uint actualValue = (uint)(object)value;
|
|
result = actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(ulong))
|
|
{
|
|
ulong actualValue = (ulong)(object)value;
|
|
result = actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(UInt128))
|
|
{
|
|
UInt128 actualValue = (UInt128)(object)value;
|
|
result = (ulong)actualValue;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(nuint))
|
|
{
|
|
nuint actualValue = (nuint)(object)value;
|
|
result = actualValue;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
result = default;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.TryConvertToChecked<TOther>(SizeT value, [MaybeNullWhen(false)] out TOther result)
|
|
{
|
|
// In order to reduce overall code duplication and improve the inlinabilty of these
|
|
// methods for the corelib types we have `ConvertFrom` handle the same sign and
|
|
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
|
|
// between signed and unsigned types, the one that handles unsigned will also
|
|
// handle `Decimal`.
|
|
//
|
|
// That is, `ConvertFrom` for `nuint` will handle the other unsigned types and
|
|
// `ConvertTo` will handle the signed types
|
|
|
|
if (typeof(TOther) == typeof(double))
|
|
{
|
|
double actualResult = value.val;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(Half))
|
|
{
|
|
Half actualResult = (Half)value.val;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(short))
|
|
{
|
|
short actualResult = checked((short)value);
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(int))
|
|
{
|
|
int actualResult = checked((int)value);
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(long))
|
|
{
|
|
long actualResult = checked((long)value);
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(Int128))
|
|
{
|
|
Int128 actualResult = value.val;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(nint))
|
|
{
|
|
nint actualResult = checked((nint)value);
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(sbyte))
|
|
{
|
|
sbyte actualResult = checked((sbyte)value);
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(float))
|
|
{
|
|
float actualResult = value.val;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
result = default;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.TryConvertToSaturating<TOther>(SizeT value, [MaybeNullWhen(false)] out TOther result)
|
|
{
|
|
if (typeof(TOther) == typeof(double))
|
|
{
|
|
double actualResult = value.val;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(Half))
|
|
{
|
|
Half actualResult = (Half)value.val;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(short))
|
|
{
|
|
short actualResult = (value >= (nuint)short.MaxValue) ? short.MaxValue : (short)value;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(int))
|
|
{
|
|
int actualResult = (value >= int.MaxValue) ? int.MaxValue : (int)value;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(long))
|
|
{
|
|
long actualResult = (value >= long.MaxValue) ? long.MaxValue : (long)value;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(Int128))
|
|
{
|
|
Int128 actualResult = value.val;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(nint))
|
|
{
|
|
nint actualResult = (value >= (nuint)nint.MaxValue) ? nint.MaxValue : (nint)value;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(sbyte))
|
|
{
|
|
sbyte actualResult = (value >= (nuint)sbyte.MaxValue) ? sbyte.MaxValue : (sbyte)value;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(float))
|
|
{
|
|
float actualResult = value.val;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
result = default;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
static bool INumberBase<SizeT>.TryConvertToTruncating<TOther>(SizeT value, [MaybeNullWhen(false)] out TOther result)
|
|
{
|
|
// In order to reduce overall code duplication and improve the inlinabilty of these
|
|
// methods for the corelib types we have `ConvertFrom` handle the same sign and
|
|
// `ConvertTo` handle the opposite sign. However, since there is an uneven split
|
|
// between signed and unsigned types, the one that handles unsigned will also
|
|
// handle `Decimal`.
|
|
//
|
|
// That is, `ConvertFrom` for `nuint` will handle the other unsigned types and
|
|
// `ConvertTo` will handle the signed types
|
|
|
|
if (typeof(TOther) == typeof(double))
|
|
{
|
|
double actualResult = value.val;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(Half))
|
|
{
|
|
Half actualResult = (Half)value.val;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(short))
|
|
{
|
|
short actualResult = (short)value;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(int))
|
|
{
|
|
int actualResult = (int)value;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(long))
|
|
{
|
|
long actualResult = (long)value;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(Int128))
|
|
{
|
|
Int128 actualResult = value.val;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(nint))
|
|
{
|
|
nint actualResult = (nint)value;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(sbyte))
|
|
{
|
|
sbyte actualResult = (sbyte)value;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else if (typeof(TOther) == typeof(float))
|
|
{
|
|
float actualResult = value.val;
|
|
result = (TOther)(object)actualResult;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
result = default;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
static bool IBinaryInteger<SizeT>.TryReadBigEndian(ReadOnlySpan<byte> source, bool isUnsigned, out SizeT value)
|
|
{
|
|
ulong result = default;
|
|
|
|
if (source.Length != 0)
|
|
{
|
|
if (!isUnsigned && sbyte.IsNegative((sbyte)source[0]))
|
|
{
|
|
// When we are signed and the sign bit is set, we are negative and therefore
|
|
// definitely out of range
|
|
|
|
value = result;
|
|
return false;
|
|
}
|
|
|
|
if ((source.Length > sizeof(ulong)) && (source[..^sizeof(ulong)].IndexOfAnyExcept((byte)0x00) >= 0))
|
|
{
|
|
// When we have any non-zero leading data, we are a large positive and therefore
|
|
// definitely out of range
|
|
|
|
value = result;
|
|
return false;
|
|
}
|
|
|
|
ref byte sourceRef = ref MemoryMarshal.GetReference(source);
|
|
|
|
if (source.Length >= sizeof(ulong))
|
|
{
|
|
sourceRef = ref Unsafe.Add(ref sourceRef, source.Length - sizeof(ulong));
|
|
|
|
// We have at least 8 bytes, so just read the ones we need directly
|
|
result = Unsafe.ReadUnaligned<ulong>(ref sourceRef);
|
|
|
|
if (BitConverter.IsLittleEndian)
|
|
{
|
|
result = BinaryPrimitives.ReverseEndianness(result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We have between 1 and 7 bytes, so construct the relevant value directly
|
|
// since the data is in Big Endian format, we can just read the bytes and
|
|
// shift left by 8-bits for each subsequent part
|
|
|
|
for (int i = 0; i < source.Length; i++)
|
|
{
|
|
result <<= 8;
|
|
result |= Unsafe.Add(ref sourceRef, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
value = result;
|
|
return true;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
static bool IBinaryInteger<SizeT>.TryReadLittleEndian(ReadOnlySpan<byte> source, bool isUnsigned, out SizeT value)
|
|
{
|
|
ulong result = default;
|
|
|
|
if (source.Length != 0)
|
|
{
|
|
if (!isUnsigned && sbyte.IsNegative((sbyte)source[^1]))
|
|
{
|
|
// When we are signed and the sign bit is set, we are negative and therefore
|
|
// definitely out of range
|
|
|
|
value = result;
|
|
return false;
|
|
}
|
|
|
|
if ((source.Length > sizeof(ulong)) && (source[sizeof(ulong)..].IndexOfAnyExcept((byte)0x00) >= 0))
|
|
{
|
|
// When we have any non-zero leading data, we are a large positive and therefore
|
|
// definitely out of range
|
|
|
|
value = result;
|
|
return false;
|
|
}
|
|
|
|
ref byte sourceRef = ref MemoryMarshal.GetReference(source);
|
|
|
|
if (source.Length >= sizeof(ulong))
|
|
{
|
|
// We have at least 8 bytes, so just read the ones we need directly
|
|
result = Unsafe.ReadUnaligned<ulong>(ref sourceRef);
|
|
|
|
if (!BitConverter.IsLittleEndian)
|
|
{
|
|
result = BinaryPrimitives.ReverseEndianness(result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We have between 1 and 7 bytes, so construct the relevant value directly
|
|
// since the data is in Little Endian format, we can just read the bytes and
|
|
// shift left by 8-bits for each subsequent part, then reverse endianness to
|
|
// ensure the order is correct. This is more efficient than iterating in reverse
|
|
// due to current JIT limitations
|
|
|
|
for (int i = 0; i < source.Length; i++)
|
|
{
|
|
ulong part = Unsafe.Add(ref sourceRef, i);
|
|
part <<= (i * 8);
|
|
result |= part;
|
|
}
|
|
}
|
|
}
|
|
|
|
value = result;
|
|
return true;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
int IBinaryInteger<SizeT>.GetByteCount() => ((IBinaryInteger<nuint>)val).GetByteCount();
|
|
|
|
/// <inheritdoc/>
|
|
int IBinaryInteger<SizeT>.GetShortestBitLength() => ((IBinaryInteger<nuint>)val).GetShortestBitLength();
|
|
|
|
/// <inheritdoc/>
|
|
bool IBinaryInteger<SizeT>.TryWriteBigEndian(Span<byte> destination, out int bytesWritten) => ((IBinaryInteger<nuint>)val).TryWriteBigEndian(destination, out bytesWritten);
|
|
|
|
/// <inheritdoc/>
|
|
bool IBinaryInteger<SizeT>.TryWriteLittleEndian(Span<byte> destination, out int bytesWritten) => ((IBinaryInteger<nuint>)val).TryWriteLittleEndian(destination, out bytesWritten);
|
|
#endif
|
|
|
|
/// <inheritdoc/>
|
|
int IComparable.CompareTo(object? obj) => Value.CompareTo(Convert.ChangeType(obj, typeof(ulong)));
|
|
|
|
/// <inheritdoc/>
|
|
bool IConvertible.ToBoolean(IFormatProvider? provider) => ((IConvertible)Value).ToBoolean(provider);
|
|
|
|
/// <inheritdoc/>
|
|
byte IConvertible.ToByte(IFormatProvider? provider)
|
|
{
|
|
var ul = Value;
|
|
if (ul < byte.MaxValue)
|
|
return (byte)ul;
|
|
if (ul is uint.MaxValue or ulong.MaxValue)
|
|
return byte.MaxValue;
|
|
throw new OverflowException();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
char IConvertible.ToChar(IFormatProvider? provider) => ((IConvertible)Value).ToChar(provider);
|
|
|
|
/// <inheritdoc/>
|
|
DateTime IConvertible.ToDateTime(IFormatProvider? provider) => ((IConvertible)Value).ToDateTime(provider);
|
|
|
|
/// <inheritdoc/>
|
|
decimal IConvertible.ToDecimal(IFormatProvider? provider)
|
|
{
|
|
var ul = Value;
|
|
if (ul < decimal.MaxValue)
|
|
return ul;
|
|
if (ul is uint.MaxValue or ulong.MaxValue)
|
|
return decimal.MaxValue;
|
|
throw new OverflowException();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
double IConvertible.ToDouble(IFormatProvider? provider)
|
|
{
|
|
var ul = Value;
|
|
if (ul < double.MaxValue)
|
|
return ul;
|
|
if (ul is uint.MaxValue or ulong.MaxValue)
|
|
return double.MaxValue;
|
|
throw new OverflowException();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
short IConvertible.ToInt16(IFormatProvider? provider)
|
|
{
|
|
var ul = Value;
|
|
if (ul < (ulong)short.MaxValue)
|
|
return (short)ul;
|
|
if (ul is uint.MaxValue or ulong.MaxValue)
|
|
return short.MaxValue;
|
|
throw new OverflowException();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
int IConvertible.ToInt32(IFormatProvider? provider)
|
|
{
|
|
var ul = Value;
|
|
if (ul < int.MaxValue)
|
|
return (int)ul;
|
|
if (ul is uint.MaxValue or ulong.MaxValue)
|
|
return int.MaxValue;
|
|
throw new OverflowException();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
long IConvertible.ToInt64(IFormatProvider? provider)
|
|
{
|
|
var ul = Value;
|
|
if (ul < long.MaxValue)
|
|
return (long)ul;
|
|
if (ul is uint.MaxValue or ulong.MaxValue)
|
|
return long.MaxValue;
|
|
throw new OverflowException();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
sbyte IConvertible.ToSByte(IFormatProvider? provider)
|
|
{
|
|
var ul = Value;
|
|
if (ul < (ulong)sbyte.MaxValue)
|
|
return (sbyte)ul;
|
|
if (ul is uint.MaxValue or ulong.MaxValue)
|
|
return sbyte.MaxValue;
|
|
throw new OverflowException();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
float IConvertible.ToSingle(IFormatProvider? provider)
|
|
{
|
|
var ul = Value;
|
|
if (ul < float.MaxValue)
|
|
return ul;
|
|
if (ul is uint.MaxValue or ulong.MaxValue)
|
|
return float.MaxValue;
|
|
throw new OverflowException();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
object IConvertible.ToType(Type conversionType, IFormatProvider? provider) => ((IConvertible)Value).ToType(conversionType, provider);
|
|
|
|
/// <inheritdoc/>
|
|
ushort IConvertible.ToUInt16(IFormatProvider? provider)
|
|
{
|
|
var ul = Value;
|
|
if (ul < ushort.MaxValue)
|
|
return (ushort)ul;
|
|
if (ul is uint.MaxValue or ulong.MaxValue)
|
|
return ushort.MaxValue;
|
|
throw new OverflowException();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
uint IConvertible.ToUInt32(IFormatProvider? provider)
|
|
{
|
|
var ul = Value;
|
|
if (ul < uint.MaxValue)
|
|
return (uint)ul;
|
|
if (ul is uint.MaxValue or ulong.MaxValue)
|
|
return uint.MaxValue;
|
|
throw new OverflowException();
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
ulong IConvertible.ToUInt64(IFormatProvider? provider) => Value;
|
|
|
|
internal class SizeTTypeConverter : UInt64Converter
|
|
{
|
|
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value) =>
|
|
new SizeT((ulong)base.ConvertFrom(context, culture, value)!);
|
|
|
|
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType) =>
|
|
value is SizeT sz ? base.ConvertTo(context, culture, sz.Value, destinationType) : throw new NotSupportedException();
|
|
}
|
|
} |