From 6c19fcc7f4a7daa4a2fea1c8980cace483d75283 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Wed, 15 Apr 2026 08:29:02 +0300
Subject: [PATCH 01/26] Thread class added
---
std/System/Threading/Thread.hpt | 80 ++++++++++++++++++++++++++++
std/System/Threading/ThreadStart.hpt | 8 +++
2 files changed, 88 insertions(+)
create mode 100644 std/System/Threading/Thread.hpt
create mode 100644 std/System/Threading/ThreadStart.hpt
diff --git a/std/System/Threading/Thread.hpt b/std/System/Threading/Thread.hpt
new file mode 100644
index 00000000..c9b444ca
--- /dev/null
+++ b/std/System/Threading/Thread.hpt
@@ -0,0 +1,80 @@
+using System;
+
+public partial class Thread
+{
+ private StartHelper _startHelper;
+
+ // State associated with starting new thread
+ private sealed class StartHelper
+ {
+ internal int _maxStackSize;
+ internal object _start;
+ internal object _startArg;
+
+ internal StartHelper(object start)
+ {
+ _start = start;
+ }
+
+ // avoid long-lived stack frame in many threads
+ internal inline void Run()
+ {
+ RunWorker();
+ }
+
+ // avoid long-lived stack frame in many threads
+ private inline void RunWorker()
+ {
+ object start = _start;
+ _start = default; // anime?
+
+ try
+ {
+ if (start is ThreadStart threadStart)
+ {
+ threadStart();
+ }
+ else
+ {
+ // TODO: parametrized shite
+ }
+ }
+ catch (Exception ex)
+ {
+ // the handler returned "true" means the exception is now "handled" and we should gracefully exit.
+ }
+ }
+ }
+
+ public Thread(ThreadStart start)
+ {
+ ArgumentNullException.ThrowIfNull(start);
+
+ _startHelper = new StartHelper(start);
+ }
+
+ private void Start()
+ {
+ // RuntimeFeature.ThrowIfMultithreadingIsNotSupported();
+ StartHelper startHelper = _startHelper;
+
+ // In the case of a null startHelper (second call to start on same thread)
+ // StartCore method will take care of the error reporting.
+ if (startHelper != null)
+ {
+ startHelper._startArg = null;
+ }
+
+ StartCore();
+ }
+
+ public bool Join(int millisecondsTimeout)
+ {
+ ArgumentOutOfRangeException.ThrowIfLessThan(millisecondsTimeout, Timeout.Infinite);
+ if ((ThreadState & ThreadState.Unstarted) != 0)
+ {
+ throw new ThreadStateException(SR.ThreadState_NotStarted);
+ }
+ return JoinInternal(millisecondsTimeout);
+ }
+}
\ No newline at end of file
diff --git a/std/System/Threading/ThreadStart.hpt b/std/System/Threading/ThreadStart.hpt
new file mode 100644
index 00000000..5895d5bc
--- /dev/null
+++ b/std/System/Threading/ThreadStart.hpt
@@ -0,0 +1,8 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+///
+/// Represents the method that executes on a .
+///
+public delegate void ThreadStart();
\ No newline at end of file
From 2cbb16910f745a80e2f51e430cf4477e9595c36f Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Wed, 15 Apr 2026 09:17:26 +0300
Subject: [PATCH 02/26] Timeout and TimeSpan added
---
std/System/Threading/Thread.hpt | 4 +
std/System/Threading/Timeout.hpt | 14 +
std/System/TimeSpan.hpt | 953 +++++++++++++++++++++++++++++++
3 files changed, 971 insertions(+)
create mode 100644 std/System/Threading/Timeout.hpt
create mode 100644 std/System/TimeSpan.hpt
diff --git a/std/System/Threading/Thread.hpt b/std/System/Threading/Thread.hpt
index c9b444ca..70f66192 100644
--- a/std/System/Threading/Thread.hpt
+++ b/std/System/Threading/Thread.hpt
@@ -1,3 +1,7 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
using System;
public partial class Thread
diff --git a/std/System/Threading/Timeout.hpt b/std/System/Threading/Timeout.hpt
new file mode 100644
index 00000000..efa40374
--- /dev/null
+++ b/std/System/Threading/Timeout.hpt
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+// A constant used by methods that take a timeout (Object.Wait, Thread.Sleep
+// etc) to indicate that no timeout should occur.
+//
+public static class Timeout
+{
+ public static readonly TimeSpan InfiniteTimeSpan = new TimeSpan(0, 0, 0, 0, Infinite);
+
+ public const int Infinite = -1;
+ internal const uint UnsignedInfinite = unchecked((uint)-1);
+}
\ No newline at end of file
diff --git a/std/System/TimeSpan.hpt b/std/System/TimeSpan.hpt
new file mode 100644
index 00000000..78064ee2
--- /dev/null
+++ b/std/System/TimeSpan.hpt
@@ -0,0 +1,953 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+// TimeSpan represents a duration of time. A TimeSpan can be negative
+// or positive.
+//
+// TimeSpan is internally represented as a number of ticks. A tick is equal
+// to 100 nanoseconds. While this maps well into units of time such as hours
+// and days, any periods longer than that aren't representable in a nice fashion.
+// For instance, a month can be between 28 and 31 days, while a year
+// can contain 365 or 366 days. A decade can have between 1 and 3 leap years,
+// depending on when you map the TimeSpan into the calendar. This is why
+// we do not provide Years() or Months().
+//
+public readonly struct TimeSpan
+ : IComparable,
+ IComparable,
+ IEquatable,
+ ISpanFormattable,
+ ISpanParsable,
+ IUtf8SpanFormattable
+{
+ ///
+ /// Represents the number of nanoseconds per tick. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 100.
+ ///
+ public const long NanosecondsPerTick = 100; // 100
+
+ ///
+ /// Represents the number of ticks in 1 microsecond. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 10.
+ ///
+ public const long TicksPerMicrosecond = 10; // 10
+
+ ///
+ /// Represents the number of ticks in 1 millisecond. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 10 thousand; that is, 10,000.
+ ///
+ public const long TicksPerMillisecond = TicksPerMicrosecond * 1000; // 10,000
+
+ ///
+ /// Represents the number of ticks in 1 second. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 10 million; that is, 10,000,000.
+ ///
+ public const long TicksPerSecond = TicksPerMillisecond * 1000; // 10,000,000
+
+ ///
+ /// Represents the number of ticks in 1 minute. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 600 million; that is, 600,000,000.
+ ///
+ public const long TicksPerMinute = TicksPerSecond * 60; // 600,000,000
+
+ ///
+ /// Represents the number of ticks in 1 hour. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 36 billion; that is, 36,000,000,000.
+ ///
+ public const long TicksPerHour = TicksPerMinute * 60; // 36,000,000,000
+
+ ///
+ /// Represents the number of ticks in 1 day. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 864 billion; that is, 864,000,000,000.
+ ///
+ public const long TicksPerDay = TicksPerHour * 24; // 864,000,000,000
+
+ ///
+ /// Represents the number of microseconds in 1 millisecond. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 1 thousand; that is, 1,000.
+ ///
+ public const long MicrosecondsPerMillisecond = TicksPerMillisecond / TicksPerMicrosecond; // 1,000
+
+ ///
+ /// Represents the number of microseconds in 1 second. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 1 million; that is, 1,000,000.
+ ///
+ public const long MicrosecondsPerSecond = TicksPerSecond / TicksPerMicrosecond; // 1,000,000
+
+ ///
+ /// Represents the number of microseconds in 1 minute. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 60 million; that is, 60,000,000.
+ ///
+ public const long MicrosecondsPerMinute = TicksPerMinute / TicksPerMicrosecond; // 60,000,000
+
+ ///
+ /// Represents the number of microseconds in 1 hour. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 3.6 billion; that is, 3,600,000,000.
+ ///
+ public const long MicrosecondsPerHour = TicksPerHour / TicksPerMicrosecond; // 3,600,000,000
+
+ ///
+ /// Represents the number of microseconds in 1 day. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 86.4 billion; that is, 86,400,000,000.
+ ///
+ public const long MicrosecondsPerDay = TicksPerDay / TicksPerMicrosecond; // 86,400,000,000
+
+ ///
+ /// Represents the number of milliseconds in 1 second. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 1 thousand; that is, 1,000.
+ ///
+ public const long MillisecondsPerSecond = TicksPerSecond / TicksPerMillisecond; // 1,000
+
+ ///
+ /// Represents the number of milliseconds in 1 minute. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 60 thousand; that is, 60,000.
+ ///
+ public const long MillisecondsPerMinute = TicksPerMinute / TicksPerMillisecond; // 60,000
+
+ ///
+ /// Represents the number of milliseconds in 1 hour. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 3.6 million; that is, 3,600,000.
+ ///
+ public const long MillisecondsPerHour = TicksPerHour / TicksPerMillisecond; // 3,600,000
+
+ ///
+ /// Represents the number of milliseconds in 1 day. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 86.4 million; that is, 86,400,000.
+ ///
+ public const long MillisecondsPerDay = TicksPerDay / TicksPerMillisecond; // 86,400,000
+
+ ///
+ /// Represents the number of seconds in 1 minute. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 60.
+ ///
+ public const long SecondsPerMinute = TicksPerMinute / TicksPerSecond; // 60
+
+ ///
+ /// Represents the number of seconds in 1 hour. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 3.6 thousand; that is, 3,600.
+ ///
+ public const long SecondsPerHour = TicksPerHour / TicksPerSecond; // 3,600
+
+ ///
+ /// Represents the number of seconds in 1 day. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 86.4 thousand; that is, 86,400.
+ ///
+ public const long SecondsPerDay = TicksPerDay / TicksPerSecond; // 86,400
+
+ ///
+ /// Represents the number of minutes in 1 hour. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 60.
+ ///
+ public const long MinutesPerHour = TicksPerHour / TicksPerMinute; // 60
+
+ ///
+ /// Represents the number of minutes in 1 day. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 1440.
+ ///
+ public const long MinutesPerDay = TicksPerDay / TicksPerMinute; // 1,440
+
+ ///
+ /// Represents the number of hours in 1 day. This field is constant.
+ ///
+ ///
+ /// The value of this constant is 24.
+ ///
+ public const int HoursPerDay = (int)(TicksPerDay / TicksPerHour); // 24
+
+ internal const long MinTicks = long.MinValue; // -9,223,372,036,854,775,808
+ internal const long MaxTicks = long.MaxValue; // +9,223,372,036,854,775,807
+
+ internal const long MinMicroseconds = MinTicks / TicksPerMicrosecond; // - 922,337,203,685,477,580
+ internal const long MaxMicroseconds = MaxTicks / TicksPerMicrosecond; // + 922,337,203,685,477,580
+
+ internal const long MinMilliseconds = MinTicks / TicksPerMillisecond; // - 922,337,203,685,477
+ internal const long MaxMilliseconds = MaxTicks / TicksPerMillisecond; // + 922,337,203,685,477
+
+ internal const long MinSeconds = MinTicks / TicksPerSecond; // - 922,337,203,685
+ internal const long MaxSeconds = MaxTicks / TicksPerSecond; // + 922,337,203,685
+
+ internal const long MinMinutes = MinTicks / TicksPerMinute; // - 15,372,286,728
+ internal const long MaxMinutes = MaxTicks / TicksPerMinute; // + 15,372,286,728
+
+ internal const long MinHours = MinTicks / TicksPerHour; // - 256,204,778
+ internal const long MaxHours = MaxTicks / TicksPerHour; // + 256,204,778
+
+ internal const long MinDays = MinTicks / TicksPerDay; // - 10,675,199
+ internal const long MaxDays = MaxTicks / TicksPerDay; // + 10,675,199
+
+ internal const long TicksPerTenthSecond = TicksPerMillisecond * 100;
+
+ public static readonly TimeSpan Zero = new TimeSpan(0);
+
+ public static readonly TimeSpan MaxValue = new TimeSpan(MaxTicks);
+ public static readonly TimeSpan MinValue = new TimeSpan(MinTicks);
+
+ // internal so that DateTime doesn't have to call an extra get
+ // method for some arithmetic operations.
+ internal readonly long _ticks; // Do not rename (binary serialization)
+
+ public TimeSpan(long ticks)
+ {
+ _ticks = ticks;
+ }
+
+ public TimeSpan(int hours, int minutes, int seconds)
+ {
+ _ticks = TimeToTicks(hours, minutes, seconds);
+ }
+
+ public TimeSpan(int days, int hours, int minutes, int seconds)
+ : this(days, hours, minutes, seconds, 0)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// days, hours, minutes, seconds, and milliseconds.
+ ///
+ /// Number of days.
+ /// Number of hours.
+ /// Number of minutes.
+ /// Number of seconds.
+ /// Number of milliseconds.
+ ///
+ /// The specified , , ,
+ /// and are converted to ticks, and that value initializes this instance.
+ ///
+ ///
+ /// The parameters specify a value less than or greater than
+ ///
+ public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds) :
+ this(days, hours, minutes, seconds, milliseconds, 0)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// days, hours, minutes, seconds, and milliseconds.
+ ///
+ /// Number of days.
+ /// Number of hours.
+ /// Number of minutes.
+ /// Number of seconds.
+ /// Number of milliseconds.
+ /// Number of microseconds.
+ ///
+ /// The specified , , ,
+ /// and are converted to ticks, and that value initializes this instance.
+ ///
+ ///
+ /// The parameters specify a value less than or greater than
+ ///
+ public TimeSpan(int days, int hours, int minutes, int seconds, int milliseconds, int microseconds)
+ {
+ long totalMicroseconds = (days * MicrosecondsPerDay)
+ + (hours * MicrosecondsPerHour)
+ + (minutes * MicrosecondsPerMinute)
+ + (seconds * MicrosecondsPerSecond)
+ + (milliseconds * MicrosecondsPerMillisecond)
+ + microseconds;
+
+ if ((totalMicroseconds > MaxMicroseconds) || (totalMicroseconds < MinMicroseconds))
+ {
+ ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
+ }
+ _ticks = totalMicroseconds * TicksPerMicrosecond;
+ }
+
+ public long Ticks => _ticks;
+
+ public int Days => (int)(_ticks / TicksPerDay);
+
+ public int Hours => (int)(_ticks / TicksPerHour % HoursPerDay);
+
+ public int Milliseconds => (int)(_ticks / TicksPerMillisecond % MillisecondsPerSecond);
+
+ ///
+ /// Gets the microseconds component of the time interval represented by the current structure.
+ ///
+ ///
+ /// The property represents whole microseconds, whereas the
+ /// property represents whole and fractional microseconds.
+ ///
+ public int Microseconds => (int)(_ticks / TicksPerMicrosecond % MicrosecondsPerMillisecond);
+
+ ///
+ /// Gets the nanoseconds component of the time interval represented by the current structure.
+ ///
+ ///
+ /// The property represents whole nanoseconds, whereas the
+ /// property represents whole and fractional nanoseconds.
+ ///
+ public int Nanoseconds => (int)(_ticks % TicksPerMicrosecond * NanosecondsPerTick);
+
+ public int Minutes => (int)(_ticks / TicksPerMinute % MinutesPerHour);
+
+ public int Seconds => (int)(_ticks / TicksPerSecond % SecondsPerMinute);
+
+ public double TotalDays => (double)_ticks / TicksPerDay;
+
+ public double TotalHours => (double)_ticks / TicksPerHour;
+
+ public double TotalMilliseconds
+ {
+ get
+ {
+ double temp = (double)_ticks / TicksPerMillisecond;
+
+ if (temp > MaxMilliseconds)
+ {
+ return MaxMilliseconds;
+ }
+
+ if (temp < MinMilliseconds)
+ {
+ return MinMilliseconds;
+ }
+ return temp;
+ }
+ }
+
+ ///
+ /// Gets the value of the current structure expressed in whole and fractional microseconds.
+ ///
+ ///
+ /// This property converts the value of this instance from ticks to microseconds.
+ /// This number might include whole and fractional microseconds.
+ ///
+ /// The property represents whole and fractional microseconds,
+ /// whereas the property represents whole microseconds.
+ ///
+ public double TotalMicroseconds => (double)_ticks / TicksPerMicrosecond;
+
+ ///
+ /// Gets the value of the current structure expressed in whole and fractional nanoseconds.
+ ///
+ ///
+ /// This property converts the value of this instance from ticks to nanoseconds.
+ /// This number might include whole and fractional nanoseconds.
+ ///
+ /// The property represents whole and fractional nanoseconds,
+ /// whereas the property represents whole nanoseconds.
+ ///
+ public double TotalNanoseconds => (double)_ticks * NanosecondsPerTick;
+
+ public double TotalMinutes => (double)_ticks / TicksPerMinute;
+
+ public double TotalSeconds => (double)_ticks / TicksPerSecond;
+
+ public TimeSpan Add(TimeSpan ts) => this + ts;
+
+ // Compares two TimeSpan values, returning an integer that indicates their
+ // relationship.
+ //
+ public static int Compare(TimeSpan t1, TimeSpan t2) => t1._ticks.CompareTo(t2._ticks);
+
+ // Returns a value less than zero if this object
+ public int CompareTo(object? value)
+ {
+ if (value is null)
+ {
+ return 1;
+ }
+
+ if (value is TimeSpan other)
+ {
+ return CompareTo(other);
+ }
+
+ throw new ArgumentException(SR.Arg_MustBeTimeSpan);
+ }
+
+ public int CompareTo(TimeSpan value) => Compare(this, value);
+
+ public static TimeSpan FromDays(double value) => Interval(value, TicksPerDay);
+
+ public TimeSpan Duration()
+ {
+ if (_ticks == MinTicks)
+ {
+ ThrowHelper.ThrowOverflowException_TimeSpanDuration();
+ }
+ return new TimeSpan(_ticks >= 0 ? _ticks : -_ticks);
+ }
+
+ public override bool Equals([NotNullWhen(true)] object? value) => (value is TimeSpan other) && Equals(other);
+
+ public bool Equals(TimeSpan obj) => Equals(this, obj);
+
+ public static bool Equals(TimeSpan t1, TimeSpan t2) => t1 == t2;
+
+ public override int GetHashCode() => _ticks.GetHashCode();
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static TimeSpan FromUnits(long units, long ticksPerUnit, long minUnits, long maxUnits)
+ {
+ System.Diagnostics.Debug.Assert(minUnits < 0);
+ System.Diagnostics.Debug.Assert(maxUnits > 0);
+
+ if (units > maxUnits || units < minUnits)
+ {
+ ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
+ }
+ return TimeSpan.FromTicks(units * ticksPerUnit);
+ }
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// days.
+ ///
+ /// Number of days.
+ /// Returns a that represents a specified number of days.
+ ///
+ /// The parameters specify a value less than or greater than
+ ///
+ public static TimeSpan FromDays(int days) => FromUnits(days, TicksPerDay, MinDays, MaxDays);
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// days, hours, minutes, seconds, milliseconds, and microseconds.
+ ///
+ /// Number of days.
+ /// Number of hours.
+ /// Number of minutes.
+ /// Number of seconds.
+ /// Number of milliseconds.
+ /// Number of microseconds.
+ /// Returns a that represents a specified number of days, hours, minutes, seconds, milliseconds, and microseconds.
+ ///
+ /// The parameters specify a value less than or greater than
+ ///
+ public static TimeSpan FromDays(int days, int hours = 0, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0)
+ {
+ Int128 totalMicroseconds = Math.BigMul(days, MicrosecondsPerDay)
+ + Math.BigMul(hours, MicrosecondsPerHour)
+ + Math.BigMul(minutes, MicrosecondsPerMinute)
+ + Math.BigMul(seconds, MicrosecondsPerSecond)
+ + Math.BigMul(milliseconds, MicrosecondsPerMillisecond)
+ + microseconds;
+
+ return FromMicroseconds(totalMicroseconds);
+ }
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// hours.
+ ///
+ /// Number of hours.
+ /// Returns a that represents a specified number of hours.
+ ///
+ /// The parameters specify a value less than or greater than
+ ///
+ public static TimeSpan FromHours(int hours) => FromUnits(hours, TicksPerHour, MinHours, MaxHours);
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// hours, minutes, seconds, milliseconds, and microseconds.
+ ///
+ /// Number of hours.
+ /// Number of minutes.
+ /// Number of seconds.
+ /// Number of milliseconds.
+ /// Number of microseconds.
+ /// Returns a that represents a specified number of hours, minutes, seconds, milliseconds, and microseconds.
+ ///
+ /// The parameters specify a value less than or greater than
+ ///
+ public static TimeSpan FromHours(int hours, long minutes = 0, long seconds = 0, long milliseconds = 0, long microseconds = 0)
+ {
+ Int128 totalMicroseconds = Math.BigMul(hours, MicrosecondsPerHour)
+ + Math.BigMul(minutes, MicrosecondsPerMinute)
+ + Math.BigMul(seconds, MicrosecondsPerSecond)
+ + Math.BigMul(milliseconds, MicrosecondsPerMillisecond)
+ + microseconds;
+
+ return FromMicroseconds(totalMicroseconds);
+ }
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// minutes.
+ ///
+ /// Number of minutes.
+ /// Returns a that represents a specified number of minutes.
+ ///
+ /// The parameters specify a value less than or greater than
+ ///
+ public static TimeSpan FromMinutes(long minutes) => FromUnits(minutes, TicksPerMinute, MinMinutes, MaxMinutes);
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// minutes, seconds, milliseconds, and microseconds.
+ ///
+ /// Number of minutes.
+ /// Number of seconds.
+ /// Number of milliseconds.
+ /// Number of microseconds.
+ /// Returns a that represents a specified number of minutes, seconds, milliseconds, and microseconds.
+ ///
+ /// The parameters specify a value less than or greater than
+ ///
+ public static TimeSpan FromMinutes(long minutes, long seconds = 0, long milliseconds = 0, long microseconds = 0)
+ {
+ Int128 totalMicroseconds = Math.BigMul(minutes, MicrosecondsPerMinute)
+ + Math.BigMul(seconds, MicrosecondsPerSecond)
+ + Math.BigMul(milliseconds, MicrosecondsPerMillisecond)
+ + microseconds;
+
+ return FromMicroseconds(totalMicroseconds);
+ }
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// seconds.
+ ///
+ /// Number of seconds.
+ /// Returns a that represents a specified number of seconds.
+ ///
+ /// The parameters specify a value less than or greater than
+ ///
+ public static TimeSpan FromSeconds(long seconds) => FromUnits(seconds, TicksPerSecond, MinSeconds, MaxSeconds);
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// seconds, milliseconds, and microseconds.
+ ///
+ /// Number of seconds.
+ /// Number of milliseconds.
+ /// Number of microseconds.
+ /// Returns a that represents a specified number of seconds, milliseconds, and microseconds.
+ ///
+ /// The parameters specify a value less than or greater than
+ ///
+ public static TimeSpan FromSeconds(long seconds, long milliseconds = 0, long microseconds = 0)
+ {
+ Int128 totalMicroseconds = Math.BigMul(seconds, MicrosecondsPerSecond)
+ + Math.BigMul(milliseconds, MicrosecondsPerMillisecond)
+ + microseconds;
+
+ return FromMicroseconds(totalMicroseconds);
+ }
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// milliseconds.
+ ///
+ /// Number of milliseconds.
+ /// Returns a that represents a specified number of milliseconds.
+ ///
+ /// The parameter specify a value less than or greater than
+ ///
+ public static TimeSpan FromMilliseconds(long milliseconds)
+ => FromUnits(milliseconds, TicksPerMillisecond, MinMilliseconds, MaxMilliseconds);
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// milliseconds, and microseconds.
+ ///
+ /// Number of milliseconds.
+ /// Number of microseconds.
+ /// Returns a that represents a specified number of milliseconds, and microseconds.
+ ///
+ /// The parameters specify a value less than or greater than
+ ///
+ public static TimeSpan FromMilliseconds(long milliseconds, long microseconds)
+ {
+ Int128 totalMicroseconds = Math.BigMul(milliseconds, MicrosecondsPerMillisecond)
+ + microseconds;
+
+ return FromMicroseconds(totalMicroseconds);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static TimeSpan FromMicroseconds(Int128 microseconds)
+ {
+ if ((microseconds > MaxMicroseconds) || (microseconds < MinMicroseconds))
+ {
+ ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
+ }
+ long ticks = (long)microseconds * TicksPerMicrosecond;
+ return TimeSpan.FromTicks(ticks);
+ }
+
+ ///
+ /// Initializes a new instance of the structure to a specified number of
+ /// microseconds.
+ ///
+ /// Number of microseconds.
+ /// Returns a that represents a specified number of microseconds.
+ ///
+ /// The parameters specify a value less than or greater than
+ ///
+ public static TimeSpan FromMicroseconds(long microseconds) => FromUnits(microseconds, TicksPerMicrosecond, MinMicroseconds, MaxMicroseconds);
+
+ public static TimeSpan FromHours(double value) => Interval(value, TicksPerHour);
+
+ private static TimeSpan Interval(double value, double scale)
+ {
+ if (double.IsNaN(value))
+ {
+ ThrowHelper.ThrowArgumentException_Arg_CannotBeNaN();
+ }
+ return IntervalFromDoubleTicks(value * scale);
+ }
+
+ private static TimeSpan IntervalFromDoubleTicks(double ticks)
+ {
+ if ((ticks > MaxTicks) || (ticks < MinTicks) || double.IsNaN(ticks))
+ {
+ ThrowHelper.ThrowOverflowException_TimeSpanTooLong();
+ }
+ if (ticks == MaxTicks)
+ {
+ return MaxValue;
+ }
+ return new TimeSpan((long)ticks);
+ }
+
+ public static TimeSpan FromMilliseconds(double value) => Interval(value, TicksPerMillisecond);
+
+ ///
+ /// Returns a that represents a specified number of microseconds.
+ ///
+ /// A number of microseconds.
+ /// An object that represents .
+ ///
+ /// is less than or greater than .
+ ///
+ /// -or-
+ ///
+ /// is
+ ///
+ /// -or-
+ ///
+ /// is
+ ///
+ ///
+ /// is equal to .
+ ///
+ public static TimeSpan FromMicroseconds(double value) => Interval(value, TicksPerMicrosecond);
+
+ public static TimeSpan FromMinutes(double value) => Interval(value, TicksPerMinute);
+
+ public TimeSpan Negate() => -this;
+
+ public static TimeSpan FromSeconds(double value) => Interval(value, TicksPerSecond);
+
+ public TimeSpan Subtract(TimeSpan ts) => this - ts;
+
+ public TimeSpan Multiply(double factor) => this * factor;
+
+ public TimeSpan Divide(double divisor) => this / divisor;
+
+ public double Divide(TimeSpan ts) => this / ts;
+
+ public static TimeSpan FromTicks(long value) => new TimeSpan(value);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static long TimeToTicks(int hour, int minute, int second)
+ {
+ // totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31,
+ // which is less than 2^44, meaning we won't overflow totalSeconds.
+ long totalSeconds = (hour * SecondsPerHour)
+ + (minute * SecondsPerMinute)
+ + second;
+
+ if ((totalSeconds > MaxSeconds) || (totalSeconds < MinSeconds))
+ {
+ ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
+ }
+ return totalSeconds * TicksPerSecond;
+ }
+
+ // See System.Globalization.TimeSpanParse and System.Globalization.TimeSpanFormat
+ #region ParseAndFormat
+ private static void ValidateStyles(TimeSpanStyles style)
+ {
+ if (style is not TimeSpanStyles.None and not TimeSpanStyles.AssumeNegative)
+ {
+ ThrowHelper.ThrowArgumentException_InvalidTimeSpanStyles();
+ }
+ }
+ public static TimeSpan Parse(string s)
+ {
+ /* Constructs a TimeSpan from a string. Leading and trailing white space characters are allowed. */
+ ArgumentNullException.ThrowIfNull(s, ExceptionArgument.input);
+ return TimeSpanParse.Parse(s, null);
+ }
+ public static TimeSpan Parse(string input, IFormatProvider? formatProvider)
+ {
+ if (input is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
+ }
+ return TimeSpanParse.Parse(input, formatProvider);
+ }
+ public static TimeSpan Parse(ReadOnlySpan input, IFormatProvider? formatProvider = null) => TimeSpanParse.Parse(input, formatProvider);
+ public static TimeSpan ParseExact(string input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string format, IFormatProvider? formatProvider)
+ {
+ ArgumentNullException.ThrowIfNull(input, ExceptionArgument.input);
+ ArgumentNullException.ThrowIfNull(format, ExceptionArgument.format);
+ return TimeSpanParse.ParseExact(input, format, formatProvider, TimeSpanStyles.None);
+ }
+ public static TimeSpan ParseExact(string input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string[] formats, IFormatProvider? formatProvider)
+ {
+ if (input is null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
+ }
+ return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None);
+ }
+ public static TimeSpan ParseExact(string input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string format, IFormatProvider? formatProvider, TimeSpanStyles styles)
+ {
+ ValidateStyles(styles);
+ ArgumentNullException.ThrowIfNull(input, ExceptionArgument.input);
+ ArgumentNullException.ThrowIfNull(format, ExceptionArgument.format);
+ return TimeSpanParse.ParseExact(input, format, formatProvider, styles);
+ }
+
+ public static TimeSpan ParseExact(ReadOnlySpan input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] ReadOnlySpan format, IFormatProvider? formatProvider, TimeSpanStyles styles = TimeSpanStyles.None)
+ {
+ ValidateStyles(styles);
+ return TimeSpanParse.ParseExact(input, format, formatProvider, styles);
+ }
+ public static TimeSpan ParseExact(string input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string[] formats, IFormatProvider? formatProvider, TimeSpanStyles styles)
+ {
+ ValidateStyles(styles);
+ ArgumentNullException.ThrowIfNull(input, ExceptionArgument.input);
+ return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles);
+ }
+ public static TimeSpan ParseExact(ReadOnlySpan input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string[] formats, IFormatProvider? formatProvider, TimeSpanStyles styles = TimeSpanStyles.None)
+ {
+ ValidateStyles(styles);
+ return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles);
+ }
+ public static bool TryParse([NotNullWhen(true)] string? s, out TimeSpan result)
+ {
+ if (s is null)
+ {
+ result = default;
+ return false;
+ }
+ return TimeSpanParse.TryParse(s, null, out result);
+ }
+ public static bool TryParse(ReadOnlySpan s, out TimeSpan result) => TimeSpanParse.TryParse(s, null, out result);
+
+ public static bool TryParse([NotNullWhen(true)] string? input, IFormatProvider? formatProvider, out TimeSpan result)
+ {
+ if (input is null)
+ {
+ result = default;
+ return false;
+ }
+ return TimeSpanParse.TryParse(input, formatProvider, out result);
+ }
+ public static bool TryParse(ReadOnlySpan input, IFormatProvider? formatProvider, out TimeSpan result) => TimeSpanParse.TryParse(input, formatProvider, out result);
+ public static bool TryParseExact([NotNullWhen(true)] string? input, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string? format, IFormatProvider? formatProvider, out TimeSpan result)
+ {
+ if (input is null || format is null)
+ {
+ result = default;
+ return false;
+ }
+ return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result);
+ }
+
+ public static bool TryParseExact(ReadOnlySpan input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] ReadOnlySpan format, IFormatProvider? formatProvider, out TimeSpan result)
+ => TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result);
+
+ public static bool TryParseExact([NotNullWhen(true)] string? input, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string?[]? formats, IFormatProvider? formatProvider, out TimeSpan result)
+ {
+ if (input is null)
+ {
+ result = default;
+ return false;
+ }
+ return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result);
+ }
+ public static bool TryParseExact(ReadOnlySpan input, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string?[]? formats, IFormatProvider? formatProvider, out TimeSpan result)
+ => TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result);
+
+ public static bool TryParseExact([NotNullWhen(true)] string? input, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string? format, IFormatProvider? formatProvider, TimeSpanStyles styles, out TimeSpan result)
+ {
+ ValidateStyles(styles);
+
+ if (input is null || format is null)
+ {
+ result = default;
+ return false;
+ }
+ return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result);
+ }
+
+ public static bool TryParseExact(ReadOnlySpan input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] ReadOnlySpan format, IFormatProvider? formatProvider, TimeSpanStyles styles, out TimeSpan result)
+ {
+ ValidateStyles(styles);
+ return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result);
+ }
+ public static bool TryParseExact([NotNullWhen(true)] string? input, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string?[]? formats, IFormatProvider? formatProvider, TimeSpanStyles styles, out TimeSpan result)
+ {
+ ValidateStyles(styles);
+
+ if (input is null)
+ {
+ result = default;
+ return false;
+ }
+ return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result);
+ }
+
+ public static bool TryParseExact(ReadOnlySpan input, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string?[]? formats, IFormatProvider? formatProvider, TimeSpanStyles styles, out TimeSpan result)
+ {
+ ValidateStyles(styles);
+ return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result);
+ }
+ public override string ToString() => TimeSpanFormat.FormatC(this);
+ public string ToString([StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string? format) => TimeSpanFormat.Format(this, format, null);
+ public string ToString([StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string? format, IFormatProvider? formatProvider) => TimeSpanFormat.Format(this, format, formatProvider);
+
+ public bool TryFormat(Span destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] ReadOnlySpan format = default, IFormatProvider? formatProvider = null)
+ => TimeSpanFormat.TryFormat(this, destination, out charsWritten, format, formatProvider);
+
+ ///
+ public bool TryFormat(Span utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] ReadOnlySpan format = default, IFormatProvider? formatProvider = null)
+ => TimeSpanFormat.TryFormat(this, utf8Destination, out bytesWritten, format, formatProvider);
+
+ #endregion
+
+ public static TimeSpan operator -(TimeSpan t)
+ {
+ if (t._ticks == MinTicks)
+ {
+ ThrowHelper.ThrowOverflowException_NegateTwosCompNum();
+ }
+ return new TimeSpan(-t._ticks);
+ }
+
+ public static TimeSpan operator -(TimeSpan t1, TimeSpan t2)
+ {
+ long result = t1._ticks - t2._ticks;
+ long t1Sign = t1._ticks >> 63;
+
+ if ((t1Sign != (t2._ticks >> 63)) && (t1Sign != (result >> 63)))
+ {
+ // Overflow if signs of operands was different and result's sign was opposite.
+ // >> 63 gives the sign bit (either 64 1's or 64 0's).
+ ThrowHelper.ThrowOverflowException_TimeSpanTooLong();
+ }
+ return new TimeSpan(result);
+ }
+
+ public static TimeSpan operator +(TimeSpan t) => t;
+
+ public static TimeSpan operator +(TimeSpan t1, TimeSpan t2)
+ {
+ long result = t1._ticks + t2._ticks;
+ long t1Sign = t1._ticks >> 63;
+
+ if ((t1Sign == (t2._ticks >> 63)) && (t1Sign != (result >> 63)))
+ {
+ // Overflow if signs of operands was identical and result's sign was opposite.
+ // >> 63 gives the sign bit (either 64 1's or 64 0's).
+ ThrowHelper.ThrowOverflowException_TimeSpanTooLong();
+ }
+ return new TimeSpan(result);
+ }
+
+ ///
+ public static TimeSpan operator *(TimeSpan timeSpan, double factor)
+ {
+ if (double.IsNaN(factor))
+ {
+ ThrowHelper.ThrowArgumentException_Arg_CannotBeNaN(ExceptionArgument.factor);
+ }
+
+ // Rounding to the nearest tick is as close to the result we would have with unlimited
+ // precision as possible, and so likely to have the least potential to surprise.
+ double ticks = Math.Round(timeSpan.Ticks * factor);
+ return IntervalFromDoubleTicks(ticks);
+ }
+
+ ///
+ public static TimeSpan operator *(double factor, TimeSpan timeSpan) => timeSpan * factor;
+
+ ///
+ public static TimeSpan operator /(TimeSpan timeSpan, double divisor)
+ {
+ if (double.IsNaN(divisor))
+ {
+ ThrowHelper.ThrowArgumentException_Arg_CannotBeNaN(ExceptionArgument.divisor);
+ }
+
+ double ticks = Math.Round(timeSpan.Ticks / divisor);
+ return IntervalFromDoubleTicks(ticks);
+ }
+
+ // Using floating-point arithmetic directly means that infinities can be returned, which is reasonable
+ // if we consider TimeSpan.FromHours(1) / TimeSpan.Zero asks how many zero-second intervals there are in
+ // an hour for which infinity is the mathematic correct answer. Having TimeSpan.Zero / TimeSpan.Zero return NaN
+ // is perhaps less useful, but no less useful than an exception.
+ ///
+ public static double operator /(TimeSpan t1, TimeSpan t2) => t1.Ticks / (double)t2.Ticks;
+
+ ///
+ public static bool operator ==(TimeSpan t1, TimeSpan t2) => t1._ticks == t2._ticks;
+
+ ///
+ public static bool operator !=(TimeSpan t1, TimeSpan t2) => t1._ticks != t2._ticks;
+
+ ///
+ public static bool operator <(TimeSpan t1, TimeSpan t2) => t1._ticks < t2._ticks;
+
+ ///
+ public static bool operator <=(TimeSpan t1, TimeSpan t2) => t1._ticks <= t2._ticks;
+
+ ///
+ public static bool operator >(TimeSpan t1, TimeSpan t2) => t1._ticks > t2._ticks;
+
+ ///
+ public static bool operator >=(TimeSpan t1, TimeSpan t2) => t1._ticks >= t2._ticks;
+}
\ No newline at end of file
From 2ebdb421bb2a77c11137bbc7c97643498dcc1485 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Wed, 15 Apr 2026 11:44:54 +0300
Subject: [PATCH 03/26] TimeSpan files small fixes
---
std/System/TimeSpan.hpt | 76 ++++++++++++++++++++---------------------
1 file changed, 38 insertions(+), 38 deletions(-)
diff --git a/std/System/TimeSpan.hpt b/std/System/TimeSpan.hpt
index 78064ee2..78470071 100644
--- a/std/System/TimeSpan.hpt
+++ b/std/System/TimeSpan.hpt
@@ -423,8 +423,7 @@ public readonly struct TimeSpan
public override int GetHashCode() => _ticks.GetHashCode();
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static TimeSpan FromUnits(long units, long ticksPerUnit, long minUnits, long maxUnits)
+ private static inline TimeSpan FromUnits(long units, long ticksPerUnit, long minUnits, long maxUnits)
{
System.Diagnostics.Debug.Assert(minUnits < 0);
System.Diagnostics.Debug.Assert(maxUnits > 0);
@@ -602,8 +601,7 @@ public readonly struct TimeSpan
return FromMicroseconds(totalMicroseconds);
}
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static TimeSpan FromMicroseconds(Int128 microseconds)
+ private static inline TimeSpan FromMicroseconds(Int128 microseconds)
{
if ((microseconds > MaxMicroseconds) || (microseconds < MinMicroseconds))
{
@@ -687,8 +685,7 @@ public readonly struct TimeSpan
public static TimeSpan FromTicks(long value) => new TimeSpan(value);
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- internal static long TimeToTicks(int hour, int minute, int second)
+ internal static inline long TimeToTicks(int hour, int minute, int second)
{
// totalSeconds is bounded by 2^31 * 2^12 + 2^31 * 2^8 + 2^31,
// which is less than 2^44, meaning we won't overflow totalSeconds.
@@ -704,10 +701,10 @@ public readonly struct TimeSpan
}
// See System.Globalization.TimeSpanParse and System.Globalization.TimeSpanFormat
- #region ParseAndFormat
+ //#region ParseAndFormat
private static void ValidateStyles(TimeSpanStyles style)
{
- if (style is not TimeSpanStyles.None and not TimeSpanStyles.AssumeNegative)
+ if (style is not TimeSpanStyles.None && style is not TimeSpanStyles.AssumeNegative)
{
ThrowHelper.ThrowArgumentException_InvalidTimeSpanStyles();
}
@@ -718,54 +715,56 @@ public readonly struct TimeSpan
ArgumentNullException.ThrowIfNull(s, ExceptionArgument.input);
return TimeSpanParse.Parse(s, null);
}
- public static TimeSpan Parse(string input, IFormatProvider? formatProvider)
+ public static TimeSpan Parse(string input, IFormatProvider formatProvider)
{
- if (input is null)
+ if (input == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
}
return TimeSpanParse.Parse(input, formatProvider);
}
public static TimeSpan Parse(ReadOnlySpan input, IFormatProvider? formatProvider = null) => TimeSpanParse.Parse(input, formatProvider);
- public static TimeSpan ParseExact(string input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string format, IFormatProvider? formatProvider)
+ public static TimeSpan ParseExact(string input, string format, IFormatProvider formatProvider)
{
- ArgumentNullException.ThrowIfNull(input, ExceptionArgument.input);
- ArgumentNullException.ThrowIfNull(format, ExceptionArgument.format);
+ ArgumentNullException.ThrowIfNull(nameof(input));
+ ArgumentNullException.ThrowIfNull(nameof(format));
+
+
return TimeSpanParse.ParseExact(input, format, formatProvider, TimeSpanStyles.None);
}
- public static TimeSpan ParseExact(string input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string[] formats, IFormatProvider? formatProvider)
+ public static TimeSpan ParseExact(string input, string[] formats, IFormatProvider formatProvider)
{
- if (input is null)
+ if (input == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
}
return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None);
}
- public static TimeSpan ParseExact(string input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string format, IFormatProvider? formatProvider, TimeSpanStyles styles)
+ public static TimeSpan ParseExact(string input, string format, IFormatProvider formatProvider, TimeSpanStyles styles)
{
ValidateStyles(styles);
- ArgumentNullException.ThrowIfNull(input, ExceptionArgument.input);
- ArgumentNullException.ThrowIfNull(format, ExceptionArgument.format);
+ ArgumentNullException.ThrowIfNull(nameof(input));
+ ArgumentNullException.ThrowIfNull(nameof(format));
return TimeSpanParse.ParseExact(input, format, formatProvider, styles);
}
- public static TimeSpan ParseExact(ReadOnlySpan input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] ReadOnlySpan format, IFormatProvider? formatProvider, TimeSpanStyles styles = TimeSpanStyles.None)
+ public static TimeSpan ParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None)
{
ValidateStyles(styles);
return TimeSpanParse.ParseExact(input, format, formatProvider, styles);
}
- public static TimeSpan ParseExact(string input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string[] formats, IFormatProvider? formatProvider, TimeSpanStyles styles)
+ public static TimeSpan ParseExact(string input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles)
{
ValidateStyles(styles);
ArgumentNullException.ThrowIfNull(input, ExceptionArgument.input);
return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles);
}
- public static TimeSpan ParseExact(ReadOnlySpan input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string[] formats, IFormatProvider? formatProvider, TimeSpanStyles styles = TimeSpanStyles.None)
+ public static TimeSpan ParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None)
{
ValidateStyles(styles);
return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles);
}
- public static bool TryParse([NotNullWhen(true)] string? s, out TimeSpan result)
+ public static bool TryParse(string s, out TimeSpan result)
{
if (s is null)
{
@@ -776,7 +775,7 @@ public readonly struct TimeSpan
}
public static bool TryParse(ReadOnlySpan s, out TimeSpan result) => TimeSpanParse.TryParse(s, null, out result);
- public static bool TryParse([NotNullWhen(true)] string? input, IFormatProvider? formatProvider, out TimeSpan result)
+ public static bool TryParse(string input, IFormatProvider formatProvider, out TimeSpan result)
{
if (input is null)
{
@@ -785,8 +784,8 @@ public readonly struct TimeSpan
}
return TimeSpanParse.TryParse(input, formatProvider, out result);
}
- public static bool TryParse(ReadOnlySpan input, IFormatProvider? formatProvider, out TimeSpan result) => TimeSpanParse.TryParse(input, formatProvider, out result);
- public static bool TryParseExact([NotNullWhen(true)] string? input, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string? format, IFormatProvider? formatProvider, out TimeSpan result)
+ public static bool TryParse(ReadOnlySpan input, IFormatProvider formatProvider, out TimeSpan result) => TimeSpanParse.TryParse(input, formatProvider, out result);
+ public static bool TryParseExact(string input, string format, IFormatProvider formatProvider, out TimeSpan result)
{
if (input is null || format is null)
{
@@ -796,10 +795,10 @@ public readonly struct TimeSpan
return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result);
}
- public static bool TryParseExact(ReadOnlySpan input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] ReadOnlySpan format, IFormatProvider? formatProvider, out TimeSpan result)
+ public static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, out TimeSpan result)
=> TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result);
- public static bool TryParseExact([NotNullWhen(true)] string? input, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string?[]? formats, IFormatProvider? formatProvider, out TimeSpan result)
+ public static bool TryParseExact(string input, string[] formats, IFormatProvider formatProvider, out TimeSpan result)
{
if (input is null)
{
@@ -808,10 +807,10 @@ public readonly struct TimeSpan
}
return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result);
}
- public static bool TryParseExact(ReadOnlySpan input, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string?[]? formats, IFormatProvider? formatProvider, out TimeSpan result)
+ public static bool TryParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, out TimeSpan result)
=> TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result);
- public static bool TryParseExact([NotNullWhen(true)] string? input, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string? format, IFormatProvider? formatProvider, TimeSpanStyles styles, out TimeSpan result)
+ public static bool TryParseExact(string input, string format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
{
ValidateStyles(styles);
@@ -823,12 +822,12 @@ public readonly struct TimeSpan
return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result);
}
- public static bool TryParseExact(ReadOnlySpan input, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] ReadOnlySpan format, IFormatProvider? formatProvider, TimeSpanStyles styles, out TimeSpan result)
+ public static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
{
ValidateStyles(styles);
return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result);
}
- public static bool TryParseExact([NotNullWhen(true)] string? input, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string?[]? formats, IFormatProvider? formatProvider, TimeSpanStyles styles, out TimeSpan result)
+ public static bool TryParseExact(string input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
{
ValidateStyles(styles);
@@ -840,23 +839,24 @@ public readonly struct TimeSpan
return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result);
}
- public static bool TryParseExact(ReadOnlySpan input, [NotNullWhen(true), StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string?[]? formats, IFormatProvider? formatProvider, TimeSpanStyles styles, out TimeSpan result)
+ public static bool TryParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
{
ValidateStyles(styles);
return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result);
}
+
public override string ToString() => TimeSpanFormat.FormatC(this);
- public string ToString([StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string? format) => TimeSpanFormat.Format(this, format, null);
- public string ToString([StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] string? format, IFormatProvider? formatProvider) => TimeSpanFormat.Format(this, format, formatProvider);
+ public string ToString(string format) => TimeSpanFormat.Format(this, format, null);
+ public string ToString(string format, IFormatProvider formatProvider) => TimeSpanFormat.Format(this, format, formatProvider);
- public bool TryFormat(Span destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] ReadOnlySpan format = default, IFormatProvider? formatProvider = null)
+ public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider formatProvider = null)
=> TimeSpanFormat.TryFormat(this, destination, out charsWritten, format, formatProvider);
///
- public bool TryFormat(Span utf8Destination, out int bytesWritten, [StringSyntax(StringSyntaxAttribute.TimeSpanFormat)] ReadOnlySpan format = default, IFormatProvider? formatProvider = null)
+ public bool TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format = default, IFormatProvider formatProvider = null)
=> TimeSpanFormat.TryFormat(this, utf8Destination, out bytesWritten, format, formatProvider);
- #endregion
+ //#endregion
public static TimeSpan operator -(TimeSpan t)
{
@@ -931,7 +931,7 @@ public readonly struct TimeSpan
// an hour for which infinity is the mathematic correct answer. Having TimeSpan.Zero / TimeSpan.Zero return NaN
// is perhaps less useful, but no less useful than an exception.
///
- public static double operator /(TimeSpan t1, TimeSpan t2) => t1.Ticks / (double)t2.Ticks;
+ public static double operator /(TimeSpan t1, TimeSpan t2) => (double)t1.Ticks / (double)t2.Ticks;
///
public static bool operator ==(TimeSpan t1, TimeSpan t2) => t1._ticks == t2._ticks;
From 1ba14b1ed5ea1b73d831aa17459f0b1706d4320d Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Wed, 15 Apr 2026 14:58:21 +0300
Subject: [PATCH 04/26] Int128 added and TimeSpan changes
---
std/System/Int128.hpt | 2158 +++++++++++++++++++++++++++++++++++++++
std/System/TimeSpan.hpt | 33 +-
2 files changed, 2176 insertions(+), 15 deletions(-)
create mode 100644 std/System/Int128.hpt
diff --git a/std/System/Int128.hpt b/std/System/Int128.hpt
new file mode 100644
index 00000000..c6cc8dda
--- /dev/null
+++ b/std/System/Int128.hpt
@@ -0,0 +1,2158 @@
+
+
+/// Represents a 128-bit signed integer.Initializes a new instance of the struct.
+ /// The upper 64-bits of the 128-bit value.
+ /// The lower 64-bits of the 128-bit value.
+ public Int128(ulong upper, ulong lower)
+ {
+ _lower = lower;
+ _upper = upper;
+ }
+
+ internal ulong Lower => _lower;
+
+ internal ulong Upper => _upper;
+
+ ///
+ public int CompareTo(object value)
+ {
+ if (value is Int128 other)
+ {
+ return CompareTo(other);
+ }
+ else if (value is null)
+ {
+ return 1;
+ }
+ else
+ {
+ throw new ArgumentException(SR.Arg_MustBeInt128);
+ }
+ }
+
+ ///
+ public int CompareTo(Int128 value)
+ {
+ if (this < value)
+ {
+ return -1;
+ }
+ else if (this > value)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ return (obj is Int128 other) && Equals(other);
+ }
+
+ ///
+ public bool Equals(Int128 other)
+ {
+ return this == other;
+ }
+
+ ///
+ public override int GetHashCode() => HashCode.Combine(_lower, _upper);
+
+ ///
+ public override string ToString()
+ {
+ return Number.Int128ToDecStr(this);
+ }
+
+ public string ToString(IFormatProvider? provider)
+ {
+ return Number.FormatInt128(this, null, provider);
+ }
+
+ public string ToString(string format)
+ {
+ return Number.FormatInt128(this, format, null);
+ }
+
+ public string ToString(string format, IFormatProvider? provider)
+ {
+ return Number.FormatInt128(this, format, provider);
+ }
+
+ public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider? provider = null)
+ {
+ return Number.TryFormatInt128(this, format, provider, destination, out charsWritten);
+ }
+
+ ///
+ public bool TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format = default, IFormatProvider? provider = null)
+ {
+ return Number.TryFormatInt128(this, format, provider, utf8Destination, out bytesWritten);
+ }
+
+ public static Int128 Parse(string s) => Parse(s, NumberStyles.Integer, provider: null);
+
+ public static Int128 Parse(string s, NumberStyles style) => Parse(s, style, provider: null);
+
+ public static Int128 Parse(string s, IFormatProvider provider) => Parse(s, NumberStyles.Integer, provider);
+
+ public static Int128 Parse(string s, NumberStyles style, IFormatProvider provider)
+ {
+ if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); }
+ return Parse(s.AsSpan(), style, provider);
+ }
+
+ public static Int128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
+ return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ public static bool TryParse(string s, out Int128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result);
+
+ public static bool TryParse(ReadOnlySpan s, out Int128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result);
+
+ /// Tries to convert a UTF-8 character span containing the string representation of a number to its 128-bit signed integer equivalent.
+ /// A span containing the UTF-8 characters representing the number to convert.
+ /// When this method returns, contains the 128-bit signed integer value equivalent to the number contained in if the conversion succeeded, or zero if the conversion failed. This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
+ /// true if was converted successfully; otherwise, false.
+ public static bool TryParse(ReadOnlySpan utf8Text, out Int128 result) => TryParse(utf8Text, NumberStyles.Integer, provider: null, out result);
+
+ public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out Int128 result)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
+
+ if (s is null)
+ {
+ result = 0;
+ return false;
+ }
+ return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
+ }
+
+ public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out Int128 result)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
+ return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
+ }
+
+ //
+ // Explicit Conversions From Int128
+ //
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator byte(Int128 value) => (byte)value._lower;
+
+ /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked byte(Int128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((byte)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator char(Int128 value) => (char)value._lower;
+
+ /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked char(Int128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((char)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator decimal(Int128 value)
+ {
+ if (IsNegative(value))
+ {
+ value = -value;
+ return -(decimal)(UInt128)(value);
+ }
+ return (decimal)(UInt128)(value);
+ }
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator double(Int128 value)
+ {
+ if (IsNegative(value))
+ {
+ value = -value;
+ return -(double)(UInt128)(value);
+ }
+ return (double)(UInt128)(value);
+ }
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator Half(Int128 value)
+ {
+ if (IsNegative(value))
+ {
+ value = -value;
+ return -(Half)(UInt128)(value);
+ }
+ return (Half)(UInt128)(value);
+ }
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator short(Int128 value) => (short)value._lower;
+
+ /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked short(Int128 value)
+ {
+ long lower = (long)value._lower;
+ if ((long)value._upper != lower >> 63)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((short)lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator int(Int128 value) => (int)value._lower;
+
+ /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked int(Int128 value)
+ {
+ long lower = (long)value._lower;
+ if ((long)value._upper != lower >> 63)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((int)lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator long(Int128 value) => (long)value._lower;
+
+ /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked long(Int128 value)
+ {
+ long lower = (long)value._lower;
+ if ((long)value._upper != lower >> 63)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return lower;
+ }
+ */
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator nint(Int128 value) => (nint)value._lower;
+
+ /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked nint(Int128 value)
+ {
+ long lower = (long)value._lower;
+ if ((long)value._upper != lower >> 63)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((nint)lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator sbyte(Int128 value) => (sbyte)value._lower;
+
+ /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked sbyte(Int128 value)
+ {
+ long lower = (long)value._lower;
+ if ((long)value._upper != lower >> 63)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((sbyte)lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator float(Int128 value)
+ {
+ if (IsNegative(value))
+ {
+ value = -value;
+ return -(float)(UInt128)(value);
+ }
+ return (float)(UInt128)(value);
+ }
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator ushort(Int128 value) => (ushort)value._lower;
+
+ /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked ushort(Int128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((ushort)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator uint(Int128 value) => (uint)value._lower;
+
+ /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked uint(Int128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((uint)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator ulong(Int128 value) => value._lower;
+
+ /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked ulong(Int128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return value._lower;
+ }
+ */
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator UInt128(Int128 value) => new UInt128(value._upper, value._lower);
+
+ /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked UInt128(Int128 value)
+ {
+ if ((long)value._upper < 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return new UInt128(value._upper, value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit signed integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator nuint(Int128 value) => (nuint)value._lower;
+
+ /// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked nuint(Int128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((nuint)value._lower);
+ }
+ */
+
+ //
+ // Explicit Conversions To Int128
+ //
+
+ /// Explicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ public static explicit operator Int128(decimal value)
+ {
+ value = decimal.Truncate(value);
+ Int128 result = new Int128(value.High, value.Low64);
+
+ if (decimal.IsNegative(value))
+ {
+ result = -result;
+ }
+ return result;
+ }
+
+ /// Explicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ public static explicit operator Int128(double value)
+ {
+ const double TwoPow127 = 170141183460469231731687303715884105728.0;
+
+ if (value <= -TwoPow127)
+ {
+ return MinValue;
+ }
+ else if (double.IsNaN(value))
+ {
+ return 0;
+ }
+ else if (value >= +TwoPow127)
+ {
+ return MaxValue;
+ }
+
+ return ToInt128(value);
+ }
+
+ /// Explicitly converts a value to a 128-bit signed integer, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked Int128(double value)
+ {
+ const double TwoPow127 = 170141183460469231731687303715884105728.0;
+
+ if ((value < -TwoPow127) || double.IsNaN(value) || (value >= +TwoPow127))
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+
+ return ToInt128(value);
+ }
+ */
+
+ internal static Int128 ToInt128(double value)
+ {
+ const double TwoPow127 = 170141183460469231731687303715884105728.0;
+
+ Debug.Assert(value >= -TwoPow127);
+ Debug.Assert(double.IsFinite(value));
+ Debug.Assert(value < TwoPow127);
+
+ // This code is based on `f64_to_i128` from m-ou-se/floatconv
+ // Copyright (c) 2020 Mara Bos . All rights reserved.
+ //
+ // Licensed under the BSD 2 - Clause "Simplified" License
+ // See THIRD-PARTY-NOTICES.TXT for the full license text
+
+ bool isNegative = double.IsNegative(value);
+
+ if (isNegative)
+ {
+ value = -value;
+ }
+
+ if (value >= 1.0)
+ {
+ // In order to convert from double to int128 we first need to extract the signficand,
+ // including the implicit leading bit, as a full 128-bit significand. We can then adjust
+ // this down to the represented integer by right shifting by the unbiased exponent, taking
+ // into account the significand is now represented as 128-bits.
+
+ ulong bits = BitConverter.DoubleToUInt64Bits(value);
+ Int128 result = new Int128((bits << 12) >> 1 | 0x8000_0000_0000_0000, 0x0000_0000_0000_0000);
+
+ result >>>= (1023 + 128 - 1 - (int)(bits >> 52));
+
+ if (isNegative)
+ {
+ result = -result;
+ }
+ return result;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ /// Explicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ public static explicit operator Int128(float value) => (Int128)(double)(value);
+
+ /// Explicitly converts a value to a 128-bit signed integer, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked Int128(float value) => checked((Int128)(double)(value));
+ */
+ //
+ // Implicit Conversions To Int128
+ //
+
+ /// Implicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ public static implicit operator Int128(byte value) => new Int128(0, value);
+
+ /// Implicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ public static implicit operator Int128(char value) => new Int128(0, value);
+
+ /// Implicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ public static implicit operator Int128(short value)
+ {
+ long lower = value;
+ return new Int128((ulong)(lower >> 63), (ulong)lower);
+ }
+
+ /// Implicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ public static implicit operator Int128(int value)
+ {
+ long lower = value;
+ return new Int128((ulong)(lower >> 63), (ulong)lower);
+ }
+
+ /// Implicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ public static implicit operator Int128(long value)
+ {
+ long lower = value;
+ return new Int128((ulong)(lower >> 63), (ulong)lower);
+ }
+
+ /// Implicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ public static implicit operator Int128(nint value)
+ {
+ long lower = value;
+ return new Int128((ulong)(lower >> 63), (ulong)lower);
+ }
+
+ /// Implicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ [CLSCompliant(false)]
+ public static implicit operator Int128(sbyte value)
+ {
+ long lower = value;
+ return new Int128((ulong)(lower >> 63), (ulong)lower);
+ }
+
+ /// Implicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ [CLSCompliant(false)]
+ public static implicit operator Int128(ushort value) => new Int128(0, value);
+
+ /// Implicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ [CLSCompliant(false)]
+ public static implicit operator Int128(uint value) => new Int128(0, value);
+
+ /// Implicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ [CLSCompliant(false)]
+ public static implicit operator Int128(ulong value) => new Int128(0, value);
+
+ /// Implicitly converts a value to a 128-bit signed integer.
+ /// The value to convert.
+ /// converted to a 128-bit signed integer.
+ [CLSCompliant(false)]
+ public static implicit operator Int128(nuint value) => new Int128(0, value);
+
+ //
+ // IAdditionOperators
+ //
+
+ ///
+ public static Int128 operator +(Int128 left, Int128 right)
+ {
+ // For unsigned addition, we can detect overflow by checking `(x + y) < x`
+ // This gives us the carry to add to upper to compute the correct result
+
+ ulong lower = left._lower + right._lower;
+ ulong carry = (lower < left._lower) ? 1UL : 0UL;
+
+ ulong upper = left._upper + right._upper + carry;
+ return new Int128(upper, lower);
+ }
+
+ ///
+ /* TODO:
+ public static Int128 operator checked +(Int128 left, Int128 right)
+ {
+ // For signed addition, we can detect overflow by checking if the sign of
+ // both inputs are the same and then if that differs from the sign of the
+ // output. The logic for how this works is explained in the
+ // System.Runtime.Intrinsics.Scalar.AddSaturate method
+
+ Int128 result = left + right;
+
+ if ((long)((result._upper ^ left._upper) & ~(left._upper ^ right._upper)) < 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return result;
+ }
+ */
+
+ //
+ // IAdditiveIdentity
+ //
+
+ ///
+ static Int128 IAdditiveIdentity.AdditiveIdentity => default;
+
+ //
+ // IBinaryInteger
+ //
+
+ ///
+ public static (Int128 Quotient, Int128 Remainder) DivRem(Int128 left, Int128 right)
+ {
+ Int128 quotient = left / right;
+ return (quotient, left - (quotient * right));
+ }
+
+ ///
+ public static Int128 LeadingZeroCount(Int128 value)
+ => LeadingZeroCountAsInt32(value);
+
+ /// Computes the number of leading zero bits in this value.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static int LeadingZeroCountAsInt32(Int128 value)
+ {
+ if (value._upper == 0)
+ {
+ return 64 + BitOperations.LeadingZeroCount(value._lower);
+ }
+ return BitOperations.LeadingZeroCount(value._upper);
+ }
+
+ ///
+ public static Int128 Log10(Int128 value)
+ {
+ if (IsNegative(value))
+ {
+ ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
+ }
+ return (Int128)UInt128.Log10((UInt128)value);
+ }
+
+ ///
+ public static Int128 PopCount(Int128 value)
+ => ulong.PopCount(value._lower) + ulong.PopCount(value._upper);
+
+ ///
+ public static Int128 RotateLeft(Int128 value, int rotateAmount)
+ => (value << rotateAmount) | (value >>> (128 - rotateAmount));
+
+ ///
+ public static Int128 RotateRight(Int128 value, int rotateAmount)
+ => (value >>> rotateAmount) | (value << (128 - rotateAmount));
+
+ ///
+ public static Int128 TrailingZeroCount(Int128 value)
+ {
+ if (value._lower == 0)
+ {
+ return 64 + ulong.TrailingZeroCount(value._upper);
+ }
+ return ulong.TrailingZeroCount(value._lower);
+ }
+
+ ///
+ static bool IBinaryInteger.TryReadBigEndian(ReadOnlySpan source, bool isUnsigned, out Int128 value)
+ {
+ Int128 result = default;
+
+ if (source.Length != 0)
+ {
+ // Propagate the most significant bit so we have `0` or `-1`
+ sbyte sign = (sbyte)(source[0]);
+ sign >>= 31;
+ Debug.Assert((sign == 0) || (sign == -1));
+
+ // We need to also track if the input data is unsigned
+ isUnsigned |= (sign == 0);
+
+ if (isUnsigned && sbyte.IsNegative(sign) && (source.Length >= Size))
+ {
+ // When we are unsigned and the most significant bit is set, we are a large positive
+ // and therefore definitely out of range
+
+ value = result;
+ return false;
+ }
+
+ if (source.Length > Size)
+ {
+ if (source[..^Size].ContainsAnyExcept((byte)sign))
+ {
+ // When we are unsigned and have any non-zero leading data or signed with any non-set leading
+ // data, we are a large positive/negative, respectively, and therefore definitely out of range
+
+ value = result;
+ return false;
+ }
+
+ if (isUnsigned == sbyte.IsNegative((sbyte)source[^Size]))
+ {
+ // When the most significant bit of the value being set/clear matches whether we are unsigned
+ // or signed then we are a large positive/negative and therefore definitely out of range
+
+ value = result;
+ return false;
+ }
+ }
+
+ ref byte sourceRef = ref MemoryMarshal.GetReference(source);
+
+ if (source.Length >= Size)
+ {
+ sourceRef = ref Unsafe.Add(ref sourceRef, source.Length - Size);
+
+ // We have at least 16 bytes, so just read the ones we need directly
+ result = Unsafe.ReadUnaligned(ref sourceRef);
+
+ if (BitConverter.IsLittleEndian)
+ {
+ result = BinaryPrimitives.ReverseEndianness(result);
+ }
+ }
+ else
+ {
+ // We have between 1 and 15 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);
+ }
+
+ if (!isUnsigned)
+ {
+ result |= ((One << ((Size * 8) - 1)) >> (((Size - source.Length) * 8) - 1));
+ }
+ }
+ }
+
+ value = result;
+ return true;
+ }
+
+ ///
+ static bool IBinaryInteger.TryReadLittleEndian(ReadOnlySpan source, bool isUnsigned, out Int128 value)
+ {
+ Int128 result = default;
+
+ if (source.Length != 0)
+ {
+ // Propagate the most significant bit so we have `0` or `-1`
+ sbyte sign = (sbyte)(source[^1]);
+ sign >>= 31;
+ Debug.Assert((sign == 0) || (sign == -1));
+
+ // We need to also track if the input data is unsigned
+ isUnsigned |= (sign == 0);
+
+ if (isUnsigned && sbyte.IsNegative(sign) && (source.Length >= Size))
+ {
+ // When we are unsigned and the most significant bit is set, we are a large positive
+ // and therefore definitely out of range
+
+ value = result;
+ return false;
+ }
+
+ if (source.Length > Size)
+ {
+ if (source[Size..].ContainsAnyExcept((byte)sign))
+ {
+ // When we are unsigned and have any non-zero leading data or signed with any non-set leading
+ // data, we are a large positive/negative, respectively, and therefore definitely out of range
+
+ value = result;
+ return false;
+ }
+
+ if (isUnsigned == sbyte.IsNegative((sbyte)source[Size - 1]))
+ {
+ // When the most significant bit of the value being set/clear matches whether we are unsigned
+ // or signed then we are a large positive/negative and therefore definitely out of range
+
+ value = result;
+ return false;
+ }
+ }
+
+ ref byte sourceRef = ref MemoryMarshal.GetReference(source);
+
+ if (source.Length >= Size)
+ {
+ // We have at least 16 bytes, so just read the ones we need directly
+ result = Unsafe.ReadUnaligned(ref sourceRef);
+
+ if (!BitConverter.IsLittleEndian)
+ {
+ result = BinaryPrimitives.ReverseEndianness(result);
+ }
+ }
+ else
+ {
+ // We have between 1 and 15 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++)
+ {
+ result <<= 8;
+ result |= Unsafe.Add(ref sourceRef, i);
+ }
+
+ result <<= ((Size - source.Length) * 8);
+ result = BinaryPrimitives.ReverseEndianness(result);
+
+ if (!isUnsigned)
+ {
+ result |= ((One << ((Size * 8) - 1)) >> (((Size - source.Length) * 8) - 1));
+ }
+ }
+ }
+
+ value = result;
+ return true;
+ }
+
+ ///
+ int IBinaryInteger.GetShortestBitLength()
+ {
+ Int128 value = this;
+
+ if (IsPositive(value))
+ {
+ return (Size * 8) - LeadingZeroCountAsInt32(value);
+ }
+ else
+ {
+ return (Size * 8) + 1 - LeadingZeroCountAsInt32(~value);
+ }
+ }
+
+ ///
+ int IBinaryInteger.GetByteCount() => Size;
+
+ ///
+ bool IBinaryInteger.TryWriteBigEndian(Span destination, out int bytesWritten)
+ {
+ if (BinaryPrimitives.TryWriteInt128BigEndian(destination, this))
+ {
+ bytesWritten = Size;
+ return true;
+ }
+
+ bytesWritten = 0;
+ return false;
+ }
+
+ ///
+ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int bytesWritten)
+ {
+ if (BinaryPrimitives.TryWriteInt128LittleEndian(destination, this))
+ {
+ bytesWritten = Size;
+ return true;
+ }
+
+ bytesWritten = 0;
+ return false;
+ }
+
+ //
+ // IBinaryNumber
+ //
+
+ ///
+ static Int128 IBinaryNumber.AllBitsSet => new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+ ///
+ public static bool IsPow2(Int128 value) => (PopCount(value) == 1U) && IsPositive(value);
+
+ ///
+ public static Int128 Log2(Int128 value)
+ {
+ if (IsNegative(value))
+ {
+ ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
+ }
+
+ if (value._upper == 0)
+ {
+ return ulong.Log2(value._lower);
+ }
+ return 64 + ulong.Log2(value._upper);
+ }
+
+ //
+ // IBitwiseOperators
+ //
+
+ ///
+ public static Int128 operator &(Int128 left, Int128 right) => new Int128(left._upper & right._upper, left._lower & right._lower);
+
+ ///
+ public static Int128 operator |(Int128 left, Int128 right) => new Int128(left._upper | right._upper, left._lower | right._lower);
+
+ ///
+ public static Int128 operator ^(Int128 left, Int128 right) => new Int128(left._upper ^ right._upper, left._lower ^ right._lower);
+
+ ///
+ public static Int128 operator ~(Int128 value) => new Int128(~value._upper, ~value._lower);
+
+ //
+ // IComparisonOperators
+ //
+
+ ///
+ public static bool operator <(Int128 left, Int128 right)
+ {
+ // If left and right have different signs: Signed comparison of _upper gives result since it is stored as two's complement
+ // If signs are equal and left._upper < right._upper: left < right for negative and positive values,
+ // since _upper is upper 64 bits in two's complement.
+ // If signs are equal and left._upper > right._upper: left > right for negative and positive values,
+ // since _upper is upper 64 bits in two's complement.
+ // If left._upper == right._upper: unsigned comparison of _lower gives the result for both negative and positive values since
+ // lower values are lower 64 bits in two's complement.
+ return ((long)left._upper < (long)right._upper)
+ || ((left._upper == right._upper) && (left._lower < right._lower));
+ }
+
+ ///
+ public static bool operator <=(Int128 left, Int128 right)
+ {
+ // See comment in < operator for how this works.
+ return ((long)left._upper < (long)right._upper)
+ || ((left._upper == right._upper) && (left._lower <= right._lower));
+ }
+
+ ///
+ public static bool operator >(Int128 left, Int128 right)
+ {
+ // See comment in < operator for how this works.
+ return ((long)left._upper > (long)right._upper)
+ || ((left._upper == right._upper) && (left._lower > right._lower));
+ }
+
+ ///
+ public static bool operator >=(Int128 left, Int128 right)
+ {
+ // See comment in < operator for how this works.
+ return ((long)left._upper > (long)right._upper)
+ || ((left._upper == right._upper) && (left._lower >= right._lower));
+ }
+
+ //
+ // IDecrementOperators
+ //
+
+ ///
+ public static Int128 operator --(Int128 value) => value - One;
+
+ ///
+ /* TODO:
+ public static Int128 operator checked --(Int128 value) => checked(value - One);
+ */
+ //
+ // IDivisionOperators
+ //
+
+ ///
+ public static Int128 operator /(Int128 left, Int128 right)
+ {
+ if ((right == -1) && (left._upper == 0x8000_0000_0000_0000) && (left._lower == 0))
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+
+ // We simplify the logic here by just doing unsigned division on the
+ // two's complement representation and then taking the correct sign.
+
+ ulong sign = (left._upper ^ right._upper) & (1UL << 63);
+
+ if (IsNegative(left))
+ {
+ left = ~left + 1U;
+ }
+
+ if (IsNegative(right))
+ {
+ right = ~right + 1U;
+ }
+
+ UInt128 result = (UInt128)(left) / (UInt128)(right);
+
+ if (sign != 0)
+ {
+ result = ~result + 1U;
+ }
+
+ return new Int128(
+ result.Upper,
+ result.Lower
+ );
+ }
+
+ ///
+ /* TODO:
+ public static Int128 operator checked /(Int128 left, Int128 right) => left / right;
+ */
+ //
+ // IEqualityOperators
+ //
+
+ ///
+ public static bool operator ==(Int128 left, Int128 right) => (left._lower == right._lower) && (left._upper == right._upper);
+
+ ///
+ public static bool operator !=(Int128 left, Int128 right) => (left._lower != right._lower) || (left._upper != right._upper);
+
+ //
+ // IIncrementOperators
+ //
+
+ ///
+ public static Int128 operator ++(Int128 value) => value + One;
+
+ ///
+ /* TODO:
+ public static Int128 operator checked ++(Int128 value) => checked(value + One);
+ */
+ //
+ // IMinMaxValue
+ //
+
+ ///
+ public static Int128 MinValue => new Int128(0x8000_0000_0000_0000, 0);
+
+ ///
+ public static Int128 MaxValue => new Int128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+ //
+ // IModulusOperators
+ //
+
+ ///
+ public static Int128 operator %(Int128 left, Int128 right)
+ {
+ Int128 quotient = left / right;
+ return left - (quotient * right);
+ }
+
+ //
+ // IMultiplicativeIdentity
+ //
+
+ ///
+ static Int128 IMultiplicativeIdentity.MultiplicativeIdentity => One;
+
+ //
+ // IMultiplyOperators
+ //
+
+ ///
+ public static Int128 operator *(Int128 left, Int128 right)
+ {
+ // Multiplication is the same for signed and unsigned provided the "upper" bits aren't needed
+ return (Int128)((UInt128)(left) * (UInt128)(right));
+ }
+
+ ///
+ /* TODO:
+ public static Int128 operator checked *(Int128 left, Int128 right)
+ {
+ Int128 upper = BigMul(left, right, out Int128 lower);
+
+ if (((upper != 0) || (lower < 0)) && ((~upper != 0) || (lower >= 0)))
+ {
+ // The upper bits can safely be either Zero or AllBitsSet
+ // where the former represents a positive value and the
+ // latter a negative value.
+ //
+ // However, when the upper bits are Zero, we also need to
+ // confirm the lower bits are positive, otherwise we have
+ // a positive value greater than MaxValue and should throw
+ //
+ // Likewise, when the upper bits are AllBitsSet, we also
+ // need to confirm the lower bits are negative, otherwise
+ // we have a large negative value less than MinValue and
+ // should throw.
+
+ ThrowHelper.ThrowOverflowException();
+ }
+
+ return lower;
+ }
+ */
+
+ /// Produces the full product of two unsigned native integers.
+ /// The integer to multiply with .
+ /// The integer to multiply with .
+ /// The lower half of the full product.
+ /// The upper half of the full product.
+ public static Int128 BigMul(Int128 left, Int128 right, out Int128 lower)
+ {
+ // This follows the same logic as is used in `long Math.BigMul(long, long, out long)`
+
+ UInt128 upper = UInt128.BigMul((UInt128)(left), (UInt128)(right), out UInt128 ulower);
+ lower = (Int128)(ulower);
+ return (Int128)(upper) - ((left >> 127) & right) - ((right >> 127) & left);
+ }
+
+ //
+ // INumber
+ //
+
+ ///
+ public static Int128 Clamp(Int128 value, Int128 min, Int128 max)
+ {
+ if (min > max)
+ {
+ Math.ThrowMinMaxException(min, max);
+ }
+
+ if (value < min)
+ {
+ return min;
+ }
+ else if (value > max)
+ {
+ return max;
+ }
+
+ return value;
+ }
+
+ ///
+ public static Int128 CopySign(Int128 value, Int128 sign)
+ {
+ Int128 absValue = value;
+
+ if (IsNegative(absValue))
+ {
+ absValue = -absValue;
+ }
+
+ if (IsPositive(sign))
+ {
+ if (IsNegative(absValue))
+ {
+ Math.ThrowNegateTwosCompOverflow();
+ }
+ return absValue;
+ }
+ return -absValue;
+ }
+
+ ///
+ public static Int128 Max(Int128 x, Int128 y) => (x >= y) ? x : y;
+
+ ///
+ static Int128 INumber.MaxNumber(Int128 x, Int128 y) => Max(x, y);
+
+ ///
+ public static Int128 Min(Int128 x, Int128 y) => (x <= y) ? x : y;
+
+ ///
+ static Int128 INumber.MinNumber(Int128 x, Int128 y) => Min(x, y);
+
+ ///
+ public static int Sign(Int128 value)
+ {
+ if (IsNegative(value))
+ {
+ return -1;
+ }
+ else if (value != default)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ //
+ // INumberBase
+ //
+
+ ///
+ public static Int128 One => new Int128(0, 1);
+
+ ///
+ static int INumberBase.Radix => 2;
+
+ ///
+ public static Int128 Zero => default;
+
+ ///
+ public static Int128 Abs(Int128 value)
+ {
+ if (IsNegative(value))
+ {
+ value = -value;
+
+ if (IsNegative(value))
+ {
+ Math.ThrowNegateTwosCompOverflow();
+ }
+ }
+ return value;
+ }
+
+ ///
+ public static inline Int128 CreateChecked(TOther value)
+ where TOther : INumberBase
+ {
+ Int128 result;
+
+ if (typeof(TOther) == typeof(Int128))
+ {
+ result = (Int128)(object)value;
+ }
+ else if (!TryConvertFromChecked(value, out result) && !TOther.TryConvertToChecked(value, out result))
+ {
+ ThrowHelper.ThrowNotSupportedException();
+ }
+
+ return result;
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Int128 CreateSaturating(TOther value)
+ where TOther : INumberBase
+ {
+ Int128 result;
+
+ if (typeof(TOther) == typeof(Int128))
+ {
+ result = (Int128)(object)value;
+ }
+ else if (!TryConvertFromSaturating(value, out result) && !TOther.TryConvertToSaturating(value, out result))
+ {
+ ThrowHelper.ThrowNotSupportedException();
+ }
+
+ return result;
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Int128 CreateTruncating(TOther value)
+ where TOther : INumberBase
+ {
+ Int128 result;
+
+ if (typeof(TOther) == typeof(Int128))
+ {
+ result = (Int128)(object)value;
+ }
+ else if (!TryConvertFromTruncating(value, out result) && !TOther.TryConvertToTruncating(value, out result))
+ {
+ ThrowHelper.ThrowNotSupportedException();
+ }
+
+ return result;
+ }
+
+ ///
+ static bool INumberBase.IsCanonical(Int128 value) => true;
+
+ ///
+ static bool INumberBase.IsComplexNumber(Int128 value) => false;
+
+ ///
+ public static bool IsEvenInteger(Int128 value) => (value._lower & 1) == 0;
+
+ ///
+ static bool INumberBase.IsFinite(Int128 value) => true;
+
+ ///
+ static bool INumberBase.IsImaginaryNumber(Int128 value) => false;
+
+ ///
+ static bool INumberBase.IsInfinity(Int128 value) => false;
+
+ ///
+ static bool INumberBase.IsInteger(Int128 value) => true;
+
+ ///
+ static bool INumberBase.IsNaN(Int128 value) => false;
+
+ ///
+ public static bool IsNegative(Int128 value) => (long)value._upper < 0;
+
+ ///
+ static bool INumberBase.IsNegativeInfinity(Int128 value) => false;
+
+ ///
+ static bool INumberBase.IsNormal(Int128 value) => value != 0;
+
+ ///
+ public static bool IsOddInteger(Int128 value) => (value._lower & 1) != 0;
+
+ ///
+ public static bool IsPositive(Int128 value) => (long)value._upper >= 0;
+
+ ///
+ static bool INumberBase.IsPositiveInfinity(Int128 value) => false;
+
+ ///
+ static bool INumberBase.IsRealNumber(Int128 value) => true;
+
+ ///
+ static bool INumberBase.IsSubnormal(Int128 value) => false;
+
+ ///
+ static bool INumberBase.IsZero(Int128 value) => (value == 0);
+
+ ///
+ public static Int128 MaxMagnitude(Int128 x, Int128 y)
+ {
+ Int128 absX = x;
+
+ if (IsNegative(absX))
+ {
+ absX = -absX;
+
+ if (IsNegative(absX))
+ {
+ return x;
+ }
+ }
+
+ Int128 absY = y;
+
+ if (IsNegative(absY))
+ {
+ absY = -absY;
+
+ if (IsNegative(absY))
+ {
+ return y;
+ }
+ }
+
+ if (absX > absY)
+ {
+ return x;
+ }
+
+ if (absX == absY)
+ {
+ return IsNegative(x) ? y : x;
+ }
+
+ return y;
+ }
+
+ ///
+ static Int128 INumberBase.MaxMagnitudeNumber(Int128 x, Int128 y) => MaxMagnitude(x, y);
+
+ ///
+ public static Int128 MinMagnitude(Int128 x, Int128 y)
+ {
+ Int128 absX = x;
+
+ if (IsNegative(absX))
+ {
+ absX = -absX;
+
+ if (IsNegative(absX))
+ {
+ return y;
+ }
+ }
+
+ Int128 absY = y;
+
+ if (IsNegative(absY))
+ {
+ absY = -absY;
+
+ if (IsNegative(absY))
+ {
+ return x;
+ }
+ }
+
+ if (absX < absY)
+ {
+ return x;
+ }
+
+ if (absX == absY)
+ {
+ return IsNegative(x) ? x : y;
+ }
+
+ return y;
+ }
+
+ ///
+ static Int128 INumberBase.MinMagnitudeNumber(Int128 x, Int128 y) => MinMagnitude(x, y);
+
+ ///
+ static Int128 INumberBase.MultiplyAddEstimate(Int128 left, Int128 right, Int128 addend) => (left * right) + addend;
+
+ ///
+ static inline bool INumberBase.TryConvertFromChecked(TOther value, out Int128 result) => TryConvertFromChecked(value, out result);
+
+ private static inline bool TryConvertFromChecked(TOther value, out Int128 result)
+ where TOther : INumberBase
+ {
+ // 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 `Int128` will handle the other signed types and
+ // `ConvertTo` will handle the unsigned types
+
+ if (typeof(TOther) == typeof(double))
+ {
+ double actualValue = (double)(object)value;
+ result = checked((Int128)actualValue);
+ return true;
+ }
+ else if (typeof(TOther) == typeof(Half))
+ {
+ Half actualValue = (Half)(object)value;
+ result = checked((Int128)actualValue);
+ return true;
+ }
+ else if (typeof(TOther) == typeof(short))
+ {
+ short actualValue = (short)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(int))
+ {
+ int actualValue = (int)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(long))
+ {
+ long actualValue = (long)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(nint))
+ {
+ nint actualValue = (nint)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(sbyte))
+ {
+ sbyte actualValue = (sbyte)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(float))
+ {
+ float actualValue = (float)(object)value;
+ result = checked((Int128)actualValue);
+ return true;
+ }
+ else
+ {
+ result = default;
+ return false;
+ }
+ }
+
+ ///
+ static inline bool INumberBase.TryConvertFromSaturating(TOther value, out Int128 result) => TryConvertFromSaturating(value, out result);
+
+ private static inline bool TryConvertFromSaturating(TOther value, out Int128 result)
+ where TOther : INumberBase
+ {
+ // 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 `Int128` will handle the other signed types and
+ // `ConvertTo` will handle the unsigned types
+
+ if (typeof(TOther) == typeof(double))
+ {
+ double actualValue = (double)(object)value;
+ result = (Int128)actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(Half))
+ {
+ Half actualValue = (Half)(object)value;
+ result = (Int128)actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(short))
+ {
+ short actualValue = (short)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(int))
+ {
+ int actualValue = (int)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(long))
+ {
+ long actualValue = (long)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(nint))
+ {
+ nint actualValue = (nint)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(sbyte))
+ {
+ sbyte actualValue = (sbyte)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(float))
+ {
+ float actualValue = (float)(object)value;
+ result = (Int128)actualValue;
+ return true;
+ }
+ else
+ {
+ result = default;
+ return false;
+ }
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ static bool INumberBase.TryConvertFromTruncating(TOther value, out Int128 result) => TryConvertFromTruncating(value, out result);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static bool TryConvertFromTruncating(TOther value, out Int128 result)
+ where TOther : INumberBase
+ {
+ // 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 `Int128` will handle the other signed types and
+ // `ConvertTo` will handle the unsigned types
+
+ if (typeof(TOther) == typeof(double))
+ {
+ double actualValue = (double)(object)value;
+ result = (Int128)actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(Half))
+ {
+ Half actualValue = (Half)(object)value;
+ result = (Int128)actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(short))
+ {
+ short actualValue = (short)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(int))
+ {
+ int actualValue = (int)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(long))
+ {
+ long actualValue = (long)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(nint))
+ {
+ nint actualValue = (nint)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(sbyte))
+ {
+ sbyte actualValue = (sbyte)(object)value;
+ result = actualValue;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(float))
+ {
+ float actualValue = (float)(object)value;
+ result = (Int128)actualValue;
+ return true;
+ }
+ else
+ {
+ result = default;
+ return false;
+ }
+ }
+
+ ///
+ static inline bool INumberBase.TryConvertToChecked(Int128 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 `Int128` will handle the other signed types and
+ // `ConvertTo` will handle the unsigned types
+
+ if (typeof(TOther) == typeof(byte))
+ {
+ byte actualResult = checked((byte)value);
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(char))
+ {
+ char actualResult = checked((char)value);
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(decimal))
+ {
+ decimal actualResult = checked((decimal)value);
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(ushort))
+ {
+ ushort actualResult = checked((ushort)value);
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(uint))
+ {
+ uint actualResult = checked((uint)value);
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(ulong))
+ {
+ ulong actualResult = checked((ulong)value);
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(UInt128))
+ {
+ UInt128 actualResult = checked((UInt128)value);
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(nuint))
+ {
+ nuint actualResult = checked((nuint)value);
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else
+ {
+ result = default;
+ return false;
+ }
+ }
+
+ ///
+ static inline bool INumberBase.TryConvertToSaturating(Int128 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 `Int128` will handle the other signed types and
+ // `ConvertTo` will handle the unsigned types
+
+ if (typeof(TOther) == typeof(byte))
+ {
+ byte actualResult = (value >= byte.MaxValue) ? byte.MaxValue :
+ (value <= byte.MinValue) ? byte.MinValue : (byte)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(char))
+ {
+ char actualResult = (value >= char.MaxValue) ? char.MaxValue :
+ (value <= char.MinValue) ? char.MinValue : (char)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(decimal))
+ {
+ decimal actualResult = (value >= new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)) ? decimal.MaxValue :
+ (value <= new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001)) ? decimal.MinValue : (decimal)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(ushort))
+ {
+ ushort actualResult = (value >= ushort.MaxValue) ? ushort.MaxValue :
+ (value <= ushort.MinValue) ? ushort.MinValue : (ushort)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(uint))
+ {
+ uint actualResult = (value >= uint.MaxValue) ? uint.MaxValue :
+ (value <= uint.MinValue) ? uint.MinValue : (uint)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(ulong))
+ {
+ ulong actualResult = (value >= ulong.MaxValue) ? ulong.MaxValue :
+ (value <= ulong.MinValue) ? ulong.MinValue : (ulong)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(UInt128))
+ {
+ UInt128 actualResult = (value <= 0) ? UInt128.MinValue : (UInt128)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(nuint))
+ {
+ nuint actualResult = (value >= nuint.MaxValue) ? nuint.MaxValue :
+ (value <= nuint.MinValue) ? nuint.MinValue : (nuint)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else
+ {
+ result = default;
+ return false;
+ }
+ }
+
+ ///
+ static inline bool INumberBase.TryConvertToTruncating(Int128 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 `Int128` will handle the other signed types and
+ // `ConvertTo` will handle the unsigned types
+
+ if (typeof(TOther) == typeof(byte))
+ {
+ byte actualResult = (byte)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(char))
+ {
+ char actualResult = (char)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(decimal))
+ {
+ decimal actualResult = (value >= new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)) ? decimal.MaxValue :
+ (value <= new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001)) ? decimal.MinValue : (decimal)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(ushort))
+ {
+ ushort actualResult = (ushort)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(uint))
+ {
+ uint actualResult = (uint)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(ulong))
+ {
+ ulong actualResult = (ulong)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(UInt128))
+ {
+ UInt128 actualResult = (UInt128)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else if (typeof(TOther) == typeof(nuint))
+ {
+ nuint actualResult = (nuint)value;
+ result = (TOther)(object)actualResult;
+ return true;
+ }
+ else
+ {
+ result = default;
+ return false;
+ }
+ }
+
+ //
+ // IParsable
+ //
+
+ ///
+ public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out Int128 result) => TryParse(s, NumberStyles.Integer, provider, out result);
+
+ //
+ // IShiftOperators
+ //
+
+ ///
+ public static Int128 operator <<(Int128 value, int shiftAmount)
+ {
+ // C# automatically masks the shift amount for UInt64 to be 0x3F. So we
+ // need to specially handle things if the 7th bit is set.
+
+ shiftAmount &= 0x7F;
+
+ if ((shiftAmount & 0x40) != 0)
+ {
+ // In the case it is set, we know the entire lower bits must be zero
+ // and so the upper bits are just the lower shifted by the remaining
+ // masked amount
+
+ ulong upper = value._lower << shiftAmount;
+ return new Int128(upper, 0);
+ }
+ else if (shiftAmount != 0)
+ {
+ // Otherwise we need to shift both upper and lower halves by the masked
+ // amount and then or that with whatever bits were shifted "out" of lower
+
+ ulong lower = value._lower << shiftAmount;
+ ulong upper = (value._upper << shiftAmount) | (value._lower >> (64 - shiftAmount));
+
+ return new Int128(upper, lower);
+ }
+ else
+ {
+ return value;
+ }
+ }
+
+ ///
+ public static Int128 operator >>(Int128 value, int shiftAmount)
+ {
+ // C# automatically masks the shift amount for UInt64 to be 0x3F. So we
+ // need to specially handle things if the 7th bit is set.
+
+ shiftAmount &= 0x7F;
+
+ if ((shiftAmount & 0x40) != 0)
+ {
+ // In the case it is set, we know the entire upper bits must be the sign
+ // and so the lower bits are just the upper shifted by the remaining
+ // masked amount
+
+ ulong lower = (ulong)((long)value._upper >> shiftAmount);
+ ulong upper = (ulong)((long)value._upper >> 63);
+
+ return new Int128(upper, lower);
+ }
+ else if (shiftAmount != 0)
+ {
+ // Otherwise we need to shift both upper and lower halves by the masked
+ // amount and then or that with whatever bits were shifted "out" of upper
+
+ ulong lower = (value._lower >> shiftAmount) | (value._upper << (64 - shiftAmount));
+ ulong upper = (ulong)((long)value._upper >> shiftAmount);
+
+ return new Int128(upper, lower);
+ }
+ else
+ {
+ return value;
+ }
+ }
+
+ /* TODO:
+ ///
+ public static Int128 operator >>>(Int128 value, int shiftAmount)
+ {
+ // C# automatically masks the shift amount for UInt64 to be 0x3F. So we
+ // need to specially handle things if the 7th bit is set.
+
+ shiftAmount &= 0x7F;
+
+ if ((shiftAmount & 0x40) != 0)
+ {
+ // In the case it is set, we know the entire upper bits must be zero
+ // and so the lower bits are just the upper shifted by the remaining
+ // masked amount
+
+ ulong lower = value._upper >> shiftAmount;
+ return new Int128(0, lower);
+ }
+ else if (shiftAmount != 0)
+ {
+ // Otherwise we need to shift both upper and lower halves by the masked
+ // amount and then or that with whatever bits were shifted "out" of upper
+
+ ulong lower = (value._lower >> shiftAmount) | (value._upper << (64 - shiftAmount));
+ ulong upper = value._upper >> shiftAmount;
+
+ return new Int128(upper, lower);
+ }
+ else
+ {
+ return value;
+ }
+ }
+ */
+
+ //
+ // ISignedNumber
+ //
+
+ ///
+ public static Int128 NegativeOne => new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+ //
+ // ISpanParsable
+ //
+
+ ///
+ public static Int128 Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider);
+
+ ///
+ public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Int128 result) => TryParse(s, NumberStyles.Integer, provider, out result);
+
+ //
+ // ISubtractionOperators
+ //
+
+ ///
+ public static Int128 operator -(Int128 left, Int128 right)
+ {
+ // For unsigned subtract, we can detect overflow by checking `(x - y) > x`
+ // This gives us the borrow to subtract from upper to compute the correct result
+
+ ulong lower = left._lower - right._lower;
+ ulong borrow = (lower > left._lower) ? 1UL : 0UL;
+
+ ulong upper = left._upper - right._upper - borrow;
+ return new Int128(upper, lower);
+ }
+
+ ///
+ /* TODO:
+ public static Int128 operator checked -(Int128 left, Int128 right)
+ {
+ // For signed subtraction, we can detect overflow by checking if the sign of
+ // both inputs are different and then if that differs from the sign of the
+ // output. The logic for how this works is explained in the
+ // System.Runtime.Intrinsics.Scalar.SubtractSaturate method
+
+ Int128 result = left - right;
+
+ if ((long)((result._upper ^ left._upper) & (left._upper ^ right._upper)) < 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return result;
+ }
+ */
+
+ //
+ // IUnaryNegationOperators
+ //
+
+ ///
+ public static Int128 operator -(Int128 value) => Zero - value;
+
+ ///
+ /* TODO:
+ public static Int128 operator checked -(Int128 value) => checked(Zero - value);
+ */
+ //
+ // IUnaryPlusOperators
+ //
+
+ ///
+ public static Int128 operator +(Int128 value) => value;
+
+ //
+ // IUtf8SpanParsable
+ //
+
+ ///
+ public static Int128 Parse(ReadOnlySpan utf8Text, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
+ return Number.ParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider));
+ }
+
+ ///
+ public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out Int128 result)
+ {
+ NumberFormatInfo.ValidateParseStyleInteger(style);
+ return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
+ }
+
+ ///
+ public static Int128 Parse(ReadOnlySpan utf8Text, IFormatProvider? provider) => Parse(utf8Text, NumberStyles.Integer, provider);
+
+ ///
+ public static bool TryParse(ReadOnlySpan utf8Text, IFormatProvider? provider, out Int128 result) => TryParse(utf8Text, NumberStyles.Integer, provider, out result);
+
+ //
+ // IBinaryIntegerParseAndFormatInfo
+ //
+
+ static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true;
+
+ static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 39; // 170_141_183_460_469_231_731_687_303_715_884_105_727
+
+ static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 32; // 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
+
+ static Int128 IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => new Int128(0x0CCC_CCCC_CCCC_CCCC, 0xCCCC_CCCC_CCCC_CCCC);
+
+ static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Int128;
+
+ static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(Int128 left, Int128 right) => (UInt128)(left) > (UInt128)(right);
+
+ static Int128 IBinaryIntegerParseAndFormatInfo.MultiplyBy10(Int128 value) => value * 10;
+
+ static Int128 IBinaryIntegerParseAndFormatInfo.MultiplyBy16(Int128 value) => value * 16;
+}
\ No newline at end of file
diff --git a/std/System/TimeSpan.hpt b/std/System/TimeSpan.hpt
index 78470071..e4cd9992 100644
--- a/std/System/TimeSpan.hpt
+++ b/std/System/TimeSpan.hpt
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// Some modifications were made for hapet-lang compatibility.
+using System;
+
// TimeSpan represents a duration of time. A TimeSpan can be negative
// or positive.
//
@@ -14,12 +16,7 @@
// we do not provide Years() or Months().
//
public readonly struct TimeSpan
- : IComparable,
- IComparable,
- IEquatable,
- ISpanFormattable,
- ISpanParsable,
- IUtf8SpanFormattable
+ : IComparable, IComparable, IEquatable
{
///
/// Represents the number of nanoseconds per tick. This field is constant.
@@ -387,9 +384,9 @@ public readonly struct TimeSpan
public static int Compare(TimeSpan t1, TimeSpan t2) => t1._ticks.CompareTo(t2._ticks);
// Returns a value less than zero if this object
- public int CompareTo(object? value)
+ public int CompareTo(object value)
{
- if (value is null)
+ if (value == null)
{
return 1;
}
@@ -415,7 +412,12 @@ public readonly struct TimeSpan
return new TimeSpan(_ticks >= 0 ? _ticks : -_ticks);
}
- public override bool Equals([NotNullWhen(true)] object? value) => (value is TimeSpan other) && Equals(other);
+ public override bool Equals(object value)
+ {
+ if (value is TimeSpan other)
+ return Equals(other);
+ return false;
+ }
public bool Equals(TimeSpan obj) => Equals(this, obj);
@@ -425,8 +427,9 @@ public readonly struct TimeSpan
private static inline TimeSpan FromUnits(long units, long ticksPerUnit, long minUnits, long maxUnits)
{
- System.Diagnostics.Debug.Assert(minUnits < 0);
- System.Diagnostics.Debug.Assert(maxUnits > 0);
+ // TODO:
+ // System.Diagnostics.Debug.Assert(minUnits < 0);
+ // System.Diagnostics.Debug.Assert(maxUnits > 0);
if (units > maxUnits || units < minUnits)
{
@@ -787,7 +790,7 @@ public readonly struct TimeSpan
public static bool TryParse(ReadOnlySpan input, IFormatProvider formatProvider, out TimeSpan result) => TimeSpanParse.TryParse(input, formatProvider, out result);
public static bool TryParseExact(string input, string format, IFormatProvider formatProvider, out TimeSpan result)
{
- if (input is null || format is null)
+ if (input == null || format == null)
{
result = default;
return false;
@@ -800,7 +803,7 @@ public readonly struct TimeSpan
public static bool TryParseExact(string input, string[] formats, IFormatProvider formatProvider, out TimeSpan result)
{
- if (input is null)
+ if (input == null)
{
result = default;
return false;
@@ -814,7 +817,7 @@ public readonly struct TimeSpan
{
ValidateStyles(styles);
- if (input is null || format is null)
+ if (input == null || format == null)
{
result = default;
return false;
@@ -831,7 +834,7 @@ public readonly struct TimeSpan
{
ValidateStyles(styles);
- if (input is null)
+ if (input == null)
{
result = default;
return false;
From f47b95d8883067230d11bb280cc5e63926bf4be8 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:17:43 +0300
Subject: [PATCH 05/26] Small changes in Int128
---
std/System/Int128.hpt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/std/System/Int128.hpt b/std/System/Int128.hpt
index c6cc8dda..ed0cd7ca 100644
--- a/std/System/Int128.hpt
+++ b/std/System/Int128.hpt
@@ -34,7 +34,7 @@ public readonly struct Int128
{
return CompareTo(other);
}
- else if (value is null)
+ else if (value == null)
{
return 1;
}
@@ -92,7 +92,7 @@ public readonly struct Int128
return Number.FormatInt128(this, format, null);
}
- public string ToString(string format, IFormatProvider? provider)
+ public string ToString(string format, IFormatProvider provider)
{
return Number.FormatInt128(this, format, provider);
}
From 958991803c82f6f3478a0ed7746a8b7661a55186 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Wed, 15 Apr 2026 15:50:23 +0300
Subject: [PATCH 06/26] Many lines removed from Int128
---
std/System/Int128.hpt | 558 ++----------------------------------------
1 file changed, 18 insertions(+), 540 deletions(-)
diff --git a/std/System/Int128.hpt b/std/System/Int128.hpt
index ed0cd7ca..fcd3ad3b 100644
--- a/std/System/Int128.hpt
+++ b/std/System/Int128.hpt
@@ -303,7 +303,7 @@ public readonly struct Int128
/// Explicitly converts a 128-bit signed integer to a value.
/// The value to convert.
/// converted to a .
- public static explicit operator nint(Int128 value) => (nint)value._lower;
+ public static explicit operator uintptr(Int128 value) => (uintptr)value._lower;
/// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
/// The value to convert.
@@ -438,7 +438,7 @@ public readonly struct Int128
/// Explicitly converts a 128-bit signed integer to a value.
/// The value to convert.
/// converted to a .
- public static explicit operator nuint(Int128 value) => (nuint)value._lower;
+ public static explicit operator uintptr(Int128 value) => (uintptr)value._lower;
/// Explicitly converts a 128-bit signed integer to a value, throwing an overflow exception for any values that fall outside the representable range.
/// The value to convert.
@@ -519,9 +519,10 @@ public readonly struct Int128
{
const double TwoPow127 = 170141183460469231731687303715884105728.0;
- Debug.Assert(value >= -TwoPow127);
- Debug.Assert(double.IsFinite(value));
- Debug.Assert(value < TwoPow127);
+ // TODO:
+ // Debug.Assert(value >= -TwoPow127);
+ // Debug.Assert(double.IsFinite(value));
+ // Debug.Assert(value < TwoPow127);
// This code is based on `f64_to_i128` from m-ou-se/floatconv
// Copyright (c) 2020 Mara Bos . All rights reserved.
@@ -546,7 +547,8 @@ public readonly struct Int128
ulong bits = BitConverter.DoubleToUInt64Bits(value);
Int128 result = new Int128((bits << 12) >> 1 | 0x8000_0000_0000_0000, 0x0000_0000_0000_0000);
- result >>>= (1023 + 128 - 1 - (int)(bits >> 52));
+ // TODO:
+ // result >>>= (1023 + 128 - 1 - (int)(bits >> 52));
if (isNegative)
{
@@ -757,224 +759,6 @@ public readonly struct Int128
return ulong.TrailingZeroCount(value._lower);
}
- ///
- static bool IBinaryInteger.TryReadBigEndian(ReadOnlySpan source, bool isUnsigned, out Int128 value)
- {
- Int128 result = default;
-
- if (source.Length != 0)
- {
- // Propagate the most significant bit so we have `0` or `-1`
- sbyte sign = (sbyte)(source[0]);
- sign >>= 31;
- Debug.Assert((sign == 0) || (sign == -1));
-
- // We need to also track if the input data is unsigned
- isUnsigned |= (sign == 0);
-
- if (isUnsigned && sbyte.IsNegative(sign) && (source.Length >= Size))
- {
- // When we are unsigned and the most significant bit is set, we are a large positive
- // and therefore definitely out of range
-
- value = result;
- return false;
- }
-
- if (source.Length > Size)
- {
- if (source[..^Size].ContainsAnyExcept((byte)sign))
- {
- // When we are unsigned and have any non-zero leading data or signed with any non-set leading
- // data, we are a large positive/negative, respectively, and therefore definitely out of range
-
- value = result;
- return false;
- }
-
- if (isUnsigned == sbyte.IsNegative((sbyte)source[^Size]))
- {
- // When the most significant bit of the value being set/clear matches whether we are unsigned
- // or signed then we are a large positive/negative and therefore definitely out of range
-
- value = result;
- return false;
- }
- }
-
- ref byte sourceRef = ref MemoryMarshal.GetReference(source);
-
- if (source.Length >= Size)
- {
- sourceRef = ref Unsafe.Add(ref sourceRef, source.Length - Size);
-
- // We have at least 16 bytes, so just read the ones we need directly
- result = Unsafe.ReadUnaligned(ref sourceRef);
-
- if (BitConverter.IsLittleEndian)
- {
- result = BinaryPrimitives.ReverseEndianness(result);
- }
- }
- else
- {
- // We have between 1 and 15 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);
- }
-
- if (!isUnsigned)
- {
- result |= ((One << ((Size * 8) - 1)) >> (((Size - source.Length) * 8) - 1));
- }
- }
- }
-
- value = result;
- return true;
- }
-
- ///
- static bool IBinaryInteger.TryReadLittleEndian(ReadOnlySpan source, bool isUnsigned, out Int128 value)
- {
- Int128 result = default;
-
- if (source.Length != 0)
- {
- // Propagate the most significant bit so we have `0` or `-1`
- sbyte sign = (sbyte)(source[^1]);
- sign >>= 31;
- Debug.Assert((sign == 0) || (sign == -1));
-
- // We need to also track if the input data is unsigned
- isUnsigned |= (sign == 0);
-
- if (isUnsigned && sbyte.IsNegative(sign) && (source.Length >= Size))
- {
- // When we are unsigned and the most significant bit is set, we are a large positive
- // and therefore definitely out of range
-
- value = result;
- return false;
- }
-
- if (source.Length > Size)
- {
- if (source[Size..].ContainsAnyExcept((byte)sign))
- {
- // When we are unsigned and have any non-zero leading data or signed with any non-set leading
- // data, we are a large positive/negative, respectively, and therefore definitely out of range
-
- value = result;
- return false;
- }
-
- if (isUnsigned == sbyte.IsNegative((sbyte)source[Size - 1]))
- {
- // When the most significant bit of the value being set/clear matches whether we are unsigned
- // or signed then we are a large positive/negative and therefore definitely out of range
-
- value = result;
- return false;
- }
- }
-
- ref byte sourceRef = ref MemoryMarshal.GetReference(source);
-
- if (source.Length >= Size)
- {
- // We have at least 16 bytes, so just read the ones we need directly
- result = Unsafe.ReadUnaligned(ref sourceRef);
-
- if (!BitConverter.IsLittleEndian)
- {
- result = BinaryPrimitives.ReverseEndianness(result);
- }
- }
- else
- {
- // We have between 1 and 15 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++)
- {
- result <<= 8;
- result |= Unsafe.Add(ref sourceRef, i);
- }
-
- result <<= ((Size - source.Length) * 8);
- result = BinaryPrimitives.ReverseEndianness(result);
-
- if (!isUnsigned)
- {
- result |= ((One << ((Size * 8) - 1)) >> (((Size - source.Length) * 8) - 1));
- }
- }
- }
-
- value = result;
- return true;
- }
-
- ///
- int IBinaryInteger.GetShortestBitLength()
- {
- Int128 value = this;
-
- if (IsPositive(value))
- {
- return (Size * 8) - LeadingZeroCountAsInt32(value);
- }
- else
- {
- return (Size * 8) + 1 - LeadingZeroCountAsInt32(~value);
- }
- }
-
- ///
- int IBinaryInteger.GetByteCount() => Size;
-
- ///
- bool IBinaryInteger.TryWriteBigEndian(Span destination, out int bytesWritten)
- {
- if (BinaryPrimitives.TryWriteInt128BigEndian(destination, this))
- {
- bytesWritten = Size;
- return true;
- }
-
- bytesWritten = 0;
- return false;
- }
-
- ///
- bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int bytesWritten)
- {
- if (BinaryPrimitives.TryWriteInt128LittleEndian(destination, this))
- {
- bytesWritten = Size;
- return true;
- }
-
- bytesWritten = 0;
- return false;
- }
-
- //
- // IBinaryNumber
- //
-
- ///
- static Int128 IBinaryNumber.AllBitsSet => new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
-
///
public static bool IsPow2(Int128 value) => (PopCount(value) == 1U) && IsPositive(value);
@@ -1148,13 +932,6 @@ public readonly struct Int128
return left - (quotient * right);
}
- //
- // IMultiplicativeIdentity
- //
-
- ///
- static Int128 IMultiplicativeIdentity.MultiplicativeIdentity => One;
-
//
// IMultiplyOperators
//
@@ -1256,15 +1033,9 @@ public readonly struct Int128
///
public static Int128 Max(Int128 x, Int128 y) => (x >= y) ? x : y;
- ///
- static Int128 INumber.MaxNumber(Int128 x, Int128 y) => Max(x, y);
-
///
public static Int128 Min(Int128 x, Int128 y) => (x <= y) ? x : y;
- ///
- static Int128 INumber.MinNumber(Int128 x, Int128 y) => Min(x, y);
-
///
public static int Sign(Int128 value)
{
@@ -1289,9 +1060,6 @@ public readonly struct Int128
///
public static Int128 One => new Int128(0, 1);
- ///
- static int INumberBase.Radix => 2;
-
///
public static Int128 Zero => default;
@@ -1329,8 +1097,7 @@ public readonly struct Int128
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Int128 CreateSaturating(TOther value)
+ public static inline Int128 CreateSaturating(TOther value)
where TOther : INumberBase
{
Int128 result;
@@ -1348,8 +1115,7 @@ public readonly struct Int128
}
///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static Int128 CreateTruncating(TOther value)
+ public static inline Int128 CreateTruncating(TOther value)
where TOther : INumberBase
{
Int128 result;
@@ -1366,57 +1132,18 @@ public readonly struct Int128
return result;
}
- ///
- static bool INumberBase.IsCanonical(Int128 value) => true;
-
- ///
- static bool INumberBase.IsComplexNumber(Int128 value) => false;
-
///
public static bool IsEvenInteger(Int128 value) => (value._lower & 1) == 0;
- ///
- static bool INumberBase.IsFinite(Int128 value) => true;
-
- ///
- static bool INumberBase.IsImaginaryNumber(Int128 value) => false;
-
- ///
- static bool INumberBase.IsInfinity(Int128 value) => false;
-
- ///
- static bool INumberBase.IsInteger(Int128 value) => true;
-
- ///
- static bool INumberBase.IsNaN(Int128 value) => false;
-
///
public static bool IsNegative(Int128 value) => (long)value._upper < 0;
- ///
- static bool INumberBase.IsNegativeInfinity(Int128 value) => false;
-
- ///
- static bool INumberBase.IsNormal(Int128 value) => value != 0;
-
///
public static bool IsOddInteger(Int128 value) => (value._lower & 1) != 0;
///
public static bool IsPositive(Int128 value) => (long)value._upper >= 0;
- ///
- static bool INumberBase.IsPositiveInfinity(Int128 value) => false;
-
- ///
- static bool INumberBase.IsRealNumber(Int128 value) => true;
-
- ///
- static bool INumberBase.IsSubnormal(Int128 value) => false;
-
- ///
- static bool INumberBase.IsZero(Int128 value) => (value == 0);
-
///
public static Int128 MaxMagnitude(Int128 x, Int128 y)
{
@@ -1457,9 +1184,6 @@ public readonly struct Int128
return y;
}
- ///
- static Int128 INumberBase.MaxMagnitudeNumber(Int128 x, Int128 y) => MaxMagnitude(x, y);
-
///
public static Int128 MinMagnitude(Int128 x, Int128 y)
{
@@ -1500,15 +1224,6 @@ public readonly struct Int128
return y;
}
- ///
- static Int128 INumberBase.MinMagnitudeNumber(Int128 x, Int128 y) => MinMagnitude(x, y);
-
- ///
- static Int128 INumberBase.MultiplyAddEstimate(Int128 left, Int128 right, Int128 addend) => (left * right) + addend;
-
- ///
- static inline bool INumberBase.TryConvertFromChecked(TOther value, out Int128 result) => TryConvertFromChecked(value, out result);
-
private static inline bool TryConvertFromChecked(TOther value, out Int128 result)
where TOther : INumberBase
{
@@ -1576,9 +1291,6 @@ public readonly struct Int128
}
}
- ///
- static inline bool INumberBase.TryConvertFromSaturating(TOther value, out Int128 result) => TryConvertFromSaturating(value, out result);
-
private static inline bool TryConvertFromSaturating(TOther value, out Int128 result)
where TOther : INumberBase
{
@@ -1621,9 +1333,9 @@ public readonly struct Int128
result = actualValue;
return true;
}
- else if (typeof(TOther) == typeof(nint))
+ else if (typeof(TOther) == typeof(uintptr))
{
- nint actualValue = (nint)(object)value;
+ uintptr actualValue = (uintptr)(object)value;
result = actualValue;
return true;
}
@@ -1646,12 +1358,7 @@ public readonly struct Int128
}
}
- ///
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- static bool INumberBase.TryConvertFromTruncating(TOther value, out Int128 result) => TryConvertFromTruncating(value, out result);
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static bool TryConvertFromTruncating(TOther value, out Int128 result)
+ private static inline bool TryConvertFromTruncating(TOther value, out Int128 result)
where TOther : INumberBase
{
// In order to reduce overall code duplication and improve the inlinabilty of these
@@ -1693,9 +1400,9 @@ public readonly struct Int128
result = actualValue;
return true;
}
- else if (typeof(TOther) == typeof(nint))
+ else if (typeof(TOther) == typeof(uintptr))
{
- nint actualValue = (nint)(object)value;
+ uintptr actualValue = (uintptr)(object)value;
result = actualValue;
return true;
}
@@ -1718,221 +1425,12 @@ public readonly struct Int128
}
}
- ///
- static inline bool INumberBase.TryConvertToChecked(Int128 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 `Int128` will handle the other signed types and
- // `ConvertTo` will handle the unsigned types
-
- if (typeof(TOther) == typeof(byte))
- {
- byte actualResult = checked((byte)value);
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(char))
- {
- char actualResult = checked((char)value);
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(decimal))
- {
- decimal actualResult = checked((decimal)value);
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(ushort))
- {
- ushort actualResult = checked((ushort)value);
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(uint))
- {
- uint actualResult = checked((uint)value);
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(ulong))
- {
- ulong actualResult = checked((ulong)value);
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(UInt128))
- {
- UInt128 actualResult = checked((UInt128)value);
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(nuint))
- {
- nuint actualResult = checked((nuint)value);
- result = (TOther)(object)actualResult;
- return true;
- }
- else
- {
- result = default;
- return false;
- }
- }
-
- ///
- static inline bool INumberBase.TryConvertToSaturating(Int128 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 `Int128` will handle the other signed types and
- // `ConvertTo` will handle the unsigned types
-
- if (typeof(TOther) == typeof(byte))
- {
- byte actualResult = (value >= byte.MaxValue) ? byte.MaxValue :
- (value <= byte.MinValue) ? byte.MinValue : (byte)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(char))
- {
- char actualResult = (value >= char.MaxValue) ? char.MaxValue :
- (value <= char.MinValue) ? char.MinValue : (char)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(decimal))
- {
- decimal actualResult = (value >= new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)) ? decimal.MaxValue :
- (value <= new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001)) ? decimal.MinValue : (decimal)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(ushort))
- {
- ushort actualResult = (value >= ushort.MaxValue) ? ushort.MaxValue :
- (value <= ushort.MinValue) ? ushort.MinValue : (ushort)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(uint))
- {
- uint actualResult = (value >= uint.MaxValue) ? uint.MaxValue :
- (value <= uint.MinValue) ? uint.MinValue : (uint)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(ulong))
- {
- ulong actualResult = (value >= ulong.MaxValue) ? ulong.MaxValue :
- (value <= ulong.MinValue) ? ulong.MinValue : (ulong)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(UInt128))
- {
- UInt128 actualResult = (value <= 0) ? UInt128.MinValue : (UInt128)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(nuint))
- {
- nuint actualResult = (value >= nuint.MaxValue) ? nuint.MaxValue :
- (value <= nuint.MinValue) ? nuint.MinValue : (nuint)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else
- {
- result = default;
- return false;
- }
- }
-
- ///
- static inline bool INumberBase.TryConvertToTruncating(Int128 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 `Int128` will handle the other signed types and
- // `ConvertTo` will handle the unsigned types
-
- if (typeof(TOther) == typeof(byte))
- {
- byte actualResult = (byte)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(char))
- {
- char actualResult = (char)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(decimal))
- {
- decimal actualResult = (value >= new Int128(0x0000_0000_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF)) ? decimal.MaxValue :
- (value <= new Int128(0xFFFF_FFFF_0000_0000, 0x0000_0000_0000_0001)) ? decimal.MinValue : (decimal)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(ushort))
- {
- ushort actualResult = (ushort)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(uint))
- {
- uint actualResult = (uint)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(ulong))
- {
- ulong actualResult = (ulong)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(UInt128))
- {
- UInt128 actualResult = (UInt128)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else if (typeof(TOther) == typeof(nuint))
- {
- nuint actualResult = (nuint)value;
- result = (TOther)(object)actualResult;
- return true;
- }
- else
- {
- result = default;
- return false;
- }
- }
-
//
// IParsable
//
///
- public static bool TryParse([NotNullWhen(true)] string? s, IFormatProvider? provider, out Int128 result) => TryParse(s, NumberStyles.Integer, provider, out result);
+ public static bool TryParse(string s, IFormatProvider provider, out Int128 result) => TryParse(s, NumberStyles.Integer, provider, out result);
//
// IShiftOperators
@@ -2053,10 +1551,10 @@ public readonly struct Int128
//
///
- public static Int128 Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider);
+ public static Int128 Parse(ReadOnlySpan s, IFormatProvider provider) => Parse(s, NumberStyles.Integer, provider);
///
- public static bool TryParse(ReadOnlySpan s, IFormatProvider? provider, out Int128 result) => TryParse(s, NumberStyles.Integer, provider, out result);
+ public static bool TryParse(ReadOnlySpan s, IFormatProvider provider, out Int128 result) => TryParse(s, NumberStyles.Integer, provider, out result);
//
// ISubtractionOperators
@@ -2135,24 +1633,4 @@ public readonly struct Int128
///
public static bool TryParse(ReadOnlySpan utf8Text, IFormatProvider? provider, out Int128 result) => TryParse(utf8Text, NumberStyles.Integer, provider, out result);
-
- //
- // IBinaryIntegerParseAndFormatInfo
- //
-
- static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true;
-
- static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 39; // 170_141_183_460_469_231_731_687_303_715_884_105_727
-
- static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 32; // 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF
-
- static Int128 IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => new Int128(0x0CCC_CCCC_CCCC_CCCC, 0xCCCC_CCCC_CCCC_CCCC);
-
- static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Int128;
-
- static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(Int128 left, Int128 right) => (UInt128)(left) > (UInt128)(right);
-
- static Int128 IBinaryIntegerParseAndFormatInfo.MultiplyBy10(Int128 value) => value * 10;
-
- static Int128 IBinaryIntegerParseAndFormatInfo.MultiplyBy16(Int128 value) => value * 16;
}
\ No newline at end of file
From 1f6d22f31f7ff3950e064e8ecb64bdec2aa53499 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Fri, 17 Apr 2026 10:56:55 +0300
Subject: [PATCH 07/26] Int128 more updates
---
std/System/BitConverter.hpt | 16 +
std/System/Double.hpt | 5 +
std/System/Int128.hpt | 509 +-----------------
std/System/Numerics/BitOperations.hpt | 61 ++-
.../Runtime/CompilerServices/Unsafe.hpt | 29 +
std/System/SR.hpt | 1 +
6 files changed, 118 insertions(+), 503 deletions(-)
create mode 100644 std/System/Runtime/CompilerServices/Unsafe.hpt
diff --git a/std/System/BitConverter.hpt b/std/System/BitConverter.hpt
index 25f22d31..9cdbf354 100644
--- a/std/System/BitConverter.hpt
+++ b/std/System/BitConverter.hpt
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// Some modifications were made for hapet-lang compatibility.
+using System.Runtime.CompilerServices;
+
///
/// Converts base data types to an array of bytes, and an array of bytes to base data types.
///
@@ -15,4 +17,18 @@ public static class BitConverter
#else
public static readonly bool IsLittleEndian = true;
#endif
+
+ ///
+ /// Converts the specified double-precision floating point number to a 64-bit signed integer.
+ ///
+ /// The number to convert.
+ /// A 64-bit signed integer whose bits are identical to .
+ public static inline long DoubleToInt64Bits(double value) => Unsafe.BitCast(value);
+
+ ///
+ /// Converts the specified double-precision floating point number to a 64-bit unsigned integer.
+ ///
+ /// The number to convert.
+ /// A 64-bit unsigned integer whose bits are identical to .
+ public static inline ulong DoubleToUInt64Bits(double value) => Unsafe.BitCast(value);
}
\ No newline at end of file
diff --git a/std/System/Double.hpt b/std/System/Double.hpt
index 84677763..3eb8c40c 100644
--- a/std/System/Double.hpt
+++ b/std/System/Double.hpt
@@ -65,4 +65,9 @@ public struct Double : IComparable, IComparable, IFormattable
// TODO: is it correct way or need to do as in c#?
return d == NaN;
}
+
+ public static inline bool IsNegative(double d)
+ {
+ return BitConverter.DoubleToInt64Bits(d) < 0;
+ }
}
\ No newline at end of file
diff --git a/std/System/Int128.hpt b/std/System/Int128.hpt
index fcd3ad3b..13809a29 100644
--- a/std/System/Int128.hpt
+++ b/std/System/Int128.hpt
@@ -1,7 +1,9 @@
+using System.Globalization;
+using System.Text.Formatting;
+using System.Runtime.InteropServices;
+using System.Numerics;
-
-/// Represents a 128-bit signed integer.Represents a 128-bit signed integer.
public readonly struct Int128
{
internal const int Size = 16;
@@ -74,7 +76,7 @@ public readonly struct Int128
}
///
- public override int GetHashCode() => HashCode.Combine(_lower, _upper);
+ public override int GetHashCode() => HashCode.Combine(_lower, _upper);
///
public override string ToString()
@@ -82,78 +84,11 @@ public readonly struct Int128
return Number.Int128ToDecStr(this);
}
- public string ToString(IFormatProvider? provider)
- {
- return Number.FormatInt128(this, null, provider);
- }
-
public string ToString(string format)
{
return Number.FormatInt128(this, format, null);
}
- public string ToString(string format, IFormatProvider provider)
- {
- return Number.FormatInt128(this, format, provider);
- }
-
- public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider? provider = null)
- {
- return Number.TryFormatInt128(this, format, provider, destination, out charsWritten);
- }
-
- ///
- public bool TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format = default, IFormatProvider? provider = null)
- {
- return Number.TryFormatInt128(this, format, provider, utf8Destination, out bytesWritten);
- }
-
- public static Int128 Parse(string s) => Parse(s, NumberStyles.Integer, provider: null);
-
- public static Int128 Parse(string s, NumberStyles style) => Parse(s, style, provider: null);
-
- public static Int128 Parse(string s, IFormatProvider provider) => Parse(s, NumberStyles.Integer, provider);
-
- public static Int128 Parse(string s, NumberStyles style, IFormatProvider provider)
- {
- if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); }
- return Parse(s.AsSpan(), style, provider);
- }
-
- public static Int128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null)
- {
- NumberFormatInfo.ValidateParseStyleInteger(style);
- return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider));
- }
-
- public static bool TryParse(string s, out Int128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result);
-
- public static bool TryParse(ReadOnlySpan s, out Int128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result);
-
- /// Tries to convert a UTF-8 character span containing the string representation of a number to its 128-bit signed integer equivalent.
- /// A span containing the UTF-8 characters representing the number to convert.
- /// When this method returns, contains the 128-bit signed integer value equivalent to the number contained in if the conversion succeeded, or zero if the conversion failed. This parameter is passed uninitialized; any value originally supplied in result will be overwritten.
- /// true if was converted successfully; otherwise, false.
- public static bool TryParse(ReadOnlySpan utf8Text, out Int128 result) => TryParse(utf8Text, NumberStyles.Integer, provider: null, out result);
-
- public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out Int128 result)
- {
- NumberFormatInfo.ValidateParseStyleInteger(style);
-
- if (s is null)
- {
- result = 0;
- return false;
- }
- return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
- }
-
- public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider provider, out Int128 result)
- {
- NumberFormatInfo.ValidateParseStyleInteger(style);
- return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
- }
-
//
// Explicit Conversions From Int128
//
@@ -198,19 +133,6 @@ public readonly struct Int128
}
*/
- /// Explicitly converts a 128-bit signed integer to a value.
- /// The value to convert.
- /// converted to a .
- public static explicit operator decimal(Int128 value)
- {
- if (IsNegative(value))
- {
- value = -value;
- return -(decimal)(UInt128)(value);
- }
- return (decimal)(UInt128)(value);
- }
-
/// Explicitly converts a 128-bit signed integer to a value.
/// The value to convert.
/// converted to a .
@@ -224,19 +146,6 @@ public readonly struct Int128
return (double)(UInt128)(value);
}
- /// Explicitly converts a 128-bit signed integer to a value.
- /// The value to convert.
- /// converted to a .
- public static explicit operator Half(Int128 value)
- {
- if (IsNegative(value))
- {
- value = -value;
- return -(Half)(UInt128)(value);
- }
- return (Half)(UInt128)(value);
- }
-
/// Explicitly converts a 128-bit signed integer to a value.
/// The value to convert.
/// converted to a .
@@ -459,21 +368,6 @@ public readonly struct Int128
// Explicit Conversions To Int128
//
- /// Explicitly converts a value to a 128-bit signed integer.
- /// The value to convert.
- /// converted to a 128-bit signed integer.
- public static explicit operator Int128(decimal value)
- {
- value = decimal.Truncate(value);
- Int128 result = new Int128(value.High, value.Low64);
-
- if (decimal.IsNegative(value))
- {
- result = -result;
- }
- return result;
- }
-
/// Explicitly converts a value to a 128-bit signed integer.
/// The value to convert.
/// converted to a 128-bit signed integer.
@@ -618,7 +512,7 @@ public readonly struct Int128
/// Implicitly converts a value to a 128-bit signed integer.
/// The value to convert.
/// converted to a 128-bit signed integer.
- public static implicit operator Int128(nint value)
+ public static implicit operator Int128(uintptr value)
{
long lower = value;
return new Int128((ulong)(lower >> 63), (ulong)lower);
@@ -627,7 +521,6 @@ public readonly struct Int128
/// Implicitly converts a value to a 128-bit signed integer.
/// The value to convert.
/// converted to a 128-bit signed integer.
- [CLSCompliant(false)]
public static implicit operator Int128(sbyte value)
{
long lower = value;
@@ -637,27 +530,18 @@ public readonly struct Int128
/// Implicitly converts a value to a 128-bit signed integer.
/// The value to convert.
/// converted to a 128-bit signed integer.
- [CLSCompliant(false)]
public static implicit operator Int128(ushort value) => new Int128(0, value);
/// Implicitly converts a value to a 128-bit signed integer.
/// The value to convert.
/// converted to a 128-bit signed integer.
- [CLSCompliant(false)]
public static implicit operator Int128(uint value) => new Int128(0, value);
/// Implicitly converts a value to a 128-bit signed integer.
/// The value to convert.
/// converted to a 128-bit signed integer.
- [CLSCompliant(false)]
public static implicit operator Int128(ulong value) => new Int128(0, value);
- /// Implicitly converts a value to a 128-bit signed integer.
- /// The value to convert.
- /// converted to a 128-bit signed integer.
- [CLSCompliant(false)]
- public static implicit operator Int128(nuint value) => new Int128(0, value);
-
//
// IAdditionOperators
//
@@ -694,89 +578,6 @@ public readonly struct Int128
}
*/
- //
- // IAdditiveIdentity
- //
-
- ///
- static Int128 IAdditiveIdentity.AdditiveIdentity => default;
-
- //
- // IBinaryInteger
- //
-
- ///
- public static (Int128 Quotient, Int128 Remainder) DivRem(Int128 left, Int128 right)
- {
- Int128 quotient = left / right;
- return (quotient, left - (quotient * right));
- }
-
- ///
- public static Int128 LeadingZeroCount(Int128 value)
- => LeadingZeroCountAsInt32(value);
-
- /// Computes the number of leading zero bits in this value.
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static int LeadingZeroCountAsInt32(Int128 value)
- {
- if (value._upper == 0)
- {
- return 64 + BitOperations.LeadingZeroCount(value._lower);
- }
- return BitOperations.LeadingZeroCount(value._upper);
- }
-
- ///
- public static Int128 Log10(Int128 value)
- {
- if (IsNegative(value))
- {
- ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
- }
- return (Int128)UInt128.Log10((UInt128)value);
- }
-
- ///
- public static Int128 PopCount(Int128 value)
- => ulong.PopCount(value._lower) + ulong.PopCount(value._upper);
-
- ///
- public static Int128 RotateLeft(Int128 value, int rotateAmount)
- => (value << rotateAmount) | (value >>> (128 - rotateAmount));
-
- ///
- public static Int128 RotateRight(Int128 value, int rotateAmount)
- => (value >>> rotateAmount) | (value << (128 - rotateAmount));
-
- ///
- public static Int128 TrailingZeroCount(Int128 value)
- {
- if (value._lower == 0)
- {
- return 64 + ulong.TrailingZeroCount(value._upper);
- }
- return ulong.TrailingZeroCount(value._lower);
- }
-
- ///
- public static bool IsPow2(Int128 value) => (PopCount(value) == 1U) && IsPositive(value);
-
- ///
- public static Int128 Log2(Int128 value)
- {
- if (IsNegative(value))
- {
- ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
- }
-
- if (value._upper == 0)
- {
- return ulong.Log2(value._lower);
- }
- return 64 + ulong.Log2(value._upper);
- }
-
//
// IBitwiseOperators
//
@@ -1078,60 +879,6 @@ public readonly struct Int128
return value;
}
- ///
- public static inline Int128 CreateChecked(TOther value)
- where TOther : INumberBase
- {
- Int128 result;
-
- if (typeof(TOther) == typeof(Int128))
- {
- result = (Int128)(object)value;
- }
- else if (!TryConvertFromChecked(value, out result) && !TOther.TryConvertToChecked(value, out result))
- {
- ThrowHelper.ThrowNotSupportedException();
- }
-
- return result;
- }
-
- ///
- public static inline Int128 CreateSaturating(TOther value)
- where TOther : INumberBase
- {
- Int128 result;
-
- if (typeof(TOther) == typeof(Int128))
- {
- result = (Int128)(object)value;
- }
- else if (!TryConvertFromSaturating(value, out result) && !TOther.TryConvertToSaturating(value, out result))
- {
- ThrowHelper.ThrowNotSupportedException();
- }
-
- return result;
- }
-
- ///
- public static inline Int128 CreateTruncating(TOther value)
- where TOther : INumberBase
- {
- Int128 result;
-
- if (typeof(TOther) == typeof(Int128))
- {
- result = (Int128)(object)value;
- }
- else if (!TryConvertFromTruncating(value, out result) && !TOther.TryConvertToTruncating(value, out result))
- {
- ThrowHelper.ThrowNotSupportedException();
- }
-
- return result;
- }
-
///
public static bool IsEvenInteger(Int128 value) => (value._lower & 1) == 0;
@@ -1224,214 +971,6 @@ public readonly struct Int128
return y;
}
- private static inline bool TryConvertFromChecked(TOther value, out Int128 result)
- where TOther : INumberBase
- {
- // 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 `Int128` will handle the other signed types and
- // `ConvertTo` will handle the unsigned types
-
- if (typeof(TOther) == typeof(double))
- {
- double actualValue = (double)(object)value;
- result = checked((Int128)actualValue);
- return true;
- }
- else if (typeof(TOther) == typeof(Half))
- {
- Half actualValue = (Half)(object)value;
- result = checked((Int128)actualValue);
- return true;
- }
- else if (typeof(TOther) == typeof(short))
- {
- short actualValue = (short)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(int))
- {
- int actualValue = (int)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(long))
- {
- long actualValue = (long)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(nint))
- {
- nint actualValue = (nint)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(sbyte))
- {
- sbyte actualValue = (sbyte)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(float))
- {
- float actualValue = (float)(object)value;
- result = checked((Int128)actualValue);
- return true;
- }
- else
- {
- result = default;
- return false;
- }
- }
-
- private static inline bool TryConvertFromSaturating(TOther value, out Int128 result)
- where TOther : INumberBase
- {
- // 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 `Int128` will handle the other signed types and
- // `ConvertTo` will handle the unsigned types
-
- if (typeof(TOther) == typeof(double))
- {
- double actualValue = (double)(object)value;
- result = (Int128)actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(Half))
- {
- Half actualValue = (Half)(object)value;
- result = (Int128)actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(short))
- {
- short actualValue = (short)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(int))
- {
- int actualValue = (int)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(long))
- {
- long actualValue = (long)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(uintptr))
- {
- uintptr actualValue = (uintptr)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(sbyte))
- {
- sbyte actualValue = (sbyte)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(float))
- {
- float actualValue = (float)(object)value;
- result = (Int128)actualValue;
- return true;
- }
- else
- {
- result = default;
- return false;
- }
- }
-
- private static inline bool TryConvertFromTruncating(TOther value, out Int128 result)
- where TOther : INumberBase
- {
- // 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 `Int128` will handle the other signed types and
- // `ConvertTo` will handle the unsigned types
-
- if (typeof(TOther) == typeof(double))
- {
- double actualValue = (double)(object)value;
- result = (Int128)actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(Half))
- {
- Half actualValue = (Half)(object)value;
- result = (Int128)actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(short))
- {
- short actualValue = (short)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(int))
- {
- int actualValue = (int)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(long))
- {
- long actualValue = (long)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(uintptr))
- {
- uintptr actualValue = (uintptr)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(sbyte))
- {
- sbyte actualValue = (sbyte)(object)value;
- result = actualValue;
- return true;
- }
- else if (typeof(TOther) == typeof(float))
- {
- float actualValue = (float)(object)value;
- result = (Int128)actualValue;
- return true;
- }
- else
- {
- result = default;
- return false;
- }
- }
-
- //
- // IParsable
- //
-
- ///
- public static bool TryParse(string s, IFormatProvider provider, out Int128 result) => TryParse(s, NumberStyles.Integer, provider, out result);
-
//
// IShiftOperators
//
@@ -1546,16 +1085,6 @@ public readonly struct Int128
///
public static Int128 NegativeOne => new Int128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
- //
- // ISpanParsable
- //
-
- ///
- public static Int128 Parse(ReadOnlySpan s, IFormatProvider provider) => Parse(s, NumberStyles.Integer, provider);
-
- ///
- public static bool TryParse(ReadOnlySpan s, IFormatProvider provider, out Int128 result) => TryParse(s, NumberStyles.Integer, provider, out result);
-
//
// ISubtractionOperators
//
@@ -1609,28 +1138,4 @@ public readonly struct Int128
///
public static Int128 operator +(Int128 value) => value;
-
- //
- // IUtf8SpanParsable
- //
-
- ///
- public static Int128 Parse(ReadOnlySpan utf8Text, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null)
- {
- NumberFormatInfo.ValidateParseStyleInteger(style);
- return Number.ParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider));
- }
-
- ///
- public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out Int128 result)
- {
- NumberFormatInfo.ValidateParseStyleInteger(style);
- return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
- }
-
- ///
- public static Int128 Parse(ReadOnlySpan utf8Text, IFormatProvider? provider) => Parse(utf8Text, NumberStyles.Integer, provider);
-
- ///
- public static bool TryParse(ReadOnlySpan utf8Text, IFormatProvider? provider, out Int128 result) => TryParse(utf8Text, NumberStyles.Integer, provider, out result);
}
\ No newline at end of file
diff --git a/std/System/Numerics/BitOperations.hpt b/std/System/Numerics/BitOperations.hpt
index ce2c36ac..79c2bf4e 100644
--- a/std/System/Numerics/BitOperations.hpt
+++ b/std/System/Numerics/BitOperations.hpt
@@ -5,9 +5,16 @@
using System;
/// Utility methods for intrinsic bit-twiddling operations.
-[SuppressStaticCtorCall]
public static class BitOperations
{
+ private static byte[] Log2DeBruijn = new byte[]
+ {
+ 00, 09, 01, 10, 13, 21, 02, 29,
+ 11, 14, 16, 18, 22, 25, 03, 30,
+ 08, 12, 20, 28, 15, 17, 24, 07,
+ 19, 27, 23, 06, 26, 05, 04, 31
+ };
+
/// Rotates the specified value left by the specified number of bits.
public static uint RotateLeft(uint value, int offset)
{
@@ -29,4 +36,56 @@ public static class BitOperations
}
return targetlevel;
}
+
+ ///
+ /// Count the number of leading zero bits in a mask.
+ /// Similar in behavior to the x86 instruction LZCNT.
+ ///
+ /// The value.
+ public static inline int LeadingZeroCount(uint value)
+ {
+ // Unguarded fallback contract is 0->31, BSR contract is 0->undefined
+ if (value == 0)
+ {
+ return 32;
+ }
+ return 31 ^ Log2SoftwareFallback(value);
+ }
+
+ ///
+ /// Count the number of leading zero bits in a mask.
+ /// Similar in behavior to the x86 instruction LZCNT.
+ ///
+ /// The value.
+ public static inline int LeadingZeroCount(ulong value)
+ {
+ uint hi = (uint)(value >> 32);
+ if (hi == 0)
+ {
+ return 32 + LeadingZeroCount((uint)value);
+ }
+ return LeadingZeroCount(hi);
+ }
+
+ ///
+ /// Returns the integer (floor) log of the specified value, base 2.
+ /// Note that by convention, input value 0 returns 0 since Log(0) is undefined.
+ /// Does not directly use any hardware intrinsics, nor does it incur branching.
+ ///
+ /// The value.
+ private static int Log2SoftwareFallback(uint value)
+ {
+ // No AggressiveInlining due to large method size
+ // Has conventional contract 0->0 (Log(0) is undefined)
+
+ // Fill trailing zeros with ones, eg 00010010 becomes 00011111
+ value |= value >> 01;
+ value |= value >> 02;
+ value |= value >> 04;
+ value |= value >> 08;
+ value |= value >> 16;
+
+ // Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_1100_0100_1010_1100_1101_1101u
+ return Log2DeBruijn[(int)((value * 0x07C4ACDDu) >> 27)];
+ }
}
\ No newline at end of file
diff --git a/std/System/Runtime/CompilerServices/Unsafe.hpt b/std/System/Runtime/CompilerServices/Unsafe.hpt
new file mode 100644
index 00000000..47cb057e
--- /dev/null
+++ b/std/System/Runtime/CompilerServices/Unsafe.hpt
@@ -0,0 +1,29 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+using System;
+
+// The implementations of most the methods in this file are provided as intrinsics.
+// In CoreCLR, the body of the functions are replaced by the EE with unsafe code. See see getILIntrinsicImplementationForUnsafe for details.
+// In AOT compilers, see Internal.IL.Stubs.UnsafeIntrinsics for details.
+
+///
+/// Contains generic, low-level functionality for manipulating pointers.
+///
+public static partial unsafe class Unsafe
+{
+ ///
+ /// Reinterprets the given value of type as a value of type .
+ ///
+ /// The sizes of and are not the same
+ /// or the type parameters are not s.
+ public static inline TTo BitCast(TFrom source)
+ {
+ if (sizeof(TFrom) != sizeof(TTo))
+ {
+ throw new NotSupportedException();
+ }
+ return *((TTo*)source);
+ }
+}
\ No newline at end of file
diff --git a/std/System/SR.hpt b/std/System/SR.hpt
index 3b5ead53..226d3549 100644
--- a/std/System/SR.hpt
+++ b/std/System/SR.hpt
@@ -31,6 +31,7 @@ internal static class SR
internal const string Arg_MustBeInt16 = "Object must be of type Int16 (short).";
internal const string Arg_MustBeInt32 = "Object must be of type Int32 (int).";
internal const string Arg_MustBeInt64 = "Object must be of type Int64 (long).";
+ internal const string Arg_MustBeInt128 = "Object must be of type Int128.";
internal const string Arg_MustBeByte = "Object must be of type Byte.";
internal const string Arg_MustBeUInt16 = "Object must be of type UInt16 (ushort).";
internal const string Arg_MustBeUInt32 = "Object must be of type UInt32 (uint).";
From 10256554b65392cdf121c45741fc3cb67a54542b Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Fri, 17 Apr 2026 15:15:21 +0300
Subject: [PATCH 08/26] UInt128 added and small other changes
---
std/System/BitConverter.hpt | 7 +
std/System/Int128.hpt | 7 +-
std/System/SR.hpt | 1 +
std/System/UInt128.hpt | 1181 +++++++++++++++++++++++++++++++++++
4 files changed, 1195 insertions(+), 1 deletion(-)
create mode 100644 std/System/UInt128.hpt
diff --git a/std/System/BitConverter.hpt b/std/System/BitConverter.hpt
index 9cdbf354..420a58ce 100644
--- a/std/System/BitConverter.hpt
+++ b/std/System/BitConverter.hpt
@@ -31,4 +31,11 @@ public static class BitConverter
/// The number to convert.
/// A 64-bit unsigned integer whose bits are identical to .
public static inline ulong DoubleToUInt64Bits(double value) => Unsafe.BitCast(value);
+
+ ///
+ /// Converts the specified 64-bit unsigned integer to a double-precision floating point number.
+ ///
+ /// The number to convert.
+ /// A double-precision floating point number whose bits are identical to .
+ public static inline double UInt64BitsToDouble(ulong value) => Unsafe.BitCast(value);
}
\ No newline at end of file
diff --git a/std/System/Int128.hpt b/std/System/Int128.hpt
index 13809a29..e98a2b75 100644
--- a/std/System/Int128.hpt
+++ b/std/System/Int128.hpt
@@ -1,3 +1,7 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
using System.Globalization;
using System.Text.Formatting;
using System.Runtime.InteropServices;
@@ -781,7 +785,8 @@ public readonly struct Int128
{
// This follows the same logic as is used in `long Math.BigMul(long, long, out long)`
- UInt128 upper = UInt128.BigMul((UInt128)(left), (UInt128)(right), out UInt128 ulower);
+ UInt128 ulower;
+ UInt128 upper = UInt128.BigMul((UInt128)(left), (UInt128)(right), out ulower);
lower = (Int128)(ulower);
return (Int128)(upper) - ((left >> 127) & right) - ((right >> 127) & left);
}
diff --git a/std/System/SR.hpt b/std/System/SR.hpt
index 226d3549..bd2bae40 100644
--- a/std/System/SR.hpt
+++ b/std/System/SR.hpt
@@ -36,6 +36,7 @@ internal static class SR
internal const string Arg_MustBeUInt16 = "Object must be of type UInt16 (ushort).";
internal const string Arg_MustBeUInt32 = "Object must be of type UInt32 (uint).";
internal const string Arg_MustBeUInt64 = "Object must be of type UInt64 (ulong).";
+ internal const string Arg_MustBeUInt128 = "Object must be of type UInt128.";
internal const string Arg_MustBePtrDiff = "Object must be of type PtrDiff.";
internal const string Arg_MustBeUIntPtr = "Object must be of type UIntPtr.";
diff --git a/std/System/UInt128.hpt b/std/System/UInt128.hpt
new file mode 100644
index 00000000..85d7a58f
--- /dev/null
+++ b/std/System/UInt128.hpt
@@ -0,0 +1,1181 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+using System.Globalization;
+using System.Text.Formatting;
+using System.Runtime.InteropServices;
+using System.Numerics;
+
+/// Represents a 128-bit unsigned integer.
+public readonly struct UInt128
+{
+ internal const int Size = 16;
+
+#if BIGENDIAN;
+ private readonly ulong _upper;
+ private readonly ulong _lower;
+#else
+ private readonly ulong _lower;
+ private readonly ulong _upper;
+#endif
+
+ /// Initializes a new instance of the struct.
+ /// The upper 64-bits of the 128-bit value.
+ /// The lower 64-bits of the 128-bit value.
+ public UInt128(ulong upper, ulong lower)
+ {
+ _lower = lower;
+ _upper = upper;
+ }
+
+ internal ulong Lower => _lower;
+
+ internal ulong Upper => _upper;
+
+ ///
+ public int CompareTo(object value)
+ {
+ if (value is UInt128 other)
+ {
+ return CompareTo(other);
+ }
+ else if (value == null)
+ {
+ return 1;
+ }
+ else
+ {
+ throw new ArgumentException(SR.Arg_MustBeUInt128);
+ }
+ }
+
+ ///
+ public int CompareTo(UInt128 value)
+ {
+ if (this < value)
+ {
+ return -1;
+ }
+ else if (this > value)
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ return (obj is UInt128 other) && Equals(other);
+ }
+
+ ///
+ public bool Equals(UInt128 other)
+ {
+ return this == other;
+ }
+
+ ///
+ public override int GetHashCode() => HashCode.Combine(_lower, _upper);
+
+ ///
+ public override string ToString()
+ {
+ return Number.UInt128ToDecStr(this);
+ }
+
+ public string ToString(string format)
+ {
+ return Number.FormatUInt128(this, format, null);
+ }
+
+ //
+ // Explicit Conversions From UInt128
+ //
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator byte(UInt128 value) => (byte)value._lower;
+
+ /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked byte(UInt128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((byte)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator char(UInt128 value) => (char)value._lower;
+
+ /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked char(UInt128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((char)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator double(UInt128 value)
+ {
+ // This code is based on `u128_to_f64_round` from m-ou-se/floatconv
+ // Copyright (c) 2020 Mara Bos . All rights reserved.
+ //
+ // Licensed under the BSD 2 - Clause "Simplified" License
+ // See THIRD-PARTY-NOTICES.TXT for the full license text
+
+ const double TwoPow52 = 4503599627370496.0;
+ const double TwoPow76 = 75557863725914323419136.0;
+ const double TwoPow104 = 20282409603651670423947251286016.0;
+ const double TwoPow128 = 340282366920938463463374607431768211456.0;
+
+ const ulong TwoPow52Bits = 0x4330000000000000;
+ const ulong TwoPow76Bits = 0x44B0000000000000;
+ const ulong TwoPow104Bits = 0x4670000000000000;
+ const ulong TwoPow128Bits = 0x47F0000000000000;
+
+ if (value._upper == 0)
+ {
+ // For values between 0 and ulong.MaxValue, we just use the existing conversion
+ return (double)(value._lower);
+ }
+ else if ((value._upper >> 40) == 0) // value < (2^104)
+ {
+ // For values greater than ulong.MaxValue but less than 2^104 this takes advantage
+ // that we can represent both "halves" of the uint128 within the 52-bit mantissa of
+ // a pair of doubles.
+
+ double lower = BitConverter.UInt64BitsToDouble(TwoPow52Bits | ((value._lower << 12) >> 12)) - TwoPow52;
+ double upper = BitConverter.UInt64BitsToDouble(TwoPow104Bits | (ulong)(value >> 52)) - TwoPow104;
+
+ return lower + upper;
+ }
+ else
+ {
+ // For values greater than 2^104 we basically do the same as before but we need to account
+ // for the precision loss that double will have. As such, the lower value effectively drops the
+ // lowest 24 bits and then or's them back to ensure rounding stays correct.
+
+ double lower = BitConverter.UInt64BitsToDouble(TwoPow76Bits | ((ulong)(value >> 12) >> 12) | (value._lower & 0xFFFFFF)) - TwoPow76;
+ double upper = BitConverter.UInt64BitsToDouble(TwoPow128Bits | (value._upper >> 12)) - TwoPow128;
+
+ return lower + upper;
+ }
+ }
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator short(UInt128 value) => (short)value._lower;
+
+ /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked short(UInt128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((short)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator int(UInt128 value) => (int)value._lower;
+
+ /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked int(UInt128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((int)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator long(UInt128 value) => (long)value._lower;
+
+ /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked long(UInt128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((long)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator Int128(UInt128 value) => new Int128(value._upper, value._lower);
+
+ /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked Int128(UInt128 value)
+ {
+ if ((long)value._upper < 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return new Int128(value._upper, value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked nint(UInt128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((nint)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator sbyte(UInt128 value) => (sbyte)value._lower;
+
+ /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked sbyte(UInt128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((sbyte)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator float(UInt128 value) => (float)(double)(value);
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator ushort(UInt128 value) => (ushort)value._lower;
+
+ /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked ushort(UInt128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((ushort)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator uint(UInt128 value) => (uint)value._lower;
+
+ /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked uint(UInt128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((uint)value._lower);
+ }
+ */
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator ulong(UInt128 value) => value._lower;
+
+ /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked ulong(UInt128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return value._lower;
+ }
+ */
+
+ /// Explicitly converts a 128-bit unsigned integer to a value.
+ /// The value to convert.
+ /// converted to a .
+ public static explicit operator uintptr(UInt128 value) => (uintptr)value._lower;
+
+ /// Explicitly converts a 128-bit unsigned integer to a value, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a .
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked nuint(UInt128 value)
+ {
+ if (value._upper != 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return checked((nuint)value._lower);
+ }
+ */
+
+ //
+ // Explicit Conversions To UInt128
+ //
+
+ /// Explicitly converts a value to a 128-bit unsigned integer.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ public static explicit operator UInt128(double value)
+ {
+ const double TwoPow128 = 340282366920938463463374607431768211456.0;
+
+ if (double.IsNegative(value) || double.IsNaN(value))
+ {
+ return MinValue;
+ }
+ else if (value >= TwoPow128)
+ {
+ return MaxValue;
+ }
+
+ return ToUInt128(value);
+ }
+
+ /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked UInt128(double value)
+ {
+ const double TwoPow128 = 340282366920938463463374607431768211456.0;
+
+ // We need to convert -0.0 to 0 and not throw, so we compare
+ // value against 0 rather than checking IsNegative
+
+ if ((value < 0.0) || double.IsNaN(value) || (value >= TwoPow128))
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+
+ return ToUInt128(value);
+ }
+ */
+
+ internal static UInt128 ToUInt128(double value)
+ {
+ const double TwoPow128 = 340282366920938463463374607431768211456.0;
+
+ // TODO:
+ //Debug.Assert(value >= 0);
+ //Debug.Assert(double.IsFinite(value));
+ //Debug.Assert(value < TwoPow128);
+
+ // This code is based on `f64_to_u128` from m-ou-se/floatconv
+ // Copyright (c) 2020 Mara Bos . All rights reserved.
+ //
+ // Licensed under the BSD 2 - Clause "Simplified" License
+ // See THIRD-PARTY-NOTICES.TXT for the full license text
+
+ if (value >= 1.0)
+ {
+ // In order to convert from double to uint128 we first need to extract the signficand,
+ // including the implicit leading bit, as a full 128-bit significand. We can then adjust
+ // this down to the represented integer by right shifting by the unbiased exponent, taking
+ // into account the significand is now represented as 128-bits.
+
+ ulong bits = BitConverter.DoubleToUInt64Bits(value);
+ UInt128 result = new UInt128((bits << 12) >> 1 | 0x8000_0000_0000_0000, 0x0000_0000_0000_0000);
+
+ result = result >> (1023 + 128 - 1 - (int)(bits >> 52));
+ return result;
+ }
+ else
+ {
+ return MinValue;
+ }
+ }
+
+ /// Explicitly converts a value to a 128-bit unsigned integer.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ public static explicit operator UInt128(short value)
+ {
+ long lower = value;
+ return new UInt128((ulong)(lower >> 63), (ulong)lower);
+ }
+
+ /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked UInt128(short value)
+ {
+ if (value < 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return new UInt128(0, (ushort)value);
+ }
+ */
+
+ /// Explicitly converts a value to a 128-bit unsigned integer.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ public static explicit operator UInt128(int value)
+ {
+ long lower = value;
+ return new UInt128((ulong)(lower >> 63), (ulong)lower);
+ }
+
+ /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked UInt128(int value)
+ {
+ if (value < 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return new UInt128(0, (uint)value);
+ }
+ */
+
+ /// Explicitly converts a value to a 128-bit unsigned integer.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ public static explicit operator UInt128(long value)
+ {
+ long lower = value;
+ return new UInt128((ulong)(lower >> 63), (ulong)lower);
+ }
+
+ /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked UInt128(long value)
+ {
+ if (value < 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return new UInt128(0, (ulong)value);
+ }
+ */
+
+ /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked UInt128(nint value)
+ {
+ if (value < 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return new UInt128(0, (nuint)value);
+ }
+ */
+
+ /// Explicitly converts a value to a 128-bit unsigned integer.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ public static explicit operator UInt128(sbyte value)
+ {
+ long lower = value;
+ return new UInt128((ulong)(lower >> 63), (ulong)lower);
+ }
+
+ /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked UInt128(sbyte value)
+ {
+ if (value < 0)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+ return new UInt128(0, (byte)value);
+ }
+ */
+
+ /// Explicitly converts a value to a 128-bit unsigned integer.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ public static explicit operator UInt128(float value) => (UInt128)(double)(value);
+
+ /// Explicitly converts a value to a 128-bit unsigned integer, throwing an overflow exception for any values that fall outside the representable range.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ /// is not representable by .
+ /* TODO:
+ public static explicit operator checked UInt128(float value) => checked((UInt128)(double)(value));
+ */
+
+ //
+ // Implicit Conversions To UInt128
+ //
+
+ /// Implicitly converts a value to a 128-bit unsigned integer.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ public static implicit operator UInt128(byte value) => new UInt128(0, value);
+
+ /// Implicitly converts a value to a 128-bit unsigned integer.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ public static implicit operator UInt128(char value) => new UInt128(0, value);
+
+ /// Implicitly converts a value to a 128-bit unsigned integer.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ public static implicit operator UInt128(ushort value) => new UInt128(0, value);
+
+ /// Implicitly converts a value to a 128-bit unsigned integer.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ public static implicit operator UInt128(uint value) => new UInt128(0, value);
+
+ /// Implicitly converts a value to a 128-bit unsigned integer.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ public static implicit operator UInt128(ulong value) => new UInt128(0, value);
+
+ /// Implicitly converts a value to a 128-bit unsigned integer.
+ /// The value to convert.
+ /// converted to a 128-bit unsigned integer.
+ public static implicit operator UInt128(uintptr value) => new UInt128(0, value);
+
+ //
+ // IAdditionOperators
+ //
+
+ ///
+ public static UInt128 operator +(UInt128 left, UInt128 right)
+ {
+ // For unsigned addition, we can detect overflow by checking `(x + y) < x`
+ // This gives us the carry to add to upper to compute the correct result
+
+ ulong lower = left._lower + right._lower;
+ ulong carry = (lower < left._lower) ? (ulong)1UL : 0UL;
+
+ ulong upper = left._upper + right._upper + carry;
+ return new UInt128(upper, lower);
+ }
+
+ ///
+ /* TODO:
+ public static UInt128 operator checked +(UInt128 left, UInt128 right)
+ {
+ // For unsigned addition, we can detect overflow by checking `(x + y) < x`
+ // This gives us the carry to add to upper to compute the correct result
+
+ ulong lower = left._lower + right._lower;
+ ulong carry = (lower < left._lower) ? 1UL : 0UL;
+
+ ulong upper = checked(left._upper + right._upper + carry);
+ return new UInt128(upper, lower);
+ }
+ */
+
+ ///
+ public static UInt128 PopCount(UInt128 value)
+ => ulong.PopCount(value._lower) + ulong.PopCount(value._upper);
+
+ //
+ // IBitwiseOperators
+ //
+
+ ///
+ public static UInt128 operator &(UInt128 left, UInt128 right) => new UInt128(left._upper & right._upper, left._lower & right._lower);
+
+ ///
+ public static UInt128 operator |(UInt128 left, UInt128 right) => new UInt128(left._upper | right._upper, left._lower | right._lower);
+
+ ///
+ public static UInt128 operator ^(UInt128 left, UInt128 right) => new UInt128(left._upper ^ right._upper, left._lower ^ right._lower);
+
+ ///
+ public static UInt128 operator ~(UInt128 value) => new UInt128(~value._upper, ~value._lower);
+
+ //
+ // IComparisonOperators
+ //
+
+ ///
+ public static bool operator <(UInt128 left, UInt128 right)
+ {
+ return (left._upper < right._upper)
+ || (left._upper == right._upper) && (left._lower < right._lower);
+ }
+
+ ///
+ public static bool operator <=(UInt128 left, UInt128 right)
+ {
+ return (left._upper < right._upper)
+ || (left._upper == right._upper) && (left._lower <= right._lower);
+ }
+
+ ///
+ public static bool operator >(UInt128 left, UInt128 right)
+ {
+ return (left._upper > right._upper)
+ || (left._upper == right._upper) && (left._lower > right._lower);
+ }
+
+ ///
+ public static bool operator >=(UInt128 left, UInt128 right)
+ {
+ return (left._upper > right._upper)
+ || (left._upper == right._upper) && (left._lower >= right._lower);
+ }
+
+ //
+ // IDecrementOperators
+ //
+
+ ///
+ public static UInt128 operator --(UInt128 value) => value - One;
+
+ ///
+ /* TODO:
+ public static UInt128 operator checked --(UInt128 value) => checked(value - One);
+ */
+
+ //
+ // IDivisionOperators
+ //
+
+ ///
+ public static UInt128 operator /(UInt128 left, UInt128 right)
+ {
+ if (right._upper == 0)
+ {
+ if (right._lower == 0)
+ {
+ ThrowHelper.ThrowDivideByZeroException();
+ }
+
+ if (left._upper == 0)
+ {
+ // left and right are both uint64
+ return left._lower / right._lower;
+ }
+ }
+
+ if (right >= left)
+ {
+ return (right == left) ? One : Zero;
+ }
+
+ return DivideSlow(left, right);
+
+ static uint AddDivisor(Span left, ReadOnlySpan right)
+ {
+ Debug.Assert(left.Length >= right.Length);
+
+ // Repairs the dividend, if the last subtract was too much
+
+ ulong carry = 0UL;
+
+ for (int i = 0; i < right.Length; i++)
+ {
+ ref uint leftElement = ref left[i];
+ ulong digit = (leftElement + carry) + right[i];
+
+ leftElement = unchecked((uint)digit);
+ carry = digit >> 32;
+ }
+
+ return (uint)carry;
+ }
+
+ static bool DivideGuessTooBig(ulong q, ulong valHi, uint valLo, uint divHi, uint divLo)
+ {
+ // TODO:
+ // Debug.Assert(q <= 0xFFFFFFFF);
+
+ // We multiply the two most significant limbs of the divisor
+ // with the current guess for the quotient. If those are bigger
+ // than the three most significant limbs of the current dividend
+ // we return true, which means the current guess is still too big.
+
+ ulong chkHi = divHi * q;
+ ulong chkLo = divLo * q;
+
+ chkHi += (chkLo >> 32);
+ chkLo = (uint)(chkLo);
+
+ return (chkHi > valHi) || ((chkHi == valHi) && (chkLo > valLo));
+ }
+
+ unsafe static UInt128 DivideSlow(UInt128 quotient, UInt128 divisor)
+ {
+ // This is the same algorithm currently used by BigInteger so
+ // we need to get a Span containing the value represented
+ // in the least number of elements possible.
+
+ // We need to ensure that we end up with 4x uints representing the bits from
+ // least significant to most significant so the math will be correct on both
+ // little and big endian systems. So we'll just allocate the relevant buffer
+ // space and then write out the four parts using the native endianness of the
+ // system.
+
+ uint* pLeft = (stackalloc uint[Size / sizeof(uint)]).Buffer;
+
+ Unsafe.WriteUnaligned(ref *(byte*)(pLeft + 0), (uint)(quotient._lower >> 00));
+ Unsafe.WriteUnaligned(ref *(byte*)(pLeft + 1), (uint)(quotient._lower >> 32));
+
+ Unsafe.WriteUnaligned(ref *(byte*)(pLeft + 2), (uint)(quotient._upper >> 00));
+ Unsafe.WriteUnaligned(ref *(byte*)(pLeft + 3), (uint)(quotient._upper >> 32));
+
+ Span left = new Span(pLeft, (Size / sizeof(uint)) - (LeadingZeroCountAsInt32(quotient) / 32));
+
+ // Repeat the same operation with the divisor
+
+ uint* pRight = stackalloc uint[Size / sizeof(uint)];
+
+ Unsafe.WriteUnaligned(ref *(byte*)(pRight + 0), (uint)(divisor._lower >> 00));
+ Unsafe.WriteUnaligned(ref *(byte*)(pRight + 1), (uint)(divisor._lower >> 32));
+
+ Unsafe.WriteUnaligned(ref *(byte*)(pRight + 2), (uint)(divisor._upper >> 00));
+ Unsafe.WriteUnaligned(ref *(byte*)(pRight + 3), (uint)(divisor._upper >> 32));
+
+ Span right = new Span(pRight, (Size / sizeof(uint)) - (LeadingZeroCountAsInt32(divisor) / 32));
+
+ Span rawBits = stackalloc uint[Size / sizeof(uint)];
+ rawBits.Clear();
+ Span bits = rawBits.Slice(0, left.Length - right.Length + 1);
+
+ Debug.Assert(left.Length >= 1);
+ Debug.Assert(right.Length >= 1);
+ Debug.Assert(left.Length >= right.Length);
+
+ // Executes the "grammar-school" algorithm for computing q = a / b.
+ // Before calculating q_i, we get more bits into the highest bit
+ // block of the divisor. Thus, guessing digits of the quotient
+ // will be more precise. Additionally we'll get r = a % b.
+
+ uint divHi = right[^1];
+ uint divLo = right.Length > 1 ? right[^2] : 0;
+
+ // We measure the leading zeros of the divisor
+ int shift = BitOperations.LeadingZeroCount(divHi);
+ int backShift = 32 - shift;
+
+ // And, we make sure the most significant bit is set
+ if (shift > 0)
+ {
+ uint divNx = right.Length > 2 ? right[^3] : 0;
+
+ divHi = (divHi << shift) | (divLo >> backShift);
+ divLo = (divLo << shift) | (divNx >> backShift);
+ }
+
+ // Then, we divide all of the bits as we would do it using
+ // pen and paper: guessing the next digit, subtracting, ...
+ for (int i = left.Length; i >= right.Length; i--)
+ {
+ int n = i - right.Length;
+ uint t = ((uint)(i) < (uint)(left.Length)) ? left[i] : 0;
+
+ ulong valHi = ((ulong)(t) << 32) | left[i - 1];
+ uint valLo = (i > 1) ? left[i - 2] : 0;
+
+ // We shifted the divisor, we shift the dividend too
+ if (shift > 0)
+ {
+ uint valNx = i > 2 ? left[i - 3] : 0;
+
+ valHi = (valHi << shift) | (valLo >> backShift);
+ valLo = (valLo << shift) | (valNx >> backShift);
+ }
+
+ // First guess for the current digit of the quotient,
+ // which naturally must have only 32 bits...
+ ulong digit = valHi / divHi;
+
+ if (digit > 0xFFFFFFFF)
+ {
+ digit = 0xFFFFFFFF;
+ }
+
+ // Our first guess may be a little bit to big
+ while (DivideGuessTooBig(digit, valHi, valLo, divHi, divLo))
+ {
+ --digit;
+ }
+
+ if (digit > 0)
+ {
+ // Now it's time to subtract our current quotient
+ uint carry = SubtractDivisor(left.Slice(n), right, digit);
+
+ if (carry != t)
+ {
+ Debug.Assert(carry == (t + 1));
+
+ // Our guess was still exactly one too high
+ carry = AddDivisor(left.Slice(n), right);
+
+ --digit;
+ Debug.Assert(carry == 1);
+ }
+ }
+
+ // We have the digit!
+ if ((uint)(n) < (uint)(bits.Length))
+ {
+ bits[n] = (uint)(digit);
+ }
+
+ if ((uint)(i) < (uint)(left.Length))
+ {
+ left[i] = 0;
+ }
+ }
+
+ return new UInt128(
+ ((ulong)(rawBits[3]) << 32) | rawBits[2],
+ ((ulong)(rawBits[1]) << 32) | rawBits[0]
+ );
+ }
+
+ static uint SubtractDivisor(Span left, ReadOnlySpan right, ulong q)
+ {
+ Debug.Assert(left.Length >= right.Length);
+ Debug.Assert(q <= 0xFFFFFFFF);
+
+ // Combines a subtract and a multiply operation, which is naturally
+ // more efficient than multiplying and then subtracting...
+
+ ulong carry = 0UL;
+
+ for (int i = 0; i < right.Length; i++)
+ {
+ carry += right[i] * q;
+
+ uint digit = (uint)(carry);
+ carry >>= 32;
+
+ ref uint leftElement = ref left[i];
+
+ if (leftElement < digit)
+ {
+ ++carry;
+ }
+ leftElement -= digit;
+ }
+
+ return (uint)(carry);
+ }
+ }
+
+ ///
+ /* TODO:
+ public static UInt128 operator checked /(UInt128 left, UInt128 right) => left / right;
+ */
+
+ //
+ // IEqualityOperators
+ //
+
+ ///
+ public static bool operator ==(UInt128 left, UInt128 right) => (left._lower == right._lower) && (left._upper == right._upper);
+
+ ///
+ public static bool operator !=(UInt128 left, UInt128 right) => (left._lower != right._lower) || (left._upper != right._upper);
+
+ //
+ // IIncrementOperators
+ //
+
+ ///
+ public static UInt128 operator ++(UInt128 value) => value + One;
+
+ ///
+ /* TODO:
+ public static UInt128 operator checked ++(UInt128 value) => checked(value + One);
+ */
+
+ //
+ // IMinMaxValue
+ //
+
+ ///
+ public static UInt128 MinValue => new UInt128(0, 0);
+
+ ///
+ public static UInt128 MaxValue => new UInt128(0xFFFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF);
+
+ //
+ // IModulusOperators
+ //
+
+ ///
+ public static UInt128 operator %(UInt128 left, UInt128 right)
+ {
+ UInt128 quotient = left / right;
+ return left - (quotient * right);
+ }
+
+ //
+ // IMultiplyOperators
+ //
+
+ ///
+ public static UInt128 operator *(UInt128 left, UInt128 right)
+ {
+ ulong upper = Math.BigMul(left._lower, right._lower, out ulong lower);
+ upper += (left._upper * right._lower) + (left._lower * right._upper);
+ return new UInt128(upper, lower);
+ }
+
+ ///
+ /* TODO:
+ public static UInt128 operator checked *(UInt128 left, UInt128 right)
+ {
+ UInt128 upper = BigMul(left, right, out UInt128 lower);
+
+ if (upper != 0U)
+ {
+ ThrowHelper.ThrowOverflowException();
+ }
+
+ return lower;
+ }
+ */
+
+ /// Produces the full product of two unsigned native integers.
+ /// The integer to multiply with .
+ /// The integer to multiply with .
+ /// The lower half of the full product.
+ /// The upper half of the full product.
+ public static UInt128 BigMul(UInt128 left, UInt128 right, out UInt128 lower)
+ {
+ // Adaptation of algorithm for multiplication
+ // of 32-bit unsigned integers described
+ // in Hacker's Delight by Henry S. Warren, Jr. (ISBN 0-201-91465-4), Chapter 8
+ // Basically, it's an optimized version of FOIL method applied to
+ // low and high qwords of each operand
+
+ ulong al = left._lower;
+ ulong ah = left._upper;
+
+ ulong bl = right._lower;
+ ulong bh = right._upper;
+
+ UInt128 mull = Math.BigMul(al, bl);
+ UInt128 t = Math.BigMul(ah, bl) + mull._upper;
+ UInt128 tl = Math.BigMul(al, bh) + t._lower;
+
+ lower = new UInt128(tl._lower, mull._lower);
+ return Math.BigMul(ah, bh) + t._upper + tl._upper;
+ }
+
+ //
+ // INumberBase
+ //
+
+ ///
+ public static UInt128 One => new UInt128(0, 1);
+
+ ///
+ public static UInt128 Zero => default;
+
+ //
+ // IShiftOperators
+ //
+
+ ///
+ public static UInt128 operator <<(UInt128 value, int shiftAmount)
+ {
+ // C# automatically masks the shift amount for UInt64 to be 0x3F. So we
+ // need to specially handle things if the 7th bit is set.
+
+ shiftAmount &= 0x7F;
+
+ if ((shiftAmount & 0x40) != 0)
+ {
+ // In the case it is set, we know the entire lower bits must be zero
+ // and so the upper bits are just the lower shifted by the remaining
+ // masked amount
+
+ ulong upper = value._lower << shiftAmount;
+ return new UInt128(upper, 0);
+ }
+ else if (shiftAmount != 0)
+ {
+ // Otherwise we need to shift both upper and lower halves by the masked
+ // amount and then or that with whatever bits were shifted "out" of lower
+
+ ulong lower = value._lower << shiftAmount;
+ ulong upper = (value._upper << shiftAmount) | (value._lower >> (64 - shiftAmount));
+
+ return new UInt128(upper, lower);
+ }
+ else
+ {
+ return value;
+ }
+ }
+
+ ///
+ public static UInt128 operator >>(UInt128 value, int shiftAmount) => value >>> shiftAmount;
+
+ ///
+ /* TODO:
+ public static UInt128 operator >>>(UInt128 value, int shiftAmount)
+ {
+ // C# automatically masks the shift amount for UInt64 to be 0x3F. So we
+ // need to specially handle things if the 7th bit is set.
+
+ shiftAmount &= 0x7F;
+
+ if ((shiftAmount & 0x40) != 0)
+ {
+ // In the case it is set, we know the entire upper bits must be zero
+ // and so the lower bits are just the upper shifted by the remaining
+ // masked amount
+
+ ulong lower = value._upper >> shiftAmount;
+ return new UInt128(0, lower);
+ }
+ else if (shiftAmount != 0)
+ {
+ // Otherwise we need to shift both upper and lower halves by the masked
+ // amount and then or that with whatever bits were shifted "out" of upper
+
+ ulong lower = (value._lower >> shiftAmount) | (value._upper << (64 - shiftAmount));
+ ulong upper = value._upper >> shiftAmount;
+
+ return new UInt128(upper, lower);
+ }
+ else
+ {
+ return value;
+ }
+ }
+ */
+
+ //
+ // ISubtractionOperators
+ //
+
+ ///
+ public static UInt128 operator -(UInt128 left, UInt128 right)
+ {
+ // For unsigned subtract, we can detect overflow by checking `(x - y) > x`
+ // This gives us the borrow to subtract from upper to compute the correct result
+
+ ulong lower = left._lower - right._lower;
+ ulong borrow = (lower > left._lower) ? 1UL : 0UL;
+
+ ulong upper = left._upper - right._upper - borrow;
+ return new UInt128(upper, lower);
+ }
+
+ ///
+ /* TODO:
+ public static UInt128 operator checked -(UInt128 left, UInt128 right)
+ {
+ // For unsigned subtract, we can detect overflow by checking `(x - y) > x`
+ // This gives us the borrow to subtract from upper to compute the correct result
+
+ ulong lower = left._lower - right._lower;
+ ulong borrow = (lower > left._lower) ? 1UL : 0UL;
+
+ ulong upper = checked(left._upper - right._upper - borrow);
+ return new UInt128(upper, lower);
+ }
+ */
+
+ //
+ // IUnaryNegationOperators
+ //
+
+ ///
+ public static UInt128 operator -(UInt128 value) => Zero - value;
+
+ ///
+ /* TODO:
+ public static UInt128 operator checked -(UInt128 value) => checked(Zero - value);
+ */
+}
From 4c1e50588a64d7ed529b336ef8d4606154617284 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Sat, 18 Apr 2026 12:59:18 +0300
Subject: [PATCH 09/26] Removed ulong cast
---
std/System/UInt128.hpt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/std/System/UInt128.hpt b/std/System/UInt128.hpt
index 85d7a58f..1faa2aaf 100644
--- a/std/System/UInt128.hpt
+++ b/std/System/UInt128.hpt
@@ -634,7 +634,7 @@ public readonly struct UInt128
// This gives us the carry to add to upper to compute the correct result
ulong lower = left._lower + right._lower;
- ulong carry = (lower < left._lower) ? (ulong)1UL : 0UL;
+ ulong carry = (lower < left._lower) ? 1UL : 0UL;
ulong upper = left._upper + right._upper + carry;
return new UInt128(upper, lower);
From b679ff58881dd52120430be63944e148c3f7645e Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Sat, 18 Apr 2026 13:26:19 +0300
Subject: [PATCH 10/26] Const field fix in Timeout.hpt
---
std/System/Threading/Timeout.hpt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/std/System/Threading/Timeout.hpt b/std/System/Threading/Timeout.hpt
index efa40374..ed7820db 100644
--- a/std/System/Threading/Timeout.hpt
+++ b/std/System/Threading/Timeout.hpt
@@ -10,5 +10,5 @@ public static class Timeout
public static readonly TimeSpan InfiniteTimeSpan = new TimeSpan(0, 0, 0, 0, Infinite);
public const int Infinite = -1;
- internal const uint UnsignedInfinite = unchecked((uint)-1);
+ internal const uint UnsignedInfinite = unchecked((uint)(-1));
}
\ No newline at end of file
From 725f662ed942e14640270f6130c5b0247f172a5f Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Tue, 21 Apr 2026 10:45:20 +0300
Subject: [PATCH 11/26] Some cringe shite added for UInt128
---
std/System/DivideByZeroException.hpt | 23 +++++++++++++++
std/System/Int128.hpt | 2 +-
std/System/Math.hpt | 44 ++++++++++++++++++++++++++++
std/System/SR.hpt | 1 +
std/System/UInt128.hpt | 44 ++++++++++++++++++----------
5 files changed, 97 insertions(+), 17 deletions(-)
create mode 100644 std/System/DivideByZeroException.hpt
diff --git a/std/System/DivideByZeroException.hpt b/std/System/DivideByZeroException.hpt
new file mode 100644
index 00000000..10352fa9
--- /dev/null
+++ b/std/System/DivideByZeroException.hpt
@@ -0,0 +1,23 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using System.Runtime.Serialization;
+
+///
+/// The exception that is thrown when there is an attempt to divide an integral or value by zero.
+///
+public class DivideByZeroException : ArithmeticException
+{
+ public DivideByZeroException()
+ : base(SR.Arg_DivideByZero)
+ {
+ }
+
+ public DivideByZeroException(string message)
+ : base(message)
+ {
+ }
+}
\ No newline at end of file
diff --git a/std/System/Int128.hpt b/std/System/Int128.hpt
index e98a2b75..2a47fe14 100644
--- a/std/System/Int128.hpt
+++ b/std/System/Int128.hpt
@@ -660,7 +660,7 @@ public readonly struct Int128
{
if ((right == -1) && (left._upper == 0x8000_0000_0000_0000) && (left._lower == 0))
{
- ThrowHelper.ThrowOverflowException();
+ throw new OverflowException();
}
// We simplify the logic here by just doing unsigned division on the
diff --git a/std/System/Math.hpt b/std/System/Math.hpt
index 9bbb9c23..10c0fe98 100644
--- a/std/System/Math.hpt
+++ b/std/System/Math.hpt
@@ -16,4 +16,48 @@ public static class Math
Marshal.ModF(d, &d);
return d;
}
+
+ /// Produces the full product of two unsigned 64-bit numbers.
+ /// The first number to multiply.
+ /// The second number to multiply.
+ /// The low 64-bit of the product of the specified numbers.
+ /// The high 64-bit of the product of the specified numbers.
+ public static inline unsafe ulong BigMul(ulong a, ulong b, out ulong low)
+ {
+ return SoftwareFallback(a, b, out low);
+
+ static ulong SoftwareFallback(ulong a, ulong b, out ulong low)
+ {
+ // Adaptation of algorithm for multiplication
+ // of 32-bit unsigned integers described
+ // in Hacker's Delight by Henry S. Warren, Jr. (ISBN 0-201-91465-4), Chapter 8
+ // Basically, it's an optimized version of FOIL method applied to
+ // low and high dwords of each operand
+
+ // Use 32-bit uints to optimize the fallback for 32-bit platforms.
+ uint al = (uint)a;
+ uint ah = (uint)(a >> 32);
+ uint bl = (uint)b;
+ uint bh = (uint)(b >> 32);
+
+ ulong mull = ((ulong)al) * bl;
+ ulong t = ((ulong)ah) * bl + (mull >> 32);
+ ulong tl = ((ulong)al) * bh + (uint)t;
+
+ low = tl << 32 | (uint)mull;
+
+ return ((ulong)ah) * bh + (t >> 32) + (tl >> 32);
+ }
+ }
+
+ /// Produces the full product of two unsigned 64-bit numbers.
+ /// The first number to multiply.
+ /// The second number to multiply.
+ /// The full product of the specified numbers.
+ public static inline UInt128 BigMul(ulong a, ulong b)
+ {
+ ulong low;
+ ulong high = BigMul(a, b, out low);
+ return new UInt128(high, low);
+ }
}
\ No newline at end of file
diff --git a/std/System/SR.hpt b/std/System/SR.hpt
index bd2bae40..8390d157 100644
--- a/std/System/SR.hpt
+++ b/std/System/SR.hpt
@@ -43,6 +43,7 @@ internal static class SR
internal const string Arg_BogusIComparer = "Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results.";
internal const string Arg_InvalidConsoleColor = "The ConsoleColor enum value was not defined on that enum. Please use a defined color from the enum.";
internal const string Arg_OverflowException = "Arithmetic operation resulted in an overflow.";
+ internal const string Arg_DivideByZero = "Attempted to divide by zero.";
internal const string Arg_DllNotFoundException = "Dll was not found.";
diff --git a/std/System/UInt128.hpt b/std/System/UInt128.hpt
index 1faa2aaf..b66af466 100644
--- a/std/System/UInt128.hpt
+++ b/std/System/UInt128.hpt
@@ -655,6 +655,20 @@ public readonly struct UInt128
}
*/
+ ///
+ public static UInt128 LeadingZeroCount(UInt128 value)
+ => (uint)LeadingZeroCountAsInt32(value);
+
+ /// Computes the number of leading zero bits in this value.
+ private static inline int LeadingZeroCountAsInt32(UInt128 value)
+ {
+ if (value._upper == 0)
+ {
+ return 64 + BitOperations.LeadingZeroCount(value._lower);
+ }
+ return BitOperations.LeadingZeroCount(value._upper);
+ }
+
///
public static UInt128 PopCount(UInt128 value)
=> ulong.PopCount(value._lower) + ulong.PopCount(value._upper);
@@ -730,7 +744,7 @@ public readonly struct UInt128
{
if (right._lower == 0)
{
- ThrowHelper.ThrowDivideByZeroException();
+ throw new DivideByZeroException();
}
if (left._upper == 0)
@@ -800,17 +814,16 @@ public readonly struct UInt128
uint* pLeft = (stackalloc uint[Size / sizeof(uint)]).Buffer;
- Unsafe.WriteUnaligned(ref *(byte*)(pLeft + 0), (uint)(quotient._lower >> 00));
- Unsafe.WriteUnaligned(ref *(byte*)(pLeft + 1), (uint)(quotient._lower >> 32));
-
- Unsafe.WriteUnaligned(ref *(byte*)(pLeft + 2), (uint)(quotient._upper >> 00));
- Unsafe.WriteUnaligned(ref *(byte*)(pLeft + 3), (uint)(quotient._upper >> 32));
+ *(pLeft + 0) = (uint)(quotient._lower >> 00);
+ *(pLeft + 1) = (uint)(quotient._lower >> 32);
+ *(pLeft + 2) = (uint)(quotient._upper >> 00);
+ *(pLeft + 3) = (uint)(quotient._upper >> 32);
Span left = new Span(pLeft, (Size / sizeof(uint)) - (LeadingZeroCountAsInt32(quotient) / 32));
// Repeat the same operation with the divisor
- uint* pRight = stackalloc uint[Size / sizeof(uint)];
+ uint* pRight = (stackalloc uint[Size / sizeof(uint)]).Buffer;
Unsafe.WriteUnaligned(ref *(byte*)(pRight + 0), (uint)(divisor._lower >> 00));
Unsafe.WriteUnaligned(ref *(byte*)(pRight + 1), (uint)(divisor._lower >> 32));
@@ -1003,7 +1016,8 @@ public readonly struct UInt128
///
public static UInt128 operator *(UInt128 left, UInt128 right)
{
- ulong upper = Math.BigMul(left._lower, right._lower, out ulong lower);
+ ulong lower;
+ ulong upper = Math.BigMul(left._lower, right._lower, out lower);
upper += (left._upper * right._lower) + (left._lower * right._upper);
return new UInt128(upper, lower);
}
@@ -1078,7 +1092,7 @@ public readonly struct UInt128
// and so the upper bits are just the lower shifted by the remaining
// masked amount
- ulong upper = value._lower << shiftAmount;
+ ulong upper = value._lower << (ulong)shiftAmount;
return new UInt128(upper, 0);
}
else if (shiftAmount != 0)
@@ -1086,8 +1100,8 @@ public readonly struct UInt128
// Otherwise we need to shift both upper and lower halves by the masked
// amount and then or that with whatever bits were shifted "out" of lower
- ulong lower = value._lower << shiftAmount;
- ulong upper = (value._upper << shiftAmount) | (value._lower >> (64 - shiftAmount));
+ ulong lower = value._lower << (ulong)shiftAmount;
+ ulong upper = (value._upper << (ulong)shiftAmount) | (value._lower >> (ulong)(64 - shiftAmount));
return new UInt128(upper, lower);
}
@@ -1101,7 +1115,6 @@ public readonly struct UInt128
public static UInt128 operator >>(UInt128 value, int shiftAmount) => value >>> shiftAmount;
///
- /* TODO:
public static UInt128 operator >>>(UInt128 value, int shiftAmount)
{
// C# automatically masks the shift amount for UInt64 to be 0x3F. So we
@@ -1115,7 +1128,7 @@ public readonly struct UInt128
// and so the lower bits are just the upper shifted by the remaining
// masked amount
- ulong lower = value._upper >> shiftAmount;
+ ulong lower = value._upper >> (ulong)shiftAmount;
return new UInt128(0, lower);
}
else if (shiftAmount != 0)
@@ -1123,8 +1136,8 @@ public readonly struct UInt128
// Otherwise we need to shift both upper and lower halves by the masked
// amount and then or that with whatever bits were shifted "out" of upper
- ulong lower = (value._lower >> shiftAmount) | (value._upper << (64 - shiftAmount));
- ulong upper = value._upper >> shiftAmount;
+ ulong lower = (value._lower >> (ulong)shiftAmount) | (value._upper << (ulong)(64 - shiftAmount));
+ ulong upper = value._upper >> (ulong)shiftAmount;
return new UInt128(upper, lower);
}
@@ -1133,7 +1146,6 @@ public readonly struct UInt128
return value;
}
}
- */
//
// ISubtractionOperators
From 09a85dcebd0fd0db09e1880344f801c5e933f092 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Tue, 21 Apr 2026 15:53:00 +0300
Subject: [PATCH 12/26] Int128 and UInt128 probably done
---
std/System/Int128.hpt | 4 +-
std/System/Math.hpt | 61 ++++-
.../Text/Formatting/Number.Formatting.hpt | 237 +++++++++++++++++-
std/System/TimeSpan.hpt | 10 +-
std/System/UInt128.hpt | 108 ++++----
5 files changed, 354 insertions(+), 66 deletions(-)
diff --git a/std/System/Int128.hpt b/std/System/Int128.hpt
index 2a47fe14..b8456315 100644
--- a/std/System/Int128.hpt
+++ b/std/System/Int128.hpt
@@ -85,12 +85,12 @@ public readonly struct Int128
///
public override string ToString()
{
- return Number.Int128ToDecStr(this);
+ return System.Text.Formatting.Number.Format(this, "");
}
public string ToString(string format)
{
- return Number.FormatInt128(this, format, null);
+ return System.Text.Formatting.Number.Format(this, format);
}
//
diff --git a/std/System/Math.hpt b/std/System/Math.hpt
index 10c0fe98..494611a6 100644
--- a/std/System/Math.hpt
+++ b/std/System/Math.hpt
@@ -6,17 +6,50 @@ using System.Runtime.InteropServices;
public static class Math
{
- public static int Min(int val1, int val2)
+ public static inline int Min(int val1, int val2)
{
return (val1 <= val2) ? val1 : val2;
}
- public static double Truncate(double d)
+ public static inline double Truncate(double d)
{
Marshal.ModF(d, &d);
return d;
}
+ /// Produces the full product of two unsigned 32-bit numbers.
+ /// The first number to multiply.
+ /// The second number to multiply.
+ /// The number containing the product of the specified numbers.
+ public static inline ulong BigMul(uint a, uint b)
+ {
+ return ((ulong)a) * b;
+ }
+
+ /// Produces the full product of two 32-bit numbers.
+ /// The first number to multiply.
+ /// The second number to multiply.
+ /// The number containing the product of the specified numbers.
+ public static inline long BigMul(int a, int b)
+ {
+ return ((long)a) * b;
+ }
+
+
+ ///
+ /// Perform multiplication between 64 and 32 bit numbers, returning lower 64 bits in
+ ///
+ /// hi bits of the result
+ /// REMOVE once BigMul(ulong, ulong) is treated as intrinsics and optimizes 32 by 64 multiplications
+ internal static inline ulong BigMul(ulong a, uint b, out ulong low)
+ {
+ return Math.BigMul((ulong)a, (ulong)b, out low);
+ }
+
+ ///
+ internal static inline ulong BigMul(uint a, ulong b, out ulong low)
+ => BigMul(b, a, out low);
+
/// Produces the full product of two unsigned 64-bit numbers.
/// The first number to multiply.
/// The second number to multiply.
@@ -50,6 +83,19 @@ public static class Math
}
}
+ /// Produces the full product of two 64-bit numbers.
+ /// The first number to multiply.
+ /// The second number to multiply.
+ /// The low 64-bit of the product of the specified numbers.
+ /// The high 64-bit of the product of the specified numbers.
+ public static inline long BigMul(long a, long b, out long low)
+ {
+ ulong ulow;
+ ulong high = BigMul((ulong)a, (ulong)b, out ulow);
+ low = (long)ulow;
+ return (long)high - ((a >> 63) & b) - ((b >> 63) & a);
+ }
+
/// Produces the full product of two unsigned 64-bit numbers.
/// The first number to multiply.
/// The second number to multiply.
@@ -60,4 +106,15 @@ public static class Math
ulong high = BigMul(a, b, out low);
return new UInt128(high, low);
}
+
+ /// Produces the full product of two 64-bit numbers.
+ /// The first number to multiply.
+ /// The second number to multiply.
+ /// The full product of the specified numbers.
+ public static inline Int128 BigMul(long a, long b)
+ {
+ long low;
+ long high = BigMul(a, b, out low);
+ return new Int128((ulong)high, (ulong)low);
+ }
}
\ No newline at end of file
diff --git a/std/System/Text/Formatting/Number.Formatting.hpt b/std/System/Text/Formatting/Number.Formatting.hpt
index 26bee5c7..a21745f0 100644
--- a/std/System/Text/Formatting/Number.Formatting.hpt
+++ b/std/System/Text/Formatting/Number.Formatting.hpt
@@ -23,6 +23,8 @@ public static class Number
const int UInt32Precision = 10;
const int Int64Precision = 19;
const int UInt64Precision = 20;
+ const int Int128Precision = 39;
+ const int UInt128Precision = 39;
const int FloatPrecision = 7;
const int DoublePrecision = 15;
const int DecimalPrecision = 29;
@@ -83,6 +85,18 @@ public static class Number
return FormatUInt64(value, specifier, cc);
}
+ public static inline string Format(Int128 value, string specifier, CultureInfo culture = null)
+ {
+ CachedCulture cc = culture == null ? CachedInvariantCulture : new CachedCulture(culture);
+ return FormatInt128(value, specifier, cc);
+ }
+
+ public static inline string Format(UInt128 value, string specifier, CultureInfo culture = null)
+ {
+ CachedCulture cc = culture == null ? CachedInvariantCulture : new CachedCulture(culture);
+ return FormatUInt128(value, specifier, cc);
+ }
+
public static inline string Format(float value, string specifier, CultureInfo culture = null)
{
CachedCulture cc = culture == null ? CachedInvariantCulture : new CachedCulture(culture);
@@ -427,6 +441,84 @@ public static class Number
}
}
+ static string FormatInt128(Int128 value, string specifier, CachedCulture culture)
+ {
+ int digits;
+ var fmt = ParseFormatSpecifier(specifier, out digits);
+
+ // ANDing with 0xFFDF has the effect of uppercasing the character
+ switch (fmt & 0xFFDF)
+ {
+ case ('G')
+ {
+ if (digits > 0)
+ goto caseDefault;
+ else
+ goto caseD;
+ }
+ case ('D') $caseD
+ {
+ return Int128ToDecStr(value, digits, culture.NegativeSign);
+ }
+ case ('X')
+ {
+ // fmt-('X'-'A'+1) gives us the base hex character in either
+ // uppercase or lowercase, depending on the casing of fmt
+ return Int128ToHexStr((UInt128)value, fmt - ('X' - 'A' + 10), digits);
+ }
+ default $caseDefault
+ {
+ var number = new NumberStruct();
+ var buffer = (stackalloc char[MaxNumberDigits + 1]).Buffer;
+ number.Digits = buffer;
+ Int128ToNumber(value, ref number);
+ if (fmt != 0)
+ return NumberToString(ref number, fmt, digits, culture);
+ else
+ return NumberToCustomFormatString(ref number, specifier, culture);
+ }
+ }
+ }
+
+ static string FormatUInt128(UInt128 value, string specifier, CachedCulture culture)
+ {
+ int digits;
+ var fmt = ParseFormatSpecifier(specifier, out digits);
+
+ // ANDing with 0xFFDF has the effect of uppercasing the character
+ switch (fmt & 0xFFDF)
+ {
+ case ('G')
+ {
+ if (digits > 0)
+ goto caseDefault;
+ else
+ goto caseD;
+ }
+ case ('D') $caseD
+ {
+ return UInt128ToDecStr(value, digits);
+ }
+ case ('X')
+ {
+ // fmt-('X'-'A'+1) gives us the base hex character in either
+ // uppercase or lowercase, depending on the casing of fmt
+ return Int128ToHexStr(value, fmt - ('X' - 'A' + 10), digits);
+ }
+ default $caseDefault
+ {
+ var number = new NumberStruct();
+ var buffer = (stackalloc char[MaxNumberDigits + 1]).Buffer;
+ number.Digits = buffer;
+ UInt128ToNumber(value, ref number);
+ if (fmt != 0)
+ return NumberToString(ref number, fmt, digits, culture);
+ else
+ return NumberToCustomFormatString(ref number, specifier, culture);
+ }
+ }
+ }
+
static string FormatSingle(float value, string specifier, CachedCulture culture)
{
int digits;
@@ -598,6 +690,47 @@ public static class Number
return GetStringFromBufferCopy(p, (int)(buffer + bufferLength - p));
}
+ static string Int128ToDecStr(Int128 value, int digits, string negativeSign)
+ {
+ if (digits < 1)
+ digits = 1;
+
+ int sign = (int)High32((ulong)(value >> 64));
+ int maxDigits = 39;
+ int bufferLength = maxDigits > 100 ? maxDigits : 100;
+
+ if (sign < 0)
+ {
+ value = -value;
+ var negativeLength = negativeSign.Length;
+ if (negativeLength > bufferLength - maxDigits)
+ bufferLength = negativeLength + maxDigits;
+ }
+
+ var buffer = (stackalloc char[bufferLength]).Buffer;
+ var p = UInt128ToDecChars(buffer + bufferLength, (UInt128)value, digits);
+ if (sign < 0)
+ {
+ // add the negative sign
+ for (int i = negativeSign.Length - 1; i >= 0; i--)
+ *(--p) = negativeSign[i];
+ }
+ return GetStringFromBufferCopy(p, (int)(buffer + bufferLength - p));
+ }
+
+ static string UInt128ToDecStr(UInt128 value, int digits)
+ {
+ if (digits < 1)
+ digits = 1;
+
+ int maxDigits = 39;
+ int bufferLength = maxDigits > 100 ? maxDigits : 100;
+
+ var buffer = (stackalloc char[bufferLength]).Buffer;
+ var p = UInt128ToDecChars(buffer + bufferLength, value, digits);
+ return GetStringFromBufferCopy(p, (int)(buffer + bufferLength - p));
+ }
+
[SuppressStackTrace]
internal static char* UInt32ToDecChars(char* p, uint value, int digits)
{
@@ -652,6 +785,24 @@ public static class Number
return p;
}
+ [SuppressStackTrace]
+ internal static char* UInt128ToDecChars(char* p, UInt128 value, int digits)
+ {
+ while (value != 0)
+ {
+ char c = (char)(value % 10 + '0');
+ *--p = c;
+ value /= 10;
+ digits--;
+ }
+
+ while (--digits >= 0)
+ {
+ *--p = '0';
+ }
+ return p;
+ }
+
static string Int32ToHexStr(uint value, int hexBase, int digits)
{
var buffer = (stackalloc char[100]).Buffer;
@@ -664,7 +815,7 @@ public static class Number
static string Int64ToHexStr(ulong value, int hexBase, int digits)
{
- var buffer = (stackalloc char[100]).Buffer;
+ char* buffer = (stackalloc char[100]).Buffer;
char* ptr;
if (High32(value) != 0)
{
@@ -681,6 +832,40 @@ public static class Number
return GetStringFromBufferCopy(ptr, (int)(buffer + 100 - ptr));
}
+ static string Int128ToHexStr(UInt128 value, int hexBase, int digits)
+ {
+ char* buffer = (stackalloc char[200]).Buffer;
+ char* ptr;
+
+ ulong low = Low64(value);
+ ulong high = High64(value);
+
+ if (high != 0)
+ {
+ Int64ToHexChars(buffer + 200, low, hexBase, 16);
+ ptr = Int64ToHexChars(buffer + 200 - 16, high, hexBase, digits - 16);
+ }
+ else
+ {
+ if (digits < 1)
+ digits = 1;
+ ptr = Int64ToHexChars(buffer + 200, low, hexBase, digits);
+ }
+
+ return GetStringFromBufferCopy(ptr, (int)(buffer + 200 - ptr));
+ }
+
+ static char* Int64ToHexChars(char* p, ulong value, int hexBase, int digits)
+ {
+ while (--digits >= 0 || value != 0)
+ {
+ var digit = value & 0xF;
+ *--p = (char)((int)digit + (digit < 10 ? '0' : hexBase));
+ value = value >> 4;
+ }
+ return p;
+ }
+
static char* Int32ToHexChars(char* p, uint value, int hexBase, int digits)
{
while (--digits >= 0 || value != 0)
@@ -768,6 +953,44 @@ public static class Number
*dest = '\0';
}
+ static void Int128ToNumber(Int128 value, ref NumberStruct number)
+ {
+ number.Precision = Int128Precision;
+ if (value >= 0)
+ number.Sign = 0;
+ else
+ {
+ number.Sign = 1;
+ value = -value;
+ }
+
+ var buffer = (stackalloc char[Int128Precision + 1]).Buffer;
+ var ptr = UInt128ToDecChars(buffer + Int128Precision, (UInt128)value, 0);
+ var len = (int)(buffer + Int128Precision - ptr);
+ number.Scale = len;
+
+ var dest = number.Digits;
+ while (--len >= 0)
+ *dest++ = *ptr++;
+ *dest = '\0';
+ }
+
+ static void UInt128ToNumber(UInt128 value, ref NumberStruct number)
+ {
+ number.Precision = UInt128Precision;
+ number.Sign = 0;
+
+ var buffer = (stackalloc char[UInt128Precision + 1]).Buffer;
+ var ptr = UInt128ToDecChars(buffer + UInt128Precision, value, 0);
+ var len = (int)(buffer + UInt128Precision - ptr);
+ number.Scale = len;
+
+ var dest = number.Digits;
+ while (--len >= 0)
+ *dest++ = *ptr++;
+ *dest = '\0';
+ }
+
static void DoubleToNumber(double value, int precision, ref NumberStruct number)
{
number.Precision = precision;
@@ -1274,6 +1497,18 @@ public static class Number
return (uint)((value & 0xFFFFFFFF00000000) >> 32);
}
+ [SuppressStackTrace]
+ internal static ulong Low64(UInt128 value)
+ {
+ return (ulong)value;
+ }
+
+ [SuppressStackTrace]
+ internal static ulong High64(UInt128 value)
+ {
+ return (ulong)(value >> 64);
+ }
+
static readonly CachedCulture CachedInvariantCulture = new CachedCulture(CultureInfo.InvariantCulture);
static readonly CachedCulture CachedCurrentCulture = new CachedCulture(CultureInfo.CurrentCulture);
}
diff --git a/std/System/TimeSpan.hpt b/std/System/TimeSpan.hpt
index e4cd9992..3095247d 100644
--- a/std/System/TimeSpan.hpt
+++ b/std/System/TimeSpan.hpt
@@ -507,7 +507,7 @@ public readonly struct TimeSpan
+ Math.BigMul(milliseconds, MicrosecondsPerMillisecond)
+ microseconds;
- return FromMicroseconds(totalMicroseconds);
+ return FromMicroseconds((Int128)totalMicroseconds);
}
///
@@ -715,7 +715,7 @@ public readonly struct TimeSpan
public static TimeSpan Parse(string s)
{
/* Constructs a TimeSpan from a string. Leading and trailing white space characters are allowed. */
- ArgumentNullException.ThrowIfNull(s, ExceptionArgument.input);
+ ArgumentNullException.ThrowIfNull(s);
return TimeSpanParse.Parse(s, null);
}
public static TimeSpan Parse(string input, IFormatProvider formatProvider)
@@ -726,7 +726,7 @@ public readonly struct TimeSpan
}
return TimeSpanParse.Parse(input, formatProvider);
}
- public static TimeSpan Parse(ReadOnlySpan input, IFormatProvider? formatProvider = null) => TimeSpanParse.Parse(input, formatProvider);
+ public static TimeSpan Parse(ReadOnlySpan input, IFormatProvider formatProvider = null) => TimeSpanParse.Parse(input, formatProvider);
public static TimeSpan ParseExact(string input, string format, IFormatProvider formatProvider)
{
ArgumentNullException.ThrowIfNull(nameof(input));
@@ -769,7 +769,7 @@ public readonly struct TimeSpan
}
public static bool TryParse(string s, out TimeSpan result)
{
- if (s is null)
+ if (s == null)
{
result = default;
return false;
@@ -780,7 +780,7 @@ public readonly struct TimeSpan
public static bool TryParse(string input, IFormatProvider formatProvider, out TimeSpan result)
{
- if (input is null)
+ if (input == null)
{
result = default;
return false;
diff --git a/std/System/UInt128.hpt b/std/System/UInt128.hpt
index b66af466..972a9cc0 100644
--- a/std/System/UInt128.hpt
+++ b/std/System/UInt128.hpt
@@ -85,12 +85,12 @@ public readonly struct UInt128
///
public override string ToString()
{
- return Number.UInt128ToDecStr(this);
+ return System.Text.Formatting.Number.Format(this, "");
}
public string ToString(string format)
{
- return Number.FormatUInt128(this, format, null);
+ return System.Text.Formatting.Number.Format(this, format);
}
//
@@ -669,10 +669,6 @@ public readonly struct UInt128
return BitOperations.LeadingZeroCount(value._upper);
}
- ///
- public static UInt128 PopCount(UInt128 value)
- => ulong.PopCount(value._lower) + ulong.PopCount(value._upper);
-
//
// IBitwiseOperators
//
@@ -761,20 +757,20 @@ public readonly struct UInt128
return DivideSlow(left, right);
- static uint AddDivisor(Span left, ReadOnlySpan right)
+ static uint AddDivisor(uint* left, int leftLength, uint* right, int rightLength)
{
- Debug.Assert(left.Length >= right.Length);
+ // TODO:
+ // Debug.Assert(left.Length >= right.Length);
// Repairs the dividend, if the last subtract was too much
ulong carry = 0UL;
- for (int i = 0; i < right.Length; i++)
+ for (int i = 0; i < rightLength; i++)
{
- ref uint leftElement = ref left[i];
- ulong digit = (leftElement + carry) + right[i];
+ ulong digit = (left[i] + carry) + right[i];
- leftElement = unchecked((uint)digit);
+ left[i] = unchecked((uint)digit);
carry = digit >> 32;
}
@@ -819,35 +815,34 @@ public readonly struct UInt128
*(pLeft + 2) = (uint)(quotient._upper >> 00);
*(pLeft + 3) = (uint)(quotient._upper >> 32);
- Span left = new Span(pLeft, (Size / sizeof(uint)) - (LeadingZeroCountAsInt32(quotient) / 32));
+ var leftLength = (Size / sizeof(uint)) - (LeadingZeroCountAsInt32(quotient) / 32);
// Repeat the same operation with the divisor
uint* pRight = (stackalloc uint[Size / sizeof(uint)]).Buffer;
- Unsafe.WriteUnaligned(ref *(byte*)(pRight + 0), (uint)(divisor._lower >> 00));
- Unsafe.WriteUnaligned(ref *(byte*)(pRight + 1), (uint)(divisor._lower >> 32));
+ *(pRight + 0) = (uint)(divisor._lower >> 00);
+ *(pRight + 1) = (uint)(divisor._lower >> 32);
+ *(pRight + 2) = (uint)(divisor._upper >> 00);
+ *(pRight + 3) = (uint)(divisor._upper >> 32);
- Unsafe.WriteUnaligned(ref *(byte*)(pRight + 2), (uint)(divisor._upper >> 00));
- Unsafe.WriteUnaligned(ref *(byte*)(pRight + 3), (uint)(divisor._upper >> 32));
+ var rightLength = (Size / sizeof(uint)) - (LeadingZeroCountAsInt32(divisor) / 32);
- Span right = new Span(pRight, (Size / sizeof(uint)) - (LeadingZeroCountAsInt32(divisor) / 32));
+ uint* rawBits = (stackalloc uint[Size / sizeof(uint)]).Buffer;
+ var bitsLength = leftLength - rightLength + 1;
- Span rawBits = stackalloc uint[Size / sizeof(uint)];
- rawBits.Clear();
- Span bits = rawBits.Slice(0, left.Length - right.Length + 1);
-
- Debug.Assert(left.Length >= 1);
- Debug.Assert(right.Length >= 1);
- Debug.Assert(left.Length >= right.Length);
+ // TODO:
+ // Debug.Assert(left.Length >= 1);
+ // Debug.Assert(right.Length >= 1);
+ // Debug.Assert(left.Length >= right.Length);
// Executes the "grammar-school" algorithm for computing q = a / b.
// Before calculating q_i, we get more bits into the highest bit
// block of the divisor. Thus, guessing digits of the quotient
// will be more precise. Additionally we'll get r = a % b.
- uint divHi = right[^1];
- uint divLo = right.Length > 1 ? right[^2] : 0;
+ uint divHi = pRight[rightLength - 1];
+ uint divLo = rightLength > 1 ? pRight[rightLength - 2] : 0;
// We measure the leading zeros of the divisor
int shift = BitOperations.LeadingZeroCount(divHi);
@@ -856,29 +851,29 @@ public readonly struct UInt128
// And, we make sure the most significant bit is set
if (shift > 0)
{
- uint divNx = right.Length > 2 ? right[^3] : 0;
-
- divHi = (divHi << shift) | (divLo >> backShift);
- divLo = (divLo << shift) | (divNx >> backShift);
+ uint divNx = rightLength > 2 ? pRight[rightLength - 3] : 0;
+
+ divHi = (divHi << (uint)shift) | (divLo >> (uint)backShift);
+ divLo = (divLo << (uint)shift) | (divNx >> (uint)backShift);
}
// Then, we divide all of the bits as we would do it using
// pen and paper: guessing the next digit, subtracting, ...
- for (int i = left.Length; i >= right.Length; i--)
+ for (int i = leftLength; i >= rightLength; i--)
{
- int n = i - right.Length;
- uint t = ((uint)(i) < (uint)(left.Length)) ? left[i] : 0;
+ int n = i - rightLength;
+ uint t = ((uint)(i) < (uint)(leftLength)) ? pLeft[i] : 0;
- ulong valHi = ((ulong)(t) << 32) | left[i - 1];
- uint valLo = (i > 1) ? left[i - 2] : 0;
+ ulong valHi = ((ulong)(t) << 32) | pLeft[i - 1];
+ uint valLo = (i > 1) ? pLeft[i - 2] : 0;
// We shifted the divisor, we shift the dividend too
if (shift > 0)
{
- uint valNx = i > 2 ? left[i - 3] : 0;
+ uint valNx = i > 2 ? pLeft[i - 3] : 0;
- valHi = (valHi << shift) | (valLo >> backShift);
- valLo = (valLo << shift) | (valNx >> backShift);
+ valHi = (valHi << (uint)shift) | (valLo >> (uint)backShift);
+ valLo = (valLo << (uint)shift) | (valNx >> (uint)backShift);
}
// First guess for the current digit of the quotient,
@@ -899,29 +894,31 @@ public readonly struct UInt128
if (digit > 0)
{
// Now it's time to subtract our current quotient
- uint carry = SubtractDivisor(left.Slice(n), right, digit);
+ uint carry = SubtractDivisor(pLeft + n, leftLength - n, pRight, rightLength, digit);
if (carry != t)
{
- Debug.Assert(carry == (t + 1));
+ // TODO:
+ // Debug.Assert(carry == (t + 1));
// Our guess was still exactly one too high
- carry = AddDivisor(left.Slice(n), right);
+ carry = AddDivisor(pLeft + n, leftLength - n, pRight, rightLength);
--digit;
- Debug.Assert(carry == 1);
+ // TODO:
+ // Debug.Assert(carry == 1);
}
}
// We have the digit!
- if ((uint)(n) < (uint)(bits.Length))
+ if ((uint)(n) < (uint)(bitsLength))
{
- bits[n] = (uint)(digit);
+ rawBits[n] = (uint)(digit);
}
- if ((uint)(i) < (uint)(left.Length))
+ if ((uint)(i) < (uint)(leftLength))
{
- left[i] = 0;
+ pLeft[i] = 0;
}
}
@@ -931,30 +928,29 @@ public readonly struct UInt128
);
}
- static uint SubtractDivisor(Span left, ReadOnlySpan right, ulong q)
+ static uint SubtractDivisor(uint* left, int leftLength, uint* right, int rightLength, ulong q)
{
- Debug.Assert(left.Length >= right.Length);
- Debug.Assert(q <= 0xFFFFFFFF);
+ // TODO:
+ //Debug.Assert(left.Length >= rightLength);
+ //Debug.Assert(q <= 0xFFFFFFFF);
// Combines a subtract and a multiply operation, which is naturally
// more efficient than multiplying and then subtracting...
ulong carry = 0UL;
- for (int i = 0; i < right.Length; i++)
+ for (int i = 0; i < rightLength; i++)
{
carry += right[i] * q;
uint digit = (uint)(carry);
- carry >>= 32;
-
- ref uint leftElement = ref left[i];
+ carry = carry >> 32;
- if (leftElement < digit)
+ if (left[i] < digit)
{
++carry;
}
- leftElement -= digit;
+ left[i] = left[i] - digit;
}
return (uint)(carry);
From fee918c2774c8349d2dba80ab933af3208fa8a24 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Wed, 6 May 2026 08:42:42 +0300
Subject: [PATCH 13/26] Some additional implementations for TimeSpan
---
HapetLsp/Colorizers/HapetColorizer.cs | 3 +-
std/System/Double.hpt | 2 +
std/System/Math.hpt | 68 ++++++++++
std/System/SR.hpt | 8 ++
std/System/Single.hpt | 2 +
std/System/Threading/Timeout.hpt | 2 +
std/System/TimeSpan.hpt | 182 ++------------------------
7 files changed, 96 insertions(+), 171 deletions(-)
diff --git a/HapetLsp/Colorizers/HapetColorizer.cs b/HapetLsp/Colorizers/HapetColorizer.cs
index 67ce6648..e0934358 100644
--- a/HapetLsp/Colorizers/HapetColorizer.cs
+++ b/HapetLsp/Colorizers/HapetColorizer.cs
@@ -754,7 +754,8 @@ private void ColorizeSATExpr(AstSATOfExpr expr)
// colorize 'sat' word
AddSemanticToken(expr, expr.Location.Beginning, _tokenTypes[2], _tokenModifiers[0]);
- ColorizeExpr(expr.TargetType);
+ if (expr.TargetType != null)
+ ColorizeExpr(expr.TargetType);
}
private void ColorizeLambdaExpr(AstLambdaExpr expr)
diff --git a/std/System/Double.hpt b/std/System/Double.hpt
index 3eb8c40c..747b7039 100644
--- a/std/System/Double.hpt
+++ b/std/System/Double.hpt
@@ -20,6 +20,8 @@ public struct Double : IComparable, IComparable, IFormattable
public const double PositiveInfinity = (double)(1.0) / (double)(0.0);
public const double NaN = (double)0.0 / (double)0.0;
+ internal const ulong SignMask = 0x8000_0000_0000_0000;
+
public int CompareTo(object value)
{
if (value == null)
diff --git a/std/System/Math.hpt b/std/System/Math.hpt
index 494611a6..3f3856f5 100644
--- a/std/System/Math.hpt
+++ b/std/System/Math.hpt
@@ -6,6 +6,14 @@ using System.Runtime.InteropServices;
public static class Math
{
+ public static inline double Abs(double value)
+ {
+ const ulong mask = 0x7FFFFFFFFFFFFFFF;
+ ulong raw = BitConverter.DoubleToUInt64Bits(value);
+
+ return BitConverter.UInt64BitsToDouble(raw & mask);
+ }
+
public static inline int Min(int val1, int val2)
{
return (val1 <= val2) ? val1 : val2;
@@ -17,6 +25,23 @@ public static class Math
return d;
}
+ public static inline double CopySign(double x, double y)
+ {
+ return SoftwareFallback(x, y);
+
+ static double SoftwareFallback(double x, double y)
+ {
+ // This method is required to work for all inputs,
+ // including NaN, so we operate on the raw bits.
+ ulong xbits = BitConverter.DoubleToUInt64Bits(x);
+ ulong ybits = BitConverter.DoubleToUInt64Bits(y);
+
+ // Remove the sign from x, and remove everything but the sign from y
+ // Then, simply OR them to get the correct sign
+ return BitConverter.UInt64BitsToDouble((xbits & ~double.SignMask) | (ybits & double.SignMask));
+ }
+ }
+
/// Produces the full product of two unsigned 32-bit numbers.
/// The first number to multiply.
/// The second number to multiply.
@@ -117,4 +142,47 @@ public static class Math
long high = BigMul(a, b, out low);
return new Int128((ulong)high, (ulong)low);
}
+
+ public static double Round(double a)
+ {
+ // ************************************************************************************
+ // IMPORTANT: Do not change this implementation without also updating MathF.Round(float),
+ // FloatingPointUtils::round(double), and FloatingPointUtils::round(float)
+ // ************************************************************************************
+
+ // This code is based on `nearbyint` from amd/aocl-libm-ose
+ // Copyright (C) 2008-2022 Advanced Micro Devices, Inc. All rights reserved.
+ //
+ // Licensed under the BSD 3-Clause "New" or "Revised" License
+ // See THIRD-PARTY-NOTICES.TXT for the full license text
+
+ // This represents the boundary at which point we can only represent whole integers
+ const double IntegerBoundary = 4503599627370496.0; // 2^52
+
+ if (Abs(a) >= IntegerBoundary)
+ {
+ // Values above this boundary don't have a fractional
+ // portion and so we can simply return them as-is.
+ return a;
+ }
+
+ // Otherwise, since floating-point takes the inputs, performs
+ // the computation as if to infinite precision and unbounded
+ // range, and then rounds to the nearest representable result
+ // using the current rounding mode, we can rely on this to
+ // cheaply round.
+ //
+ // In particular, .NET doesn't support changing the rounding
+ // mode and defaults to "round to nearest, ties to even", thus
+ // by adding the original value to the IntegerBoundary we get
+ // an exactly represented whole integer that is precisely the
+ // IntegerBoundary greater in magnitude than the answer we want.
+ //
+ // We can then simply remove that offset to get the correct answer,
+ // noting that we also need to copy back the original sign to
+ // correctly handle -0.0
+
+ double temp = CopySign(IntegerBoundary, a);
+ return CopySign((a + temp) - temp, a);
+ }
}
\ No newline at end of file
diff --git a/std/System/SR.hpt b/std/System/SR.hpt
index 8390d157..316a2ca1 100644
--- a/std/System/SR.hpt
+++ b/std/System/SR.hpt
@@ -39,13 +39,21 @@ internal static class SR
internal const string Arg_MustBeUInt128 = "Object must be of type UInt128.";
internal const string Arg_MustBePtrDiff = "Object must be of type PtrDiff.";
internal const string Arg_MustBeUIntPtr = "Object must be of type UIntPtr.";
+ internal const string Arg_MustBeTimeSpan = "Object must be of type TimeSpan.";
internal const string Arg_BogusIComparer = "Unable to sort because the IComparer.Compare() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results.";
internal const string Arg_InvalidConsoleColor = "The ConsoleColor enum value was not defined on that enum. Please use a defined color from the enum.";
internal const string Arg_OverflowException = "Arithmetic operation resulted in an overflow.";
internal const string Arg_DivideByZero = "Attempted to divide by zero.";
+ internal const string Argument_InvalidTimeSpanStyles = "An undefined TimeSpanStyles value is being used.";
internal const string Arg_DllNotFoundException = "Dll was not found.";
+ internal const string Arg_CannotBeNaN = "TimeSpan does not accept floating point Not-a-Number values.";
+
+ // overflow
+ internal const string Overflow_TimeSpanTooLong = "TimeSpan overflowed because the duration is too long.";
+ internal const string Overflow_Duration = "The duration cannot be returned for TimeSpan.MinValue because the absolute value of TimeSpan.MinValue exceeds the value of TimeSpan.MaxValue.";
+ internal const string Overflow_NegateTwosCompNum = "Negating the minimum value of a twos complement number is invalid.";
// not
internal const string NotSupported_ReadOnlyCollection = "Collection is read-only.";
diff --git a/std/System/Single.hpt b/std/System/Single.hpt
index da9a2b9f..c5bff4f8 100644
--- a/std/System/Single.hpt
+++ b/std/System/Single.hpt
@@ -18,6 +18,8 @@ public struct Single : IComparable, IComparable, IFormattable
public const float PositiveInfinity = (float)(1.0) / (float)(0.0);
public const float NaN = (float)0.0 / (float)0.0;
+ internal const uint SignMask = 0x8000_0000;
+
public int CompareTo(object value)
{
if (value == null)
diff --git a/std/System/Threading/Timeout.hpt b/std/System/Threading/Timeout.hpt
index ed7820db..7d26ef2e 100644
--- a/std/System/Threading/Timeout.hpt
+++ b/std/System/Threading/Timeout.hpt
@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// Some modifications were made for hapet-lang compatibility.
+using System;
+
// A constant used by methods that take a timeout (Object.Wait, Thread.Sleep
// etc) to indicate that no timeout should occur.
//
diff --git a/std/System/TimeSpan.hpt b/std/System/TimeSpan.hpt
index 3095247d..76d2c29a 100644
--- a/std/System/TimeSpan.hpt
+++ b/std/System/TimeSpan.hpt
@@ -290,7 +290,7 @@ public readonly struct TimeSpan
if ((totalMicroseconds > MaxMicroseconds) || (totalMicroseconds < MinMicroseconds))
{
- ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
+ throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong);
}
_ticks = totalMicroseconds * TicksPerMicrosecond;
}
@@ -407,7 +407,7 @@ public readonly struct TimeSpan
{
if (_ticks == MinTicks)
{
- ThrowHelper.ThrowOverflowException_TimeSpanDuration();
+ throw new OverflowException(SR.Overflow_Duration);
}
return new TimeSpan(_ticks >= 0 ? _ticks : -_ticks);
}
@@ -433,7 +433,7 @@ public readonly struct TimeSpan
if (units > maxUnits || units < minUnits)
{
- ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
+ throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong);
}
return TimeSpan.FromTicks(units * ticksPerUnit);
}
@@ -608,7 +608,7 @@ public readonly struct TimeSpan
{
if ((microseconds > MaxMicroseconds) || (microseconds < MinMicroseconds))
{
- ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
+ throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong);
}
long ticks = (long)microseconds * TicksPerMicrosecond;
return TimeSpan.FromTicks(ticks);
@@ -631,7 +631,7 @@ public readonly struct TimeSpan
{
if (double.IsNaN(value))
{
- ThrowHelper.ThrowArgumentException_Arg_CannotBeNaN();
+ throw new ArgumentException(SR.Arg_CannotBeNaN);
}
return IntervalFromDoubleTicks(value * scale);
}
@@ -640,7 +640,7 @@ public readonly struct TimeSpan
{
if ((ticks > MaxTicks) || (ticks < MinTicks) || double.IsNaN(ticks))
{
- ThrowHelper.ThrowOverflowException_TimeSpanTooLong();
+ throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong);
}
if (ticks == MaxTicks)
{
@@ -698,174 +698,16 @@ public readonly struct TimeSpan
if ((totalSeconds > MaxSeconds) || (totalSeconds < MinSeconds))
{
- ThrowHelper.ThrowArgumentOutOfRange_TimeSpanTooLong();
+ throw new ArgumentOutOfRangeException(null, SR.Overflow_TimeSpanTooLong);
}
return totalSeconds * TicksPerSecond;
}
- // See System.Globalization.TimeSpanParse and System.Globalization.TimeSpanFormat
- //#region ParseAndFormat
- private static void ValidateStyles(TimeSpanStyles style)
- {
- if (style is not TimeSpanStyles.None && style is not TimeSpanStyles.AssumeNegative)
- {
- ThrowHelper.ThrowArgumentException_InvalidTimeSpanStyles();
- }
- }
- public static TimeSpan Parse(string s)
- {
- /* Constructs a TimeSpan from a string. Leading and trailing white space characters are allowed. */
- ArgumentNullException.ThrowIfNull(s);
- return TimeSpanParse.Parse(s, null);
- }
- public static TimeSpan Parse(string input, IFormatProvider formatProvider)
- {
- if (input == null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
- }
- return TimeSpanParse.Parse(input, formatProvider);
- }
- public static TimeSpan Parse(ReadOnlySpan input, IFormatProvider formatProvider = null) => TimeSpanParse.Parse(input, formatProvider);
- public static TimeSpan ParseExact(string input, string format, IFormatProvider formatProvider)
- {
- ArgumentNullException.ThrowIfNull(nameof(input));
- ArgumentNullException.ThrowIfNull(nameof(format));
-
-
- return TimeSpanParse.ParseExact(input, format, formatProvider, TimeSpanStyles.None);
- }
- public static TimeSpan ParseExact(string input, string[] formats, IFormatProvider formatProvider)
- {
- if (input == null)
- {
- ThrowHelper.ThrowArgumentNullException(ExceptionArgument.input);
- }
- return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None);
- }
- public static TimeSpan ParseExact(string input, string format, IFormatProvider formatProvider, TimeSpanStyles styles)
- {
- ValidateStyles(styles);
- ArgumentNullException.ThrowIfNull(nameof(input));
- ArgumentNullException.ThrowIfNull(nameof(format));
- return TimeSpanParse.ParseExact(input, format, formatProvider, styles);
- }
-
- public static TimeSpan ParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None)
- {
- ValidateStyles(styles);
- return TimeSpanParse.ParseExact(input, format, formatProvider, styles);
- }
- public static TimeSpan ParseExact(string input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles)
- {
- ValidateStyles(styles);
- ArgumentNullException.ThrowIfNull(input, ExceptionArgument.input);
- return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles);
- }
- public static TimeSpan ParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles = TimeSpanStyles.None)
- {
- ValidateStyles(styles);
- return TimeSpanParse.ParseExactMultiple(input, formats, formatProvider, styles);
- }
- public static bool TryParse(string s, out TimeSpan result)
- {
- if (s == null)
- {
- result = default;
- return false;
- }
- return TimeSpanParse.TryParse(s, null, out result);
- }
- public static bool TryParse(ReadOnlySpan s, out TimeSpan result) => TimeSpanParse.TryParse(s, null, out result);
-
- public static bool TryParse(string input, IFormatProvider formatProvider, out TimeSpan result)
- {
- if (input == null)
- {
- result = default;
- return false;
- }
- return TimeSpanParse.TryParse(input, formatProvider, out result);
- }
- public static bool TryParse(ReadOnlySpan input, IFormatProvider formatProvider, out TimeSpan result) => TimeSpanParse.TryParse(input, formatProvider, out result);
- public static bool TryParseExact(string input, string format, IFormatProvider formatProvider, out TimeSpan result)
- {
- if (input == null || format == null)
- {
- result = default;
- return false;
- }
- return TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result);
- }
-
- public static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, out TimeSpan result)
- => TimeSpanParse.TryParseExact(input, format, formatProvider, TimeSpanStyles.None, out result);
-
- public static bool TryParseExact(string input, string[] formats, IFormatProvider formatProvider, out TimeSpan result)
- {
- if (input == null)
- {
- result = default;
- return false;
- }
- return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result);
- }
- public static bool TryParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, out TimeSpan result)
- => TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, TimeSpanStyles.None, out result);
-
- public static bool TryParseExact(string input, string format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
- {
- ValidateStyles(styles);
-
- if (input == null || format == null)
- {
- result = default;
- return false;
- }
- return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result);
- }
-
- public static bool TryParseExact(ReadOnlySpan input, ReadOnlySpan format, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
- {
- ValidateStyles(styles);
- return TimeSpanParse.TryParseExact(input, format, formatProvider, styles, out result);
- }
- public static bool TryParseExact(string input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
- {
- ValidateStyles(styles);
-
- if (input == null)
- {
- result = default;
- return false;
- }
- return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result);
- }
-
- public static bool TryParseExact(ReadOnlySpan input, string[] formats, IFormatProvider formatProvider, TimeSpanStyles styles, out TimeSpan result)
- {
- ValidateStyles(styles);
- return TimeSpanParse.TryParseExactMultiple(input, formats, formatProvider, styles, out result);
- }
-
- public override string ToString() => TimeSpanFormat.FormatC(this);
- public string ToString(string format) => TimeSpanFormat.Format(this, format, null);
- public string ToString(string format, IFormatProvider formatProvider) => TimeSpanFormat.Format(this, format, formatProvider);
-
- public bool TryFormat(Span destination, out int charsWritten, ReadOnlySpan format = default, IFormatProvider formatProvider = null)
- => TimeSpanFormat.TryFormat(this, destination, out charsWritten, format, formatProvider);
-
- ///
- public bool TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format = default, IFormatProvider formatProvider = null)
- => TimeSpanFormat.TryFormat(this, utf8Destination, out bytesWritten, format, formatProvider);
-
- //#endregion
-
public static TimeSpan operator -(TimeSpan t)
{
if (t._ticks == MinTicks)
{
- ThrowHelper.ThrowOverflowException_NegateTwosCompNum();
+ throw new OverflowException(SR.Overflow_NegateTwosCompNum);
}
return new TimeSpan(-t._ticks);
}
@@ -879,7 +721,7 @@ public readonly struct TimeSpan
{
// Overflow if signs of operands was different and result's sign was opposite.
// >> 63 gives the sign bit (either 64 1's or 64 0's).
- ThrowHelper.ThrowOverflowException_TimeSpanTooLong();
+ throw new OverflowException(SR.Overflow_TimeSpanTooLong);
}
return new TimeSpan(result);
}
@@ -895,7 +737,7 @@ public readonly struct TimeSpan
{
// Overflow if signs of operands was identical and result's sign was opposite.
// >> 63 gives the sign bit (either 64 1's or 64 0's).
- ThrowHelper.ThrowOverflowException_TimeSpanTooLong();
+ throw new OverflowException(SR.Overflow_TimeSpanTooLong);
}
return new TimeSpan(result);
}
@@ -905,7 +747,7 @@ public readonly struct TimeSpan
{
if (double.IsNaN(factor))
{
- ThrowHelper.ThrowArgumentException_Arg_CannotBeNaN(ExceptionArgument.factor);
+ throw new ArgumentException(SR.Arg_CannotBeNaN, nameof(factor));
}
// Rounding to the nearest tick is as close to the result we would have with unlimited
@@ -922,7 +764,7 @@ public readonly struct TimeSpan
{
if (double.IsNaN(divisor))
{
- ThrowHelper.ThrowArgumentException_Arg_CannotBeNaN(ExceptionArgument.divisor);
+ throw new ArgumentException(SR.Arg_CannotBeNaN, nameof(divisor));
}
double ticks = Math.Round(timeSpan.Ticks / divisor);
From cf5233166101d0d9f9fdaa72d7fcb163cf75e4d6 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Wed, 6 May 2026 14:31:07 +0300
Subject: [PATCH 14/26] ThreadState and ThreadStateException added
---
std/System/SR.hpt | 4 +++
std/System/Threading/Thread.hpt | 29 ++++++++++++++++++-
std/System/Threading/ThreadState.hpt | 20 +++++++++++++
std/System/Threading/ThreadStateException.hpt | 14 +++++++++
4 files changed, 66 insertions(+), 1 deletion(-)
create mode 100644 std/System/Threading/ThreadState.hpt
create mode 100644 std/System/Threading/ThreadStateException.hpt
diff --git a/std/System/SR.hpt b/std/System/SR.hpt
index 316a2ca1..21c6ae04 100644
--- a/std/System/SR.hpt
+++ b/std/System/SR.hpt
@@ -67,4 +67,8 @@ internal static class SR
// io
internal const string Arg_IOException = "I/O error occurred.";
internal const string IO_NoConsole = "There is no console.";
+
+ // threading
+ internal const string Arg_ThreadStateException = "Thread was in an invalid state for the operation being executed.";
+ internal const string ThreadState_NotStarted = "Thread has not been started.";
}
\ No newline at end of file
diff --git a/std/System/Threading/Thread.hpt b/std/System/Threading/Thread.hpt
index 70f66192..f47d1779 100644
--- a/std/System/Threading/Thread.hpt
+++ b/std/System/Threading/Thread.hpt
@@ -6,6 +6,33 @@ using System;
public partial class Thread
{
+ private uintptr _threadHandle;
+ private bool _isDead;
+
+ ///
+ /// Return the thread state as a consistent set of bits. This is more
+ /// general then IsAlive or IsBackground.
+ ///
+ public ThreadState ThreadState
+ {
+ get
+ {
+ if (_isDead)
+ {
+ return System.Threading.ThreadState.Stopped;
+ }
+
+ var state = (System.Threading.ThreadState.ThreadState)GetThreadState();
+ // GC.KeepAlive(this); TODO: do i need it?
+ return state;
+ }
+ }
+
+ private int GetThreadState()
+ {
+ return 0; // TODO:
+ }
+
private StartHelper _startHelper;
// State associated with starting new thread
@@ -75,7 +102,7 @@ public partial class Thread
public bool Join(int millisecondsTimeout)
{
ArgumentOutOfRangeException.ThrowIfLessThan(millisecondsTimeout, Timeout.Infinite);
- if ((ThreadState & ThreadState.Unstarted) != 0)
+ if ((int)(ThreadState & ThreadState.Unstarted) != 0)
{
throw new ThreadStateException(SR.ThreadState_NotStarted);
}
diff --git a/std/System/Threading/ThreadState.hpt b/std/System/Threading/ThreadState.hpt
new file mode 100644
index 00000000..38883b88
--- /dev/null
+++ b/std/System/Threading/ThreadState.hpt
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+public enum ThreadState
+{
+ /*=========================================================================
+ ** Constants for thread states.
+ =========================================================================*/
+ Running = 0,
+ StopRequested = 1,
+ SuspendRequested = 2,
+ Background = 4,
+ Unstarted = 8,
+ Stopped = 16,
+ WaitSleepJoin = 32,
+ Suspended = 64,
+ AbortRequested = 128,
+ Aborted = 256
+}
diff --git a/std/System/Threading/ThreadStateException.hpt b/std/System/Threading/ThreadStateException.hpt
new file mode 100644
index 00000000..ac07e32b
--- /dev/null
+++ b/std/System/Threading/ThreadStateException.hpt
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+using System;
+
+///
+/// The exception that is thrown when a is in an invalid for the method call.
+///
+public class ThreadStateException : SystemException
+{
+ public ThreadStateException() : base(SR.Arg_ThreadStateException) { }
+ public ThreadStateException(string message) : base(message ?? SR.Arg_ThreadStateException) { }
+}
\ No newline at end of file
From 07cce2e9f43d60b04c73ec05f10756db6f46bd99 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Thu, 7 May 2026 09:43:11 +0300
Subject: [PATCH 15/26] Remove cringe thread state access
---
std/System/Threading/Thread.hpt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/std/System/Threading/Thread.hpt b/std/System/Threading/Thread.hpt
index f47d1779..94e36ec3 100644
--- a/std/System/Threading/Thread.hpt
+++ b/std/System/Threading/Thread.hpt
@@ -22,7 +22,7 @@ public partial class Thread
return System.Threading.ThreadState.Stopped;
}
- var state = (System.Threading.ThreadState.ThreadState)GetThreadState();
+ var state = (System.Threading.ThreadState)GetThreadState();
// GC.KeepAlive(this); TODO: do i need it?
return state;
}
From 1a1aa28a322e5a378fecb008b862f03e43380b43 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Thu, 7 May 2026 09:55:25 +0300
Subject: [PATCH 16/26] Unroll colorizer change
---
HapetLsp/Colorizers/HapetColorizer.cs | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/HapetLsp/Colorizers/HapetColorizer.cs b/HapetLsp/Colorizers/HapetColorizer.cs
index d613a0fb..c3b03d97 100644
--- a/HapetLsp/Colorizers/HapetColorizer.cs
+++ b/HapetLsp/Colorizers/HapetColorizer.cs
@@ -754,8 +754,7 @@ private void ColorizeSATExpr(AstSATOfExpr expr)
// colorize 'sat' word
AddSemanticToken(expr, expr.Location.Beginning, _tokenTypes[2], _tokenModifiers[0]);
- if (expr.TargetType != null)
- ColorizeExpr(expr.TargetType);
+ ColorizeExpr(expr.TargetType);
}
private void ColorizeLambdaExpr(AstLambdaExpr expr)
From 87a7e0a929b71fce53725f310e32e0453203392f Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Fri, 8 May 2026 09:29:55 +0300
Subject: [PATCH 17/26] ThreadPal for windows added and small fix in ConsolePal
---
std/System/ConsolePal.Windows.hpt | 1 -
std/System/Threading/ThreadPal.Windows.hpt | 14 ++++++++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
create mode 100644 std/System/Threading/ThreadPal.Windows.hpt
diff --git a/std/System/ConsolePal.Windows.hpt b/std/System/ConsolePal.Windows.hpt
index 9a79e79d..b1bba26c 100644
--- a/std/System/ConsolePal.Windows.hpt
+++ b/std/System/ConsolePal.Windows.hpt
@@ -7,7 +7,6 @@ using System.Text;
using System.Runtime.InteropServices;
#if TARGET_PLATFORM == "win-x64" || TARGET_PLATFORM == "win-x86";
-[SuppressStaticCtorCall]
internal static class ConsolePal
{
[LibImport("", "fputs")]
diff --git a/std/System/Threading/ThreadPal.Windows.hpt b/std/System/Threading/ThreadPal.Windows.hpt
new file mode 100644
index 00000000..325f249e
--- /dev/null
+++ b/std/System/Threading/ThreadPal.Windows.hpt
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+using System.IO;
+using System.Text;
+using System.Runtime.InteropServices;
+
+#if TARGET_PLATFORM == "win-x64" || TARGET_PLATFORM == "win-x86";
+internal static class ThreadPal
+{
+
+}
+#endif
\ No newline at end of file
From c33943ecff88598b13e278e8aba0f84e7c87d482 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Fri, 8 May 2026 10:12:51 +0300
Subject: [PATCH 18/26] ThreadPriority added and props for name and pool
---
std/System/Threading/Thread.hpt | 10 +++++++++-
std/System/Threading/ThreadPal.Windows.hpt | 3 +++
std/System/Threading/ThreadPriority.hpt | 15 +++++++++++++++
3 files changed, 27 insertions(+), 1 deletion(-)
create mode 100644 std/System/Threading/ThreadPriority.hpt
diff --git a/std/System/Threading/Thread.hpt b/std/System/Threading/Thread.hpt
index 94e36ec3..56a365d7 100644
--- a/std/System/Threading/Thread.hpt
+++ b/std/System/Threading/Thread.hpt
@@ -6,8 +6,16 @@ using System;
public partial class Thread
{
+ /// Gets or sets a value indicating the scheduling priority of a thread.
+ public ThreadPriority Priority { get; set; } = ThreadPriority.Normal;
+ /// Returns true if the thread is a threadpool thread.
+ public bool IsThreadPoolThread { get; set; } = false;
+ /// Get or sets the name of the thread.
+ public string Name { get; set; }
+
private uintptr _threadHandle;
private bool _isDead;
+ private bool _isThreadPool;
///
/// Return the thread state as a consistent set of bits. This is more
@@ -33,7 +41,7 @@ public partial class Thread
return 0; // TODO:
}
- private StartHelper _startHelper;
+ internal StartHelper _startHelper;
// State associated with starting new thread
private sealed class StartHelper
diff --git a/std/System/Threading/ThreadPal.Windows.hpt b/std/System/Threading/ThreadPal.Windows.hpt
index 325f249e..5b204827 100644
--- a/std/System/Threading/ThreadPal.Windows.hpt
+++ b/std/System/Threading/ThreadPal.Windows.hpt
@@ -9,6 +9,9 @@ using System.Runtime.InteropServices;
#if TARGET_PLATFORM == "win-x64" || TARGET_PLATFORM == "win-x86";
internal static class ThreadPal
{
+ public static void StartThread(Thread thread)
+ {
+ }
}
#endif
\ No newline at end of file
diff --git a/std/System/Threading/ThreadPriority.hpt b/std/System/Threading/ThreadPriority.hpt
new file mode 100644
index 00000000..c40c93e3
--- /dev/null
+++ b/std/System/Threading/ThreadPriority.hpt
@@ -0,0 +1,15 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+public enum ThreadPriority
+{
+ /*=========================================================================
+ ** Constants for thread priorities.
+ =========================================================================*/
+ Lowest = 0,
+ BelowNormal = 1,
+ Normal = 2,
+ AboveNormal = 3,
+ Highest = 4
+}
\ No newline at end of file
From 9c478a8967b11be6c2f885230f6dd11900ec154b Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Fri, 8 May 2026 10:32:06 +0300
Subject: [PATCH 19/26] Check that thread is already running in start
---
std/System/SR.hpt | 1 +
std/System/Threading/Thread.hpt | 6 ++++++
std/System/Threading/ThreadPal.Windows.hpt | 2 +-
3 files changed, 8 insertions(+), 1 deletion(-)
diff --git a/std/System/SR.hpt b/std/System/SR.hpt
index 21c6ae04..55baea6a 100644
--- a/std/System/SR.hpt
+++ b/std/System/SR.hpt
@@ -71,4 +71,5 @@ internal static class SR
// threading
internal const string Arg_ThreadStateException = "Thread was in an invalid state for the operation being executed.";
internal const string ThreadState_NotStarted = "Thread has not been started.";
+ internal const string ThreadState_AlreadyStarted = "Thread is running or terminated; it cannot restart.";
}
\ No newline at end of file
diff --git a/std/System/Threading/Thread.hpt b/std/System/Threading/Thread.hpt
index 56a365d7..21926b50 100644
--- a/std/System/Threading/Thread.hpt
+++ b/std/System/Threading/Thread.hpt
@@ -104,6 +104,12 @@ public partial class Thread
startHelper._startArg = null;
}
+ // if already running - throw exception
+ if (ThreadState == System.Threading.ThreadState.Running)
+ {
+ throw new ThreadStateException(SR.ThreadState_AlreadyStarted);
+ }
+
StartCore();
}
diff --git a/std/System/Threading/ThreadPal.Windows.hpt b/std/System/Threading/ThreadPal.Windows.hpt
index 5b204827..59c12c5d 100644
--- a/std/System/Threading/ThreadPal.Windows.hpt
+++ b/std/System/Threading/ThreadPal.Windows.hpt
@@ -9,7 +9,7 @@ using System.Runtime.InteropServices;
#if TARGET_PLATFORM == "win-x64" || TARGET_PLATFORM == "win-x86";
internal static class ThreadPal
{
- public static void StartThread(Thread thread)
+ internal static void StartThread(Thread thread)
{
}
From 3ef5bb0d9a60922b787ae66d1d159bb283234aad Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Fri, 8 May 2026 15:32:13 +0300
Subject: [PATCH 20/26] Many things in windows threading added
---
.../Runtime/InteropServices/Marshal.hpt | 6 +++
.../InteropServices/MarshalPal.Windows.hpt | 46 +++++++++++++++++++
std/System/SR.hpt | 1 +
std/System/Threading/Thread.hpt | 40 +++++++---------
std/System/Threading/ThreadPal.Windows.hpt | 45 +++++++++++++++++-
std/System/Threading/ThreadStartException.hpt | 14 ++++++
6 files changed, 126 insertions(+), 26 deletions(-)
create mode 100644 std/System/Runtime/InteropServices/MarshalPal.Windows.hpt
create mode 100644 std/System/Threading/ThreadStartException.hpt
diff --git a/std/System/Runtime/InteropServices/Marshal.hpt b/std/System/Runtime/InteropServices/Marshal.hpt
index 7b580eeb..14ee348e 100644
--- a/std/System/Runtime/InteropServices/Marshal.hpt
+++ b/std/System/Runtime/InteropServices/Marshal.hpt
@@ -43,4 +43,10 @@ public static class Marshal
// TODO: is it crossplatform?
Fflush(AcrtIobFunc(1));
}
+
+ [SuppressStackTrace]
+ public static inline string GetLastStringError()
+ {
+ return MarshalPal.GetLastStringError();
+ }
}
\ No newline at end of file
diff --git a/std/System/Runtime/InteropServices/MarshalPal.Windows.hpt b/std/System/Runtime/InteropServices/MarshalPal.Windows.hpt
new file mode 100644
index 00000000..412366e8
--- /dev/null
+++ b/std/System/Runtime/InteropServices/MarshalPal.Windows.hpt
@@ -0,0 +1,46 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+using System;
+using System.Text;
+using System.Runtime.InteropServices;
+
+#if TARGET_PLATFORM == "win-x64" || TARGET_PLATFORM == "win-x86";
+[SuppressStaticCtorCall]
+internal static class MarshalPal
+{
+ [DeclarationUsed]
+ [LibImport("", "GetLastError")]
+ internal static extern inline uint GetLastError();
+ [DeclarationUsed]
+ [LibImport("", "FormatMessageA")]
+ internal static extern inline uint FormatMessageA(uint flags, void* source, uint messageId, uint langId, void* buffer, uint size, void* args);
+ [DeclarationUsed]
+ [LibImport("", "LocalFree")]
+ internal static extern inline void* LocalFree(void*);
+
+ internal static string GetLastStringError()
+ {
+ // https://stackoverflow.com/questions/1387064/how-to-get-the-error-message-from-the-error-code-returned-by-getlasterror
+ // get the error message ID, if any.
+ var error = GetLastError();
+ if (error == 0)
+ return "";
+
+ byte* messageBuffer = null;
+ // FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS
+ uint flags = 0x00000100 | 0x00001000 | 0x00000200;
+ uint langId = (uint)(((short)(0x00) << 10) | ((short)(0x01)));
+ // Ask Win32 to give us the string version of that message ID.
+ // The parameters we pass in, tell Win32 to create the buffer that holds the message for us (because we don't yet know how long the message string will be).
+ uint size = FormatMessageA(flags, null, error, langId, &messageBuffer, 0, null);
+ if (messageBuffer == null)
+ return "";
+ string toReturn = UTF8Encoding.GetString(messageBuffer);
+ // Free the Win32's string's buffer.
+ LocalFree(messageBuffer);
+ return toReturn;
+ }
+}
+#endif
\ No newline at end of file
diff --git a/std/System/SR.hpt b/std/System/SR.hpt
index 55baea6a..8a59d26a 100644
--- a/std/System/SR.hpt
+++ b/std/System/SR.hpt
@@ -72,4 +72,5 @@ internal static class SR
internal const string Arg_ThreadStateException = "Thread was in an invalid state for the operation being executed.";
internal const string ThreadState_NotStarted = "Thread has not been started.";
internal const string ThreadState_AlreadyStarted = "Thread is running or terminated; it cannot restart.";
+ internal const string Arg_ThreadStartException = "Thread failed to start.";
}
\ No newline at end of file
diff --git a/std/System/Threading/Thread.hpt b/std/System/Threading/Thread.hpt
index 21926b50..6e259276 100644
--- a/std/System/Threading/Thread.hpt
+++ b/std/System/Threading/Thread.hpt
@@ -21,38 +21,22 @@ public partial class Thread
/// Return the thread state as a consistent set of bits. This is more
/// general then IsAlive or IsBackground.
///
- public ThreadState ThreadState
- {
- get
- {
- if (_isDead)
- {
- return System.Threading.ThreadState.Stopped;
- }
-
- var state = (System.Threading.ThreadState)GetThreadState();
- // GC.KeepAlive(this); TODO: do i need it?
- return state;
- }
- }
-
- private int GetThreadState()
- {
- return 0; // TODO:
- }
+ public ThreadState ThreadState { get; set; }
internal StartHelper _startHelper;
// State associated with starting new thread
private sealed class StartHelper
{
+ internal Thread _thread;
internal int _maxStackSize;
internal object _start;
internal object _startArg;
- internal StartHelper(object start)
+ internal StartHelper(object start, Thread thread)
{
_start = start;
+ _thread = thread;
}
// avoid long-lived stack frame in many threads
@@ -64,6 +48,9 @@ public partial class Thread
// avoid long-lived stack frame in many threads
private inline void RunWorker()
{
+ // set that the thread is currently running
+ _thread.ThreadState = System.Threading.ThreadState.Running;
+
object start = _start;
_start = default; // anime?
@@ -80,7 +67,12 @@ public partial class Thread
}
catch (Exception ex)
{
- // the handler returned "true" means the exception is now "handled" and we should gracefully exit.
+
+ }
+ finally
+ {
+ // set that the thread is stopped
+ _thread.ThreadState = System.Threading.ThreadState.Stopped;
}
}
}
@@ -89,7 +81,7 @@ public partial class Thread
{
ArgumentNullException.ThrowIfNull(start);
- _startHelper = new StartHelper(start);
+ _startHelper = new StartHelper(start, this);
}
private void Start()
@@ -110,13 +102,13 @@ public partial class Thread
throw new ThreadStateException(SR.ThreadState_AlreadyStarted);
}
- StartCore();
+ ThreadPal.StartThread(this);
}
public bool Join(int millisecondsTimeout)
{
ArgumentOutOfRangeException.ThrowIfLessThan(millisecondsTimeout, Timeout.Infinite);
- if ((int)(ThreadState & ThreadState.Unstarted) != 0)
+ if ((int)(ThreadState & System.Threading.ThreadState.Unstarted) != 0)
{
throw new ThreadStateException(SR.ThreadState_NotStarted);
}
diff --git a/std/System/Threading/ThreadPal.Windows.hpt b/std/System/Threading/ThreadPal.Windows.hpt
index 59c12c5d..f23ec8f3 100644
--- a/std/System/Threading/ThreadPal.Windows.hpt
+++ b/std/System/Threading/ThreadPal.Windows.hpt
@@ -2,16 +2,57 @@
// The .NET Foundation licenses this file to you under the MIT license.
// Some modifications were made for hapet-lang compatibility.
-using System.IO;
-using System.Text;
+using System;
using System.Runtime.InteropServices;
#if TARGET_PLATFORM == "win-x64" || TARGET_PLATFORM == "win-x86";
internal static class ThreadPal
{
+ [LibImport("", "CreateThread")]
+ private static extern inline void* CreateThread(void* attrs, uintptr stackSize, void* func, void* funcParam, uint creationFlags, uint* threadId);
+ [LibImport("", "ResumeThread")]
+ private static extern inline int ResumeThread(void* handle);
+ [LibImport("", "SetThreadPriority")]
+ private static extern inline int SetThreadPriority(void* handle, int priority);
+ // fuck win < 10
+ [LibImport("", "SetThreadDescription")]
+ private static extern inline int SetThreadDescription(void* handle, ushort* desc);
+
internal static void StartThread(Thread thread)
{
+ uint threadId = 0;
+ // get delegate to extract func and object ptrs
+ Delegate del = thread._startHelper.Run;
+ // CREATE_SUSPENDED and STACK_SIZE_PARAM_IS_A_RESERVATION
+ uint creationFlags = 0x00000004 | 0x00010000;
+ // create a thread in suspended state and start it manually
+ uintptr handle = CreateThread(null, thread._startHelper._maxStackSize, del._funcPtr, del._objectPtr, creationFlags, &threadId);
+ if (handle == null) throw new ThreadStartException(Marshal.GetLastStringError());
+
+ // set handle
+ thread._threadHandle = handle;
+ // set name
+ SetThreadDescription(handle, (ushort*)thread.Name.Buffer); // do we need to check result?
+ // set priority
+ SetThreadPriority(handle, ConvertPriority(thread.Priority)); // do we need to check result?
+ // resume thread
+ int resumeResult = ResumeThread(handle);
+ if (resumeResult == -1) throw new ThreadStartException(Marshal.GetLastStringError());
+ }
+
+ private static int ConvertPriority(ThreadPriority priority)
+ {
+ // https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-setthreadpriority#parameters
+ switch (priority)
+ {
+ case (ThreadPriority.Lowest) return -2;
+ case (ThreadPriority.BelowNormal) return -1;
+ case (ThreadPriority.Normal) return 0;
+ case (ThreadPriority.AboveNormal) return 1;
+ case (ThreadPriority.Highest) return 2;
+ }
+ return 0;
}
}
#endif
\ No newline at end of file
diff --git a/std/System/Threading/ThreadStartException.hpt b/std/System/Threading/ThreadStartException.hpt
new file mode 100644
index 00000000..30dd0934
--- /dev/null
+++ b/std/System/Threading/ThreadStartException.hpt
@@ -0,0 +1,14 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+using System;
+
+///
+/// The exception that is thrown when a is could not be started.
+///
+public class ThreadStartException : SystemException
+{
+ public ThreadStartException() : base(SR.Arg_ThreadStartException) { }
+ public ThreadStartException(string message) : base(message ?? SR.Arg_ThreadStartException) { }
+}
\ No newline at end of file
From 989df7385c44871ab5dc6967a726a86fe1a0c803 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Tue, 12 May 2026 10:58:44 +0300
Subject: [PATCH 21/26] Current thread prop impl
---
std/System/Threading/Thread.hpt | 17 +++++
std/System/Threading/ThreadPal.Windows.hpt | 82 ++++++++++++++++++++--
std/System/Threading/ThreadStatic.hpt | 20 ++++++
3 files changed, 115 insertions(+), 4 deletions(-)
create mode 100644 std/System/Threading/ThreadStatic.hpt
diff --git a/std/System/Threading/Thread.hpt b/std/System/Threading/Thread.hpt
index 6e259276..69c8da7b 100644
--- a/std/System/Threading/Thread.hpt
+++ b/std/System/Threading/Thread.hpt
@@ -6,6 +6,15 @@ using System;
public partial class Thread
{
+ private static ThreadStatic t_currentThread = new ThreadStatic();
+ public static Thread CurrentThread
+ {
+ get
+ {
+ return t_currentThread.Value ?? InitializeCurrentThread();
+ }
+ }
+
/// Gets or sets a value indicating the scheduling priority of a thread.
public ThreadPriority Priority { get; set; } = ThreadPriority.Normal;
/// Returns true if the thread is a threadpool thread.
@@ -114,4 +123,12 @@ public partial class Thread
}
return JoinInternal(millisecondsTimeout);
}
+
+ private static Thread InitializeCurrentThread()
+ {
+ Thread thread = null;
+ GetCurrentThread(ref thread);
+ t_currentThread.Value = thread;
+ return thread;
+ }
}
\ No newline at end of file
diff --git a/std/System/Threading/ThreadPal.Windows.hpt b/std/System/Threading/ThreadPal.Windows.hpt
index f23ec8f3..136f2a31 100644
--- a/std/System/Threading/ThreadPal.Windows.hpt
+++ b/std/System/Threading/ThreadPal.Windows.hpt
@@ -9,14 +9,23 @@ using System.Runtime.InteropServices;
internal static class ThreadPal
{
[LibImport("", "CreateThread")]
- private static extern inline void* CreateThread(void* attrs, uintptr stackSize, void* func, void* funcParam, uint creationFlags, uint* threadId);
+ internal static extern inline void* CreateThread(void* attrs, uintptr stackSize, void* func, void* funcParam, uint creationFlags, uint* threadId);
[LibImport("", "ResumeThread")]
- private static extern inline int ResumeThread(void* handle);
+ internal static extern inline int ResumeThread(void* handle);
[LibImport("", "SetThreadPriority")]
- private static extern inline int SetThreadPriority(void* handle, int priority);
+ internal static extern inline int SetThreadPriority(void* handle, int priority);
// fuck win < 10
[LibImport("", "SetThreadDescription")]
- private static extern inline int SetThreadDescription(void* handle, ushort* desc);
+ internal static extern inline int SetThreadDescription(void* handle, ushort* desc);
+ [LibImport("", "GetCurrentThreadId")]
+ internal static extern inline int GetCurrentThreadId();
+ [LibImport("", "WaitForSingleObject")]
+ internal static extern inline uint WaitForSingleObject(void* handle, uint millis);
+
+ // cringe winapi consts
+ internal const uint WAIT_FAILED = (0xFFFFFFFF);
+ internal const uint STATUS_WAIT_0 = ((uint)0x00000000L);
+ internal const uint WAIT_OBJECT_0 = ((STATUS_WAIT_0) + 0);
internal static void StartThread(Thread thread)
{
@@ -54,5 +63,70 @@ internal static class ThreadPal
}
return 0;
}
+
+ internal static bool JoinThread(Thread thread, int millisecondsTimeout)
+ {
+ // This method assumes the thread has been started
+ // Debug.Assert((thread.ThreadState & ThreadState.Unstarted) == 0 || (millisecondsTimeout == 0)); // TODO:
+
+ if (thread._threadHandle == null)
+ {
+ // If the thread has died, the handle can be marked as invalid.
+ // Waiting on an invalid handle will never complete, so complete now.
+ return true;
+ }
+
+ if (millisecondsTimeout == 0)
+ {
+ int result = (int)WaitForSingleObject(thread._threadHandle, 0);
+ return result == (int)WAIT_OBJECT_0;
+ }
+ else
+ {
+ Thread currentThread = CurrentThread;
+ currentThread.SetWaitSleepJoinState();
+ uint result;
+ if (millisecondsTimeout == Timeout.Infinite)
+ {
+ // Infinite wait
+ while (true)
+ {
+ result = Interop.Kernel32.WaitForSingleObjectEx(waitHandle.DangerousGetHandle(), Timeout.UnsignedInfinite, Interop.BOOL.TRUE);
+ if (result != Interop.Kernel32.WAIT_IO_COMPLETION)
+ {
+ break;
+ }
+ // Check if this was our interrupt APC
+ CheckForPendingInterrupt();
+ }
+ }
+ else
+ {
+ long startTime = Environment.TickCount64;
+ while (true)
+ {
+ result = Interop.Kernel32.WaitForSingleObjectEx(waitHandle.DangerousGetHandle(), (uint)millisecondsTimeout, Interop.BOOL.TRUE);
+ if (result != Interop.Kernel32.WAIT_IO_COMPLETION)
+ {
+ break;
+ }
+ // Check if this was our interrupt APC
+ CheckForPendingInterrupt();
+ // Handle APC completion by adjusting timeout and retrying
+ long currentTime = Environment.TickCount64;
+ long elapsed = currentTime - startTime;
+ if (elapsed >= millisecondsTimeout)
+ {
+ result = Interop.Kernel32.WAIT_TIMEOUT;
+ break;
+ }
+ millisecondsTimeout -= (int)elapsed;
+ startTime = currentTime;
+ }
+ }
+ currentThread.ClearWaitSleepJoinState();
+ return result == (int)Interop.Kernel32.WAIT_OBJECT_0;
+ }
+ }
}
#endif
\ No newline at end of file
diff --git a/std/System/Threading/ThreadStatic.hpt b/std/System/Threading/ThreadStatic.hpt
new file mode 100644
index 00000000..165c8f64
--- /dev/null
+++ b/std/System/Threading/ThreadStatic.hpt
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// Some modifications were made for hapet-lang compatibility.
+
+public class ThreadStatic
+{
+ private T _defaultValue;
+ public ThreadStatic(T defaultValue = default)
+ {
+ _defaultValue = defaultValue;
+ }
+
+ public T Value
+ {
+ get
+ {}
+ set
+ {}
+ }
+}
\ No newline at end of file
From 3442cd15668f22e44a460583c9522e7acae24004 Mon Sep 17 00:00:00 2001
From: Airat Abdrakov <52558686+CrackAndDie@users.noreply.github.com>
Date: Tue, 12 May 2026 14:40:03 +0300
Subject: [PATCH 22/26] Dictionary impl begin
---
HapetFrontend/Helpers/GenericsHelper.cs | 4 +-
std/System/Collections/Generic/Dictionary.hpt | 1301 +++++++++++++++++
.../Collections/Generic/IDictionary.hpt | 45 +
.../Generic/IDictionaryEnumerator.hpt | 55 +
.../Collections/Generic/KeyValuePair.hpt | 49 +
std/System/Comparer.hpt | 5 +
6 files changed, 1458 insertions(+), 1 deletion(-)
create mode 100644 std/System/Collections/Generic/Dictionary.hpt
create mode 100644 std/System/Collections/Generic/IDictionary.hpt
create mode 100644 std/System/Collections/Generic/IDictionaryEnumerator.hpt
create mode 100644 std/System/Collections/Generic/KeyValuePair.hpt
diff --git a/HapetFrontend/Helpers/GenericsHelper.cs b/HapetFrontend/Helpers/GenericsHelper.cs
index 13eb0756..02e3d913 100644
--- a/HapetFrontend/Helpers/GenericsHelper.cs
+++ b/HapetFrontend/Helpers/GenericsHelper.cs
@@ -302,7 +302,9 @@ public static bool HasAnyGenericTypes(AstExpression expr)
return HasAnyGenericTypes(nullE);
else if (expr is AstIdExpr || expr is AstEmptyExpr || expr is AstNullableExpr)
return false; // just return false because OutType is checked above
- Debug.Assert(false, "Unexcepted expr type to check");
+
+ // do not error, just skip this shite. probably errored somewhere above
+ // Debug.Assert(false, "Unexcepted expr type to check");
return false;
}
diff --git a/std/System/Collections/Generic/Dictionary.hpt b/std/System/Collections/Generic/Dictionary.hpt
new file mode 100644
index 00000000..6ad2107e
--- /dev/null
+++ b/std/System/Collections/Generic/Dictionary.hpt
@@ -0,0 +1,1301 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Collections;
+
+public class Dictionary : IDictionary
+{
+ // constants for serialization
+ private const string VersionName = "Version"; // Do not rename (binary serialization)
+ private const string HashSizeName = "HashSize"; // Do not rename (binary serialization). Must save buckets.Length
+ private const string KeyValuePairsName = "KeyValuePairs"; // Do not rename (binary serialization)
+ private const string ComparerName = "Comparer"; // Do not rename (binary serialization)
+
+ private int[] _buckets;
+ private Entry[] _entries;
+#if TARGET_64BIT;
+ private ulong _fastModMultiplier;
+#endif
+ private int _count;
+ private int _freeList;
+ private int _freeCount;
+ private int _version;
+ private IComparer _comparer;
+ private List _keys;
+ private List _values;
+ private const int StartOfFreeList = -3;
+
+ public Dictionary() : this(0, null) { }
+
+ public Dictionary(int capacity) : this(capacity, null) { }
+
+ public Dictionary(IComparer comparer) : this(0, comparer) { }
+
+ public Dictionary(int capacity, IComparer comparer)
+ {
+ if (capacity < 0)
+ {
+ ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity);
+ }
+
+ if (capacity > 0)
+ {
+ Initialize(capacity);
+ }
+
+ // For reference types, we always want to store a comparer instance, either
+ // the one provided, or if one wasn't provided, the default (accessing
+ // EqualityComparer.Default with shared generics on every dictionary
+ // access can add measurable overhead). For value types, if no comparer is
+ // provided, or if the default is provided, we'd prefer to use
+ // EqualityComparer.Default.Equals on every use, enabling the JIT to
+ // devirtualize and possibly inline the operation.
+ if (!typeof(TKey).IsValueType)
+ {
+ _comparer = comparer ?? System.Comparer.Default();
+
+ // Special-case EqualityComparer.Default, StringComparer.Ordinal, and StringComparer.OrdinalIgnoreCase.
+ // We use a non-randomized comparer for improved perf, falling back to a randomized comparer if the
+ // hash buckets become unbalanced.
+ if (typeof(TKey) == typeof(string) &&
+ NonRandomizedStringEqualityComparer.GetStringComparer(_comparer) is IComparer stringComparer)
+ {
+ _comparer = (IComparer)stringComparer;
+ }
+ }
+ else if (comparer != null && // first check for null to avoid forcing default comparer instantiation unnecessarily
+ comparer != System.Comparer.Default())
+ {
+ _comparer = comparer;
+ }
+ }
+
+ public Dictionary(IDictionary dictionary) : this(dictionary, null) { }
+
+ public Dictionary(IDictionary dictionary, IComparer comparer) :
+ this(dictionary?.Count ?? 0, comparer)
+ {
+ if (dictionary == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.dictionary);
+ }
+
+ AddRange(dictionary);
+ }
+
+ public Dictionary(IEnumerable> collection) : this(collection, null) { }
+
+ public Dictionary(IEnumerable> collection, IComparer comparer) :
+ this((collection as ICollection>)?.Count ?? 0, comparer)
+ {
+ if (collection == null)
+ {
+ ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
+ }
+
+ AddRange(collection);
+ }
+
+ private void AddRange(IEnumerable> enumerable)
+ {
+ // Fallback path for all other enumerables
+ for (KeyValuePair pair in enumerable)
+ {
+ Add(pair.Key, pair.Value);
+ }
+ }
+
+ public IComparer Comparer
+ {
+ get
+ {
+ if (typeof(TKey) == typeof(string))
+ {
+ // TODO:
+ // Debug.Assert(_comparer is not null, "The comparer should never be null for a reference type.");
+ return (IComparer)IInternalStringEqualityComparer.GetUnderlyingEqualityComparer((IEqualityComparer)_comparer);
+ }
+ else
+ {
+ return _comparer ?? System.Comparer.Default();
+ }
+ }
+ }
+
+ public int Count => _count - _freeCount;
+
+ ///
+ /// Gets the total numbers of elements the internal data structure can hold without resizing.
+ ///
+ public int Capacity => (int)(_entries?.Length ?? 0);
+
+ public List Keys
+ {
+ get
+ {
+ _keys ??= new List();
+ return _keys;
+ }
+ }
+
+ ICollection IDictionary.Keys => Keys;
+
+ public List Values
+ {
+ get
+ {
+ _values ??= new List