From 51e359f980bba66e738cacf16092f6aa3c5c762d Mon Sep 17 00:00:00 2001 From: Danilo Borges Santos Date: Sat, 14 Dec 2024 16:57:20 -0300 Subject: [PATCH] Implementa TimeSpan --- includes/csharp/exception.hpp | 30 ++++ includes/csharp/sr.hpp | 12 ++ includes/csharp/time.hpp | 320 ++++++++++++++++++++++++++++++++++ sources/csharp/CMakeLists.txt | 2 +- 4 files changed, 363 insertions(+), 1 deletion(-) create mode 100644 includes/csharp/time.hpp diff --git a/includes/csharp/exception.hpp b/includes/csharp/exception.hpp index 71282eb..feecef2 100644 --- a/includes/csharp/exception.hpp +++ b/includes/csharp/exception.hpp @@ -318,6 +318,36 @@ namespace csharp { HRresult = HResults::HR_COR_E_UNAUTHORIZEDACCESS; } }; + + //The exception that is thrown for errors in an arithmetic, casting, or conversion operation. + class ArithmeticException : public SystemException { + public: + ArithmeticException(OptionalString const& message = std::nullopt, std::source_location const& source = std::source_location::current()) + : ArithmeticException(message, nullptr, source) + { + } + + ArithmeticException(OptionalString const& message, std::shared_ptr const& innerException, std::source_location const& source = std::source_location::current()) + : SystemException(message.value_or(SR::Arg_ArithmeticException), innerException, source) + { + HRresult = HResults::HR_COR_E_ARITHMETIC; + } + }; + + //The exception that is thrown for errors in an arithmetic, casting, or conversion operation. + class OverflowException : public ArithmeticException { + public: + OverflowException(OptionalString const& message = std::nullopt, std::source_location const& source = std::source_location::current()) + : OverflowException(message, nullptr, source) + { + } + + OverflowException(OptionalString const& message, std::shared_ptr const& innerException, std::source_location const& source = std::source_location::current()) + : ArithmeticException(message.value_or(SR::Arg_OverflowException), innerException, source) + { + HRresult = HResults::HR_COR_E_OVERFLOW; + } + }; } #endif \ No newline at end of file diff --git a/includes/csharp/sr.hpp b/includes/csharp/sr.hpp index 4c0b57f..87ac8fb 100644 --- a/includes/csharp/sr.hpp +++ b/includes/csharp/sr.hpp @@ -59,6 +59,18 @@ namespace csharp { = "BinaryReader encountered an invalid string length."; inline static const std::string Argument_StreamNotWritable = "Stream was not writable."; + inline static const std::string Overflow_TimeSpanTooLong + = "TimeSpan overflowed because the duration is too long."; + inline static const std::string Arg_ArithmeticException + = "Overflow or underflow in the arithmetic operation."; + inline static const std::string Arg_OverflowException + = "Arithmetic operation resulted in an overflow."; + inline static const std::string Arg_CannotBeNaN + = "TimeSpan does not accept floating point Not-a-Number values."; + inline static const std::string Overflow_NegateTwosCompNum + = "Negating the minimum value of a twos complement number is invalid."; + inline static const std::string Overflow_Duration + = "The duration cannot be returned for TimeSpan.MinValue because the absolute value of TimeSpan.MinValue exceeds the value of TimeSpan.MaxValue."; }; }; diff --git a/includes/csharp/time.hpp b/includes/csharp/time.hpp new file mode 100644 index 0000000..69c59f2 --- /dev/null +++ b/includes/csharp/time.hpp @@ -0,0 +1,320 @@ +#ifndef CSHARP_TIME_HPP +#define CSHARP_TIME_HPP + +#include +#include +#include +#include "exception.hpp" +#include "sr.hpp" + +namespace csharp { + struct TimeSpan { + constexpr TimeSpan() = default; + constexpr TimeSpan(int64_t ticks) : _ticks(ticks) {} + + constexpr TimeSpan(int32_t hours, int32_t minutes, int32_t seconds) + { + _ticks = TimeToTicks(hours, minutes, seconds); + } + + constexpr TimeSpan(int32_t days, int32_t hours, int32_t minutes, int32_t seconds) + : TimeSpan(days, hours, minutes, seconds, 0) + { + } + + constexpr TimeSpan(int32_t days, int32_t hours, int32_t minutes, int32_t seconds, int32_t milliseconds) + : TimeSpan(days, hours, minutes, seconds, milliseconds, 0) + { + } + + constexpr TimeSpan(int32_t days, int32_t hours, int32_t minutes, int32_t seconds, int32_t milliseconds, int32_t microseconds) + { + const auto totalMicroseconds = (days * MicrosecondsPerDay) + + (hours * MicrosecondsPerHour) + + (minutes * MicrosecondsPerMinute) + + (seconds * MicrosecondsPerSecond) + + (milliseconds * MicrosecondsPerMillisecond) + + microseconds; + + if ((totalMicroseconds > MaxMicroseconds) || (totalMicroseconds < MinMicroseconds)) + { + throw ArgumentOutOfRangeException(SR::Overflow_TimeSpanTooLong); + } + _ticks = totalMicroseconds * TicksPerMicrosecond; + } + + + static constexpr int64_t NanosecondsPerTick = 100; + static constexpr int64_t TicksPerMicrosecond = 10; + static constexpr int64_t TicksPerMillisecond = TicksPerMicrosecond * 1000; + static constexpr int64_t TicksPerSecond = TicksPerMillisecond * 1000; + static constexpr int64_t TicksPerMinute = TicksPerSecond * 60; + static constexpr int64_t TicksPerHour = TicksPerMinute * 60; + static constexpr int64_t TicksPerDay = TicksPerHour * 24; + static constexpr int64_t MicrosecondsPerMillisecond = TicksPerMillisecond / TicksPerMicrosecond; + static constexpr int64_t MicrosecondsPerSecond = TicksPerSecond / TicksPerMicrosecond; + static constexpr int64_t MicrosecondsPerMinute = TicksPerMinute / TicksPerMicrosecond; + static constexpr int64_t MicrosecondsPerHour = TicksPerHour / TicksPerMicrosecond; + static constexpr int64_t MicrosecondsPerDay = TicksPerDay / TicksPerMicrosecond; + static constexpr int64_t MillisecondsPerSecond = TicksPerSecond / TicksPerMillisecond; + static constexpr int64_t MillisecondsPerMinute = TicksPerMinute / TicksPerMillisecond; + static constexpr int64_t MillisecondsPerHour = TicksPerHour / TicksPerMillisecond; + static constexpr int64_t MillisecondsPerDay = TicksPerDay / TicksPerMillisecond; + static constexpr int64_t SecondsPerMinute = TicksPerMinute / TicksPerSecond; + static constexpr int64_t SecondsPerHour = TicksPerHour / TicksPerSecond; + static constexpr int64_t SecondsPerDay = TicksPerDay / TicksPerSecond; + static constexpr int64_t MinutesPerHour = TicksPerHour / TicksPerMinute; + static constexpr int64_t MinutesPerDay = TicksPerDay / TicksPerMinute; + static constexpr int32_t HoursPerDay = static_cast(TicksPerDay / TicksPerHour); + static constexpr int64_t MinTicks = (std::numeric_limits::min)(); + static constexpr int64_t MaxTicks = (std::numeric_limits::max)(); + static constexpr int64_t MinMicroseconds = MinTicks / TicksPerMicrosecond; + static constexpr int64_t MaxMicroseconds = MaxTicks / TicksPerMicrosecond; + static constexpr int64_t MinMilliseconds = MinTicks / TicksPerMillisecond; + static constexpr int64_t MaxMilliseconds = MaxTicks / TicksPerMillisecond; + static constexpr int64_t MinSeconds = MinTicks / TicksPerSecond; + static constexpr int64_t MaxSeconds = MaxTicks / TicksPerSecond; + static constexpr int64_t MinMinutes = MinTicks / TicksPerMinute; + static constexpr int64_t MaxMinutes = MaxTicks / TicksPerMinute; + static constexpr int64_t MinHours = MinTicks / TicksPerHour; + static constexpr int64_t MaxHours = MaxTicks / TicksPerHour; + static constexpr int64_t MinDays = MinTicks / TicksPerDay; + static constexpr int64_t MaxDays = MaxTicks / TicksPerDay; + static constexpr int64_t TicksPerTenthSecond = TicksPerMillisecond * 100; + + static constexpr TimeSpan Zero() { return TimeSpan(0); } + static constexpr TimeSpan MaxValue() { return TimeSpan(MaxTicks); } + static constexpr TimeSpan MinValue() { return TimeSpan(MinTicks); } + + constexpr int64_t Ticks() const { return _ticks; } + constexpr int32_t Days() const { return static_cast(_ticks / TicksPerDay); } + constexpr int32_t Hours() const { return static_cast(_ticks / TicksPerHour % HoursPerDay); } + constexpr int32_t Milliseconds() const { return static_cast(_ticks / TicksPerMillisecond % MillisecondsPerSecond); } + constexpr int32_t Microseconds() const { return static_cast(_ticks / TicksPerMicrosecond % MicrosecondsPerMillisecond); } + constexpr int32_t Nanoseconds() const { return static_cast(_ticks % TicksPerMicrosecond * NanosecondsPerTick); } + constexpr int32_t Minutes() const { return static_cast(_ticks / TicksPerMinute % MinutesPerHour); } + constexpr int32_t Seconds() const { return static_cast(_ticks / TicksPerSecond % SecondsPerMinute); } + constexpr double TotalDays() const { return static_cast(_ticks) / TicksPerDay; } + constexpr double TotalHours() const { return static_cast(_ticks) / TicksPerHour; } + + constexpr double TotalMilliseconds() const + { + const double temp = static_cast(_ticks) / TicksPerMillisecond; + + if (temp > MaxMilliseconds) + { + return MaxMilliseconds; + } + + if (temp < MinMilliseconds) + { + return MinMilliseconds; + } + return temp; + } + + constexpr double TotalMicroseconds() const { return static_cast(_ticks) / TicksPerMicrosecond; } + constexpr double TotalNanoseconds() const { return static_cast(_ticks) * NanosecondsPerTick; } + constexpr double TotalMinutes() const { return static_cast(_ticks) / TicksPerMinute; } + constexpr double TotalSeconds() const { return static_cast(_ticks) / TicksPerSecond; } + + constexpr TimeSpan Add(TimeSpan const& ts) const { return *this + ts; } + + static constexpr TimeSpan FromDays(double value) { + return Interval(value, TicksPerDay); + } + + constexpr TimeSpan Duration() const { + if (_ticks == MinTicks) + { + throw OverflowException(SR::Overflow_Duration); + } + + return TimeSpan(_ticks >= 0 ? _ticks : -_ticks); + } + + constexpr static int64_t TimeToTicks(int32_t hour, int32_t minute, int32_t second) { + const auto totalSeconds = (hour * SecondsPerHour) + + (minute * SecondsPerMinute) + + second; + + if ((totalSeconds > MaxSeconds) || (totalSeconds < MinSeconds)) + { + throw ArgumentOutOfRangeException(SR::Overflow_TimeSpanTooLong); + } + return totalSeconds * TicksPerSecond; + } + + static constexpr TimeSpan FromDays(int32_t days) { + return FromUnits(days, TicksPerDay, MinDays, MaxDays); + } + + static constexpr TimeSpan FromHours(int32_t hours) { + return FromUnits(hours, TicksPerHour, MinHours, MaxHours); + } + + static constexpr TimeSpan FromMinutes(int32_t minutes) { + return FromUnits(minutes, TicksPerMinute, MinMinutes, MaxMinutes); + } + + static constexpr TimeSpan FromSeconds(int32_t seconds) { + return FromUnits(seconds, TicksPerSecond, MinSeconds, MaxSeconds); + } + + static constexpr TimeSpan FromMicroseconds(int32_t microseconds) { + return FromUnits(microseconds, TicksPerMicrosecond, MinMicroseconds, MaxMicroseconds); + } + + static constexpr TimeSpan FromHours(double value) { + return Interval(value, TicksPerHour); + } + + static constexpr TimeSpan FromMilliseconds(double value) { + return Interval(value, TicksPerMillisecond); + } + + static constexpr TimeSpan FromMicroseconds(double value) { + return Interval(value, TicksPerMicrosecond); + } + + static constexpr TimeSpan FromMinutes(double value) { + return Interval(value, TicksPerMinute); + } + + constexpr TimeSpan Negate() const { + return -(*this); + } + + static constexpr TimeSpan FromSeconds(double value) { + return Interval(value, TicksPerSecond); + } + + constexpr TimeSpan Subtract(TimeSpan const& ts) const { + return *this - ts; + } + + TimeSpan Multiply(double factor) const { + return *this * factor; + } + + TimeSpan Divide(double divisor) const { + return *this / divisor; + } + + double Divide(TimeSpan const& ts) const { + return *this / ts; + } + + static constexpr TimeSpan FromTicks(int64_t value) { + return TimeSpan(value); + } + + constexpr TimeSpan operator-() const { + if (_ticks == MinTicks) + { + throw OverflowException(SR::Overflow_NegateTwosCompNum); + } + + return TimeSpan(_ticks); + } + + friend constexpr TimeSpan operator-(TimeSpan const& t1, TimeSpan const t2) { + const auto result = t1._ticks - t2._ticks; + const auto t1Sign = t1._ticks >> 63; + + if ((t1Sign != (t2._ticks >> 63)) && (t1Sign != (result >> 63))) + { + throw OverflowException(SR::Overflow_TimeSpanTooLong); + } + + return TimeSpan(result); + } + + constexpr TimeSpan operator+() const { + return *this; + } + + friend constexpr TimeSpan operator+(TimeSpan const& t1, TimeSpan const t2) { + long result = t1._ticks + t2._ticks; + long t1Sign = t1._ticks >> 63; + + if ((t1Sign == (t2._ticks >> 63)) && (t1Sign != (result >> 63))) + { + throw OverflowException(SR::Overflow_TimeSpanTooLong); + } + return TimeSpan(result); + } + + friend TimeSpan operator*(TimeSpan const& timeSpan, double factor) { + const auto ticks = std::round(timeSpan.Ticks() * factor); + return IntervalFromDoubleTicks(ticks); + } + + friend TimeSpan operator*(double factor, TimeSpan const& timeSpan) { + return timeSpan * factor; + } + + friend TimeSpan operator/(TimeSpan const& timeSpan, double divisor) { + const auto ticks = std::round(timeSpan.Ticks() / divisor); + return IntervalFromDoubleTicks(ticks); + } + + friend double operator/(TimeSpan const& t1, TimeSpan const t2) { + return t1.Ticks() / static_cast(t2.Ticks()); + } + + constexpr bool operator==(TimeSpan const& other) const { + return _ticks == other._ticks; + } + + constexpr bool operator<(TimeSpan const& other) const { + return _ticks < other._ticks; + } + + constexpr bool operator<=(TimeSpan const& other) const { + return _ticks <= other._ticks; + } + + constexpr bool operator>(TimeSpan const& other) const { + return _ticks > other._ticks; + } + + constexpr bool operator>=(TimeSpan const& other) const { + return _ticks >= other._ticks; + } + + private: + + static constexpr TimeSpan FromUnits(int64_t units, int64_t ticksPerUnit, int64_t minUnits, int64_t maxUnits) { + if (units > maxUnits || units < minUnits) + { + throw ArgumentOutOfRangeException(SR::Overflow_TimeSpanTooLong); + } + + return TimeSpan::FromTicks(units * ticksPerUnit); + } + + static constexpr TimeSpan Interval(double value, double scale) { + return IntervalFromDoubleTicks(value * scale); + } + + static constexpr TimeSpan IntervalFromDoubleTicks(double ticks) { + if ((ticks > MaxTicks) || (ticks < MinTicks)) + { + throw OverflowException(SR::Overflow_TimeSpanTooLong); + } + + if (ticks == MaxTicks) + { + return MaxValue(); + } + + return TimeSpan(static_cast(ticks)); + } + + private: + int64_t _ticks{ 0 }; + }; +} + +#endif \ No newline at end of file diff --git a/sources/csharp/CMakeLists.txt b/sources/csharp/CMakeLists.txt index 3dc38f9..a1f6924 100644 --- a/sources/csharp/CMakeLists.txt +++ b/sources/csharp/CMakeLists.txt @@ -5,7 +5,7 @@ # Add source to this project's executable. add_library (CSharp++ STATIC "exception.cpp" - "io/stream.cpp" "io/binary.cpp") + "io/stream.cpp" "io/binary.cpp" ) if (CMAKE_VERSION VERSION_GREATER 3.12) set_property(TARGET CSharp++ PROPERTY CXX_STANDARD 20)