mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-22 08:33:12 -05:00
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
164 lines
7.6 KiB
C++
164 lines
7.6 KiB
C++
#include "prx/libc/include/general/VabiMacros.hpp"
|
|
#include "prx/libc/include/GuestTime.hpp"
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <limits>
|
|
#include <random>
|
|
|
|
struct Xtime {
|
|
std::int64_t sec;
|
|
std::int64_t nsec;
|
|
};
|
|
|
|
extern "C" {
|
|
GuestTm* APS5_VABI gmtime_s_nid_postfix(const std::int64_t*, GuestTm*);
|
|
GuestTm* APS5_VABI localtime_s_nid_postfix(const std::int64_t*, GuestTm*);
|
|
GuestTm* APS5_VABI gmtime_r_nid_postfix(const std::int64_t*, GuestTm*);
|
|
GuestTm* APS5_VABI localtime_r_nid_postfix(const std::int64_t*, GuestTm*);
|
|
std::int64_t APS5_VABI timegm_nid_postfix(GuestTm*);
|
|
void APS5_VABI tzset_nid_postfix();
|
|
int APS5_VABI setenv_nid_postfix(const char*, const char*, int);
|
|
int APS5_VABI xtime_get_nid_postfix(Xtime*, int);
|
|
Xtime APS5_VABI _Xtime_to_ts_nid_postfix(const Xtime*);
|
|
Xtime APS5_VABI _Xtime_diff_to_ts_nid_postfix(const Xtime*);
|
|
}
|
|
|
|
static void Require(bool value, const char* message) {
|
|
if (!value) {
|
|
std::fprintf(stderr, "%s\n", message);
|
|
std::abort();
|
|
}
|
|
}
|
|
|
|
static bool Equal(const GuestTm& left, const GuestTm& right) {
|
|
return left.tm_sec == right.tm_sec && left.tm_min == right.tm_min && left.tm_hour == right.tm_hour
|
|
&& left.tm_mday == right.tm_mday && left.tm_mon == right.tm_mon && left.tm_year == right.tm_year
|
|
&& left.tm_wday == right.tm_wday && left.tm_yday == right.tm_yday && left.tm_isdst == right.tm_isdst;
|
|
}
|
|
|
|
struct TimegmCase {
|
|
int sec, min, hour, mday, mon, year;
|
|
std::int64_t expected;
|
|
int normalizedMday, normalizedMon, normalizedYear, wday, yday;
|
|
};
|
|
|
|
static void CheckTimegm(const TimegmCase& test) {
|
|
GuestTm time{};
|
|
time.tm_sec = test.sec;
|
|
time.tm_min = test.min;
|
|
time.tm_hour = test.hour;
|
|
time.tm_mday = test.mday;
|
|
time.tm_mon = test.mon;
|
|
time.tm_year = test.year;
|
|
time.tm_wday = 9;
|
|
time.tm_yday = 999;
|
|
time.tm_isdst = 5;
|
|
time.tm_gmtoff = 77;
|
|
if (timegm_nid_postfix(&time) != test.expected) {
|
|
std::fprintf(stderr, "timegm(%d-%d-%d) returned the wrong time\n", test.year, test.mon, test.mday);
|
|
std::abort();
|
|
}
|
|
Require(time.tm_mday == test.normalizedMday && time.tm_mon == test.normalizedMon && time.tm_year == test.normalizedYear
|
|
&& time.tm_wday == test.wday && time.tm_yday == test.yday && time.tm_isdst == 0 && time.tm_gmtoff == 0,
|
|
"timegm did not normalize the calendar fields");
|
|
}
|
|
|
|
static void CheckReentrantConversions() {
|
|
constexpr std::int64_t timers[] = {0, -1, 86399, 951782400, 4107542400, 253402300799, -62135596800};
|
|
for (const std::int64_t timer : timers) {
|
|
GuestTm expected{};
|
|
GuestTm actual{};
|
|
Require(gmtime_s_nid_postfix(&timer, &expected) == &expected, "gmtime_s rejected a representable time");
|
|
Require(gmtime_r_nid_postfix(&timer, &actual) == &actual && Equal(actual, expected), "gmtime_r differs from gmtime_s");
|
|
Require(localtime_s_nid_postfix(&timer, &expected) == &expected, "localtime_s rejected a representable time");
|
|
Require(localtime_r_nid_postfix(&timer, &actual) == &actual && Equal(actual, expected) && actual.tm_gmtoff == 7200,
|
|
"localtime_r differs from localtime_s");
|
|
}
|
|
const std::int64_t epoch = 0;
|
|
Require(gmtime_r_nid_postfix(&epoch, nullptr) == nullptr, "gmtime_r accepted a null result");
|
|
Require(localtime_r_nid_postfix(&epoch, nullptr) == nullptr, "localtime_r accepted a null result");
|
|
const std::int64_t underflow = std::numeric_limits<std::int64_t>::min();
|
|
GuestTm scratch{};
|
|
Require(gmtime_r_nid_postfix(&underflow, &scratch) == nullptr, "gmtime_r accepted an unrepresentable time");
|
|
}
|
|
|
|
static void CheckTimegmCases() {
|
|
constexpr TimegmCase cases[] = {
|
|
{0, 0, 0, 1, 0, 70, 0, 1, 0, 70, 4, 0},
|
|
{59, 59, 23, 31, 11, 69, -1, 31, 11, 69, 3, 364},
|
|
{7, 14, 3, 19, 9, 126, 1792379647, 19, 9, 126, 1, 291},
|
|
{0, 0, 0, 0, 0, 70, -86400, 31, 11, 69, 3, 364},
|
|
{0, 0, 0, 1, 12, 69, 0, 1, 0, 70, 4, 0},
|
|
{0, 0, 0, 1, -1, 71, 28857600, 1, 11, 70, 2, 334},
|
|
{0, 0, 0, 1, -13, 71, -2678400, 1, 11, 69, 1, 334},
|
|
{0, 0, 0, 32, 1, 100, 952041600, 3, 2, 100, 5, 62},
|
|
{31536000, 0, 0, 1, 0, 70, 31536000, 1, 0, 71, 5, 0},
|
|
{0, 0, 0, 1, 2, 200, 4107542400, 1, 2, 200, 1, 59},
|
|
{59, 59, 23, 31, 11, 8099, 253402300799, 31, 11, 8099, 5, 364},
|
|
{0, 0, 0, 1, 0, -1899, -62135596800, 1, 0, -1899, 1, 0},
|
|
};
|
|
for (const auto& test : cases) CheckTimegm(test);
|
|
|
|
GuestTm beyond{};
|
|
beyond.tm_year = std::numeric_limits<int>::max();
|
|
beyond.tm_mon = 12;
|
|
beyond.tm_mday = 1;
|
|
const GuestTm before = beyond;
|
|
Require(timegm_nid_postfix(&beyond) == -1 && Equal(beyond, before), "timegm accepted a year past the int range");
|
|
|
|
std::mt19937_64 random(7);
|
|
for (int iteration = 0; iteration < 100000; ++iteration) {
|
|
const std::int64_t timer = static_cast<std::int64_t>(random() % 600000000000ull) - 300000000000;
|
|
GuestTm fields{};
|
|
Require(gmtime_r_nid_postfix(&timer, &fields) == &fields, "gmtime_r rejected a representable time");
|
|
GuestTm copy = fields;
|
|
Require(timegm_nid_postfix(©) == timer && Equal(copy, fields), "timegm did not invert gmtime_r");
|
|
}
|
|
}
|
|
|
|
static void CheckXtime() {
|
|
Xtime time{-5, -5};
|
|
Require(xtime_get_nid_postfix(&time, 0) == 0 && time.sec == -5 && time.nsec == -5, "xtime_get accepted base 0");
|
|
Require(xtime_get_nid_postfix(&time, 2) == 0 && time.sec == -5 && time.nsec == -5, "xtime_get accepted base 2");
|
|
Require(xtime_get_nid_postfix(nullptr, 1) == 0, "xtime_get accepted a null xtime");
|
|
const std::int64_t before = static_cast<std::int64_t>(std::time(nullptr));
|
|
Require(xtime_get_nid_postfix(&time, 1) == 1, "xtime_get rejected TIME_UTC");
|
|
Require(time.sec >= before - 1 && time.sec <= before + 2 && time.nsec >= 0 && time.nsec < 1000000000 && time.nsec % 1000 == 0,
|
|
"xtime_get did not return the wall clock in microseconds");
|
|
|
|
const Xtime unnormalized{7, -3};
|
|
const Xtime copied = _Xtime_to_ts_nid_postfix(&unnormalized);
|
|
Require(copied.sec == 7 && copied.nsec == -3, "_Xtime_to_ts changed the time");
|
|
|
|
Xtime now{};
|
|
Require(xtime_get_nid_postfix(&now, 1) == 1, "xtime_get rejected TIME_UTC");
|
|
const Xtime past{now.sec - 1, now.nsec};
|
|
const Xtime remaining = _Xtime_diff_to_ts_nid_postfix(&past);
|
|
Require(remaining.sec == 0 && remaining.nsec == 0, "_Xtime_diff_to_ts did not clamp a past time to zero");
|
|
const Xtime epoch{0, 0};
|
|
const Xtime none = _Xtime_diff_to_ts_nid_postfix(&epoch);
|
|
Require(none.sec == 0 && none.nsec == 0, "_Xtime_diff_to_ts did not clamp the epoch to zero");
|
|
const Xtime later{now.sec + 100, now.nsec};
|
|
const Xtime wait = _Xtime_diff_to_ts_nid_postfix(&later);
|
|
Require(wait.sec >= 98 && wait.sec <= 100 && wait.nsec >= 0 && wait.nsec < 1000000000, "_Xtime_diff_to_ts returned the wrong wait");
|
|
const Xtime carried{now.sec, now.nsec + 5000000000};
|
|
const Xtime carriedWait = _Xtime_diff_to_ts_nid_postfix(&carried);
|
|
Require(carriedWait.sec >= 3 && carriedWait.sec <= 5 && carriedWait.nsec >= 0 && carriedWait.nsec < 1000000000,
|
|
"_Xtime_diff_to_ts did not carry whole seconds out of nsec");
|
|
const Xtime borrowed{now.sec + 10, -1};
|
|
const Xtime borrowedWait = _Xtime_diff_to_ts_nid_postfix(&borrowed);
|
|
Require(borrowedWait.sec >= 8 && borrowedWait.sec <= 9 && borrowedWait.nsec >= 0 && borrowedWait.nsec < 1000000000,
|
|
"_Xtime_diff_to_ts did not borrow a second for a negative nsec");
|
|
}
|
|
|
|
int main() {
|
|
Require(setenv_nid_postfix("TZ", "UTC-2", 1) == 0, "Could not set the time zone");
|
|
tzset_nid_postfix();
|
|
CheckReentrantConversions();
|
|
CheckTimegmCases();
|
|
CheckXtime();
|
|
return 0;
|
|
}
|