Files
2025-05-18 13:04:45 +08:00

734 lines
14 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "Misc/Timespan.h"
#include <limits>
namespace Electra
{
class FTimeFraction;
/**
* Keeps a time value in hundred nanoseconds (HNS).
*/
class FTimeValue
{
public:
static FTimeValue GetInvalid()
{
static FTimeValue kInvalid;
return kInvalid;
}
static FTimeValue GetZero()
{
static FTimeValue kZero((int64)0);
return kZero;
}
static FTimeValue GetPositiveInfinity()
{
static FTimeValue kPosInf(std::numeric_limits<double>::infinity());
return kPosInf;
}
static FTimeValue GetNegativeInfinity()
{
static FTimeValue kNegInf(-std::numeric_limits<double>::infinity());
return kNegInf;
}
static int64 MillisecondsToHNS(int64 InMilliseconds)
{
return InMilliseconds * 10000;
}
static int64 MicrosecondsToHNS(int64 InMicroseconds)
{
return InMicroseconds * 10;
}
static int64 NinetykHzToHNS(int64 In90kHz)
{
return In90kHz * 1000 / 9;
}
FTimeValue()
: HNS(0)
, SequenceIndex(0)
, bIsValid(false)
, bIsInfinity(false)
{
}
FTimeValue(const FTimeValue& rhs)
: HNS(rhs.HNS)
, SequenceIndex(rhs.SequenceIndex)
, bIsValid(rhs.bIsValid)
, bIsInfinity(rhs.bIsInfinity)
{
}
FTimeValue& operator=(const FTimeValue& rhs)
{
HNS = rhs.HNS;
SequenceIndex = rhs.SequenceIndex;
bIsValid = rhs.bIsValid;
bIsInfinity = rhs.bIsInfinity;
return *this;
}
explicit FTimeValue(int64 InHNS, int64 InSequenceIndex=0) : HNS(InHNS), SequenceIndex(InSequenceIndex), bIsValid(true), bIsInfinity(false)
{
}
explicit FTimeValue(double Seconds, int64 InSequenceIndex=0)
{
SetFromSeconds(Seconds, InSequenceIndex);
}
explicit FTimeValue(int64 Numerator, uint32 Denominator, int64 InSequenceIndex=0)
{
SetFromND(Numerator, Denominator, InSequenceIndex);
}
bool IsValid() const
{
return bIsValid;
}
bool IsZero() const
{
return bIsValid && HNS == 0;
}
bool IsInfinity() const
{
return bIsInfinity;
}
bool IsPositiveInfinity() const
{
return bIsInfinity && HNS >= 0;
}
bool IsNegativeInfinity() const
{
return bIsInfinity && HNS < 0;
}
double GetAsSeconds(double DefaultIfInvalid=0.0) const
{
return bIsValid ? (bIsInfinity ? (HNS >= 0 ? std::numeric_limits<double>::infinity() : -std::numeric_limits<double>::infinity()) : HNS / 10000000.0) : DefaultIfInvalid;
}
int64 GetAsMilliseconds(int64 DefaultIfInvalid=0) const
{
return bIsValid ? (bIsInfinity ? (HNS >= 0 ? 0x7fffffffffffffffLL : -0x7fffffffffffffffLL) : HNS / 10000) : DefaultIfInvalid;
}
int64 GetAsMicroseconds(int64 DefaultIfInvalid=0) const
{
return bIsValid ? (bIsInfinity ? (HNS >= 0 ? 0x7fffffffffffffffLL : -0x7fffffffffffffffLL) : HNS / 10) : DefaultIfInvalid;
}
int64 GetAsHNS(int64 DefaultIfInvalid=0) const
{
return bIsValid ? (bIsInfinity ? (HNS >= 0 ? 0x7fffffffffffffffLL : -0x7fffffffffffffffLL) : HNS) : DefaultIfInvalid;
}
int64 GetAs90kHz(int64 DefaultIfInvalid=0) const
{
return bIsValid ? (bIsInfinity ? (HNS >= 0 ? 0x7fffffffffffffffLL : -0x7fffffffffffffffLL) : HNS * 9 / 1000) : DefaultIfInvalid;
}
//! Returns this time value in a custom timebase. Requires internal bigint conversion and is therefor SLOW!
ELECTRABASE_API int64 GetAsTimebase(uint32 CustomTimebase) const;
FTimespan GetAsTimespan() const
{
if (!bIsValid)
{
return FTimespan::MinValue();
}
if (IsInfinity())
{
return HNS >= 0 ? FTimespan::MaxValue() : FTimespan::MinValue();
}
return FTimespan(HNS);
}
FTimeValue& SetToInvalid()
{
HNS = 0;
SequenceIndex = 0;
bIsValid = false;
bIsInfinity = false;
return *this;
}
FTimeValue& SetToZero(int64 InSequenceIndex=0)
{
HNS = 0;
SequenceIndex = InSequenceIndex;
bIsValid = true;
bIsInfinity = false;
return *this;
}
FTimeValue& SetToPositiveInfinity(int64 InSequenceIndex=0)
{
HNS = 0x7fffffffffffffffLL;
SequenceIndex = InSequenceIndex;
bIsValid = true;
bIsInfinity = true;
return *this;
}
FTimeValue& SetToNegativeInfinity(int64 InSequenceIndex=0)
{
HNS = -0x7fffffffffffffffLL;
SequenceIndex = InSequenceIndex;
bIsValid = true;
bIsInfinity = true;
return *this;
}
FTimeValue& SetFromSeconds(double Seconds, int64 InSequenceIndex=0)
{
if ((bIsInfinity = (Seconds == std::numeric_limits<double>::infinity() || Seconds == -std::numeric_limits<double>::infinity())) == true)
{
HNS = Seconds < 0.0 ? -0x7fffffffffffffffLL : 0x7fffffffffffffffLL;
bIsValid = true;
}
else
{
if ((bIsValid = Seconds >= -922337203685.0 && Seconds <= 922337203685.0) == true)
{
HNS = (int64)(Seconds * 10000000.0);
}
else
{
check(!"Value cannot be represented!");
HNS = 0;
}
}
SequenceIndex = InSequenceIndex;
return *this;
}
FTimeValue& SetFromMilliseconds(int64 Milliseconds, int64 InSequenceIndex=0)
{
bIsInfinity = false;
if ((bIsValid = Milliseconds >= -922337203685477 && Milliseconds <= 922337203685477) == true)
{
HNS = Milliseconds * 10000;
}
else
{
check(!"Value cannot be represented!");
HNS = 0;
}
SequenceIndex = InSequenceIndex;
return *this;
}
FTimeValue& SetFromMicroseconds(int64 Microseconds, int64 InSequenceIndex=0)
{
bIsInfinity = false;
if ((bIsValid = Microseconds >= -922337203685477580 && Microseconds <= 922337203685477580) == true)
{
HNS = Microseconds * 10;
}
else
{
check(!"Value cannot be represented!");
HNS = 0;
}
SequenceIndex = InSequenceIndex;
return *this;
}
FTimeValue& SetFromHNS(int64 InHNS, int64 InSequenceIndex=0)
{
HNS = InHNS;
bIsValid = true;
bIsInfinity = false;
SequenceIndex = InSequenceIndex;
return *this;
}
FTimeValue& SetFrom90kHz(int64 Ticks, int64 InSequenceIndex=0)
{
HNS = Ticks * 1000 / 9;
bIsValid = true;
bIsInfinity = false;
SequenceIndex = InSequenceIndex;
return *this;
}
ELECTRABASE_API FTimeValue& SetFromND(int64 Numerator, uint32 Denominator, int64 InSequenceIndex=0);
ELECTRABASE_API FTimeValue& SetFromTimeFraction(const FTimeFraction& TimeFraction, int64 InSequenceIndex=0);
FTimeValue& SetFromTimespan(const FTimespan& InTimespan, int64 InSequenceIndex=0)
{
SequenceIndex = InSequenceIndex;
bIsValid = true;
if (InTimespan == FTimespan::MaxValue())
{
HNS = 0x7fffffffffffffffLL;
bIsInfinity = true;
}
else
{
HNS = InTimespan.GetTicks();
bIsInfinity = false;
}
return *this;
}
void SetSequenceIndex(int64 InSequenceIndex)
{
SequenceIndex = InSequenceIndex;
}
int64 GetSequenceIndex() const
{
return SequenceIndex;
}
/*
Note: We MUST NOT compare the SequenceIndex in any of the relational operators!
It is considered to be a kind of "user value".
It is also not really possible to perform calculations on time values of different
sequence indices.
What should the result be for eg. (HNS=1234,SequenceIndex=4) + (HNS=987,SequenceIndex=5) ?
*/
bool operator == (const FTimeValue& rhs) const
{
return (!bIsValid && !rhs.bIsValid) || (bIsValid == rhs.bIsValid && bIsInfinity == rhs.bIsInfinity && HNS == rhs.HNS);
}
bool operator != (const FTimeValue& rhs) const
{
return !(*this == rhs);
}
bool operator < (const FTimeValue& rhs) const
{
if (bIsValid && rhs.bIsValid)
{
if (!bIsInfinity)
{
return !rhs.bIsInfinity ? HNS < rhs.HNS : rhs.HNS > 0;
}
else
{
return rhs.bIsInfinity ? HNS < rhs.HNS : HNS < 0;
}
}
return false;
}
bool operator <= (const FTimeValue& rhs) const
{
return (!bIsValid || !rhs.bIsValid) ? false : (*this < rhs || *this == rhs);
}
bool operator > (const FTimeValue &rhs) const
{
return (!bIsValid || !rhs.bIsValid) ? false : !(*this <= rhs);
}
bool operator >= (const FTimeValue &rhs) const
{
return (!bIsValid || !rhs.bIsValid) ? false : !(*this < rhs);
}
inline FTimeValue& operator += (const FTimeValue& rhs)
{
if (bIsValid)
{
if (rhs.bIsValid)
{
if (!bIsInfinity && !rhs.bIsInfinity)
{
if (WillOverflow(HNS, HNS, rhs.HNS))
{
SetToPositiveInfinity();
}
}
else
{
SetToPositiveInfinity();
}
}
else
{
SetToInvalid();
}
}
return *this;
}
inline FTimeValue& operator -= (const FTimeValue& rhs)
{
if (bIsValid)
{
if (rhs.bIsValid)
{
if (!bIsInfinity && !rhs.bIsInfinity)
{
HNS -= rhs.HNS;
}
else
{
SetToPositiveInfinity();
}
}
else
{
SetToInvalid();
}
}
return *this;
}
inline FTimeValue& operator /= (int32 Scale)
{
if (bIsValid && !bIsInfinity)
{
if (Scale)
{
HNS /= Scale;
}
else
{
SetToPositiveInfinity();
}
}
return *this;
}
inline FTimeValue& operator *= (int32 Scale)
{
if (bIsValid && !bIsInfinity)
{
// FIXME: what if this overflows?
HNS *= Scale;
}
return *this;
}
inline FTimeValue operator + (const FTimeValue& rhs) const
{
FTimeValue Result;
if (bIsValid && rhs.bIsValid)
{
if (!bIsInfinity && !rhs.bIsInfinity)
{
Result.bIsValid = true;
if (WillOverflow(Result.HNS, HNS, rhs.HNS))
{
Result.SetToPositiveInfinity();
}
}
else
{
Result.SetToPositiveInfinity();
}
}
return Result;
}
inline FTimeValue operator - (const FTimeValue& rhs) const
{
FTimeValue Result;
if (bIsValid && rhs.bIsValid)
{
if (!bIsInfinity && !rhs.bIsInfinity)
{
Result.HNS = HNS - rhs.HNS;
Result.bIsValid = true;
}
else
{
Result.SetToPositiveInfinity();
}
}
return Result;
}
inline FTimeValue operator << (int32 Shift) const
{
FTimeValue Result(*this);
if (bIsValid)
{
if (!bIsInfinity)
{
Result.HNS <<= Shift;
}
else
{
Result.SetToPositiveInfinity();
}
}
return Result;
}
inline FTimeValue operator >> (int32 Shift) const
{
FTimeValue Result(*this);
if (bIsValid)
{
if (!bIsInfinity)
{
Result.HNS >>= Shift;
}
else
{
Result.SetToPositiveInfinity();
}
}
return Result;
}
inline FTimeValue operator * (int32 Scale) const
{
FTimeValue Result(*this);
if (bIsValid)
{
if (!bIsInfinity)
{
Result.HNS *= Scale;
}
else
{
Result.SetToPositiveInfinity();
}
}
return Result;
}
inline FTimeValue operator / (int32 Scale) const
{
FTimeValue Result(*this);
Result.HNS /= Scale;
return Result;
}
inline FTimeValue operator - () const
{
FTimeValue Result(*this);
Result.HNS = -Result.HNS;
return Result;
}
inline FTimeValue Abs() const
{
FTimeValue Result(*this);
if (Result.HNS < 0)
{
Result.HNS = -Result.HNS;
}
return Result;
}
private:
static inline bool WillOverflow(int64& OutTemp, const int64 InA, const int64 InB)
{
int64 Temp = InA + InB;
OutTemp = Temp;
return (InA >= 0 && InB >= 0 && Temp < 0) || (InA < 0 && InB < 0 && Temp >= 0);
}
int64 HNS;
int64 SequenceIndex;
bool bIsValid;
bool bIsInfinity;
};
struct FTimeRange
{
void Reset()
{
Start.SetToInvalid();
End.SetToInvalid();
}
bool IsValid() const
{
return Start.IsValid() && End.IsValid();
}
bool Contains(const FTimeValue& Value)
{
return Value >= Start && (!End.IsValid() || Value < End);
}
bool Overlaps(const FTimeRange& OtherRange)
{
return End > OtherRange.Start && Start < OtherRange.End;
}
FTimeValue Start;
FTimeValue End;
};
/**
* Keeps a time value as a fractional.
*/
class FTimeFraction
{
public:
static const FTimeFraction& GetInvalid()
{
static FTimeFraction Invalid;
return Invalid;
}
static const FTimeFraction& GetZero()
{
static FTimeFraction Zero(0, 1);
return Zero;
}
static const FTimeFraction& GetPositiveInfinity()
{
static FTimeFraction Inf(0, 0);
return Inf;
}
FTimeFraction() : Numerator(0), Denominator(0), bIsValid(false)
{
}
FTimeFraction(int64 n, uint32 d) : Numerator(n), Denominator(d), bIsValid(true)
{
}
FTimeFraction(const FTimeFraction& rhs) : Numerator(rhs.Numerator), Denominator(rhs.Denominator), bIsValid(rhs.bIsValid)
{
}
FTimeFraction(const FTimeValue& tv)
{
SetFromTimeValue(tv);
}
FTimeFraction(const FTimespan& tv)
{
SetFromTimespan(tv);
}
FTimeFraction& operator=(const FTimeFraction& rhs)
{
Numerator = rhs.Numerator;
Denominator = rhs.Denominator;
bIsValid = rhs.bIsValid;
return *this;
}
bool IsValid() const
{
return bIsValid;
}
bool IsPositiveInfinity() const
{
return bIsValid && Denominator==0 && Numerator>=0;
}
int64 GetNumerator() const
{
return Numerator;
}
uint32 GetDenominator() const
{
return Denominator;
}
double GetAsDouble() const
{
return Numerator / (double)Denominator;
}
//! Returns this time value in a custom timebase. Requires internal bigint conversion and is therefor SLOW!
ELECTRABASE_API int64 GetAsTimebase(uint32 CustomTimebase) const;
ELECTRABASE_API FTimespan GetAsTimespan() const;
FTimeFraction& SetFromND(int64 InNumerator, uint32 InDenominator)
{
Numerator = InNumerator;
Denominator = InDenominator;
bIsValid = true;
return *this;
}
FTimeFraction& SetToPositiveInfinity()
{
Numerator = 0;
Denominator = 0;
bIsValid = true;
return *this;
}
FTimeFraction& SetFromTimeValue(const FTimeValue& tv)
{
if (tv.IsValid())
{
Numerator = tv.GetAsHNS();
Denominator = tv.IsInfinity() ? 0 : 10000000;
bIsValid = true;
}
else
{
Numerator = 0;
Denominator = 0;
bIsValid = false;
}
return *this;
}
FTimeFraction& SetFromTimespan(const FTimespan& tv)
{
Numerator = tv.GetTicks();
Denominator = (Numerator == ETimespan::MinTicks || Numerator == ETimespan::MaxTicks) ? 0U : (uint32)ETimespan::TicksPerSecond;
bIsValid = true;
return *this;
}
ELECTRABASE_API FTimeFraction& SetFromFloatString(const FString& In);
bool operator == (const FTimeFraction& rhs) const
{
return bIsValid == rhs.bIsValid && Numerator == rhs.Numerator && Denominator == rhs.Denominator;
}
bool operator != (const FTimeFraction& rhs) const
{
return !(*this == rhs);
}
private:
int64 Numerator;
uint32 Denominator;
bool bIsValid;
};
}