1
0
mirror of https://github.com/VCMP-SqMod/SqMod.git synced 2024-11-08 00:37:15 +01:00
SqMod/module/Library/Chrono.cpp
2021-02-03 17:50:39 +02:00

303 lines
9.7 KiB
C++

// ------------------------------------------------------------------------------------------------
#include "Library/Chrono.hpp"
#include "Library/Chrono/Date.hpp"
#include "Library/Chrono/Timer.hpp"
#include "Library/Chrono/Timestamp.hpp"
#include "Library/Numeric/Long.hpp"
#include "Core/Utility.hpp"
// ------------------------------------------------------------------------------------------------
#ifdef SQMOD_OS_WINDOWS
#if defined(_SQ64) && (_WIN32_WINNT < 0x0600)
// We need this for the GetTickCount64() function
#undef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#endif // _SQ64
#include <windows.h>
#else
#include <time.h>
#endif // SQMOD_OS_WINDOWS
// ------------------------------------------------------------------------------------------------
namespace SqMod {
// ------------------------------------------------------------------------------------------------
extern void Register_ChronoDate(HSQUIRRELVM vm, Table & cns);
extern void Register_ChronoDatetime(HSQUIRRELVM vm, Table & cns);
extern void Register_ChronoTime(HSQUIRRELVM vm, Table & cns);
extern void Register_ChronoTimer(HSQUIRRELVM vm, Table & cns);
extern void Register_ChronoTimestamp(HSQUIRRELVM vm, Table & cns);
// ------------------------------------------------------------------------------------------------
const uint8_t Chrono::MonthLengths[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
// ------------------------------------------------------------------------------------------------
#ifdef SQMOD_OS_WINDOWS
/* ------------------------------------------------------------------------------------------------
* Used by GetCurrentSysTime to obtain the system frequency on initial call.
*/
inline LARGE_INTEGER GetFrequency()
{
LARGE_INTEGER frequency;
QueryPerformanceFrequency(&frequency);
return frequency;
}
// ------------------------------------------------------------------------------------------------
int64_t Chrono::GetCurrentSysTime()
{
// Force the following code to run on first core
// (see http://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx)
HANDLE current_thread = GetCurrentThread();
DWORD_PTR previous_mask = SetThreadAffinityMask(current_thread, 1);
// Get the frequency of the performance counter
// (it is constant across the program lifetime)
static const LARGE_INTEGER frequency = GetFrequency();
// Get the current time
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
// Restore the thread affinity
SetThreadAffinityMask(current_thread, previous_mask);
// Return the current time as microseconds
return int64_t(1000000LL * time.QuadPart / frequency.QuadPart);
}
// ------------------------------------------------------------------------------------------------
int64_t Chrono::GetEpochTimeMicro()
{
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
// Extract the nanoseconds from the resulted timestamp
uint64_t time = ft.dwHighDateTime;
time <<= 32;
time |= ft.dwLowDateTime;
time /= 10;
time -= 11644473600000000ULL;
// Return the resulted timestamp
return int64_t(time);
}
// ------------------------------------------------------------------------------------------------
#ifndef _SQ64
int64_t GetTickCount64()
{
return static_cast< int64_t >(GetTickCount()); // Fall-back to 32 bit?
}
#endif // _SQ64
#else
// ------------------------------------------------------------------------------------------------
int64_t Chrono::GetCurrentSysTime()
{
// POSIX implementation
timespec time;
clock_gettime(CLOCK_MONOTONIC, &time);
return int64_t(uint64_t(time.tv_sec) * 1000000 + time.tv_nsec / 1000);
}
// ------------------------------------------------------------------------------------------------
int64_t Chrono::GetEpochTimeMicro()
{
// POSIX implementation
timespec time;
clock_gettime(CLOCK_REALTIME, &time);
return int64_t(uint64_t(time.tv_sec) * 1000000 + time.tv_nsec / 1000);
}
// ------------------------------------------------------------------------------------------------
uint32_t GetTickCount()
{
// POSIX implementation
struct timespec time;
if (clock_gettime(CLOCK_MONOTONIC, &time))
{
return 0;
}
return time.tv_sec * 1000.0 + time.tv_nsec / 1000000.0;
}
// ------------------------------------------------------------------------------------------------
int64_t GetTickCount64()
{
struct timespec time;
if (clock_gettime(CLOCK_MONOTONIC, &time))
{
return 0;
}
return time.tv_sec * 1000.0 + time.tv_nsec / 1000000.0;
}
#endif // SQMOD_OS_WINDOWS
// ------------------------------------------------------------------------------------------------
int64_t Chrono::GetEpochTimeMilli()
{
return (GetEpochTimeMicro() / 1000L);
}
// ------------------------------------------------------------------------------------------------
bool Chrono::ValidDate(uint16_t year, uint8_t month, uint8_t day)
{
// Is this a valid date? & Is the month within range?
if (year == 0 || month == 0 || day == 0 || month > 12)
{
return false;
}
// Return whether the day inside the month
return day <= DaysInMonth(year, month);
}
// ------------------------------------------------------------------------------------------------
uint8_t Chrono::DaysInMonth(uint16_t year, uint8_t month)
{
// Is the specified month within range?
if (month > 12)
{
STHROWF("Month value is out of range: {} > 12", month);
}
// Obtain the days in this month
uint8_t days = *(MonthLengths + month);
// Should we account for January?
if (month == 2 && IsLeapYear(year))
{
++days;
}
// Return the resulted days
return days;
}
// ------------------------------------------------------------------------------------------------
uint16_t Chrono::DayOfYear(uint16_t year, uint8_t month, uint8_t day)
{
// Start with 0 days
uint16_t doy = 0;
// Cumulate the days in months
for (uint8_t m = 1; m < month; ++month)
{
doy += DaysInMonth(year, m);
}
// Add the specified days
doy += day;
// Return the result
return doy;
}
// ------------------------------------------------------------------------------------------------
Date Chrono::ReverseDayOfYear(uint16_t year, uint16_t doy)
{
// The resulted month
uint8_t month = 1;
// Calculate the months till the specified day of year
for (; month < 12; ++month)
{
// Get the number of days in the current month
uint32_t days = DaysInMonth(year, month);
// Can this month fit in the remaining days?
if (days >= doy)
{
break; // The search is complete
}
// Subtract the month days from days of year
doy -= days;
}
// Return the resulted date
return Date(year, month, doy);
}
// ------------------------------------------------------------------------------------------------
int64_t Chrono::DateRangeToSeconds(uint16_t _year, uint8_t _month, uint8_t _day, uint16_t year_, uint8_t month_, uint8_t day_)
{
// Are we within the same year?
if (_year == year_)
{
return std::abs((DayOfYear(_year, _month, _day) - DayOfYear(year_, month_, day_)) * 86400LL);
}
// Is the start year greater than the end year?
else if (_year > year_)
{
std::swap(_year, year_);
std::swap(_month, month_);
std::swap(_day, day_);
}
// Calculate the remaining days from the first year
int64_t num = DaysInYear(_year) - DayOfYear(_year, _month, _day);
// Calculate the days withing the years range
while (++_year < year_)
{
num += DaysInYear(_year);
}
// Calculate the days up to the last day
num += DayOfYear(year_, month_, day_);
// Convert the obtained days in seconds
num *= 86400ULL;
// Return the result
return std::abs(num);
}
// ------------------------------------------------------------------------------------------------
static SLongInt SqGetEpochTimeMicro()
{
return SLongInt(Chrono::GetEpochTimeMicro());
}
// ------------------------------------------------------------------------------------------------
static SLongInt SqGetEpochTimeMilli()
{
return SLongInt(Chrono::GetEpochTimeMilli());
}
// ------------------------------------------------------------------------------------------------
static SLongInt SqGetCurrentSysTime()
{
return SLongInt(Chrono::GetCurrentSysTime());
}
// ------------------------------------------------------------------------------------------------
static SQInteger SqGetTickCount()
{
return ConvTo< SQInteger >::From(GetTickCount());
}
// ------------------------------------------------------------------------------------------------
static SLongInt SqGetTickCount64()
{
return SLongInt(GetTickCount64());
}
// ================================================================================================
void Register_Chrono(HSQUIRRELVM vm)
{
Table cns(vm);
Register_ChronoDate(vm, cns);
Register_ChronoDatetime(vm, cns);
Register_ChronoTime(vm, cns);
Register_ChronoTimer(vm, cns);
Register_ChronoTimestamp(vm, cns);
cns
.Func(_SC("EpochMicro"), &SqGetEpochTimeMicro)
.Func(_SC("EpochMilli"), &SqGetEpochTimeMilli)
.Func(_SC("Current"), &SqGetCurrentSysTime)
.Func(_SC("TickCount"), &SqGetTickCount)
.Func(_SC("TickCount64"), &SqGetTickCount64)
.Func(_SC("IsLeapYear"), &Chrono::IsLeapYear)
.Func(_SC("IsDateValid"), &Chrono::ValidDate)
.Func(_SC("DaysInYear"), &Chrono::DaysInYear)
.Func(_SC("DaysInMonth"), &Chrono::DaysInMonth)
.Func(_SC("DayOfYear"), &Chrono::DayOfYear)
.Func(_SC("ReverseDayOfYear"), &Chrono::ReverseDayOfYear);
RootTable(vm).Bind(_SC("SqChrono"), cns);
}
} // Namespace:: SqMod