Initial commit

This commit is contained in:
InoriRus
2021-12-01 19:29:27 +10:00
parent b1e7dcdc5d
commit 43f49c8763
1843 changed files with 1111694 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
add_subdirectory(Core)
add_subdirectory(Math)
add_subdirectory(Scripts)
add_subdirectory(Sys)
+40
View File
@@ -0,0 +1,40 @@
file(GLOB core_src
"src/*.cpp"
)
if (MSVC AND CLANG)
set_source_files_properties(${core_src} PROPERTIES COMPILE_FLAGS "-Wno-pragma-pack")
endif()
add_library(core_obj OBJECT ${core_src})
#add_library(core STATIC ${core_src})
add_library(core STATIC $<TARGET_OBJECTS:core_obj>)
target_link_libraries(core sys)
target_link_libraries(core math)
target_link_libraries(core sdl2)
target_link_libraries(core sqlite)
target_link_libraries(core lzma)
target_link_libraries(core zstd)
get_property(inc_headers TARGET core PROPERTY INCLUDE_DIRECTORIES)
list(APPEND inc_headers
${CMAKE_SOURCE_DIR}/3rdparty/sdl2/include
${CMAKE_SOURCE_DIR}/3rdparty/sqlite/include
${CMAKE_SOURCE_DIR}/3rdparty/lzma/include
${CMAKE_SOURCE_DIR}/3rdparty/zstd/lib
)
target_include_directories(core PRIVATE ${inc_headers})
target_include_directories(core_obj PRIVATE ${inc_headers})
list(APPEND check_headers
${CMAKE_SOURCE_DIR}/include
)
clang_tidy_check(core_obj "" "${check_headers}" "${inc_headers}")
include_what_you_use(core_obj "${inc_headers}")
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+35
View File
@@ -0,0 +1,35 @@
#include "Kyty/Core/Core.h"
#include "Kyty/Core/ArrayWrapper.h" // IWYU pragma: associated
#include "Kyty/Core/ByteBuffer.h" // IWYU pragma: associated
#include "Kyty/Core/Common.h" // IWYU pragma: associated
#include "Kyty/Core/Database.h"
#include "Kyty/Core/Debug.h"
#include "Kyty/Core/File.h"
#include "Kyty/Core/Hash.h" // IWYU pragma: associated
#include "Kyty/Core/Language.h"
#include "Kyty/Core/LinkList.h" // IWYU pragma: associated
#include "Kyty/Core/MagicEnum.h" // IWYU pragma: associated
#include "Kyty/Core/MemoryAlloc.h"
#include "Kyty/Core/RefCounter.h" // IWYU pragma: associated
#include "Kyty/Core/SafeDelete.h" // IWYU pragma: associated
#include "Kyty/Core/SimpleArray.h" // IWYU pragma: associated
#include "Kyty/Core/Singleton.h" // IWYU pragma: associated
#include "Kyty/Core/Vector.h" // IWYU pragma: associated
namespace Kyty::Core {
KYTY_SUBSYSTEM_INIT(Core)
{
core_memory_init();
core_file_init();
core_debug_init(parent->GetArgv()[0]);
Language::Init();
Database::Init();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Core) {}
KYTY_SUBSYSTEM_DESTROY(Core) {}
} // namespace Kyty::Core
File diff suppressed because it is too large Load Diff
+883
View File
@@ -0,0 +1,883 @@
#include "Kyty/Core/DateTime.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Math/MathAll.h"
#include "Kyty/Sys/SysTimer.h"
namespace Kyty::Core {
static void ymd_to_jd(int year, int month, int day, jd_t* jd)
{
jd_t t_year = year;
jd_t t_month = month;
jd_t t_day = day;
if (t_year < 0)
{
t_year++;
}
jd_t a = Math::floordiv<jd_t>(14 - t_month, 12);
jd_t y = t_year + 4800 - a;
jd_t m = t_month + 12 * a - 3;
*jd = t_day + Math::floordiv<jd_t>(153 * m + 2, 5) + 365 * y + Math::floordiv<jd_t>(y, 4) - Math::floordiv<jd_t>(y, 100) +
Math::floordiv<jd_t>(y, 400) - 32045;
}
static void jd_to_ymd(int* year, int* month, int* day, jd_t jd)
{
jd_t a = jd + 32044;
jd_t b = Math::floordiv<jd_t>(4 * a + 3, 146097);
jd_t c = a - Math::floordiv<jd_t>(146097 * b, 4);
jd_t d = Math::floordiv<jd_t>(4 * c + 3, 1461);
jd_t e = c - Math::floordiv<jd_t>(1461 * d, 4);
jd_t m = Math::floordiv<jd_t>(5 * e + 2, 153);
int t_day = e - Math::floordiv<jd_t>(153 * m + 2, 5) + 1;
int t_month = m + 3 - 12 * Math::floordiv<jd_t>(m, 10);
int t_year = 100 * b + d - 4800 + Math::floordiv<jd_t>(m, 10);
if (t_year <= 0)
{
t_year--;
}
if (year != nullptr)
{
*year = t_year;
}
if (month != nullptr)
{
*month = t_month;
}
if (day != nullptr)
{
*day = t_day;
}
}
static void hms_to_ms(int hour, int minute, int second, int msec, int* ms)
{
*ms = hour * 60 * 60 * 1000 + minute * 60 * 1000 + second * 1000 + msec;
}
static void ms_to_hms(int* hour, int* minute, int* second, int* msec, int ms)
{
if (hour != nullptr)
{
*hour = ms / (60 * 60 * 1000);
}
if (minute != nullptr)
{
*minute = (ms % (60 * 60 * 1000)) / (60 * 1000);
}
if (second != nullptr)
{
*second = (ms / 1000) % 60;
}
if (msec != nullptr)
{
*msec = ms % 1000;
}
}
Date::Date(int year, int month, int day)
{
Set(year, month, day);
}
bool Date::IsValid(int year, int month, int day)
{
if (year == 0)
{
return false;
}
return (day >= 1) && (day <= DaysInMonth(month) || (day == 29 && month == 2 && IsLeapYear(year)));
}
int Date::DaysInMonth() const
{
if (IsInvalid())
{
return 0;
}
int year = 0;
int month = 0;
jd_to_ymd(&year, &month, nullptr, m_jd);
if (month == 2 && IsLeapYear(year))
{
return 29;
}
return DaysInMonth(month);
}
bool Date::IsLeapYear() const
{
if (IsInvalid())
{
return false;
}
int year = 0;
jd_to_ymd(&year, nullptr, nullptr, m_jd);
return IsLeapYear(year);
}
int Date::DaysInMonth(int month)
{
static const int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month < 1 || month > 12)
{
month = 0;
}
return days[month];
}
void Date::Set(int year, int month, int day)
{
if (IsValid(year, month, day))
{
ymd_to_jd(year, month, day, &m_jd);
} else
{
m_jd = DATE_JD_INVALID;
}
}
void Date::Get(int* year, int* month, int* day) const
{
if (IsInvalid())
{
if (year != nullptr)
{
*year = 0;
}
if (month != nullptr)
{
*month = 0;
}
if (day != nullptr)
{
*day = 0;
}
} else
{
jd_to_ymd(year, month, day, m_jd);
}
}
bool Date::IsLeapYear(int year)
{
if (year < 1)
{
year++;
}
return ((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0;
}
int Date::Year() const
{
if (IsInvalid())
{
return 0;
}
int year = 0;
jd_to_ymd(&year, nullptr, nullptr, m_jd);
return year;
}
int Date::Month() const
{
if (IsInvalid())
{
return 0;
}
int month = 0;
jd_to_ymd(nullptr, &month, nullptr, m_jd);
return month;
}
int Date::Day() const
{
if (IsInvalid())
{
return 0;
}
int day = 0;
jd_to_ymd(nullptr, nullptr, &day, m_jd);
return day;
}
int Date::DaysInYear() const
{
if (IsInvalid())
{
return 0;
}
return IsLeapYear() ? 366 : 365;
}
int Date::DayOfWeek() const
{
if (IsInvalid())
{
return 0;
}
if (m_jd >= 0)
{
return (m_jd % 7) + 1;
}
return ((m_jd + 1) % 7) + 7;
}
int Date::DayOfYear() const
{
if (IsInvalid())
{
return 0;
}
jd_t d = 0;
ymd_to_jd(Year(), 1, 1, &d);
return m_jd - d + 1;
}
int Date::DaysInYear(int year)
{
return IsLeapYear(year) ? 366 : 365;
}
// NOLINTNEXTLINE(readability-non-const-parameter)
static bool format_date(const Date* d, const String& f, uint32_t* out_i, String* out_r, LanguageId lang_id)
{
auto& i = *out_i;
auto& r = *out_r;
if (f.Mid(i, 4) == U"YYYY")
{
int y = d->Year();
EXIT_IF(y < 0 || y > 9999);
r += String::FromPrintf("%04d", y);
i += 3;
} else if (f.Mid(i, 3) == U"YYY")
{
int y = d->Year();
EXIT_IF(y < 0 || y > 9999);
r += String::FromPrintf("%04d", y).Mid(1);
i += 2;
} else if (f.Mid(i, 2) == U"YY")
{
int y = d->Year();
EXIT_IF(y < 0 || y > 9999);
r += String::FromPrintf("%04d", y).Mid(2);
i += 1;
} else if (f.Mid(i, 1) == U"Y")
{
int y = d->Year();
EXIT_IF(y < 0 || y > 9999);
r += String::FromPrintf("%04d", y).Mid(3);
} else if (f.Mid(i, 1) == U"Q")
{
r += String::FromPrintf("%d", d->QuarterOfYear());
} else if (f.Mid(i, 2) == U"MM")
{
r += String::FromPrintf("%02d", d->Month());
i += 1;
} else if (f.Mid(i, 5) == U"MONTH")
{
r += Language::GetNameOfMonth(d->Month(), lang_id);
i += 4;
} else if (f.Mid(i, 3) == U"MON")
{
r += Language::GetNameOfMonthShort(d->Month(), lang_id);
i += 2;
} else if (f.Mid(i, 3) == U"DDD")
{
r += String::FromPrintf("%03d", d->DayOfYear());
i += 2;
} else if (f.Mid(i, 2) == U"DD")
{
r += String::FromPrintf("%02d", d->Day());
i += 1;
} else if (f.Mid(i, 2) == U"DY")
{
r += Language::GetNameOfDayShort(d->DayOfWeek(), lang_id);
i += 1;
} else if (f.Mid(i, 3) == U"DAY")
{
r += Language::GetNameOfDay(d->DayOfWeek(), lang_id);
i += 2;
} else if (f.Mid(i, 1) == U"D")
{
r += String::FromPrintf("%d", d->DayOfWeek());
} else if (f.Mid(i, 1) == U"J")
{
r += String::FromPrintf("%d", d->JulianDay());
} else
{
return false;
}
return true;
}
/**
* YYYY - 4-digit year
* YYY, YY, Y - Last 3, 2, or 1 digit(s) of year.
* Q - Quarter of year (1, 2, 3, 4; JAN-MAR = 1).
* MM - Month (01-12; JAN = 01).
* MON - Abbreviated name of month.
* MONTH - Name of month
* D - Day of week (1-7).
* DAY - Name of day.
* DY - Abbreviated name of day.
* DD - Day of month (01-31).
* DDD - Day of year (001-366).
* J - Julian day
*/
String Date::ToString(const char* format, LanguageId lang_id) const
{
if (IsInvalid())
{
return U"";
}
String r;
String f = String::FromUtf8(format);
FOR (i, f)
{
if (!format_date(this, f, &i, &r, lang_id))
{
r += f.At(i);
}
}
return r;
}
int Date::QuarterOfYear() const
{
if (IsInvalid())
{
return 0;
}
return (Month() - 1) / 3 + 1;
}
Date Date::FromMacros(const String& date)
{
StringList lst = date.Split(U" ", String::SplitType::SplitNoEmptyParts);
if (lst.Size() != 3)
{
return Date();
}
static const char* month_str[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
int month = -1;
for (int i = 0; i < 12; i++)
{
if (lst.At(0).EqualAscii(month_str[i]))
{
month = i + 1;
break;
}
}
if (month < 0)
{
return Date();
}
return Date(lst.At(2).ToInt32(), month, lst.At(1).ToInt32());
}
Date Date::FromSystem()
{
SysTimeStruct t {};
sys_get_system_time(t);
return Date(t.Year, t.Month, t.Day);
}
Date Date::FromSystemUTC()
{
SysTimeStruct t {};
sys_get_system_time_utc(t);
return Date(t.Year, t.Month, t.Day);
}
Time::Time(int hour, int minute, int second, int msec)
{
Set(hour, minute, second, msec);
}
Time Time::FromSystem()
{
SysTimeStruct t {};
sys_get_system_time(t);
return Time(t.Hour, t.Minute, t.Second, t.Milliseconds);
}
Time Time::FromSystemUTC()
{
SysTimeStruct t {};
sys_get_system_time_utc(t);
return Time(t.Hour, t.Minute, t.Second, t.Milliseconds);
}
void Time::Set(int hour, int minute, int second, int msec)
{
if (IsValid(hour, minute, second, msec))
{
hms_to_ms(hour, minute, second, msec, &m_ms);
} else
{
m_ms = TIME_MS_INVALID;
}
}
void Time::Get(int* hour, int* minute, int* second, int* msec) const
{
if (IsInvalid())
{
if (hour != nullptr)
{
*hour = -1;
}
if (minute != nullptr)
{
*minute = -1;
}
if (second != nullptr)
{
*second = -1;
}
if (msec != nullptr)
{
*msec = -1;
}
} else
{
ms_to_hms(hour, minute, second, msec, m_ms);
}
}
bool Time::IsValid(int hour, int minute, int second, int msec)
{
return (hour >= 0 && hour <= 23) && (minute >= 0 && minute <= 59) && (second >= 0 && second <= 59) && (msec >= 0 && msec <= 999);
}
int Time::Hour24() const
{
if (IsInvalid())
{
return -1;
}
int h = 0;
ms_to_hms(&h, nullptr, nullptr, nullptr, m_ms);
return h;
}
int Time::Hour12() const
{
if (IsInvalid())
{
return -1;
}
int h = 0;
ms_to_hms(&h, nullptr, nullptr, nullptr, m_ms);
h = h % 12;
return h == 0 ? 12 : h;
}
int Time::Minute() const
{
if (IsInvalid())
{
return -1;
}
int m = 0;
ms_to_hms(nullptr, &m, nullptr, nullptr, m_ms);
return m;
}
int Time::Second() const
{
if (IsInvalid())
{
return -1;
}
int s = 0;
ms_to_hms(nullptr, nullptr, &s, nullptr, m_ms);
return s;
}
int Time::Msec() const
{
if (IsInvalid())
{
return -1;
}
int m = 0;
ms_to_hms(nullptr, nullptr, nullptr, &m, m_ms);
return m;
}
bool Time::IsAM() const
{
if (IsInvalid())
{
return false;
}
int h = 0;
ms_to_hms(&h, nullptr, nullptr, nullptr, m_ms);
return h < 12;
}
bool Time::IsPM() const
{
if (IsInvalid())
{
return false;
}
int h = 0;
ms_to_hms(&h, nullptr, nullptr, nullptr, m_ms);
return h >= 12;
}
// NOLINTNEXTLINE(readability-non-const-parameter)
static bool format_time(const Time* t, const String& f, uint32_t* out_i, String* out_r)
{
auto& i = *out_i;
auto& r = *out_r;
if (f.Mid(i, 4) == U"HH24")
{
r += String::FromPrintf("%02d", t->Hour24());
i += 3;
} else if (f.Mid(i, 4) == U"HH12")
{
r += String::FromPrintf("%02d", t->Hour12());
i += 3;
} else if (f.Mid(i, 2) == U"HH")
{
r += String::FromPrintf("%02d", t->Hour12());
i += 1;
} else if (f.Mid(i, 2) == U"MI")
{
r += String::FromPrintf("%02d", t->Minute());
i += 1;
} else if (f.Mid(i, 5) == U"SSSSS")
{
r += String::FromPrintf("%05d", t->MsecTotal() / 1000);
i += 4;
} else if (f.Mid(i, 2) == U"SS")
{
r += String::FromPrintf("%02d", t->Second());
i += 1;
} else if (f.Mid(i, 3) == U"FFF")
{
r += String::FromPrintf("%03d", t->Msec());
i += 2;
} else if (f.Mid(i, 2) == U"AM")
{
r += t->IsAM() ? U"AM" : U"PM";
i += 1;
} else if (f.Mid(i, 4) == U"A.M.")
{
r += t->IsAM() ? U"A.M." : U"P.M.";
i += 3;
} else
{
return false;
}
return true;
}
/**
* HH - Hour of day (1-12)
* HH12 - Hour of day (1-12)
* HH24 - Hour of day (0-23)
* MI - Minute (0-59)
* SS - Second (0-59)
* SSSSS - Seconds past midnight (0-86399)
* FFF - Milliseconds
* AM - AM or PM
* A.M. - A.M. or P.M.
*/
String Time::ToString(const char* format) const
{
if (IsInvalid())
{
return U"";
}
String r;
String f = String::FromUtf8(format);
FOR (i, f)
{
if (!format_time(this, f, &i, &r))
{
r += f.At(i);
}
}
return r;
}
Time Time::operator+(int secs) const
{
int ms_secs = secs * 1000;
EXIT_IF(ms_secs > TIME_MS_IN_DAY || ms_secs < -TIME_MS_IN_DAY);
if (IsInvalid())
{
return Time();
}
int r = m_ms + ms_secs;
if (r < 0)
{
r += TIME_MS_IN_DAY;
}
if (r >= TIME_MS_IN_DAY)
{
r -= TIME_MS_IN_DAY;
}
return Time(r);
}
Time Time::operator-(int secs) const
{
int ms_secs = secs * 1000;
EXIT_IF(ms_secs > TIME_MS_IN_DAY || ms_secs < -TIME_MS_IN_DAY);
if (IsInvalid())
{
return Time();
}
int r = m_ms - ms_secs;
if (r < 0)
{
r += TIME_MS_IN_DAY;
}
if (r >= TIME_MS_IN_DAY)
{
r -= TIME_MS_IN_DAY;
}
return Time(r);
}
Time Time::operator+=(int secs)
{
int ms_secs = secs * 1000;
EXIT_IF(ms_secs > TIME_MS_IN_DAY || ms_secs < -TIME_MS_IN_DAY);
if (!IsInvalid())
{
m_ms += ms_secs;
if (m_ms < 0)
{
m_ms += TIME_MS_IN_DAY;
}
if (m_ms >= TIME_MS_IN_DAY)
{
m_ms -= TIME_MS_IN_DAY;
}
}
return *this;
}
Time Time::operator-=(int secs)
{
int ms_secs = secs * 1000;
EXIT_IF(ms_secs > TIME_MS_IN_DAY || ms_secs < -TIME_MS_IN_DAY);
if (!IsInvalid())
{
m_ms -= ms_secs;
if (m_ms < 0)
{
m_ms += TIME_MS_IN_DAY;
}
if (m_ms >= TIME_MS_IN_DAY)
{
m_ms -= TIME_MS_IN_DAY;
}
}
return *this;
}
DateTime DateTime::FromSystem()
{
SysTimeStruct t {};
sys_get_system_time(t);
if (t.is_invalid)
{
return DateTime();
}
return DateTime(Date(t.Year, t.Month, t.Day), Time(t.Hour, t.Minute, t.Second, t.Milliseconds));
}
DateTime DateTime::FromSystemUTC()
{
SysTimeStruct t {};
sys_get_system_time_utc(t);
if (t.is_invalid)
{
return DateTime();
}
return DateTime(Date(t.Year, t.Month, t.Day), Time(t.Hour, t.Minute, t.Second, t.Milliseconds));
}
/**
* YYYY - 4-digit year
* YYY, YY, Y - Last 3, 2, or 1 digit(s) of year.
* Q - Quarter of year (1, 2, 3, 4; JAN-MAR = 1).
* MM - Month (01-12; JAN = 01).
* MON - Abbreviated name of month.
* MONTH - Name of month
* D - Day of week (1-7).
* DAY - Name of day.
* DY - Abbreviated name of day.
* DD - Day of month (01-31).
* DDD - Day of year (001-366).
* J - Julian day
* HH - Hour of day (1-12)
* HH12 - Hour of day (1-12)
* HH24 - Hour of day (0-23)
* MI - Minute (0-59)
* SS - Second (0-59)
* SSSSS - Seconds past midnight (0-86399)
* FFF - Milliseconds
* AM - AM or PM
* A.M. - A.M. or P.M.
*/
String DateTime::ToString(const char* format, LanguageId lang_id) const
{
if (IsInvalid())
{
return U"";
}
String r;
String f = String::FromUtf8(format);
FOR (i, f)
{
if (!format_date(&m_date, f, &i, &r, lang_id) && !format_time(&m_time, f, &i, &r))
{
r += f.At(i);
}
}
return r;
}
uint64_t DateTime::DistanceMs(const DateTime& other) const
{
EXIT_IF(IsInvalid() || other.IsInvalid());
if (other == *this)
{
return 0;
}
if (other > *this)
{
return other.DistanceMs(*this);
}
jd_t j1 = other.m_date.JulianDay();
jd_t j2 = m_date.JulianDay();
return int64_t(j2 - j1 - 1) * int64_t(TIME_MS_IN_DAY) + (int64_t(TIME_MS_IN_DAY) - int64_t(other.m_time.MsecTotal())) +
int64_t(m_time.MsecTotal());
}
DateTime DateTime::FromSQLiteJulian(double jd)
{
double i = NAN;
double f = modf(jd + 0.5, &i);
return DateTime(Date(jd_t(i)), Time(int(f * double(TIME_MS_IN_DAY))));
}
double DateTime::ToSQLiteJulian() const
{
return -0.5 + double(GetDate().JulianDay()) + double(GetTime().MsecTotal()) / double(TIME_MS_IN_DAY);
}
int64_t DateTime::ToSQLiteJulianInt64() const
{
return -(static_cast<int64_t>(TIME_MS_IN_DAY)) / 2 + int64_t(GetDate().JulianDay()) * (static_cast<int64_t>(TIME_MS_IN_DAY)) +
int64_t(GetTime().MsecTotal());
}
DateTime DateTime::FromUnix(double seconds)
{
return FromSQLiteJulian(2440587.5 + seconds / 86400.0);
}
double DateTime::ToUnix() const
{
return (ToSQLiteJulian() - 2440587.5) * 86400.0;
}
} // namespace Kyty::Core
+84
View File
@@ -0,0 +1,84 @@
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/Debug.h"
#include "Kyty/Core/Subsystems.h"
#include <cstdarg>
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
#include <windows.h> // IWYU pragma: keep
// IWYU pragma: no_include <debugapi.h>
#endif
namespace Kyty::Core {
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BUILD == KYTY_BUILD_DEBUG && KYTY_COMPILER == KYTY_COMPILER_CLANG
constexpr int PRINT_STACK_FROM = 4;
#else
constexpr int PRINT_STACK_FROM = 2;
#endif
void dbg_print_stack()
{
DebugStack s;
DebugStack::Trace(&s);
printf("--- Stack Trace ---\n");
s.Print(PRINT_STACK_FROM);
}
int dbg_assert_handler(char const* expr, char const* file, int line)
{
dbg_print_stack();
KYTY_LOGE("--- Fatal Error ---\n");
KYTY_LOGE("Assertion (%s) failed in %s:%d\n", expr, file, line);
SubsystemsListSingleton::Instance()->ShutdownAll();
return 1;
}
int dbg_exit_if_handler(char const* expr, char const* file, int line)
{
dbg_print_stack();
KYTY_LOGE("--- Fatal Error ---\n");
KYTY_LOGE("Error: condition (%s) is true in %s:%d\n", expr, file, line);
SubsystemsListSingleton::Instance()->ShutdownAll();
return 1;
}
int dbg_not_implemented_handler(char const* expr, char const* file, int line)
{
dbg_print_stack();
KYTY_LOGE("--- Fatal Error ---\n");
KYTY_LOGE("Not implemented (%s) in %s:%d\n", expr, file, line);
SubsystemsListSingleton::Instance()->ShutdownAll();
return 1;
}
int dbg_exit_handler(char const* file, int line, const char* f, ...)
{
va_list args {};
va_start(args, f);
dbg_print_stack();
KYTY_LOGE("--- Error ---\n");
vprintf(f, args);
KYTY_LOGE(" in %s:%d\n", file, line);
SubsystemsListSingleton::Instance()->ShutdownAll();
va_end(args);
return 1;
}
bool dbg_is_debugger_present()
{
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
return !(IsDebuggerPresent() == 0);
#endif
return false;
}
} // namespace Kyty::Core
+917
View File
@@ -0,0 +1,917 @@
#include "Kyty/Core/Debug.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/File.h"
#include "Kyty/Core/Hashmap.h"
#include "Kyty/Core/SafeDelete.h"
#include "Kyty/Core/Vector.h"
#include "Kyty/Sys/SysDbg.h"
#include "Kyty/Sys/SysStdlib.h"
// IWYU pragma: no_include <sec_api/string_s.h>
#if 0 && (KYTY_COMPILER == KYTY_COMPILER_MSVC || KYTY_COMPILER == KYTY_COMPILER_MINGW)
String unDName(const String &mangled,
void* (*memget)(size_t), void (*memfree)(void*),
unsigned short int flags);
#endif
namespace Kyty::Core {
#ifndef KYTY_FINAL
#define DEBUG_MAP_ENABLED
#endif
constexpr bool DBG_PRINTF = false;
static DebugMap* g_dbg_map = nullptr;
static String* g_exe_name = nullptr;
struct DebugFunctionInfo
{
uintptr_t addr;
uintptr_t length;
String::Utf8 name;
String::Utf8 obj;
bool operator<(const DebugFunctionInfo& f) const { return addr < f.addr; }
};
struct DebugFunctionInfo2
{
uintptr_t addr;
uintptr_t length;
const char* name;
const char* obj;
bool operator<(const DebugFunctionInfo2& f) const { return addr < f.addr; }
};
using DebugMapType = Hashmap<uintptr_t, uint32_t>;
using DebugDataType = Vector<DebugFunctionInfo>;
using DebugDataType2 = Vector<DebugFunctionInfo2>;
struct DebugMapPrivate
{
DebugMapPrivate() = default;
~DebugMapPrivate()
{
if (buf != nullptr)
{
DeleteArray(buf);
}
}
void FixBaseAddress();
static const DebugFunctionInfo* FindFunc(DebugMap* map, uintptr_t addr);
KYTY_CLASS_NO_COPY(DebugMapPrivate)
DebugDataType data;
DebugDataType2 data2;
DebugMapType map;
char* buf {nullptr};
};
static void exception_filter(void* /*addr*/)
{
EXIT("Exception!!!");
}
void core_debug_init(const char* app_name)
{
sys_set_exception_filter(exception_filter);
g_exe_name = new String(String::FromUtf8(app_name));
g_dbg_map = new DebugMap;
// g_dbg_map->LoadMap();
g_dbg_map->LoadCsv();
}
DebugMap::DebugMap(): m_p(new DebugMapPrivate) {}
void DebugMap::LoadMap()
{
#ifdef DEBUG_MAP_ENABLED
printf("exe_name = %s\n", g_exe_name->utf8_str().GetData());
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
String linker = Debug::GetLinker();
String map_file =
g_exe_name->FilenameWithoutExtension() + U"_" + Debug::GetCompiler() + U"_" + linker + U"_" + Debug::GetBitness() + U".map";
if (linker == U"ld")
{
LoadGnuLd(map_file, KYTY_BITNESS);
} else if (linker == U"link" || linker == U"lld_link")
{
LoadMsvcLink(map_file, KYTY_BITNESS);
} else if (linker == U"lld")
{
LoadLlvmLld(map_file, KYTY_BITNESS);
} else
{
return;
}
#elif KYTY_PLATFORM == KYTY_PLATFORM_ANDROID
#if KYTY_ABI == KYTY_ABI_ARMEABI
LoadGnuLd(U"armeabi/fc_android.map", 32);
#elif KYTY_ABI == KYTY_ABI_ARM64_V8A
LoadGnuLd(U"arm64-v8a/fc_android.map", 64);
#elif KYTY_ABI == KYTY_ABI_ARMEABI_V7A
LoadGnuLd(U"armeabi-v7a/fc_android.map", 32);
#elif KYTY_ABI == KYTY_ABI_MIPS
LoadGnuLd(U"mips/fc_android.map", 64);
#elif KYTY_ABI == KYTY_ABI_MIPS64
LoadGnuLd(U"mips64/fc_android.map", 64);
#elif KYTY_ABI == KYTY_ABI_X86
LoadGnuLd(U"x86/fc_android.map", 32);
#elif KYTY_ABI == KYTY_ABI_X86_64
LoadGnuLd(U"x86_64/fc_android.map", 64);
#endif
#endif
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
DumpMap(map_file.FilenameWithoutExtension() + U".csv");
if (linker == U"lld")
{
m_p->FixBaseAddress();
}
#elif KYTY_PLATFORM == KYTY_PLATFORM_ANDROID
DumpMap(U"_map.csv");
#endif
// EXIT("1");
#endif
}
void DebugMap::LoadCsv()
{
#ifdef DEBUG_MAP_ENABLED
printf("exe_name = %s\n", g_exe_name->utf8_str().GetData());
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
String linker = Debug::GetLinker();
String map_file =
g_exe_name->FilenameWithoutExtension() + U"_" + Debug::GetCompiler() + U"_" + linker + U"_" + Debug::GetBitness() + U".csv";
LoadCsv(map_file);
if (linker == U"lld")
{
m_p->FixBaseAddress();
}
#elif KYTY_PLATFORM == KYTY_PLATFORM_ANDROID
#if KYTY_ABI == KYTY_ABI_ARMEABI
LoadCsv(U"armeabi/fc_android.csv");
#elif KYTY_ABI == KYTY_ABI_ARM64_V8A
LoadCsv(U"arm64-v8a/fc_android.csv");
#elif KYTY_ABI == KYTY_ABI_ARMEABI_V7A
LoadCsv(U"armeabi-v7a/fc_android.csv");
#elif KYTY_ABI == KYTY_ABI_MIPS
LoadCsv(U"mips/fc_android.csv");
#elif KYTY_ABI == KYTY_ABI_MIPS64
LoadCsv(U"mips64/fc_android.csv");
#elif KYTY_ABI == KYTY_ABI_X86
LoadCsv(U"x86/fc_android.csv");
#elif KYTY_ABI == KYTY_ABI_X86_64
LoadCsv(U"x86_64/fc_android.csv");
#endif
#endif
// EXIT("1");
#endif
}
DebugMap::~DebugMap()
{
Delete(m_p);
}
KYTY_ARRAY_DEFINE_SWAP(DebugMapSortSwapFunc, DebugFunctionInfo)
{
DebugMapType& map = *static_cast<DebugMapType*>(arg);
map[array[i].addr] = j;
map[array[j].addr] = i;
// DebugMap::DataType &data = *(DebugMap::DataType*)arg;
//
// printf("swap %d, %d\n", i, j);
// for(uint32_t ii = 0; ii < data.Size(); ii++)
// {
// const DebugMap::FunctionInfo& f = data.At(ii);
//
// printf("\t0x%016I64x;%I64u;%s;%s\n", f.addr, f.length, f.name.utf8_str().GetData(), f.obj.utf8_str().GetData());
// }
DebugFunctionInfo t = array[i];
array[i] = array[j];
array[j] = t;
}
void DebugMap::LoadMsvcLink(const String& name, int mode)
{
File pf(name, File::Mode::Read);
if (pf.IsInvalid())
{
return;
}
auto* buf = new uint8_t[pf.Size()];
pf.Read(buf, pf.Size());
File f;
f.OpenInMem(buf, pf.Size());
f.SetEncoding(File::Encoding::Utf8);
pf.Close();
for (;;)
{
if (f.IsEOF())
{
break;
}
String s = f.ReadLine();
s = s.RemoveChar(U'\n');
StringList list = s.Split(U" ");
if (list.Size() >= 5 && list[3] == U"f")
{
EXIT_IF(!((list.Size() == 5 && list[3] == U"f") || (list.Size() == 6 && list[3] == U"f" && list[4] == U"i")));
uintptr_t addr = (mode == 32 ? list[2].ToUint32(16) : list[2].ToUint64(16));
const String func = list.At(1);
const String obj = list.Size() == 5 ? list[4] : list[5];
if (DBG_PRINTF)
{
printf("%016" PRIx64 "; %s; %s\n", static_cast<uint64_t>(addr), func.utf8_str().GetData(), obj.utf8_str().GetData());
fflush(stdout);
}
DebugFunctionInfo inf = {addr, 0, func.utf8_str(), obj.utf8_str()};
if (m_p->map.Contains(addr))
{
String name1(m_p->data.At(m_p->map[addr]).name);
if (name1.StartsWith(U"_"))
{
name1 = name1.Mid(1);
}
// EXIT_IF(data.At(map[addr]).name != func_name);
if (name1 != func && !name1.ContainsStr(func) && !func.ContainsStr(name1))
{
if (DBG_PRINTF)
{
printf("warning: name1: %s, name2: %s\n", name1.utf8_str().GetData(), func.utf8_str().GetData());
}
// exit(1);
}
continue;
}
// if (mem_alloc_obj.Contains(inf.obj, String::CASE_INSENSITIVE)
// && func.ContainsAny(mem_alloc_names))
// {
// inf.is_mem_alloc = true;
// }
m_p->data.Add(inf);
m_p->map.Put(addr, m_p->data.Size() - 1);
} else
{
EXIT_IF(list.Size() >= 5 && list[0].ContainsStr(U":") && list[3] != U"f");
}
}
f.Close();
DeleteArray(buf);
m_p->data.Sort(DebugMapSortSwapFunc, &m_p->map);
for (uint32_t i = 0; i < m_p->data.Size(); i++)
{
if (i > 0)
{
m_p->data[i - 1].length = m_p->data[i].addr - m_p->data[i - 1].addr;
}
// UNDNAME_COMPLETE
#if 0 && (KYTY_COMPILER == KYTY_COMPILER_MSVC || KYTY_COMPILER == KYTY_COMPILER_MINGW)
if (String(data[i].name).ContainsStr("?"))
{
String n = String(data[i].name).Mid(String(data[i].name).FindIndex("?"));
//char name[1024*16];
//char name_all[1024*16];
//UnDecorateSymbolName(data[i].name.utf8_str().GetData(), name, sizeof(name) - 1, UNDNAME_COMPLETE | UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY);
String name = unDName(n, malloc, free, 0x1000);
String name_all = unDName(n, malloc, free, 0);
//UnDecorateSymbolName(n.utf8_str().GetData(), name, sizeof(name) - 1, 0x1000);
//UnDecorateSymbolName(n.utf8_str().GetData(), name_all, sizeof(name_all) - 1, 0);
if (name.At(0) == '?' || name_all.At(0) == '?')
{
n = n.Left(n.FindLastIndex("$"));
name = unDName(n, malloc, free, 0x1000);
name_all = unDName(n, malloc, free, 0);
//UnDecorateSymbolName(n.utf8_str().GetData(), name, sizeof(name) - 1, 0x1000);
//UnDecorateSymbolName(n.utf8_str().GetData(), name_all, sizeof(name_all) - 1, 0);
}
//String name_str = String(name);
//String name_all_str = String(name_all);
//if (name_all_str.Contains(name_str))
{
uint32_t first = name_all.FindIndex("(");
uint32_t last = name_all.FindLastIndex(")");
//printf("%08x, %u, %u, %s, %s, %s\n", (uint32_t)data[i].addr, first, last, name_str.utf8_str().GetData(), name_all_str.utf8_str().GetData(), n.utf8_str().GetData());
data[i].name = (name + name_all.Mid(first, last - first + 1)).utf8_str();
}// else
//{
// data[i].name = String(name_all);
//}
}
#endif
}
}
void DebugMap::LoadLlvmLld(const String& name, int bitness)
{
if (bitness != 64)
{
return;
}
File pf(name, File::Mode::Read);
if (pf.IsInvalid())
{
return;
}
auto* buf = new uint8_t[pf.Size()];
pf.Read(buf, pf.Size());
File f;
f.OpenInMem(buf, pf.Size());
f.SetEncoding(File::Encoding::Utf8);
pf.Close();
String header = f.ReadLine().RemoveChar(U'\n');
String in;
if (header == U"Address Size Align Out In Symbol")
{
uint32_t prev_addr = 0;
for (;;)
{
if (f.IsEOF())
{
break;
}
String s = f.ReadLine().RemoveChar(U'\n');
uint32_t address = 0;
if (s.At(8) == U' ')
{
address = s.Mid(0, 8).ToUint32(16);
} else
{
break;
}
if (address != prev_addr)
{
if (s.At(24) != U' ')
{
if (!s.Mid(24).StartsWith(U".text"))
{
break;
}
continue;
}
if (s.At(32) != U' ')
{
in = s.Mid(32);
in = in.FixFilenameSlash().FilenameWithoutDirectory();
in = in.Mid(0, in.FindIndex(U':'));
} else
{
String sym = s.Mid(40);
DebugFunctionInfo inf = {address, 0, sym.utf8_str(), in.utf8_str()};
m_p->data.Add(inf);
m_p->map.Put(address, m_p->data.Size() - 1);
prev_addr = address;
}
}
}
}
f.Close();
DeleteArray(buf);
m_p->data.Sort(DebugMapSortSwapFunc, &m_p->map);
for (uint32_t i = 0; i < m_p->data.Size(); i++)
{
if (i > 0 && m_p->data[i - 1].length == 0)
{
m_p->data[i - 1].length = m_p->data[i].addr - m_p->data[i - 1].addr;
}
}
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
void DebugMap::LoadGnuLd(const String& name, int bitness)
{
File pf(name, File::Mode::Read);
if (pf.IsInvalid())
{
return;
}
auto* buf = new uint8_t[pf.Size()];
pf.Read(buf, pf.Size());
File f;
f.OpenInMem(buf, pf.Size());
f.SetEncoding(File::Encoding::Utf8);
pf.Close();
int mode = 0;
String object_name;
uintptr_t object_addr = 0;
uintptr_t object_size = 0;
bool object_start = false;
bool object_unsorted = false;
for (;;)
{
if (f.IsEOF())
{
break;
}
String s = f.ReadLine();
s = s.RemoveChar(U'\n');
if (mode == 1)
{
if (s.StartsWith(U" "))
{
uintptr_t addr = bitness == 32 ? s.Mid(16, 10).ToUint32(16) : s.Mid(16, 18).ToUint64(16);
String func_name = bitness == 32 ? s.Mid(42) : s.Mid(50);
if (DBG_PRINTF)
{
printf("\t%08" PRIx32 ", %s\n", static_cast<uint32_t>(addr), func_name.utf8_str().GetData());
}
DebugFunctionInfo inf = {addr, object_size, func_name.utf8_str(), object_name.utf8_str()};
if (m_p->map.Contains(addr))
{
String name1(m_p->data.At(m_p->map[addr]).name);
if (name1.StartsWith(U"_"))
{
name1 = name1.Mid(1);
}
// EXIT_IF(data.At(map[addr]).name != func_name);
if (name1 != func_name && !name1.ContainsStr(func_name) && !func_name.ContainsStr(name1))
{
if (DBG_PRINTF)
{
printf("warning: name1: %s, name2: %s\n", name1.utf8_str().GetData(), func_name.utf8_str().GetData());
}
// exit(1);
}
continue;
}
if (object_unsorted)
{
inf.length = 0;
} else if (m_p->data.Size() > 0 && String(m_p->data.At(m_p->data.Size() - 1).obj) == object_name && !object_start)
{
// EXIT_IF(addr <= data.At(data.Size() - 1).addr);
if (addr <= m_p->data.At(m_p->data.Size() - 1).addr)
{
object_unsorted = true;
m_p->data[m_p->data.Size() - 1].length = 0;
inf.length = 0;
} else
{
uintptr_t l = addr - m_p->data.At(m_p->data.Size() - 1).addr;
m_p->data[m_p->data.Size() - 1].length = l;
EXIT_IF(object_size < l);
object_size -= l;
inf.length -= l;
}
const DebugFunctionInfo& func = m_p->data.At(m_p->data.Size() - 1);
if (DBG_PRINTF)
{
printf(" -- > %016" PRIx64 "; %" PRIu64 "; %s; %s\n", static_cast<uint64_t>(func.addr),
static_cast<uint64_t>(func.length), func.name.GetData(), func.obj.GetData());
}
} else if (inf.addr != object_addr)
{
// EXIT_IF(inf.addr < object_addr);
if (inf.addr < object_addr)
{
object_unsorted = true;
inf.length = 0;
} else
{
uintptr_t l = addr - object_addr;
object_size -= l;
inf.length -= l;
}
}
// if (mem_alloc_obj.Contains(inf.obj, String::CASE_INSENSITIVE)
// && func_name.ContainsAny(mem_alloc_names))
// {
// inf.is_mem_alloc = true;
// }
m_p->data.Add(inf);
m_p->map.Put(addr, m_p->data.Size() - 1);
const DebugFunctionInfo& func = m_p->data.At(m_p->data.Size() - 1);
if (DBG_PRINTF)
{
printf(" -- > %016" PRIx64 "; %" PRIu64 "; %s; %s\n", static_cast<uint64_t>(func.addr),
static_cast<uint64_t>(func.length), func.name.GetData(), func.obj.GetData());
}
object_start = false;
} else
{
mode = 0;
}
}
if (mode == 2)
{
StringList l = s.Split(U" ");
if (l.Size() == 3 && l[0].StartsWith(U"0x"))
{
s = U".text " + s;
mode = 0;
}
}
if (mode == 0)
{
if (s.TrimLeft().StartsWith(U".text"))
{
s = s.RemoveChar(U'\n');
StringList l = s.Split(U" ");
if (l.Size() == 4 && l[0] == U".text")
{
mode = 1;
object_name = l[3].Mid(l[3].FindLastIndex(U"/") + 1);
object_addr = l[1].ToUint64(16);
object_size = l[2].ToUint64(16);
object_start = true;
object_unsorted = false;
if (DBG_PRINTF)
{
printf("%08" PRIx32 ", %08" PRIx32 ", %s\n", static_cast<uint32_t>(object_addr), static_cast<uint32_t>(object_size),
object_name.utf8_str().GetData());
}
} else if (l.Size() == 1 && l[0] != U".text")
{
mode = 2;
}
}
}
}
f.Close();
DeleteArray(buf);
m_p->data.Sort(DebugMapSortSwapFunc, &m_p->map);
// data.Sort();
for (uint32_t i = 0; i < m_p->data.Size(); i++)
{
if (i > 0 && m_p->data[i - 1].length == 0)
{
m_p->data[i - 1].length = m_p->data[i].addr - m_p->data[i - 1].addr;
}
}
}
void DebugMap::DumpMap(const String& name)
{
File::CreateDirectories(name.DirectoryWithoutFilename());
File file(name);
if (file.IsInvalid())
{
return;
}
uint32_t size = m_p->data.Size();
file.Printf("Addr;Size;Func;Obj\n");
for (uint32_t i = 0; i < size; i++)
{
const DebugFunctionInfo& f = m_p->data.At(i);
file.Printf("0x%016" PRIx64 ";%" PRIu64 ";%s;%s\n", static_cast<uint64_t>(f.addr), static_cast<uint64_t>(f.length),
f.name.GetData(), f.obj.GetData());
}
file.Close();
}
static const DebugFunctionInfo* GetFunc(const DebugStack& s, int i)
{
uintptr_t addr = s.GetAddr(i);
if (g_dbg_map != nullptr)
{
return DebugMapPrivate::FindFunc(g_dbg_map, addr);
}
return nullptr;
}
template <class T>
static const T* find_info(const T* data, uintptr_t addr, uint32_t low, uint32_t high)
{
// AGAIN:
for (;;)
{
if (low == high)
{
const T* f = data + low;
if (addr >= f->addr && addr < f->addr + f->length)
{
return f;
}
} else
{
uint32_t mid = (low + high) >> 1u;
const T* f = data + mid;
if (addr < f->addr)
{
// return find_info(data, addr, low, mid);
high = mid;
// goto AGAIN;
continue;
}
if (addr >= f->addr + f->length)
{
// return find_info(data, addr, mid + 1, high);
low = mid + 1;
// goto AGAIN;
continue;
}
return f;
}
break;
}
return nullptr;
}
void DebugMapPrivate::FixBaseAddress()
{
uintptr_t code_base = 0;
size_t code_size = 0;
sys_get_code_info(&code_base, &code_size);
if (data.Size() != 0)
{
for (auto& p: data)
{
p.addr += code_base;
}
}
if (data2.Size() != 0)
{
for (auto& p: data2)
{
p.addr += code_base;
}
}
}
const DebugFunctionInfo* DebugMapPrivate::FindFunc(DebugMap* map, uintptr_t addr)
{
static DebugFunctionInfo s {};
if (map->m_p->data.Size() == 0)
{
if (map->m_p->data2.Size() == 0)
{
return nullptr;
}
const DebugFunctionInfo2* f2 = find_info(map->m_p->data2.GetData(), addr, 0, map->m_p->data2.Size() - 1);
if (f2 != nullptr)
{
s.addr = f2->addr;
s.length = f2->length;
s.name = String::FromUtf8(f2->name).utf8_str();
s.obj = String::FromUtf8(f2->obj).utf8_str();
return &s;
}
return nullptr;
}
return find_info(map->m_p->data.GetData(), addr, 0, map->m_p->data.Size() - 1);
}
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
static char* strtok_s(char* _Str, const char* _Delim, char** /*_Context*/)
{
return strtok(_Str, _Delim);
}
#endif
void DebugMap::LoadCsv(const String& name)
{
File pf(name, File::Mode::Read);
if (pf.IsInvalid())
{
return;
}
uint32_t size = pf.Size();
m_p->buf = new char[size + 1];
pf.Read(m_p->buf, size);
pf.Close();
m_p->buf[size] = 0;
char* context = nullptr;
[[maybe_unused]] char* p = strtok_s(m_p->buf, "\r\n;", &context);
EXIT_IF(String::FromUtf8(p) != U"Addr");
p = strtok_s(nullptr, "\r\n;", &context);
EXIT_IF(String::FromUtf8(p) != U"Size");
p = strtok_s(nullptr, "\r\n;", &context);
EXIT_IF(String::FromUtf8(p) != U"Func");
p = strtok_s(nullptr, "\r\n;", &context);
EXIT_IF(String::FromUtf8(p) != U"Obj");
for (;;)
{
char* s1 = strtok_s(nullptr, "\r\n;", &context);
char* s2 = strtok_s(nullptr, "\r\n;", &context);
char* s3 = strtok_s(nullptr, "\r\n;", &context);
char* s4 = strtok_s(nullptr, "\r\n;", &context);
if ((s1 == nullptr) || (s2 == nullptr) || (s3 == nullptr) || (s4 == nullptr))
{
break;
}
DebugFunctionInfo2 inf = {static_cast<uintptr_t>(sys_strtoui64(s1 + 2, nullptr, 16)),
static_cast<uintptr_t>(sys_strtoui64(s2, nullptr, 10)), s3, s4};
m_p->data2.Add(inf);
}
}
void DebugStack::Print(int from, bool with_name) const
{
for (int i = from; i < depth; i++)
{
const DebugFunctionInfo* f = with_name ? GetFunc(*this, i) : nullptr;
if (sizeof(uintptr_t) == 4)
{
printf("[%d] %08" PRIx32 ", %08" PRIx32 ", %s, %s\n", i - from, static_cast<uint32_t>(GetAddr(i)),
f != nullptr ? static_cast<uint32_t>(f->addr) : 0, f != nullptr ? f->obj.GetData() : "unknown",
f != nullptr ? f->name.GetData() : "unknown");
} else
{
printf("[%d] %016" PRIx64 ", %016" PRIx64 ", %s, %s\n", i - from, static_cast<uint64_t>(GetAddr(i)),
f != nullptr ? static_cast<uint64_t>(f->addr) : 0, f != nullptr ? f->obj.GetData() : "unknown",
f != nullptr ? f->name.GetData() : "unknown");
}
}
}
void DebugStack::Trace(DebugStack* stack)
{
stack->depth = DEBUG_MAX_STACK_DEPTH;
sys_stack_walk(stack->stack, &stack->depth);
}
void DebugStack::PrintAndroid(int from, bool with_name) const
{
KYTY_LOGI("---stack---\n");
for (int i = from; i < depth; i++)
{
const DebugFunctionInfo* f = with_name ? GetFunc(*this, i) : nullptr;
if (sizeof(uintptr_t) == 4)
{
KYTY_LOGI("[%d] %08" PRIx32 ", %08" PRIx32 ", %s, %s\n", i - from, static_cast<uint32_t>(GetAddr(i)),
f != nullptr ? static_cast<uint32_t>(f->addr) : 0, f != nullptr ? f->obj.GetData() : "unknown",
f != nullptr ? f->name.GetData() : "unknown");
} else
{
KYTY_LOGI("[%d] %016" PRIx64 ", %016" PRIx64 ", %s, %s\n", i - from, static_cast<uint64_t>(GetAddr(i)),
f != nullptr ? static_cast<uint64_t>(f->addr) : 0, f != nullptr ? f->obj.GetData() : "unknown",
f != nullptr ? f->name.GetData() : "unknown");
}
}
}
String Debug::GetCompiler()
{
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
return U"msvc";
#elif KYTY_COMPILER == KYTY_COMPILER_CLANG
return U"clang";
#elif KYTY_COMPILER == KYTY_COMPILER_MINGW
return U"mingw";
#elif KYTY_COMPILER == KYTY_COMPILER_GCC
return U"gcc";
#else
return U"????";
#endif
}
String Debug::GetLinker()
{
#if KYTY_LINKER == KYTY_LINKER_LD
return U"ld";
#elif KYTY_LINKER == KYTY_LINKER_LLD
return U"lld";
#elif KYTY_LINKER == KYTY_LINKER_LINK
return U"link";
#elif KYTY_LINKER == KYTY_LINKER_LLD_LINK
return U"lld_link";
#else
return U"??";
#endif
}
String Debug::GetBitness()
{
#if KYTY_BITNESS == 32
return U"32";
#elif KYTY_BITNESS == 64
return U"64";
#else
return U"??";
#endif
}
} // namespace Kyty::Core
File diff suppressed because it is too large Load Diff
+507
View File
@@ -0,0 +1,507 @@
#include "Kyty/Core/Hashmap.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/Hash.h"
#include "Kyty/Core/SafeDelete.h"
#include <cstring>
namespace Kyty::Core {
constexpr uint32_t HASH_MAX_KEY_SIZE = 32;
constexpr uint32_t HASH_MAX_VALUE_SIZE = 16;
#define KYTY_HASH_DEFINE_INT(type) \
KYTY_HASH_DEFINE_CALC(type) \
{ \
switch (sizeof(type)) \
{ \
case 8: return hash64(uint64_t(*key)); \
case 4: return hash32(uint32_t(*key)); \
case 2: return hash16(uint16_t(*key)); \
case 1: return hash8(uint8_t(*key)); \
} \
return hash((void*)key, sizeof(type)); \
} \
KYTY_HASH_DEFINE_EQUALS(type) { return (*key_a) == (*key_b); }
#define KYTY_HASH_DEFINE_PTR(type) \
KYTY_HASH_DEFINE_CALC(type) \
{ \
switch (sizeof(type)) \
{ \
case 8: return hash64(uint64_t(*key)); \
case 4: return hash32(uint32_t(uintptr_t(*key))); \
} \
return hash((void*)key, sizeof(type)); \
} \
KYTY_HASH_DEFINE_EQUALS(type) { return (*key_a) == (*key_b); }
KYTY_HASH_DEFINE_INT(int8_t);
KYTY_HASH_DEFINE_INT(uint8_t);
KYTY_HASH_DEFINE_INT(int16_t);
KYTY_HASH_DEFINE_INT(uint16_t);
KYTY_HASH_DEFINE_INT(int32_t);
KYTY_HASH_DEFINE_INT(uint32_t);
KYTY_HASH_DEFINE_INT(int64_t);
KYTY_HASH_DEFINE_INT(uint64_t);
KYTY_HASH_DEFINE_INT(char16_t);
KYTY_HASH_DEFINE_INT(char32_t);
KYTY_HASH_DEFINE_PTR(int8_t*);
KYTY_HASH_DEFINE_PTR(uint8_t*);
KYTY_HASH_DEFINE_PTR(int16_t*);
KYTY_HASH_DEFINE_PTR(uint16_t*);
KYTY_HASH_DEFINE_PTR(int32_t*);
KYTY_HASH_DEFINE_PTR(uint32_t*);
KYTY_HASH_DEFINE_PTR(int64_t*);
KYTY_HASH_DEFINE_PTR(uint64_t*);
KYTY_HASH_DEFINE_PTR(char16_t*);
KYTY_HASH_DEFINE_PTR(char32_t*);
KYTY_HASH_DEFINE_PTR(void*);
class HashmapPrivate
{
public:
struct Entry
{
uint8_t key[HASH_MAX_KEY_SIZE];
uint32_t hash;
uint8_t value[HASH_MAX_VALUE_SIZE];
Entry* next;
};
KYTY_CLASS_NO_COPY(HashmapPrivate);
HashmapPrivate(uint32_t initial_capacity, uint32_t key_size, uint32_t value_size, hash_calc_func_t hash, hash_key_equals_func_t equals,
hash_key_copy_func_t key_copy, hash_value_copy_func_t value_copy, hash_key_free_func_t key_free,
hash_value_free_func_t value_free)
: bucket_count_initial(initial_capacity), m_bucket_count(bucket_count_initial), size_max((m_bucket_count * 3) / 4),
m_key_size(key_size), m_value_size(value_size), hash_func(hash), equals_func(equals), key_copy_func(key_copy),
value_copy_func(value_copy), key_free_func(key_free), value_free_func(value_free)
{
EXIT_IF(hash == nullptr);
EXIT_IF(equals == nullptr);
EXIT_IF(key_copy == nullptr);
EXIT_IF(value_copy == nullptr);
EXIT_IF(key_free == nullptr);
EXIT_IF(value_free == nullptr);
EXIT_IF(initial_capacity & (initial_capacity - 1));
EXIT_IF(key_size > HASH_MAX_KEY_SIZE);
EXIT_IF(value_size > HASH_MAX_VALUE_SIZE);
}
virtual ~HashmapPrivate() { Clear(); }
void Clear()
{
if (size > 0)
{
for (uint32_t i = 0; i < m_bucket_count; i++)
{
Entry* entry = buckets[i];
while (entry != nullptr)
{
Entry* next = entry->next;
value_free_func(entry->value);
key_free_func(entry->key);
Delete(entry);
entry = next;
}
}
DeleteArray(buckets);
m_bucket_count = bucket_count_initial;
size_max = (m_bucket_count * 3) / 4;
buckets = nullptr;
size = 0;
}
}
inline uint32_t HashKey(const void* key) const
{
auto h = hash_func(key);
return h;
}
inline bool EqualKeys(const void* key_a, uint32_t hash_a, const void* key_b, uint32_t hash_b) const
{
if (key_a == key_b)
{
return true;
}
if (hash_a != hash_b)
{
return false;
}
return equals_func(key_a, key_b);
}
static inline uint32_t CalcIndex(uint32_t bucket_count, uint32_t hash) { return hash & (bucket_count - 1); }
uint32_t Size() const { return size; }
Entry* CreateEntry(const void* key, uint32_t hash, const void* value) const
{
auto* entry = new Entry;
key_copy_func(entry->key, key);
entry->hash = hash;
value_copy_func(entry->value, value);
entry->next = nullptr;
return entry;
}
void ExpandIfNecessary()
{
if (size > size_max)
{
uint32_t new_bucket_count = m_bucket_count << 1u;
auto** new_buckets = new Entry*[new_bucket_count];
// NOLINTNEXTLINE(bugprone-sizeof-expression)
std::memset(new_buckets, 0, sizeof(Entry*) * new_bucket_count);
// Move over existing entries.
for (uint32_t i = 0; i < m_bucket_count; i++)
{
Entry* entry = buckets[i];
while (entry != nullptr)
{
Entry* next = entry->next;
size_t index = CalcIndex(new_bucket_count, entry->hash);
entry->next = new_buckets[index];
new_buckets[index] = entry;
entry = next;
}
}
// Copy over internals.
DeleteArray(buckets);
buckets = new_buckets;
m_bucket_count = new_bucket_count;
size_max = (m_bucket_count * 3) / 4;
}
}
void Put(const void* key, const void* value)
{
uint32_t hash = HashKey(key);
uint32_t index = CalcIndex(m_bucket_count, hash);
if (buckets == nullptr)
{
buckets = new Entry*[m_bucket_count];
// NOLINTNEXTLINE(bugprone-sizeof-expression)
std::memset(buckets, 0, sizeof(Entry*) * m_bucket_count);
}
Entry** p = &(buckets[index]);
for (;;)
{
Entry* current = *p;
// Add a new entry.
if (current == nullptr)
{
*p = CreateEntry(key, hash, value);
size++;
ExpandIfNecessary();
return;
}
// Replace existing entry.
if (EqualKeys(current->key, current->hash, key, hash))
{
value_free_func(current->value);
value_copy_func(current->value, value);
return;
}
// Move to next entry.
p = &current->next;
}
}
const void* Get(const void* key) const
{
if (size == 0)
{
return nullptr;
}
uint32_t hash = HashKey(key);
uint32_t index = CalcIndex(m_bucket_count, hash);
Entry* entry = buckets[index];
while (entry != nullptr)
{
if (EqualKeys(entry->key, entry->hash, key, hash))
{
return entry->value;
}
entry = entry->next;
}
return nullptr;
}
void* OperatorSqBr(const void* key, const void* default_value)
{
uint32_t hash = HashKey(key);
uint32_t index = CalcIndex(m_bucket_count, hash);
if (buckets == nullptr)
{
buckets = new Entry*[m_bucket_count];
// NOLINTNEXTLINE(bugprone-sizeof-expression)
std::memset(buckets, 0, sizeof(Entry*) * m_bucket_count);
}
Entry** p = &(buckets[index]);
for (;;)
{
Entry* current = *p;
// Add a new entry.
if (current == nullptr)
{
current = *p = CreateEntry(key, hash, default_value);
size++;
ExpandIfNecessary();
return current->value;
}
// Replace existing entry.
if (EqualKeys(current->key, current->hash, key, hash))
{
return current->value;
}
// Move to next entry.
p = &current->next;
}
return nullptr;
}
void Remove(const void* key)
{
if (size == 0)
{
return;
}
uint32_t hash = HashKey(key);
uint32_t index = CalcIndex(m_bucket_count, hash);
// Pointer to the current entry.
Entry** p = &(buckets[index]);
Entry* current = nullptr;
while ((current = *p) != nullptr)
{
if (EqualKeys(current->key, current->hash, key, hash))
{
key_free_func(current->key);
value_free_func(current->value);
*p = current->next;
Delete(current);
size--;
if (size == 0)
{
DeleteArray(buckets);
m_bucket_count = bucket_count_initial;
size_max = (m_bucket_count * 3) / 4;
buckets = nullptr;
}
return;
}
p = &current->next;
}
}
void Start(uint32_t start_from) const
{
loop_entry = nullptr;
if (size == 0)
{
return;
}
for (loop_index = start_from; loop_index < m_bucket_count; loop_index++)
{
loop_entry = buckets[loop_index];
if (loop_entry != nullptr)
{
break;
}
}
}
bool End() const { return loop_entry == nullptr || loop_index >= m_bucket_count; }
void Next() const
{
EXIT_IF(loop_entry == nullptr);
loop_entry = loop_entry->next;
if (loop_entry == nullptr)
{
Start(loop_index + 1);
}
}
const void* Value() const
{
EXIT_IF(loop_entry == nullptr);
return loop_entry->value;
}
const void* Key() const
{
EXIT_IF(loop_entry == nullptr);
return loop_entry->key;
}
void ForEach(hash_callback_func_t callback, void* arg) const
{
for (Start(0); !End(); Next())
{
if (!callback(Key(), Value(), arg))
{
break;
}
}
}
uint32_t CollisionsCount() const
{
if (size == 0)
{
return 0;
}
uint32_t collisions = 0;
for (uint32_t i = 0; i < m_bucket_count; i++)
{
Entry* entry = buckets[i];
while (entry != nullptr)
{
if (entry->next != nullptr)
{
collisions++;
}
entry = entry->next;
}
}
return collisions;
}
mutable uint32_t loop_index = 0;
mutable Entry* loop_entry = nullptr;
Entry** buckets = nullptr;
uint32_t bucket_count_initial;
uint32_t m_bucket_count;
uint32_t size = 0;
uint32_t size_max;
uint32_t m_key_size;
uint32_t m_value_size;
hash_calc_func_t hash_func;
hash_key_equals_func_t equals_func;
hash_key_copy_func_t key_copy_func;
hash_value_copy_func_t value_copy_func;
hash_key_free_func_t key_free_func;
hash_value_free_func_t value_free_func;
};
HashmapBase::HashmapBase(uint32_t key_size, uint32_t value_size, hash_calc_func_t hash, hash_key_equals_func_t equals,
hash_key_copy_func_t key_copy, hash_value_copy_func_t value_copy, hash_key_free_func_t key_free,
hash_value_free_func_t value_free)
: m_p(new HashmapPrivate(8, key_size, value_size, hash, equals, key_copy, value_copy, key_free, value_free))
{
}
HashmapBase::~HashmapBase()
{
Delete(m_p);
}
uint32_t HashmapBase::Size() const
{
return m_p->Size();
}
void HashmapBase::Put(const void* key, const void* value)
{
m_p->Put(key, value);
}
const void* HashmapBase::Get(const void* key) const
{
return m_p->Get(key);
}
void HashmapBase::Remove(const void* key)
{
m_p->Remove(key);
}
void HashmapBase::Start() const
{
m_p->Start(0);
}
bool HashmapBase::End() const
{
return m_p->End();
}
void HashmapBase::Next() const
{
m_p->Next();
}
const void* HashmapBase::Value() const
{
return m_p->Value();
}
const void* HashmapBase::Key() const
{
return m_p->Key();
}
void HashmapBase::ForEach(hash_callback_func_t callback, void* arg) const
{
m_p->ForEach(callback, arg);
}
void* HashmapBase::OperatorSquareBrackets(const void* key, const void* default_value)
{
void* r = m_p->OperatorSqBr(key, default_value);
EXIT_IF(r == nullptr);
return r;
}
void HashmapBase::Clear()
{
m_p->Clear();
}
uint32_t HashmapBase::CollisionsCount() const
{
return m_p->CollisionsCount();
}
} // namespace Kyty::Core
+477
View File
@@ -0,0 +1,477 @@
#include "Kyty/Core/JsonReader.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/SafeDelete.h"
namespace Kyty::Core {
static String* g_json_error = nullptr;
static Json* g_null = nullptr;
static void set_error(const String& str)
{
if (g_json_error == nullptr)
{
g_json_error = new String;
}
*g_json_error = str;
}
static const char32_t* skip(const char32_t* in)
{
if (in == nullptr)
{
return nullptr;
}
while ((*in != 0u) && Char::IsSpace(*in))
{
in++;
}
return in;
}
static const char32_t* skip_number(const char32_t* in)
{
if (in == nullptr)
{
return nullptr;
}
while ((*in != 0u) && (Char::IsDecimal(*in) || *in == U'-' || *in == U'+' || *in == U'e' || *in == U'E' || *in == U'.'))
{
in++;
}
return in;
}
const char32_t* Json::parse_value(const char32_t* value)
{
EXIT_IF(!value);
switch (*value)
{
case U'n':
{
if (Char::EqualAsciiN(value, "null", 4))
{
m_type = JsonNULL;
return value + 4;
}
break;
}
case U'f':
{
if (Char::EqualAsciiN(value, "false", 5))
{
m_type = JsonBool;
return value + 5;
}
break;
}
case U't':
{
if (Char::EqualAsciiN(value, "true", 4))
{
m_type = JsonBool;
m_value_bool = true;
m_value_int = 1;
return value + 4;
}
break;
}
case U'\"': return parse_string(value);
case U'[': return parse_array(value);
case U'{': return parse_object(value);
case U'-':
case U'0':
case U'1':
case U'2':
case U'3':
case U'4':
case U'5':
case U'6':
case U'7':
case U'8':
case U'9': return parse_number(value);
default: return value; break;
}
set_error(value);
return nullptr;
}
const char32_t* Json::parse_array(const char32_t* value)
{
if (*value != U'[')
{
set_error(value);
return nullptr;
}
m_type = JsonArray;
value = skip(value + 1);
if (*value == U']')
{
return value + 1;
}
Json* child = new Json();
value = skip(child->parse_value(skip(value)));
if (value == nullptr)
{
Delete(child);
return nullptr;
}
m_list.Add(child);
while (*value == U',')
{
child = new Json();
value = skip(child->parse_value(skip(value + 1)));
if (value == nullptr)
{
Delete(child);
return nullptr;
}
m_list.Add(child);
}
if (*value == ']')
{
return value + 1;
}
set_error(value);
return nullptr;
}
const char32_t* Json::parse_object(const char32_t* value)
{
if (*value != U'{')
{
set_error(value);
return nullptr;
}
m_type = JsonObject;
value = skip(value + 1);
if (*value == U'}')
{
return value + 1;
}
Json* child = new Json();
value = skip(child->parse_value(skip(value)));
if (value == nullptr)
{
Delete(child);
return nullptr;
}
child->m_name = child->m_value_string;
child->m_value_string = U"";
if (*value != U':')
{
Delete(child);
set_error(value);
return nullptr;
}
value = skip(child->parse_value(skip(value + 1)));
if (value == nullptr)
{
Delete(child);
return nullptr;
}
m_list.Add(child);
while (*value == U',')
{
child = new Json();
value = skip(child->parse_value(skip(value + 1)));
if (value == nullptr)
{
Delete(child);
return nullptr;
}
child->m_name = child->m_value_string;
child->m_value_string = U"";
if (*value != U':')
{
Delete(child);
set_error(value);
return nullptr;
}
value = skip(child->parse_value(skip(value + 1)));
if (value == nullptr)
{
Delete(child);
return nullptr;
}
m_list.Add(child);
}
if (*value == U'}')
{
return value + 1;
}
set_error(value);
return nullptr;
}
const char32_t* Json::parse_number(const char32_t* value)
{
const char32_t* end = skip_number(value);
uint32_t len = end - value;
if (len != 0u)
{
String str(U' ', len);
std::memcpy(str.GetData(), value, len * sizeof(char32_t));
m_type = JsonNumber;
m_value_double = str.ToDouble();
m_value_int = static_cast<int64_t>(m_value_double);
m_value_string = str;
return end;
}
set_error(value);
return nullptr;
}
const char32_t* Json::parse_string(const char32_t* str)
{
if (*str != U'\"')
{
set_error(str);
return nullptr;
}
const char32_t* ptr = str + 1;
uint32_t len = 0;
for (;; len++, ptr++)
{
if (*ptr == U'\"' || *ptr == 0)
{
break;
}
if (*ptr == U'\\')
{
ptr++;
}
}
String out(' ', len);
ptr = str + 1;
char32_t* ptr2 = out.GetData();
while (*ptr != '\"' && *ptr != 0)
{
if (*ptr != '\\')
{
*ptr2++ = *ptr++;
} else
{
ptr++;
switch (*ptr)
{
case U'b': *ptr2++ = U'\b'; break;
case U'f': *ptr2++ = U'\f'; break;
case U'n': *ptr2++ = U'\n'; break;
case U'r': *ptr2++ = U'\r'; break;
case U't': *ptr2++ = U'\t'; break;
case U'u':
set_error(ptr);
return nullptr;
break;
default: *ptr2++ = *ptr; break;
}
ptr++;
}
}
if (*ptr == U'\"')
{
ptr++;
}
len = ptr2 - out.GetDataConst();
m_value_string = out.Left(len);
m_type = JsonString;
return ptr;
}
// Json::Json()
//{
// m_value_int = 0;
// m_value_double = 0.0;
// m_value_bool = false;
// m_type = JsonNULL;
//}
Json::~Json()
{
FOR (i, m_list)
{
Delete(m_list[i]);
}
}
const Json* Json::Create(const String& str)
{
Json* c = new Json();
const char32_t* value = c->parse_value(skip(str.GetDataConst()));
if (value == nullptr)
{
Delete(c);
return nullptr;
}
return c;
}
String Json::GetError()
{
if (g_json_error != nullptr)
{
return *g_json_error;
}
return U"";
}
void Json::Init()
{
if (g_null == nullptr)
{
g_null = new Json();
}
}
// const Json* Json::GetItem(const String& string) const
const Json* Json::GetItem(const char* string) const
{
FOR (i, m_list)
{
const Json* n = m_list.At(i);
if (n->m_name.EqualAsciiNoCase(string))
{
return n;
}
}
Init();
return g_null;
}
// String Json::GetString(const String& name, const String& default_value) const
String Json::GetString(const char* name, const String& default_value) const
{
const Json* n = GetItem(name);
if (!n->IsNull())
{
return n->m_value_string;
}
return default_value;
}
String Json::GetString(const char* name) const
{
const Json* n = GetItem(name);
if (!n->IsNull())
{
return n->m_value_string;
}
return U"";
}
// double Json::GetFloat(const String& name, double default_value) const
double Json::GetFloat(const char* name, double default_value) const
{
const Json* n = GetItem(name);
if (!n->IsNull())
{
return n->m_value_double;
}
return default_value;
}
// int64_t Json::GetInt(const String& name, int64_t default_value) const
int64_t Json::GetInt(const char* name, int64_t default_value) const
{
const Json* n = GetItem(name);
if (!n->IsNull())
{
return n->m_value_int;
}
return default_value;
}
bool Json::GetBool(const char* name, bool default_value) const
{
const Json* n = GetItem(name);
if (!n->IsNull())
{
return n->m_value_bool;
}
return default_value;
}
StringList Json::DbgCheckList(const StringList& required, const StringList& optional) const
{
StringList errors;
FOR (i, required)
{
if (GetItem(required.At(i).C_Str())->m_name.IsEmpty())
{
errors.Add(U"missing: " + required.At(i));
}
}
FOR (i, m_list)
{
const Json* n = m_list.At(i);
if (!n->m_name.IsEmpty() && !(required.Contains(n->m_name) || optional.Contains(n->m_name)))
{
errors.Add(U"unknown: " + n->m_name);
}
}
return errors;
}
} // namespace Kyty::Core
+342
View File
@@ -0,0 +1,342 @@
#include "Kyty/Core/Language.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/Hashmap.h"
#include "Kyty/Core/Vector.h"
namespace Kyty::Core {
static const char32_t* g_list_numeric = U"0123456789";
static const char32_t* g_list_punctuation = U" !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
static const char32_t* g_list_punctuation_2 = U"ªº¡¿";
static const char32_t* g_alphabet_english_1 = U"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const char32_t* g_alphabet_english_2 = U"abcdefghijklmnopqrstuvwxyz";
static const char32_t* g_alphabet_russian_1 = U"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ";
static const char32_t* g_alphabet_russian_2 = U"абвгдеёжзийклмнопрстуфхцчшщьыъэюя";
static const char32_t* g_alphabet_german_1 = U"ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜẞ";
static const char32_t* g_alphabet_german_2 = U"abcdefghijklmnopqrstuvwxyzäöüß";
static const char32_t* g_alphabet_french_1 = U"ABCDEFGHIJKLMNOPQRSTUVWXYZÀÂÆÈÉÊËÎÏÔŒÙÛÜŸÇ";
static const char32_t* g_alphabet_french_2 = U"abcdefghijklmnopqrstuvwxyzàâæèéêëîïôœùûüÿç";
static const char32_t* g_alphabet_italian_1 = U"ABCDEFGHILMNOPQRSTUVÀÈÉÌÍÏÒÓÙÚ";
static const char32_t* g_alphabet_italian_2 = U"abcdefghilmnopqrstuvàèéìíïòóùú";
static const char32_t* g_alphabet_spanish_1 = U"ABCDEFGHIJKLMNÑOPQRSTUVWXYZÁÉÍÓÚÜ";
static const char32_t* g_alphabet_spanish_2 = U"abcdefghijklmnñopqrstuvwxyzáéíóúü";
static const char32_t* g_alphabet_portuguese_1 = U"ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÉÊÍÒÓÔÕÚÜÇ";
static const char32_t* g_alphabet_portuguese_2 = U"abcdefghijklmnopqrstuvwxyzàáâãéêíòóôõúüç";
static const char32_t* g_short_month_english[] = {U"Jan", U"Feb", U"Mar", U"Apr", U"May", U"Jun",
U"Jul", U"Aug", U"Sep", U"Oct", U"Nov", U"Dec"};
static const char32_t* g_month_english[] = {U"January", U"February", U"March", U"April", U"May", U"June",
U"July", U"August", U"September", U"October", U"November", U"December"};
static const char32_t* g_short_month_russian[] = {U"Янв", U"Фев", U"Мар", U"Апр", U"Май", U"Июн",
U"Июл", U"Авг", U"Сен", U"Окт", U"Ноя", U"Дек"};
static const char32_t* g_month_russian[] = {U"Январь", U"Февраль", U"Март", U"Апрель", U"Май", U"Июнь",
U"Июль", U"Август", U"Сентябрь", U"Октябрь", U"Ноябрь", U"Декабрь"};
static const char32_t* g_short_day_english[] = {U"Mon", U"Tue", U"Wed", U"Thu", U"Fri", U"Sat", U"Sun"};
static const char32_t* g_day_english[] = {U"Monday", U"Tuesday", U"Wednesday", U"Thursday", U"Friday", U"Saturday", U"Sunday"};
static const char32_t* g_short_day_russian[] = {U"Пнд", U"Втн", U"Срд", U"Чтв", U"Птн", U"Сбт", U"Вск"};
static const char32_t* g_day_russian[] = {U"Понедельник", U"Вторник", U"Среда", U"Четверг", U"Пятница", U"Суббота", U"Воскресенье"};
Hashmap<String, LanguageId>* g_lang_map = nullptr;
void Language::Init()
{
g_lang_map = new Hashmap<String, LanguageId>;
g_lang_map->Put(U"de", LanguageId::German);
g_lang_map->Put(U"en", LanguageId::English);
g_lang_map->Put(U"fr", LanguageId::French);
g_lang_map->Put(U"it", LanguageId::Italian);
g_lang_map->Put(U"pt", LanguageId::Portuguese);
g_lang_map->Put(U"ru", LanguageId::Russian);
g_lang_map->Put(U"es", LanguageId::Spanish);
}
static String string_remove_duplicates(const String& s)
{
Vector<char32_t> r;
for (auto ch: s)
{
if (!r.Contains(ch))
{
r.Add(ch);
}
}
r.Sort();
r.Add(U'\0');
return r.GetDataConst();
}
String Language::GetLettersList(LanguageId lang_id)
{
String ret = U"";
switch (lang_id)
{
case LanguageId::English:
ret += g_alphabet_english_1;
ret += g_alphabet_english_2;
break;
case LanguageId::Russian:
ret += g_alphabet_russian_1;
ret += g_alphabet_russian_2;
break;
case LanguageId::German:
ret += g_alphabet_german_1;
ret += g_alphabet_german_2;
break;
case LanguageId::French:
ret += g_alphabet_french_1;
ret += g_alphabet_french_2;
break;
case LanguageId::Italian:
ret += g_alphabet_italian_1;
ret += g_alphabet_italian_2;
break;
case LanguageId::Spanish:
ret += g_alphabet_spanish_1;
ret += g_alphabet_spanish_2;
break;
case LanguageId::Portuguese:
ret += g_alphabet_portuguese_1;
ret += g_alphabet_portuguese_2;
break;
case LanguageId::Unknown: EXIT("unknown language\n");
}
return string_remove_duplicates(ret);
}
String Language::GetLettersList(const String& id)
{
return GetLettersList(GetId(id));
}
String Language::GetNumericList(const String& /*id*/)
{
String ret = U"";
ret += g_list_numeric;
return string_remove_duplicates(ret);
}
String Language::GetPunctuationList(const String& id)
{
String ret = U"";
ret += g_list_punctuation;
if (GetId(id) == LanguageId::Spanish)
{
ret += g_list_punctuation_2;
}
return string_remove_duplicates(ret);
}
String Language::GetCharList(const String& id)
{
String ret = U"";
ret += g_list_numeric;
ret += g_list_punctuation;
switch (GetId(id))
{
case LanguageId::English:
ret += g_alphabet_english_1;
ret += g_alphabet_english_2;
break;
case LanguageId::Russian:
ret += g_alphabet_russian_1;
ret += g_alphabet_russian_2;
break;
case LanguageId::German:
ret += g_alphabet_german_1;
ret += g_alphabet_german_2;
break;
case LanguageId::French:
ret += g_alphabet_french_1;
ret += g_alphabet_french_2;
break;
case LanguageId::Italian:
ret += g_alphabet_italian_1;
ret += g_alphabet_italian_2;
break;
case LanguageId::Spanish:
ret += g_list_punctuation_2;
ret += g_alphabet_spanish_1;
ret += g_alphabet_spanish_2;
break;
case LanguageId::Portuguese:
ret += g_alphabet_portuguese_1;
ret += g_alphabet_portuguese_2;
break;
case LanguageId::Unknown: EXIT("unknown language\n");
}
return string_remove_duplicates(ret);
}
String Language::GetCharListAll()
{
String ret = U"";
ret += g_list_numeric;
ret += g_list_punctuation;
ret += g_list_punctuation_2;
ret += g_alphabet_english_1;
ret += g_alphabet_english_2;
ret += g_alphabet_russian_1;
ret += g_alphabet_russian_2;
ret += g_alphabet_german_1;
ret += g_alphabet_german_2;
ret += g_alphabet_french_1;
ret += g_alphabet_french_2;
ret += g_alphabet_italian_1;
ret += g_alphabet_italian_2;
ret += g_alphabet_spanish_1;
ret += g_alphabet_spanish_2;
ret += g_alphabet_portuguese_1;
ret += g_alphabet_portuguese_2;
return string_remove_duplicates(ret);
}
String Language::GetNameOfMonth(int month, LanguageId lang_id)
{
EXIT_IF(month < 1 || month > 12);
switch (lang_id)
{
case LanguageId::English: return g_month_english[month - 1]; break;
case LanguageId::Russian: return g_month_russian[month - 1]; break;
case LanguageId::Unknown:
case LanguageId::German:
case LanguageId::French:
case LanguageId::Italian:
case LanguageId::Portuguese:
case LanguageId::Spanish: EXIT("unknown language\n");
}
return U"";
}
String Language::GetNameOfMonthShort(int month, LanguageId lang_id)
{
EXIT_IF(month < 1 || month > 12);
switch (lang_id)
{
case LanguageId::English: return g_short_month_english[month - 1]; break;
case LanguageId::Russian: return g_short_month_russian[month - 1]; break;
case LanguageId::Unknown:
case LanguageId::German:
case LanguageId::French:
case LanguageId::Italian:
case LanguageId::Portuguese:
case LanguageId::Spanish: EXIT("unknown language\n");
}
return U"";
}
String Language::GetNameOfDay(int day, LanguageId lang_id)
{
EXIT_IF(day < 1 || day > 7);
switch (lang_id)
{
case LanguageId::English: return g_day_english[day - 1]; break;
case LanguageId::Russian: return g_day_russian[day - 1]; break;
case LanguageId::Unknown:
case LanguageId::German:
case LanguageId::French:
case LanguageId::Italian:
case LanguageId::Portuguese:
case LanguageId::Spanish: EXIT("unknown language\n");
}
return U"";
}
LanguageId Language::GetId(const String& id)
{
return g_lang_map->Get(id, LanguageId::Unknown);
}
StringList Language::GetLanguages()
{
StringList ret;
FOR_HASH (*g_lang_map)
{
ret.Add(g_lang_map->Key());
}
return ret;
}
String Language::GetNameOfDayShort(int day, LanguageId lang_id)
{
EXIT_IF(day < 1 || day > 7);
switch (lang_id)
{
case LanguageId::English: return g_short_day_english[day - 1]; break;
case LanguageId::Russian: return g_short_day_russian[day - 1]; break;
case LanguageId::Unknown:
case LanguageId::German:
case LanguageId::French:
case LanguageId::Italian:
case LanguageId::Portuguese:
case LanguageId::Spanish: EXIT("unknown language\n");
}
return U"";
}
} // namespace Kyty::Core
+619
View File
@@ -0,0 +1,619 @@
#include "Kyty/Core/MemoryAlloc.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DateTime.h" // IWYU pragma: keep
#include "Kyty/Core/Debug.h" // IWYU pragma: keep
#include "Kyty/Core/Hashmap.h" // IWYU pragma: keep
#include "Kyty/Sys/SysHeap.h"
#include "Kyty/Sys/SysSync.h"
#include <cstdlib>
#include <new>
namespace Kyty::Core {
#if !defined(KYTY_FINAL) && !defined(KYTY_SHARED_DLL)
#define MEM_TRACKER
#endif
#define MEM_ALLOC_ALIGNED
#ifdef MEM_ALLOC_ALIGNED
#if KYTY_PLATFORM == KYTY_PLATFORM_ANDROID
constexpr int MEM_ALLOC_ALIGN = 8;
#else
constexpr int MEM_ALLOC_ALIGN = 16;
#endif
#endif
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BITNESS == 64
[[maybe_unused]] constexpr int STACK_CHECK_FROM = 5;
#elif KYTY_PLATFORM == KYTY_PLATFORM_ANDROID
[[maybe_unused]] constexpr int STACK_CHECK_FROM = 4;
#else
[[maybe_unused]] constexpr int STACK_CHECK_FROM = 2;
#endif
static SysCS* g_mem_cs = nullptr;
static bool g_mem_initialized = false;
static sys_heap_id_t g_default_heap = nullptr;
static size_t g_mem_max_size = 0;
#ifdef MEM_TRACKER
using pattern_t = uint32_t;
constexpr size_t PATTERN_SIZE = (sizeof(pattern_t));
constexpr size_t PATTERNS_NUM = 4;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
struct MemBlockInfoT
{
uintptr_t addr;
size_t size;
int state;
DebugStack stack;
pattern_t left_pattern;
pattern_t right_pattern;
};
thread_local int g_mem_depth = 0;
thread_local bool g_mem_tracker_enabled = true;
static Hashmap<uintptr_t, MemBlockInfoT*>* g_mem_map = nullptr;
static int g_mem_state = 0;
static size_t g_total_allocated = 0;
#define KYTY_MDBG(str, ptr) \
{ \
}
#endif
class MemLock
{
public:
MemLock()
{
g_mem_cs->Enter();
#ifdef MEM_TRACKER
g_mem_depth++;
#endif
}
~MemLock()
{
#ifdef MEM_TRACKER
g_mem_depth--;
#endif
g_mem_cs->Leave();
}
KYTY_CLASS_NO_COPY(MemLock);
#ifdef MEM_TRACKER
[[nodiscard]] bool IsRecursive() const { return g_mem_depth > 1; } // NOLINT(readability-convert-member-functions-to-static)
#endif
};
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
#pragma code_seg(push)
#endif
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
#pragma code_seg(".mem_a")
#endif
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
#pragma code_seg(".mem_b")
#endif
#ifdef MEM_TRACKER
static const Array<pattern_t, 4> g_mem_patterns = {0xAAAAAAAA, 0xCCCCCCCC, 0x55555555, 0x33333333};
static int g_pattern_id = 0;
static pattern_t pattern_next()
{
return g_mem_patterns[g_pattern_id++ % g_mem_patterns.Size()];
}
static void pattern_write(MemBlockInfoT* info)
{
auto* ptr = reinterpret_cast<uint8_t*>(info->addr);
for (int i = 0; i < 4; i++)
{
(reinterpret_cast<pattern_t*>(ptr - PATTERN_SIZE * PATTERNS_NUM))[i] = info->left_pattern;
(reinterpret_cast<pattern_t*>(ptr + info->size))[i] = info->right_pattern;
}
}
static bool pattern_check(MemBlockInfoT* info)
{
auto* ptr = reinterpret_cast<uint8_t*>(info->addr);
for (int i = 0; i < 4; i++)
{
if ((reinterpret_cast<pattern_t*>(ptr - PATTERN_SIZE * PATTERNS_NUM))[i] != info->left_pattern ||
(reinterpret_cast<pattern_t*>(ptr + info->size))[i] != info->right_pattern)
{
return false;
}
}
return true;
}
#endif
static void mem_init()
{
if (g_mem_initialized)
{
return;
}
g_mem_initialized = true;
#ifdef MEM_TRACKER
srand(DateTime::FromSystemUTC().GetTime().MsecTotal());
g_pattern_id = static_cast<int>(rand() % g_mem_patterns.Size()); // NOLINT(cert-msc30-c,cert-msc50-cpp)
#endif
#ifdef MEM_TRACKER
g_mem_depth++;
#endif
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
g_mem_cs = new (std::malloc(sizeof(SysCS))) SysCS;
g_mem_cs->Init();
#ifdef MEM_TRACKER
g_mem_map = new Hashmap<uintptr_t, MemBlockInfoT*>;
#endif
g_default_heap = sys_heap_create();
#ifdef MEM_TRACKER
g_mem_depth--;
#endif
}
void core_memory_init()
{
mem_init();
}
void* mem_alloc_check_alignment(void* ptr)
{
#ifdef MEM_ALLOC_ALIGNED
if ((uintptr_t(ptr) & uintptr_t(MEM_ALLOC_ALIGN - 1)) != 0u)
{
EXIT("mem alloc not aligned!\n");
}
#endif
return ptr;
}
void* mem_alloc(size_t size)
{
if (size == 0)
{
EXIT("size == 0\n");
}
if ((g_mem_max_size != 0u) && size > g_mem_max_size)
{
EXIT("mem_alloc(): size(%" PRIu64 ") > max(%" PRIu64 ")\n", uint64_t(size), uint64_t(g_mem_max_size));
}
mem_init();
MemLock lock;
#ifdef MEM_TRACKER
DebugStack stack;
DebugStack::Trace(&stack);
if (lock.IsRecursive() || !g_mem_tracker_enabled)
{
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
void* r = std::malloc(size);
KYTY_MDBG("- std alloc -", r);
return mem_alloc_check_alignment(r);
}
#endif
#ifdef MEM_TRACKER
auto* ptr_p = static_cast<pattern_t*>(sys_heap_alloc(g_default_heap, size + PATTERN_SIZE * PATTERNS_NUM * 2));
void* ptr = ptr_p + PATTERNS_NUM;
#else
void* ptr = sys_heap_alloc(g_default_heap, size);
#endif
if (ptr == nullptr)
{
EXIT("mem_alloc(): can't alloc %" PRIu64 " bytes\n", uint64_t(size));
}
#ifdef MEM_TRACKER
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
auto* info = static_cast<MemBlockInfoT*>(std::malloc(sizeof(MemBlockInfoT)));
info->addr = reinterpret_cast<uintptr_t>(ptr);
info->size = size;
info->state = g_mem_state;
stack.CopyTo(&info->stack);
info->left_pattern = pattern_next();
info->right_pattern = pattern_next();
pattern_write(info);
g_mem_map->Put(reinterpret_cast<uintptr_t>(ptr), info);
g_total_allocated += size;
KYTY_MDBG("- mem_alloc -", ptr);
#endif
return mem_alloc_check_alignment(ptr);
}
void* mem_realloc(void* ptr, size_t size)
{
EXIT_IF(size == 0);
if ((g_mem_max_size != 0u) && size > g_mem_max_size)
{
EXIT("mem_realloc(): size(%" PRIu64 ") > max(%" PRIu64 ")\n", uint64_t(size), uint64_t(g_mem_max_size));
}
mem_init();
MemLock lock;
#ifdef MEM_TRACKER
DebugStack stack;
DebugStack::Trace(&stack);
if (lock.IsRecursive() || !g_mem_tracker_enabled)
{
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
void* ptr2 = std::realloc(ptr, size);
KYTY_MDBG("- std realloc old -", ptr);
KYTY_MDBG("- std realloc new -", ptr2);
return mem_alloc_check_alignment(ptr2);
}
#endif
#ifdef MEM_TRACKER
auto* ptr2_b = static_cast<pattern_t*>(sys_heap_realloc(
g_default_heap, ptr != nullptr ? (static_cast<pattern_t*>(ptr)) - PATTERNS_NUM : nullptr, size + PATTERN_SIZE * PATTERNS_NUM * 2));
void* ptr2 = ptr2_b + PATTERNS_NUM;
#else
void* ptr2 = sys_heap_realloc(g_default_heap, ptr, size);
#endif
if (ptr2 == nullptr)
{
EXIT("mem_realloc(): can't alloc %" PRIu64 " bytes\n", uint64_t(size));
}
#ifdef MEM_TRACKER
if (ptr != nullptr)
{
MemBlockInfoT* const* info_p = g_mem_map->Find(reinterpret_cast<uintptr_t>(ptr));
// EXIT_IF(info == 0);
if (info_p == nullptr)
{
printf("error %016" PRIx64 "\n", reinterpret_cast<uint64_t>(ptr));
EXIT_IF(info_p == nullptr);
}
MemBlockInfoT* info = *info_p;
g_total_allocated -= info->size;
g_total_allocated += size;
if (ptr == ptr2)
{
info->size = size;
info->state = g_mem_state;
stack.CopyTo(&info->stack);
} else
{
info->addr = reinterpret_cast<uintptr_t>(ptr2);
info->size = size;
info->state = g_mem_state;
stack.CopyTo(&info->stack);
g_mem_map->Put(reinterpret_cast<uintptr_t>(ptr2), info);
g_mem_map->Remove(reinterpret_cast<uintptr_t>(ptr));
}
pattern_write(info);
} else
{
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
auto* info = static_cast<MemBlockInfoT*>(std::malloc(sizeof(MemBlockInfoT)));
info->addr = reinterpret_cast<uintptr_t>(ptr2);
info->size = size;
info->state = g_mem_state;
stack.CopyTo(&info->stack);
info->left_pattern = pattern_next();
info->right_pattern = pattern_next();
pattern_write(info);
g_total_allocated += size;
g_mem_map->Put(reinterpret_cast<uintptr_t>(ptr2), info);
}
KYTY_MDBG("- mem_realloc old -", ptr);
KYTY_MDBG("- mem_realloc new -", ptr2);
#endif
// g_mem_cs->Leave();
return mem_alloc_check_alignment(ptr2);
}
void mem_free(void* ptr)
{
EXIT_IF(!g_mem_initialized);
MemLock lock;
#ifdef MEM_TRACKER
MemBlockInfoT* const* info = g_mem_map->Find(reinterpret_cast<uintptr_t>(ptr));
if (info != nullptr)
{
if (!pattern_check(*info))
{
EXIT("memory overflow\n");
}
g_total_allocated -= (*info)->size;
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
std::free(*info);
g_mem_map->Remove(reinterpret_cast<uintptr_t>(ptr));
// printf("heap_free: %x\n", uintptr_t(ptr));
#endif
#ifdef MEM_TRACKER
sys_heap_free(g_default_heap, ptr != nullptr ? (static_cast<pattern_t*>(ptr)) - PATTERNS_NUM : nullptr);
#else
sys_heap_free(g_default_heap, ptr);
#endif
#ifdef MEM_TRACKER
} else
{
if (ptr != nullptr)
{
// printf("free: %x\n", uintptr_t(ptr));
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
std::free(ptr);
}
}
KYTY_MDBG("- mem_free -", ptr);
#endif
}
bool mem_check([[maybe_unused]] const void* ptr)
{
#ifdef MEM_TRACKER
EXIT_IF(!g_mem_initialized);
MemLock lock;
MemBlockInfoT* const* info = g_mem_map->Find(reinterpret_cast<uintptr_t>(ptr));
return info != nullptr && pattern_check(*info);
#else
return true;
#endif
}
#ifdef MEM_TRACKER
static bool KYTY_HASH_CALL mem_map_print_callback(const uintptr_t* /*key*/, MemBlockInfoT* const* value, void* arg)
{
int state = static_cast<int>(reinterpret_cast<intptr_t>(arg));
if (state <= (*value)->state)
{
if (sizeof(uintptr_t) == 4)
{
printf("\n%08" PRIx32 ", %" PRIu32 ", %d\n", static_cast<uint32_t>((*value)->addr), static_cast<uint32_t>((*value)->size),
(*value)->state);
(*value)->stack.Print(STACK_CHECK_FROM);
} else
{
printf("\n%016" PRIx64 ", %" PRIu64 ", %d\n", static_cast<uint64_t>((*value)->addr), static_cast<uint64_t>((*value)->size),
(*value)->state);
(*value)->stack.Print(STACK_CHECK_FROM);
}
}
return true;
}
static bool KYTY_HASH_CALL mem_map_stat_callback(const uintptr_t* /*key*/, MemBlockInfoT* const* value, void* arg)
{
auto* s = static_cast<MemStats*>(arg);
if (s->state <= (*value)->state)
{
s->total_allocated += (*value)->size;
s->blocks_num++;
}
return true;
}
#endif
void mem_get_stat(MemStats* s)
{
#ifdef MEM_TRACKER
if (!g_mem_initialized)
{
#endif
s->total_allocated = 0;
s->blocks_num = 0;
#ifdef MEM_TRACKER
return;
}
MemLock lock;
if (s->state == 0)
{
s->total_allocated = g_total_allocated;
s->blocks_num = g_mem_map->Size();
} else
{
s->total_allocated = 0;
s->blocks_num = 0;
g_mem_map->ForEach(mem_map_stat_callback, static_cast<void*>(s));
}
#endif
}
int mem_new_state()
{
#ifdef MEM_TRACKER
if (!g_mem_initialized)
{
#endif
return 0;
#ifdef MEM_TRACKER
}
MemLock lock;
g_mem_state++;
return g_mem_state;
#endif
}
void mem_print(int from_state)
{
#ifdef MEM_TRACKER
if (!g_mem_initialized)
{
return;
}
MemLock lock;
intptr_t s = from_state;
g_mem_map->ForEach(mem_map_print_callback, reinterpret_cast<void*>(s));
#endif
}
} // namespace Kyty::Core
#ifndef KYTY_SHARED_DLL
void* operator new(size_t size)
{
return Kyty::Core::mem_alloc(size);
}
void* operator new(std::size_t size, const std::nothrow_t& /*nothrow_value*/) noexcept
{
return Kyty::Core::mem_alloc(size);
}
void* operator new[](size_t size)
{
return Kyty::Core::mem_alloc(size);
}
void* operator new[](std::size_t size, const std::nothrow_t& /*nothrow_value*/) noexcept
{
return Kyty::Core::mem_alloc(size);
}
void operator delete(void* block) noexcept
{
Kyty::Core::mem_free(block);
}
void operator delete[](void* block) noexcept
{
Kyty::Core::mem_free(block);
}
#else
//#error "haha"
#endif
extern "C" {
void* mem_alloc_c(size_t size)
{
return Kyty::Core::mem_alloc(size);
}
void* mem_realloc_c(void* ptr, size_t size)
{
return Kyty::Core::mem_realloc(ptr, size);
}
void mem_free_c(void* ptr)
{
Kyty::Core::mem_free(ptr);
}
}
namespace Kyty::Core {
bool mem_tracker_enabled()
{
#ifdef MEM_TRACKER
return g_mem_tracker_enabled;
#else
return false;
#endif
}
void mem_tracker_enable()
{
#ifdef MEM_TRACKER
g_mem_tracker_enabled = true;
#endif
}
void mem_tracker_disable()
{
#ifdef MEM_TRACKER
g_mem_tracker_enabled = false;
#endif
}
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
#pragma code_seg(".mem_c")
#endif
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
#pragma code_seg(pop)
#endif
void mem_set_max_size(size_t size)
{
g_mem_max_size = size;
}
} // namespace Kyty::Core
+71
View File
@@ -0,0 +1,71 @@
#include "Kyty/Core/SDLSubsystem.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/MemoryAlloc.h"
#include <cstring>
// IWYU pragma: no_include <intrin.h>
// IWYU pragma: no_include "SDL_error.h"
// IWYU pragma: no_include "SDL_platform.h"
// IWYU pragma: no_include "SDL_stdinc.h"
#include "SDL.h"
namespace Kyty::Core {
void* SDLCALL game_malloc_func(size_t size)
{
return Core::mem_alloc(size);
}
void* SDLCALL game_calloc_func(size_t nmemb, size_t size)
{
void* p = Core::mem_alloc(nmemb * size);
std::memset(p, 0, nmemb * size);
return p;
}
void* SDLCALL game_realloc_func(void* mem, size_t size)
{
return Core::mem_realloc(mem, size);
}
void SDLCALL game_free_func(void* mem)
{
Core::mem_free(mem);
}
KYTY_SUBSYSTEM_INIT(SDL)
{
int sdl_alloc = SDL_GetNumAllocations();
if (sdl_alloc != 0)
{
printf("warning: SDL static alloc: %d blocks\n", sdl_alloc);
} else
{
if (SDL_SetMemoryFunctions(game_malloc_func, game_calloc_func, game_realloc_func, game_free_func) != 0)
{
KYTY_SUBSYSTEM_FAIL("%s\n", SDL_GetError());
}
}
#if SDL_DYNAMIC_API != 0
#error "SDL_DYNAMIC_API"
#endif
if (SDL_Init(0) < 0)
{
KYTY_SUBSYSTEM_FAIL("%s\n", SDL_GetError());
}
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(SDL) {}
KYTY_SUBSYSTEM_DESTROY(SDL)
{
SDL_Quit();
}
} // namespace Kyty::Core
File diff suppressed because it is too large Load Diff
+370
View File
@@ -0,0 +1,370 @@
#include "Kyty/Core/Subsystems.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/SafeDelete.h"
#include "Kyty/Sys/SysStdio.h"
#include <cstdarg>
#include <cstdlib>
#include <cstring>
#include <new>
namespace Kyty::Core {
class SubsystemPrivate
{
public:
SubsystemPrivate() = default;
virtual ~SubsystemPrivate()
{
if (fail_msg != nullptr)
{
DeleteArray(fail_msg);
}
}
KYTY_CLASS_NO_COPY(SubsystemPrivate);
bool failed {false};
char* fail_msg {nullptr};
};
class SubsystemsListPrivate
{
public:
explicit SubsystemsListPrivate(SubsystemsList* p): parent(p) {}
virtual ~SubsystemsListPrivate()
{
SubsListStruct* n = list;
for (;;)
{
if (n == nullptr)
{
break;
}
SubsListStruct* nn = n;
DepsListStruct* dl = n->deps;
for (;;)
{
if (dl == nullptr)
{
break;
}
DepsListStruct* ddl = dl;
dl = dl->next;
Delete(ddl);
}
n = n->next;
Delete(nn);
}
}
void SetArgs(int argc, char** argv)
{
this->m_argc = argc;
this->m_argv = argv;
}
void Add(Subsystem* s, std::initializer_list<Subsystem*> deps)
{
EXIT_IF(!s);
const char* name = s->Id();
auto* nl = new SubsListStruct;
nl->s = s;
nl->name = name;
nl->deps = nullptr;
nl->next = list;
nl->prev_init = nullptr;
list = nl;
for (auto* dep: deps)
{
const char* str = dep->Id();
auto* l = new DepsListStruct;
l->dep_name = str;
l->next = nl->deps;
nl->deps = l;
}
nl->initialized = false;
}
bool InitAll(bool print_msg)
{
for (;;)
{
SubsListStruct* n = FindNextToInitialize();
if (n == nullptr)
{
break;
}
n->s->Init(parent);
if (n->s->m_p->failed)
{
fail_msg = n->s->m_p->fail_msg;
fail_name = n->name;
return false;
}
if (print_msg)
{
printf("Initialized: %s\n", n->name);
}
n->initialized = true;
SubsListStruct* last = last_init;
last_init = n;
n->prev_init = last;
}
return true;
}
void DestroyAll(bool print_msg)
{
SubsListStruct* n = last_init;
for (;;)
{
if (n == nullptr)
{
break;
}
n->s->Destroy(parent);
n->initialized = false;
if (print_msg)
{
printf("Destroyed: %s\n", n->name);
}
n = n->prev_init;
}
last_init = nullptr;
}
void ShutdownAll()
{
SubsListStruct* n = last_init;
for (;;)
{
if (n == nullptr)
{
break;
}
n->s->UnexpectedShutdown(parent);
n->initialized = false;
n = n->prev_init;
}
last_init = nullptr;
}
struct DepsListStruct
{
const char* dep_name;
DepsListStruct* next;
};
struct SubsListStruct
{
Subsystem* s;
const char* name;
DepsListStruct* deps;
SubsListStruct* next;
SubsListStruct* prev_init;
bool initialized;
};
[[nodiscard]] SubsListStruct* FindByName(const char* name) const
{
SubsListStruct* n = list;
for (;;)
{
if ((n == nullptr) || std::strcmp(n->name, name) == 0)
{
break;
}
n = n->next;
}
return n;
}
[[nodiscard]] SubsListStruct* FindNextToInitialize() const
{
SubsListStruct* n = list;
for (;;)
{
if (n == nullptr)
{
break;
}
if (!n->initialized)
{
DepsListStruct* d = n->deps;
for (;;)
{
if (d == nullptr)
{
break;
}
SubsListStruct* s = FindByName(d->dep_name);
if ((s == nullptr) || !s->initialized)
{
break;
}
d = d->next;
}
if (d == nullptr)
{
return n;
}
}
n = n->next;
}
return nullptr;
}
KYTY_CLASS_NO_COPY(SubsystemsListPrivate);
SubsListStruct* list = nullptr;
SubsListStruct* last_init = nullptr;
int m_argc = 0;
char** m_argv = nullptr;
const char* fail_msg = nullptr;
const char* fail_name = nullptr;
SubsystemsList* parent;
};
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
SubsystemsList::SubsystemsList(): m_p(static_cast<SubsystemsListPrivate*>(std::malloc(sizeof(SubsystemsListPrivate))))
{
new (m_p) SubsystemsListPrivate(this);
}
SubsystemsList::~SubsystemsList()
{
m_p->~SubsystemsListPrivate();
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
std::free(m_p);
// delete p;
}
void SubsystemsList::Add(Subsystem* s, std::initializer_list<Subsystem*> deps)
{
m_p->Add(s, deps);
}
bool SubsystemsList::InitAll(bool print_msg)
{
return m_p->InitAll(print_msg);
}
void SubsystemsList::DestroyAll(bool print_msg)
{
m_p->DestroyAll(print_msg);
}
int* SubsystemsList::GetArgc()
{
return &m_p->m_argc;
}
char** SubsystemsList::GetArgv()
{
return m_p->m_argv;
}
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
Subsystem::Subsystem(): m_p(static_cast<SubsystemPrivate*>(std::malloc(sizeof(SubsystemPrivate))))
{
new (m_p) SubsystemPrivate;
}
Subsystem::~Subsystem()
{
m_p->~SubsystemPrivate();
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
std::free(m_p);
// delete p;
}
void Subsystem::Fail(const char* format, ...)
{
va_list args {};
va_start(args, format);
uint32_t len = sys_vscprintf(format, args);
if (len != 0)
{
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
char* d = static_cast<char*>(std::malloc(len + 1));
std::memset(d, 0, len + 1);
/*len = */ sys_vsnprintf(d, len, format, args);
m_p->fail_msg = d;
m_p->failed = true;
}
va_end(args);
}
const char* SubsystemsList::GetFailName() const
{
return m_p->fail_name;
}
const char* SubsystemsList::GetFailMsg() const
{
return m_p->fail_msg;
}
void SubsystemsList::SetArgs(int argc, char* argv[])
{
m_p->SetArgs(argc, argv);
}
void SubsystemsList::ShutdownAll()
{
m_p->ShutdownAll();
}
} // namespace Kyty::Core
+305
View File
@@ -0,0 +1,305 @@
#include "Kyty/Core/Threads.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/SafeDelete.h"
#include "Kyty/Core/String.h"
//#define THREADS_SDL
#ifdef THREADS_SDL
#include "SDL.h"
#else
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#endif
namespace Kyty::Core {
#ifdef THREADS_SDL
typedef uint64_t thread_id_t;
#else
using thread_id_t = std::thread::id;
#endif
struct Thread::ThreadPrivate
{
ThreadPrivate(thread_func_t func, void* arg)
: finished(false), auto_delete(false),
#ifdef THREADS_SDL
m_func(func), m_arg(arg)
{
sdl = SDL_CreateThread(thread_run, "sdl_thread", this);
}
#else
m_thread(func, arg)
{
}
#endif
bool finished;
bool auto_delete;
#ifdef THREADS_SDL
SDL_Thread* sdl;
thread_func_t m_func;
void* m_arg;
static int thread_run(void* data)
{
ThreadPrivate* t = (ThreadPrivate*)data;
t->m_func(t->m_arg);
return 0;
}
#else
std::thread m_thread;
#endif
};
struct Mutex::MutexPrivate
{
#ifdef THREADS_SDL
SDL_mutex* sdl;
#else
std::recursive_mutex m_mutex;
#endif
};
struct CondVar::CondVarPrivate
{
#ifdef THREADS_SDL
SDL_cond* sdl;
#else
std::condition_variable_any m_cv;
#endif
};
static thread_id_t g_main_thread;
static int g_main_thread_int;
static std::atomic<int> g_thread_counter = 0;
KYTY_SUBSYSTEM_INIT(Threads)
{
#ifdef THREADS_SDL
g_main_thread = SDL_ThreadID();
#else
g_main_thread = std::this_thread::get_id();
g_main_thread_int = Thread::GetThreadIdUnique();
#endif
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Threads) {}
KYTY_SUBSYSTEM_DESTROY(Threads) {}
Thread::Thread(thread_func_t func, void* arg): m_thread(new ThreadPrivate(func, arg)) // @suppress("Symbol is not resolved")
{
}
Thread::~Thread()
{
EXIT_IF(!m_thread->finished && !m_thread->auto_delete);
Delete(m_thread);
}
void Thread::Join()
{
EXIT_IF(m_thread->finished || m_thread->auto_delete);
#ifdef THREADS_SDL
int status = -1;
SDL_WaitThread(m_thread->sdl, &status);
EXIT_IF(status != 0);
#else
m_thread->m_thread.join();
#endif
m_thread->finished = true;
}
void Thread::Detach()
{
EXIT_IF(m_thread->finished || m_thread->auto_delete);
m_thread->auto_delete = true;
#ifdef THREADS_SDL
SDL_DetachThread(m_thread->sdl);
#else
m_thread->m_thread.detach();
#endif
}
void Thread::Sleep(uint32_t millis)
{
#ifdef THREADS_SDL
SDL_Delay(millis);
#else
std::this_thread::sleep_for(std::chrono::milliseconds(millis));
#endif
}
void Thread::SleepMicro(uint32_t micros)
{
#ifdef THREADS_SDL
SDL_Delay(micros / 1000);
#else
std::this_thread::sleep_for(std::chrono::microseconds(micros));
#endif
}
void Thread::SleepNano(uint64_t nanos)
{
#ifdef THREADS_SDL
SDL_Delay(nanos / 1000000);
#else
std::this_thread::sleep_for(std::chrono::nanoseconds(nanos));
#endif
}
bool Thread::IsMainThread()
{
#ifdef THREADS_SDL
return g_main_thread == thread_id_t(SDL_ThreadID());
#else
return g_main_thread == std::this_thread::get_id();
#endif
}
String Thread::GetId() const
{
#ifdef THREADS_SDL
return String::FromPrintf("%" PRIu64, (uint64_t)SDL_GetThreadID(m_thread->sdl));
#else
std::stringstream ss;
ss << m_thread->m_thread.get_id();
return String::FromUtf8(ss.str().c_str());
#endif
}
String Thread::GetThreadId()
{
#ifdef THREADS_SDL
return String::FromPrintf("%" PRIu64, (uint64_t)SDL_ThreadID());
#else
std::stringstream ss;
ss << std::this_thread::get_id();
return String::FromUtf8(ss.str().c_str());
#endif
}
Mutex::Mutex(): m_mutex(new MutexPrivate)
{
#ifdef THREADS_SDL
m_mutex->sdl = SDL_CreateMutex();
EXIT_IF(!m_mutex->sdl);
#endif
}
Mutex::~Mutex()
{
#ifdef THREADS_SDL
SDL_DestroyMutex(m_mutex->sdl);
#endif
Delete(m_mutex);
}
void Mutex::Lock()
{
#ifdef THREADS_SDL
SDL_LockMutex(m_mutex->sdl);
#else
m_mutex->m_mutex.lock();
#endif
}
void Mutex::Unlock()
{
#ifdef THREADS_SDL
SDL_UnlockMutex(m_mutex->sdl);
#else
m_mutex->m_mutex.unlock();
#endif
}
bool Mutex::TryLock()
{
#ifdef THREADS_SDL
int status = SDL_TryLockMutex(m_mutex->sdl);
if (status == 0)
{
return true;
}
return false;
#else
return m_mutex->m_mutex.try_lock();
#endif
}
CondVar::CondVar(): m_cond_var(new CondVarPrivate)
{
#ifdef THREADS_SDL
m_cond_var->sdl = SDL_CreateCond();
EXIT_IF(!m_cond_var->sdl);
#endif
}
CondVar::~CondVar()
{
#ifdef THREADS_SDL
SDL_DestroyCond(m_cond_var->sdl);
#endif
Delete(m_cond_var);
}
void CondVar::Wait(Mutex* mutex)
{
#ifdef THREADS_SDL
SDL_CondWait(m_cond_var->sdl, mutex->m_mutex->sdl);
#else
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
m_cond_var->m_cv.wait(cpp_lock);
cpp_lock.release();
#endif
}
void CondVar::WaitFor(Mutex* mutex, uint32_t micros)
{
#ifdef THREADS_SDL
SDL_CondWaitTimeout(m_cond_var->sdl, mutex->m_mutex->sdl, (micros < 1000 ? 1 : micros / 1000));
#else
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
m_cond_var->m_cv.wait_for(cpp_lock, std::chrono::microseconds(micros));
cpp_lock.release();
#endif
}
void CondVar::Signal()
{
#ifdef THREADS_SDL
SDL_CondSignal(m_cond_var->sdl);
#else
m_cond_var->m_cv.notify_one();
#endif
}
void CondVar::SignalAll()
{
#ifdef THREADS_SDL
SDL_CondBroadcast(m_cond_var->sdl);
#else
m_cond_var->m_cv.notify_all();
#endif
}
int Thread::GetThreadIdUnique()
{
static thread_local int tid = ++g_thread_counter;
return tid;
}
} // namespace Kyty::Core
+106
View File
@@ -0,0 +1,106 @@
#include "Kyty/Core/Timer.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Sys/SysTimer.h"
namespace Kyty::Core {
Timer::Timer() noexcept
{
sys_query_performance_frequency(&m_Frequency);
}
void Timer::Start()
{
sys_query_performance_counter(&m_StartTime);
m_is_paused = false;
}
void Timer::Pause()
{
EXIT_IF(m_is_paused);
sys_query_performance_counter(&m_PauseTime);
m_is_paused = true;
}
void Timer::Resume()
{
EXIT_IF(!m_is_paused);
uint64_t current_time = 0;
sys_query_performance_counter(&current_time);
m_StartTime += current_time - m_PauseTime;
m_is_paused = false;
}
bool Timer::IsPaused() const
{
return m_is_paused;
}
// return time in milliseconds
double Timer::GetTimeMs() const
{
if (m_is_paused)
{
return 1000.0 * (static_cast<double>(m_PauseTime - m_StartTime)) / static_cast<double>(m_Frequency);
}
uint64_t current_time = 0;
sys_query_performance_counter(&current_time);
return 1000.0 * (static_cast<double>(current_time - m_StartTime)) / static_cast<double>(m_Frequency);
}
// return time in seconds
double Timer::GetTimeS() const
{
if (m_is_paused)
{
return (static_cast<double>(m_PauseTime - m_StartTime)) / static_cast<double>(m_Frequency);
}
uint64_t current_time = 0;
sys_query_performance_counter(&current_time);
return (static_cast<double>(current_time - m_StartTime)) / static_cast<double>(m_Frequency);
}
// return time in ticks
uint64_t Timer::GetTicks() const
{
if (m_is_paused)
{
return (m_PauseTime - m_StartTime);
}
uint64_t current_time = 0;
sys_query_performance_counter(&current_time);
return (current_time - m_StartTime);
}
// return ticks frequency
uint64_t Timer::GetFrequency() const
{
return m_Frequency;
}
uint64_t Timer::QueryPerformanceFrequency()
{
uint64_t ret = 0;
sys_query_performance_frequency(&ret);
return ret;
}
uint64_t Timer::QueryPerformanceCounter()
{
uint64_t ret = 0;
sys_query_performance_counter(&ret);
return ret;
}
} // namespace Kyty::Core
+23
View File
@@ -0,0 +1,23 @@
file(GLOB math_src
"src/*.cpp"
)
add_library(math_obj OBJECT ${math_src})
add_library(math STATIC $<TARGET_OBJECTS:math_obj>)
target_link_libraries(math core)
target_link_libraries(math rijndael)
get_property(inc_headers TARGET math PROPERTY INCLUDE_DIRECTORIES)
target_include_directories(math_obj PRIVATE ${inc_headers})
list(APPEND check_headers
${CMAKE_SOURCE_DIR}/include
)
clang_tidy_check(math_obj "" "${check_headers}" "${inc_headers}")
include_what_you_use(math_obj "${inc_headers}")
+185
View File
@@ -0,0 +1,185 @@
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Math/Crypto.h" // IWYU pragma: associated
extern "C" {
#include "rijndael-alg-fst.h"
}
namespace Kyty::Math {
Core::ByteBuffer AES::Encrypt(const uint8_t* buf, uint32_t length, const uint8_t* key, const uint8_t* iv, Mode mode)
{
Core::ByteBuffer out;
EXIT_IF(!buf || !key);
EXIT_IF(length == 0);
EXIT_IF(mode != Mode::Cbc256Pkcs7Padding && mode != Mode::Cbc256ZeroPadding);
uint32_t rk[4 * (MAXNR + 1)];
uint8_t tmp_buf[16];
uint8_t tmp_iv[16];
if (iv != nullptr)
{
std::memcpy(tmp_iv, iv, 16);
} else
{
std::memset(tmp_iv, 0, 16);
}
if (mode == Mode::Cbc256Pkcs7Padding || mode == Mode::Cbc256ZeroPadding)
{
int nr = rijndaelKeySetupEnc(rk, key, 256);
EXIT_IF(nr != 14);
uint32_t padding = 16 - (length % 16);
if (mode == Mode::Cbc256ZeroPadding && padding == 16)
{
padding = 0;
}
Core::ByteBuffer o(length + padding);
auto* out_ptr = reinterpret_cast<uint8_t*>(o.GetData());
while (length >= 16)
{
for (int n = 0; n < 16; n++)
{
tmp_buf[n] = buf[n] ^ tmp_iv[n];
}
rijndaelEncrypt(rk, nr, tmp_buf, out_ptr);
std::memcpy(tmp_iv, out_ptr, 16);
length -= 16;
buf += 16;
out_ptr += 16;
}
if ((length + padding) != 0u)
{
EXIT_IF(length + padding != 16);
for (uint32_t n = 0; n < length; n++)
{
tmp_buf[n] = buf[n] ^ tmp_iv[n];
}
if (mode == Mode::Cbc256ZeroPadding)
{
for (uint32_t n = length; n < 16; n++)
{
tmp_buf[n] = 0u ^ tmp_iv[n];
}
} else
{
for (uint32_t n = length; n < 16; n++)
{
tmp_buf[n] = padding ^ tmp_iv[n];
}
}
rijndaelEncrypt(rk, nr, tmp_buf, out_ptr);
}
out = o;
}
return out;
}
Core::ByteBuffer AES::Encrypt(const Core::ByteBuffer& buf, const uint8_t* key, const uint8_t* iv, Mode mode)
{
return AES::Encrypt(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size(), key, iv, mode);
}
Core::ByteBuffer AES::Decrypt(const uint8_t* buf, uint32_t length, const uint8_t* key, const uint8_t* iv, Mode mode)
{
Core::ByteBuffer out;
EXIT_IF(!buf || !key);
EXIT_IF(length == 0);
EXIT_IF(mode != Mode::Cbc256Pkcs7Padding && mode != Mode::Cbc256ZeroPadding);
EXIT_IF((length % 16) != 0);
uint32_t rk[4 * (MAXNR + 1)];
uint8_t tmp_buf[16];
uint8_t tmp_iv[16];
if (iv != nullptr)
{
std::memcpy(tmp_iv, iv, 16);
} else
{
std::memset(tmp_iv, 0, 16);
}
if (mode == Mode::Cbc256Pkcs7Padding || mode == Mode::Cbc256ZeroPadding)
{
int nr = rijndaelKeySetupDec(rk, key, 256);
EXIT_IF(nr != 14);
Core::ByteBuffer o(length);
auto* out_ptr = reinterpret_cast<uint8_t*>(o.GetData());
while (length >= 16)
{
rijndaelDecrypt(rk, nr, buf, tmp_buf);
for (int n = 0; n < 16; n++)
{
out_ptr[n] = tmp_buf[n] ^ tmp_iv[n];
}
std::memcpy(tmp_iv, buf, 16);
length -= 16;
buf += 16;
out_ptr += 16;
}
EXIT_IF(length > 0);
if (mode == Mode::Cbc256Pkcs7Padding)
{
auto padding = std::to_integer<uint32_t>(o.At(o.Size() - 1));
o.RemoveAt(o.Size() - padding, padding);
}
out = o;
}
return out;
}
Core::ByteBuffer AES::EncryptStr(const String& str, const uint8_t* key, const uint8_t* iv, Mode mode)
{
String::Utf8 utf8 = str.utf8_str();
return Encrypt(reinterpret_cast<const uint8_t*>(utf8.GetDataConst()), utf8.Size(), key, iv, mode);
}
Core::ByteBuffer AES::Decrypt(const Core::ByteBuffer& buf, const uint8_t* key, const uint8_t* iv, Mode mode)
{
return AES::Decrypt(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size(), key, iv, mode);
}
String AES::DecryptStr(const uint8_t* buf, uint32_t length, const uint8_t* key, const uint8_t* iv, Mode mode)
{
Core::ByteBuffer bin = AES::Decrypt(buf, length, key, iv, mode);
EXIT_IF(bin.At(bin.Size() - 1) != (Core::Byte)0);
return String::FromUtf8(reinterpret_cast<const char*>(bin.GetDataConst()));
}
String AES::DecryptStr(const Core::ByteBuffer& buf, const uint8_t* key, const uint8_t* iv, Mode mode)
{
return AES::DecryptStr(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size(), key, iv, mode);
}
} // namespace Kyty::Math
+387
View File
@@ -0,0 +1,387 @@
#include "Kyty/Math/Crypto.h" // IWYU pragma: associated
namespace Kyty::Math {
namespace MD5 {
constexpr uint32_t S11 = 7;
constexpr uint32_t S12 = 12;
constexpr uint32_t S13 = 17;
constexpr uint32_t S14 = 22;
constexpr uint32_t S21 = 5;
constexpr uint32_t S22 = 9;
constexpr uint32_t S23 = 14;
constexpr uint32_t S24 = 20;
constexpr uint32_t S31 = 4;
constexpr uint32_t S32 = 11;
constexpr uint32_t S33 = 16;
constexpr uint32_t S34 = 23;
constexpr uint32_t S41 = 6;
constexpr uint32_t S42 = 10;
constexpr uint32_t S43 = 15;
constexpr uint32_t S44 = 21;
/* F, G, H and I are basic MD5 functions.
*/
//#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
//#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
//#define H(x, y, z) ((x) ^ (y) ^ (z))
//#define I(x, y, z) ((y) ^ ((x) | (~z)))
static uint32_t F(uint32_t x, uint32_t y, uint32_t z)
{
return (((x) & (y)) | ((~x) & (z)));
}
static uint32_t G(uint32_t x, uint32_t y, uint32_t z)
{
return (((x) & (z)) | ((y) & (~z)));
}
static uint32_t H(uint32_t x, uint32_t y, uint32_t z)
{
return ((x) ^ (y) ^ (z));
}
static uint32_t I(uint32_t x, uint32_t y, uint32_t z)
{
return ((y) ^ ((x) | (~z)));
}
/* ROTATE_LEFT rotates x left n bits.
*/
//#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
static uint32_t ROTATE_LEFT(uint32_t x, uint32_t n)
{
return (((x) << (n)) | ((x) >> (32u - (n))));
}
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
Rotation is separate from addition to prevent recomputation.
*/
//#define FF(a, b, c, d, x, s, ac) {
// (a) += F ((b), (c), (d)) + (x) + (uint32_t)(ac);
// (a) = ROTATE_LEFT ((a), (s));
// (a) += (b);
// }
//#define GG(a, b, c, d, x, s, ac) {
// (a) += G ((b), (c), (d)) + (x) + (uint32_t)(ac);
// (a) = ROTATE_LEFT ((a), (s));
// (a) += (b);
// }
//#define HH(a, b, c, d, x, s, ac) {
// (a) += H ((b), (c), (d)) + (x) + (uint32_t)(ac);
// (a) = ROTATE_LEFT ((a), (s));
// (a) += (b);
// }
//#define II(a, b, c, d, x, s, ac) {
// (a) += I ((b), (c), (d)) + (x) + (uint32_t)(ac);
// (a) = ROTATE_LEFT ((a), (s));
// (a) += (b);
// }
static void FF(uint32_t* a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
{
(*a) += F((b), (c), (d)) + (x) + (ac);
(*a) = ROTATE_LEFT((*a), (s));
(*a) += (b);
}
static void GG(uint32_t* a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
{
(*a) += G((b), (c), (d)) + (x) + (ac);
(*a) = ROTATE_LEFT((*a), (s));
(*a) += (b);
}
static void HH(uint32_t* a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
{
(*a) += H((b), (c), (d)) + (x) + (ac);
(*a) = ROTATE_LEFT((*a), (s));
(*a) += (b);
}
static void II(uint32_t* a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, uint32_t ac)
{
(*a) += I((b), (c), (d)) + (x) + (ac);
(*a) = ROTATE_LEFT((*a), (s));
(*a) += (b);
}
static void Transform(uint32_t state[4], const uint8_t block[64]);
static void Encode(uint8_t* output, const uint32_t* input, uint32_t len);
static void Decode(uint32_t* output, const uint8_t* input, uint32_t len);
static uint8_t g_padding[64] = {0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
void Init(CTX* context)
{
context->count[0] = context->count[1] = 0;
/* Load magic initialization constants.*/
context->state[0] = 0x67452301;
context->state[1] = 0xefcdab89;
context->state[2] = 0x98badcfe;
context->state[3] = 0x10325476;
}
void Update(CTX* context, const uint8_t* input, uint32_t input_len)
{
unsigned int i = 0;
unsigned int index = 0;
unsigned int part_len = 0;
/* Compute number of bytes mod 64 */
index = ((context->count[0] >> 3u) & 0x3Fu);
/* Update number of bits */
if ((context->count[0] += (input_len << 3u)) < (input_len << 3u))
{
context->count[1]++;
}
context->count[1] += (input_len >> 29u);
part_len = 64 - index;
/* Transform as many times as possible.
*/
if (input_len >= part_len)
{
std::memcpy(&context->buffer[index], (input), part_len);
Transform(context->state, context->buffer);
for (i = part_len; i + 63 < input_len; i += 64)
{
Transform(context->state, &input[i]);
}
index = 0;
} else
{
i = 0;
}
/* Buffer remaining input */
std::memcpy(&context->buffer[index], (&input[i]), input_len - i);
}
void Final(uint8_t digest[16], CTX* context)
{
unsigned char bits[8];
unsigned int index = 0;
unsigned int pad_len = 0;
/* Save number of bits */
Encode(bits, context->count, 8);
/* Pad out to 56 mod 64.
*/
index = ((context->count[0] >> 3u) & 0x3fu);
pad_len = (index < 56) ? (56 - index) : (120 - index);
Update(context, g_padding, pad_len);
/* Append length (before padding) */
Update(context, bits, 8);
/* Store state in digest */
Encode(digest, context->state, 16);
/* Zeroize sensitive information.
*/
std::memset(reinterpret_cast<uint8_t*>(context), 0, sizeof(*context));
}
static void Transform(uint32_t state[4], const uint8_t block[64])
{
uint32_t a = state[0];
uint32_t b = state[1];
uint32_t c = state[2];
uint32_t d = state[3];
uint32_t x[16];
Decode(x, block, 64);
/* Round 1 */
FF(&a, b, c, d, x[0], S11, 0xd76aa478); /* 1 */
FF(&d, a, b, c, x[1], S12, 0xe8c7b756); /* 2 */
FF(&c, d, a, b, x[2], S13, 0x242070db); /* 3 */
FF(&b, c, d, a, x[3], S14, 0xc1bdceee); /* 4 */
FF(&a, b, c, d, x[4], S11, 0xf57c0faf); /* 5 */
FF(&d, a, b, c, x[5], S12, 0x4787c62a); /* 6 */
FF(&c, d, a, b, x[6], S13, 0xa8304613); /* 7 */
FF(&b, c, d, a, x[7], S14, 0xfd469501); /* 8 */
FF(&a, b, c, d, x[8], S11, 0x698098d8); /* 9 */
FF(&d, a, b, c, x[9], S12, 0x8b44f7af); /* 10 */
FF(&c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
FF(&b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
FF(&a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
FF(&d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
FF(&c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
FF(&b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
/* Round 2 */
GG(&a, b, c, d, x[1], S21, 0xf61e2562); /* 17 */
GG(&d, a, b, c, x[6], S22, 0xc040b340); /* 18 */
GG(&c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
GG(&b, c, d, a, x[0], S24, 0xe9b6c7aa); /* 20 */
GG(&a, b, c, d, x[5], S21, 0xd62f105d); /* 21 */
GG(&d, a, b, c, x[10], S22, 0x2441453); /* 22 */
GG(&c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
GG(&b, c, d, a, x[4], S24, 0xe7d3fbc8); /* 24 */
GG(&a, b, c, d, x[9], S21, 0x21e1cde6); /* 25 */
GG(&d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
GG(&c, d, a, b, x[3], S23, 0xf4d50d87); /* 27 */
GG(&b, c, d, a, x[8], S24, 0x455a14ed); /* 28 */
GG(&a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
GG(&d, a, b, c, x[2], S22, 0xfcefa3f8); /* 30 */
GG(&c, d, a, b, x[7], S23, 0x676f02d9); /* 31 */
GG(&b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
/* Round 3 */
HH(&a, b, c, d, x[5], S31, 0xfffa3942); /* 33 */
HH(&d, a, b, c, x[8], S32, 0x8771f681); /* 34 */
HH(&c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
HH(&b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
HH(&a, b, c, d, x[1], S31, 0xa4beea44); /* 37 */
HH(&d, a, b, c, x[4], S32, 0x4bdecfa9); /* 38 */
HH(&c, d, a, b, x[7], S33, 0xf6bb4b60); /* 39 */
HH(&b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
HH(&a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
HH(&d, a, b, c, x[0], S32, 0xeaa127fa); /* 42 */
HH(&c, d, a, b, x[3], S33, 0xd4ef3085); /* 43 */
HH(&b, c, d, a, x[6], S34, 0x4881d05); /* 44 */
HH(&a, b, c, d, x[9], S31, 0xd9d4d039); /* 45 */
HH(&d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
HH(&c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
HH(&b, c, d, a, x[2], S34, 0xc4ac5665); /* 48 */
/* Round 4 */
II(&a, b, c, d, x[0], S41, 0xf4292244); /* 49 */
II(&d, a, b, c, x[7], S42, 0x432aff97); /* 50 */
II(&c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
II(&b, c, d, a, x[5], S44, 0xfc93a039); /* 52 */
II(&a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
II(&d, a, b, c, x[3], S42, 0x8f0ccc92); /* 54 */
II(&c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
II(&b, c, d, a, x[1], S44, 0x85845dd1); /* 56 */
II(&a, b, c, d, x[8], S41, 0x6fa87e4f); /* 57 */
II(&d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
II(&c, d, a, b, x[6], S43, 0xa3014314); /* 59 */
II(&b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
II(&a, b, c, d, x[4], S41, 0xf7537e82); /* 61 */
II(&d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
II(&c, d, a, b, x[2], S43, 0x2ad7d2bb); /* 63 */
II(&b, c, d, a, x[9], S44, 0xeb86d391); /* 64 */
state[0] += a;
state[1] += b;
state[2] += c;
state[3] += d;
/* Zeroize sensitive information.
*/
std::memset(reinterpret_cast<uint8_t*>(x), 0, sizeof(x));
}
/* Encodes input (uint32_t) into output (unsigned char). Assumes len is
a multiple of 4.
*/
static void Encode(uint8_t* output, const uint32_t* input, uint32_t len)
{
unsigned int i = 0;
unsigned int j = 0;
for (i = 0, j = 0; j < len; i++, j += 4)
{
output[j] = static_cast<unsigned char>(input[i] & 0xffu);
output[j + 1] = static_cast<unsigned char>((input[i] >> 8u) & 0xffu);
output[j + 2] = static_cast<unsigned char>((input[i] >> 16u) & 0xffu);
output[j + 3] = static_cast<unsigned char>((input[i] >> 24u) & 0xffu);
}
}
/* Decodes input (unsigned char) into output (uint32_t). Assumes len is
a multiple of 4.
*/
static void Decode(uint32_t* output, const uint8_t* input, uint32_t len)
{
unsigned int i = 0;
unsigned int j = 0;
for (i = 0, j = 0; j < len; i++, j += 4)
{
output[i] = (static_cast<uint32_t>(input[j])) | ((static_cast<uint32_t>(input[j + 1])) << 8u) |
((static_cast<uint32_t>(input[j + 2])) << 16u) | ((static_cast<uint32_t>(input[j + 3])) << 24u);
}
}
Core::ByteBuffer Hash(const uint8_t* buf, uint32_t length)
{
CTX ctx {};
Core::ByteBuffer ret(16);
Init(&ctx);
Update(&ctx, buf, length);
Final(reinterpret_cast<uint8_t*>(ret.GetData()), &ctx);
return ret;
}
Core::ByteBuffer Hash(const Core::ByteBuffer& buf)
{
return Hash(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size());
}
Core::ByteBuffer Hash(const String& str)
{
String::Utf8 utf8 = str.utf8_str();
return Hash(reinterpret_cast<const uint8_t*>(utf8.GetDataConst()), utf8.Size() - 1);
}
} // namespace MD5
namespace CRC32 {
static uint32_t g_crc_table[256] = {0};
static bool g_crc_initialized = false;
uint32_t Hash(const uint8_t* buf, uint32_t length)
{
uint32_t crc = 0;
if (!g_crc_initialized)
{
for (int i = 0; i < 256; i++)
{
crc = i;
for (int j = 0; j < 8; j++)
{
crc = (crc & 1u) != 0u ? (crc >> 1u) ^ 0xEDB88320u : crc >> 1u;
}
g_crc_table[i] = crc;
};
g_crc_initialized = true;
}
crc = 0xFFFFFFFF;
while ((length--) != 0u)
{
crc = g_crc_table[(crc ^ *buf++) & 0xFFu] ^ (crc >> 8u);
}
return crc ^ 0xFFFFFFFF;
}
uint32_t Hash(const Core::ByteBuffer& buf)
{
return Hash(reinterpret_cast<const uint8_t*>(buf.GetDataConst()), buf.Size());
}
uint32_t Hash(const String& str)
{
String::Utf8 utf8 = str.utf8_str();
return Hash(reinterpret_cast<const uint8_t*>(utf8.GetDataConst()), utf8.Size() - 1);
}
} // namespace CRC32
} // namespace Kyty::Math
+15
View File
@@ -0,0 +1,15 @@
#include "Kyty/Math/MathAll.h" // IWYU pragma: associated
#include "Kyty/Math/Rand.h"
namespace Kyty::Math {
KYTY_SUBSYSTEM_INIT(Math)
{
Rand::Init();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Math) {}
KYTY_SUBSYSTEM_DESTROY(Math) {}
} // namespace Kyty::Math
+153
View File
@@ -0,0 +1,153 @@
#include "Kyty/Math/Rand.h"
#include "Kyty/Core/DateTime.h"
#include "Kyty/Core/DbgAssert.h"
#include <cfloat>
#include <cmath>
#include <random>
namespace Kyty::Math {
struct RandContextT // NOLINT(cert-msc32-c,cert-msc51-cpp)
{
std::mt19937 rnd;
std::uniform_real_distribution<double> double_distribution = std::uniform_real_distribution<double>(0.0, 1.0);
std::uniform_real_distribution<double> double_distribution_i =
std::uniform_real_distribution<double>(0.0, std::nextafter(1.0, DBL_MAX));
std::uniform_real_distribution<float> float_distribution = std::uniform_real_distribution<float>(0.0f, 1.0f);
std::uniform_real_distribution<float> float_distribution_i =
std::uniform_real_distribution<float>(0.0f, std::nextafterf(1.0f, FLT_MAX));
};
RandContextT* g_rand_context = nullptr;
void Rand::Init()
{
g_rand_context = new RandContextT;
}
// random in range [0, 2^32-1]
uint32_t Rand::Uint()
{
return g_rand_context->rnd();
}
// random in range [0.0, 1.0]
double Rand::DoubleInclusive()
{
return g_rand_context->double_distribution_i(g_rand_context->rnd);
}
// random in range [0.0, 1.0)
double Rand::Double()
{
return g_rand_context->double_distribution(g_rand_context->rnd);
}
// random in range [from, to]
double Rand::DoubleInclusiveRange(double from_incl, double to_incl)
{
EXIT_IF(!(from_incl <= to_incl));
if (from_incl == to_incl)
{
return from_incl;
}
std::uniform_real_distribution<double> d(from_incl, std::nextafter(to_incl, DBL_MAX));
return d(g_rand_context->rnd);
}
// random in range [from, to)
double Rand::DoubleRange(double from_incl, double to_excl)
{
EXIT_IF(!(from_incl < to_excl));
std::uniform_real_distribution<double> d(from_incl, to_excl);
return d(g_rand_context->rnd);
}
// random in range [0.0, 1.0]
float Rand::FloatInclusive()
{
return g_rand_context->float_distribution_i(g_rand_context->rnd);
}
// random in range [0.0, 1.0)
float Rand::Float()
{
return g_rand_context->float_distribution(g_rand_context->rnd);
}
// random in range [from, to]
float Rand::FloatInclusiveRange(float from_incl, float to_incl)
{
EXIT_IF(!(from_incl <= to_incl));
if (from_incl == to_incl)
{
return from_incl;
}
std::uniform_real_distribution<float> d(from_incl, std::nextafterf(to_incl, FLT_MAX));
return d(g_rand_context->rnd);
}
// random in range [from, to)
float Rand::FloatRange(float from_incl, float to_excl)
{
EXIT_IF(!(from_incl < to_excl));
std::uniform_real_distribution<float> d(from_incl, to_excl);
return d(g_rand_context->rnd);
}
// random in range [from, to]
uint32_t Rand::UintInclusiveRange(uint32_t from_incl, uint32_t to_incl)
{
EXIT_IF(!(from_incl <= to_incl));
std::uniform_int_distribution<uint32_t> d(from_incl, to_incl);
return d(g_rand_context->rnd);
}
void Rand::Seed(unsigned int s)
{
g_rand_context->rnd.seed(s);
}
// random in range [-2147483648, 2147483647]
int32_t Rand::Int()
{
union cast_u
{
uint32_t in;
int32_t out;
} cast {};
cast.in = Uint();
return cast.out;
}
// random in range [from, to]
int32_t Rand::IntInclusiveRange(int32_t from_incl, int32_t to_incl)
{
EXIT_IF(!(from_incl <= to_incl));
std::uniform_int_distribution<int32_t> d(from_incl, to_incl);
return d(g_rand_context->rnd);
}
void Rand::SeedBySystemTime()
{
Rand::Seed(Core::Time::FromSystem().MsecTotal());
}
std::mt19937 Rand::GetRandomEngine()
{
return g_rand_context->rnd;
}
} // namespace Kyty::Math
+19
View File
@@ -0,0 +1,19 @@
#include "Kyty/Math/VectorAndMatrix.h"
//#include "Kyty/MathAll.h"
//#define VEC2_DECL
//#include "vec2_impl.h"
//#define VEC3_DECL
//#include "vec3_impl.h"
//#define VEC4_DECL
//#include "vec4_impl.h"
//#define MAT2_DECL
//#include "mat2_impl.h"
//#define MAT3_DECL
//#include "mat3_impl.h"
//#define MAT4_DECL
//#include "mat4_impl.h"
namespace Kyty::Math::m {
} // namespace Kyty::Math::m
+25
View File
@@ -0,0 +1,25 @@
file(GLOB scripts_src
"src/*.cpp"
)
add_library(scripts_obj OBJECT ${scripts_src})
add_library(scripts STATIC $<TARGET_OBJECTS:scripts_obj>)
target_link_libraries(scripts core)
#target_include_directories(scripts PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include")
get_property(inc_headers TARGET scripts PROPERTY INCLUDE_DIRECTORIES)
target_include_directories(scripts_obj PRIVATE ${inc_headers})
list(APPEND check_headers
${CMAKE_SOURCE_DIR}/include
)
clang_tidy_check(scripts_obj "" "${check_headers}" "${inc_headers}")
include_what_you_use(scripts_obj "${inc_headers}")
+329
View File
@@ -0,0 +1,329 @@
#include "Kyty/Scripts/BuildTools.h"
#include "Kyty/Core/ByteBuffer.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/Database.h"
#include "Kyty/Core/DateTime.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/Debug.h"
#include "Kyty/Core/File.h"
#include "Kyty/Core/String.h"
#include "Kyty/Scripts/Scripts.h"
namespace Kyty::BuildTools {
KYTY_SCRIPT_FUNC(call_func)
{
return 0;
}
void call_help()
{
// TODO(#108)
}
KYTY_SCRIPT_FUNC(rd_func)
{
return 0;
}
void rd_help()
{
// TODO(#108)
}
KYTY_SCRIPT_FUNC(copy_func)
{
String src;
String dst;
if (Scripts::ArgGetVarCount() == 2)
{
src = Scripts::ArgGetVar(0).ToString();
dst = Scripts::ArgGetVar(1).ToString();
} else
{
EXIT("invalid args\n");
}
Core::File::CreateDirectories(dst.DirectoryWithoutFilename());
Core::File::CopyFile(src, dst);
return 0;
}
void copy_help()
{
// TODO(#108)
}
KYTY_SCRIPT_FUNC(move_func)
{
String src;
String dst;
if (Scripts::ArgGetVarCount() == 2)
{
src = Scripts::ArgGetVar(0).ToString();
dst = Scripts::ArgGetVar(1).ToString();
} else
{
EXIT("invalid args\n");
}
Core::File::CreateDirectories(dst.DirectoryWithoutFilename());
if (!Core::File::MoveFile(src, dst))
{
EXIT("move\n");
}
return 0;
}
void move_help()
{
// TODO(#108)
}
KYTY_SCRIPT_FUNC(sync_func)
{
String src;
String dst;
int del = 1;
if (Scripts::ArgGetVarCount() == 2)
{
src = Scripts::ArgGetVar(0).ToString();
dst = Scripts::ArgGetVar(1).ToString();
} else if (Scripts::ArgGetVarCount() == 3)
{
src = Scripts::ArgGetVar(0).ToString();
dst = Scripts::ArgGetVar(1).ToString();
del = Scripts::ArgGetVar(1).ToInteger();
} else
{
EXIT("invalid args\n");
}
Core::File::SyncDirectories(src, dst, del != 0);
return 0;
}
void sync_help()
{
// TODO(#108)
}
KYTY_SCRIPT_FUNC(str_to_file_func)
{
String str;
String dst;
if (Scripts::ArgGetVarCount() == 2)
{
str = Scripts::ArgGetVar(0).ToString();
dst = Scripts::ArgGetVar(1).ToString();
} else
{
EXIT("invalid args\n");
}
Core::File f;
f.Create(dst);
f.SetEncoding(Core::File::Encoding::Utf8);
if (!f.IsInvalid())
{
f.Write(str);
}
f.Close();
return 0;
}
void str_to_file_help()
{
// TODO(#108)
}
KYTY_SCRIPT_FUNC(replace_str_func)
{
String str1;
String str2;
String dst;
if (Scripts::ArgGetVarCount() == 3)
{
str1 = Scripts::ArgGetVar(0).ToString();
str2 = Scripts::ArgGetVar(1).ToString();
dst = Scripts::ArgGetVar(2).ToString();
} else
{
EXIT("invalid args\n");
}
Core::DateTime at;
Core::DateTime wt;
Core::File::GetLastAccessAndWriteTimeUTC(dst, &at, &wt);
Core::File f;
f.Open(dst, Core::File::Mode::Read);
f.SetEncoding(Core::File::Encoding::Utf8);
bool ok = false;
if (!f.IsInvalid())
{
String s = f.ReadWholeString();
s = s.ReplaceStr(str1, str2, String::Case::Insensitive);
f.Close();
f.Create(dst);
f.SetEncoding(Core::File::Encoding::Utf8);
if (!f.IsInvalid())
{
f.Write(s);
ok = true;
}
}
f.Close();
if (ok)
{
Core::File::SetLastAccessAndWriteTimeUTC(dst, at, wt);
}
return 0;
}
void replace_str_help()
{
// TODO(#108)
}
KYTY_SCRIPT_FUNC(map_to_csv_func)
{
String mode;
String src;
String dst;
if (Scripts::ArgGetVarCount() == 3)
{
mode = Scripts::ArgGetVar(0).ToString();
src = Scripts::ArgGetVar(1).ToString();
dst = Scripts::ArgGetVar(2).ToString();
} else
{
EXIT("invalid args\n");
}
Core::DebugMap map;
if (Core::File::IsFileExisting(src))
{
Core::File::DeleteFile(dst);
printf("[ map] %s: %s -> %s\n", mode.C_Str(), src.C_Str(), dst.C_Str());
if (mode.EqualNoCase(U"mingw_ld_32") || mode.EqualNoCase(U"clang_ld_32"))
{
map.LoadGnuLd(src, 32);
map.DumpMap(dst);
} else if (mode.EqualNoCase(U"mingw_ld_64") || mode.EqualNoCase(U"clang_ld_64"))
{
map.LoadGnuLd(src, 64);
map.DumpMap(dst);
} else if (mode.EqualNoCase(U"clang_lld_64"))
{
map.LoadLlvmLld(src, 64);
map.DumpMap(dst);
} else if (mode.EqualNoCase(U"msvc_link_32"))
{
map.LoadMsvcLink(src, 32);
map.DumpMap(dst);
} else if (mode.EqualNoCase(U"msvc_link_64") || mode.EqualNoCase(U"msvc_lld_link_64"))
{
map.LoadMsvcLink(src, 64);
map.DumpMap(dst);
} else
{
printf("unknown map: %s\n", mode.C_Str());
}
}
return 0;
}
void map_to_csv_help()
{
// TODO(#108)
}
void atlas_repack_help()
{
// TODO(#108)
}
KYTY_SCRIPT_FUNC(db_key_func)
{
if (Scripts::ArgGetVarCount() != 3)
{
EXIT("invalid args\n");
}
String database = Scripts::ArgGetVar(0).ToString();
String password = Scripts::ArgGetVar(1).ToString();
int legacy = Scripts::ArgGetVar(2).ToInteger();
Core::Database::Connection db;
db.Open(database, Core::Database::Connection::Mode::ReadOnly);
if (db.IsInvalid() || db.IsError())
{
EXIT("Can't open file: %s\n", database.C_Str());
}
db.SetPassword(password, legacy);
auto key = db.GetKey();
FOR (i, key)
{
printf("%s0x%02" PRIx8 "%s", (i > 0 ? ", " : "{ "), std::to_integer<uint8_t>(key.At(i)), (i == key_size_ - 1 ? " }\n" : ""));
}
db.Close();
return 0;
}
void db_key_help()
{
// TODO(#108)
}
void Init()
{
Scripts::RegisterFunc("sync", BuildTools::sync_func, BuildTools::sync_help);
Scripts::RegisterFunc("copy", BuildTools::copy_func, BuildTools::copy_help);
Scripts::RegisterFunc("map_to_csv", BuildTools::map_to_csv_func, BuildTools::map_to_csv_help);
Scripts::RegisterFunc("str_to_file", BuildTools::str_to_file_func, BuildTools::str_to_file_help);
Scripts::RegisterFunc("replace_str", BuildTools::replace_str_func, BuildTools::replace_str_help);
Scripts::RegisterFunc("db_key", BuildTools::db_key_func, BuildTools::db_key_help);
}
KYTY_SUBSYSTEM_INIT(BuildTools)
{
BuildTools::Init();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(BuildTools) {}
KYTY_SUBSYSTEM_DESTROY(BuildTools) {}
} // namespace Kyty::BuildTools
+923
View File
@@ -0,0 +1,923 @@
#include "Kyty/Scripts/Scripts.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/File.h"
#include "Kyty/Core/Hashmap.h"
#include "Kyty/Core/MemoryAlloc.h"
#include "Kyty/Core/RefCounter.h"
#include "Kyty/Core/SafeDelete.h"
#include "Kyty/Core/Threads.h"
#include "Kyty/Scripts/LuaCpp.h"
// IWYU pragma: no_include <sec_api/string_s.h>
namespace Kyty::Scripts {
using Core::File;
using Core::Hashmap;
using Core::mem_tracker_disable;
using Core::mem_tracker_enable;
static thread_local lua_State* g_lua_state = nullptr;
static thread_local String* g_lua_error = nullptr;
struct HelpFuncList
{
Hashmap<String, help_func_t> map;
};
static thread_local HelpFuncList* g_help_list = nullptr;
static const luaL_Reg g_loadedlibs[] = {{"_G", luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_COLIBNAME, luaopen_coroutine},
{LUA_TABLIBNAME, luaopen_table},
{LUA_IOLIBNAME, luaopen_io},
{LUA_OSLIBNAME, luaopen_os},
{LUA_STRLIBNAME, luaopen_string},
{LUA_BITLIBNAME, luaopen_bit32},
{LUA_MATHLIBNAME, luaopen_math},
{LUA_DBLIBNAME, luaopen_debug},
{nullptr, nullptr}};
static void lua_my_openlibs(lua_State* l)
{
for (const luaL_Reg* lib = g_loadedlibs; lib->func != nullptr; lib++)
{
luaL_requiref(l, lib->name, lib->func, 1);
lua_pop(l, 1);
}
}
void scripts_file_lib_reg();
static void load_libs()
{
lua_my_openlibs(g_lua_state);
scripts_file_lib_reg();
}
static void lua_init()
{
if (g_lua_state == nullptr)
{
mem_tracker_disable();
g_lua_state = luaL_newstate();
if (g_lua_state == nullptr)
{
EXIT("Lua state is NULL");
}
load_libs();
g_help_list = new HelpFuncList;
mem_tracker_enable();
}
}
KYTY_SUBSYSTEM_INIT(Scripts)
{
g_lua_state = nullptr;
g_lua_error = nullptr;
g_help_list = nullptr;
if (RunString(U"_script_check_a = _script_check_b") != ScriptError::Ok || RunString(U"_a _b _c _d") != ScriptError::SyntaxError ||
RunString(U"_script_check_a = _script_check_b[1]") != ScriptError::RunError)
{
KYTY_SUBSYSTEM_FAIL("Can't run Lua scripts");
}
ResetErrMsg();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Scripts) {}
KYTY_SUBSYSTEM_DESTROY(Scripts)
{
// lua_close(g_lua_state);
}
void script_dbg_dump_stack(const String& prefix)
{
lua_init();
printf("%s\n", prefix.C_Str());
int top = lua_gettop(g_lua_state);
for (int i = 1; i <= top; i++)
{
int t = lua_type(g_lua_state, i);
printf("%d: %s, n: %g\n", i, lua_typename(g_lua_state, t), lua_tonumber(g_lua_state, i));
}
}
ScriptError LoadString(const String& source)
{
lua_init();
int err = luaL_loadstring(g_lua_state, source.C_Str());
// printf("source:\n%s\n", source.C_Str());
// printf("binary:\n");
// ScriptVar::ReadVar(-1).DbgPrint(0);
if (err != LUA_OK)
{
ScriptVar s = ScriptVar::ReadVar(-1, false);
SetErrMsg(s.ToString());
lua_pop(g_lua_state, 1);
}
if (err == LUA_ERRSYNTAX)
{
return ScriptError::SyntaxError;
}
if (err != LUA_OK)
{
return ScriptError::UnknownError;
}
return ScriptError::Ok;
}
ScriptError Run()
{
lua_init();
int err = lua_pcall(g_lua_state, 0, LUA_MULTRET, 0);
// int top = lua_gettop(g_lua_state);
if (err != LUA_OK)
{
// script_dbg_dump_stack("");
ScriptVar s = ScriptVar::ReadVar(-1, false);
SetErrMsg(s.ToString());
lua_pop(g_lua_state, 1);
}
// EXIT_IF(lua_gettop(g_lua_state));
if (err == LUA_ERRRUN)
{
return ScriptError::RunError;
}
if (err != LUA_OK)
{
return ScriptError::UnknownError;
}
return ScriptError::Ok;
}
ScriptError RunFile(const String& file_name)
{
lua_init();
// EXIT_IF(lua_gettop(g_lua_state) > 0);
File f(file_name, File::Mode::Read);
f.SetEncoding(File::Encoding::Utf8);
if (f.IsInvalid())
{
return ScriptError::FileError;
}
String str = f.ReadWholeString();
f.Close();
// TODO(#117): load and run binary files
ScriptError status = LoadString(str);
if (status != ScriptError::Ok)
{
return status;
}
status = Run();
return status;
}
ScriptError RunString(const String& source)
{
lua_init();
// EXIT_IF(lua_gettop(g_lua_state) > 0);
ScriptError status = LoadString(source);
if (status != ScriptError::Ok)
{
return status;
}
status = Run();
return status;
}
void RegisterSystemFunc(const char* name, script_func_t func)
{
lua_init();
// EXIT_IF(GlobalGetVar(name).IsNil());
lua_register(g_lua_state, name, reinterpret_cast<lua_CFunction>(func));
}
void RegisterFunc(const char* name, script_func_t func, help_func_t help)
{
lua_init();
String n = String::FromUtf8(name);
EXIT_IF(!GlobalGetVar(n).IsNil());
EXIT_IF(g_help_list->map.Contains(n));
lua_register(g_lua_state, name, reinterpret_cast<lua_CFunction>(func));
g_help_list->map.Put(n, help);
}
void UnregisterFunc(const char* name)
{
lua_init();
String n = String::FromUtf8(name);
EXIT_IF(!GlobalGetVar(n).IsCFunction());
EXIT_IF(!g_help_list->map.Contains(n));
RunString(n + U" = nil");
g_help_list->map.Remove(n);
}
int ArgGetVarCount()
{
lua_init();
return lua_gettop(g_lua_state);
}
void ArgDbgDump()
{
lua_init();
int num = ArgGetVarCount();
for (int i = 0; i < num; i++)
{
printf("--[%d]--\n", i);
ArgGetVar(i).DbgPrint(0);
}
}
void PrintHelp()
{
lua_init();
FOR_HASH (g_help_list->map)
{
printf("Lua function: %s\n", g_help_list->map.Key().C_Str());
auto f = g_help_list->map.Value();
f();
}
}
class ScriptVar::ScriptVarPrivate: public Core::RefCounter<Core::DummyMutex>
{
public:
ScriptVarPrivate() = default;
bool is_table = {false};
bool is_nil = {true};
bool is_function = {false};
bool is_c_function = {false};
bool is_double = {false};
#if KYTY_LUA_VER == KYTY_LUA_5_3
bool is_integer = {false};
#endif
bool is_string = {false};
bool is_userdata = {false};
double val_double = {0.0};
bool val_bool = {false};
#if KYTY_LUA_VER == KYTY_LUA_5_3
int64_t val_integer = {0};
#endif
String val_string;
ScriptTable val_table;
ScriptFunction val_function;
void* val_userdata = {nullptr};
};
// ScriptVar::ScriptVarPrivate::ScriptVarPrivate()
// : is_table(false), is_nil(true), is_function(false), is_c_function(false), is_double(false),
//#if KYTY_LUA_VER == KYTY_LUA_5_3
// is_integer(false),
//#endif
// is_string(false), is_userdata(false), val_double(0.0), val_bool(false),
//#if KYTY_LUA_VER == KYTY_LUA_5_3
// val_integer(0),
//#endif
// val_userdata(nullptr)
//{
//}
void ScriptVar::DbgPrint(int depth) const
{
lua_init();
for (int i = 0; i < depth; i++)
{
printf(" ");
}
if (m_p->is_nil)
{
printf("nil\n");
} else if (m_p->is_table)
{
printf("Table:\n");
m_p->val_table.DbgPrint(depth + 1);
} else if (m_p->is_c_function)
{
printf("CFunction\n");
} else if (m_p->is_function)
{
printf("Function:\n");
printf("%s\n", m_p->val_function.ToDbgString().C_Str());
} else if (m_p->is_userdata)
{
printf("Userdata: %016" PRIx64 "\n", reinterpret_cast<uint64_t>(m_p->val_userdata));
} else
{
#if KYTY_LUA_VER == KYTY_LUA_5_3
printf("d: %g, i: %" PRIi64 " s: %s\n", val_double, val_integer, val_string.C_Str());
#else
printf("d: %g, s: %s\n", m_p->val_double, m_p->val_string.C_Str());
#endif
}
}
ScriptVar::ScriptVar(): m_p(new ScriptVarPrivate)
{
// m_p = new ScriptVarPrivate;
}
ScriptVar::~ScriptVar()
{
if (m_p != nullptr)
{
m_p->Release();
}
}
ScriptVar::ScriptVar(const ScriptVar& src)
{
src.m_p->CopyPtr(&m_p, src.m_p);
}
ScriptVar::ScriptVar(ScriptVar&& src) noexcept: m_p(src.m_p)
{
// m_p = src.m_p;
src.m_p = nullptr;
}
ScriptVar& ScriptVar::operator=(const ScriptVar& src)
{
if (this != &src && m_p != src.m_p)
{
if (m_p != nullptr)
{
m_p->Release();
}
src.m_p->CopyPtr(&m_p, src.m_p);
}
return *this;
}
ScriptVar& ScriptVar::operator=(ScriptVar&& src) noexcept
{
if (m_p != src.m_p)
{
if (m_p != nullptr)
{
m_p->Release();
}
m_p = src.m_p;
src.m_p = nullptr;
}
return *this;
}
ScriptVar ScriptVar::ReadVar(int index, bool with_metatable_index)
{
lua_init();
ScriptVar r;
EXIT_IF(index == 0);
// script_dbg_dump_stack(String::FromPrintf("script_read_var( %d )", index));
if (index < 0)
{
index = lua_gettop(g_lua_state) + index + 1;
}
lua_pushvalue(g_lua_state, index);
// printf("sizeof(lua_Integer) = %d\n", (int)sizeof(lua_Integer));
// printf("LUA_INTEGER_FRMLEN = %s\n", LUA_INTEGER_FRMLEN);
// printf("LUA_MAXINTEGER = %" PRIi64"\n", (int64_t)LUA_MAXINTEGER);
// printf("LUA_MININTEGER = %" PRIi64"\n", (int64_t)LUA_MININTEGER);
r.m_p->val_double = lua_tonumber(g_lua_state, -1);
r.m_p->val_bool = (lua_toboolean(g_lua_state, -1) != 0);
#if KYTY_LUA_VER == KYTY_LUA_5_3
Read.m_p->val_integer = lua_tointeger(g_lua_state, -1);
#endif
r.m_p->val_string = String::FromUtf8(lua_tostring(g_lua_state, -1));
r.m_p->val_userdata = lua_touserdata(g_lua_state, -1);
r.m_p->is_c_function = (lua_iscfunction(g_lua_state, -1) != 0);
r.m_p->is_double = (lua_isnumber(g_lua_state, -1) != 0);
r.m_p->is_function = lua_isfunction(g_lua_state, -1);
#if KYTY_LUA_VER == KYTY_LUA_5_3
Read.is_integer = lua_isinteger(g_lua_state, -1);
if (!Read.is_integer)
{
Read.is_integer = lua_tointeger(g_lua_state, -1) != 0 || lua_tostring(g_lua_state, -1) == String("0");
}
#endif
r.m_p->is_nil = lua_isnil(g_lua_state, -1);
r.m_p->is_string = (lua_isstring(g_lua_state, -1) != 0);
r.m_p->is_table = lua_istable(g_lua_state, -1);
r.m_p->is_userdata = (lua_isuserdata(g_lua_state, -1) != 0);
if (r.m_p->is_function)
{
r.m_p->val_function.LoadFromStack();
}
lua_pop(g_lua_state, 1);
if (r.m_p->is_table)
{
lua_pushnil(g_lua_state); /* first key */
// script_dbg_dump_stack(String::FromPrintf("is_table"));
while (lua_next(g_lua_state, index) != 0)
{
// script_dbg_dump_stack(String::FromPrintf("after_next"));
int top = lua_gettop(g_lua_state);
/* uses 'key' (at index top-2+1) and 'value' (at index top-1+1) */
ScriptVar k = ReadVar(top - 2 + 1, with_metatable_index);
ScriptVar v = ReadVar(top - 1 + 1, with_metatable_index);
r.m_p->val_table.Add(k, v);
/* removes 'value'; keeps 'key' for next iteration */
lua_pop(g_lua_state, 1);
// script_dbg_dump_stack(String::FromPrintf("end_of_while"));
}
// lua_pop(g_lua_state, 1);
// script_dbg_dump_stack(String::FromPrintf("last_pop"));
if (with_metatable_index)
{
if (lua_getmetatable(g_lua_state, index) != 0)
{
ScriptVar metatable = ReadVar(-1, true);
ScriptVar parent = metatable.At(U"__index");
if (parent.IsTable())
{
// NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks)
for (const auto& p: parent.GetPairs())
{
if (r.At(p.GetKey().ToString()).IsNil())
{
r.m_p->val_table.Add(p.GetKey(), p.GetValue());
}
}
}
lua_pop(g_lua_state, 1);
}
}
}
return r;
}
bool ScriptVar::IsTable() const
{
return m_p->is_table;
}
bool ScriptVar::IsNil() const
{
return m_p->is_nil;
}
bool ScriptVar::IsFunction() const
{
return m_p->is_function;
}
bool ScriptVar::IsCFunction() const
{
return m_p->is_c_function;
}
bool ScriptVar::IsDouble() const
{
return m_p->is_double;
}
#if KYTY_LUA_VER == KYTY_LUA_5_3
bool ScriptVar::IsInteger() const
{
return m_p->is_integer;
}
#else
bool ScriptVar::IsInteger() const
{
return m_p->is_double && double(int32_t(m_p->val_double)) == m_p->val_double;
}
#endif
bool ScriptVar::IsString() const
{
return m_p->is_string;
}
bool ScriptVar::IsUserdata() const
{
return m_p->is_string;
}
#if KYTY_LUA_VER == KYTY_LUA_5_3
int64_t ScriptVar::ToInteger() const
{
return val_integer;
}
#else
int32_t ScriptVar::ToInteger() const
{
return IsInteger() ? int32_t(m_p->val_double) : 0;
}
#endif
double ScriptVar::ToDouble() const
{
return m_p->val_double;
}
float ScriptVar::ToFloat() const
{
return static_cast<float>(m_p->val_double);
}
bool ScriptVar::ToBool() const
{
return m_p->val_bool;
}
String ScriptVar::ToString() const
{
return m_p->val_string;
}
void* ScriptVar::ToUserdata() const
{
return m_p->val_userdata;
}
const ScriptTable& ScriptVar::ToTable() const
{
return m_p->val_table;
}
const ScriptTable::List& ScriptVar::GetPairs() const
{
return m_p->val_table.GetList();
}
ScriptVar ScriptVar::At(int64_t key) const
{
return m_p->val_table.At(static_cast<double>(key));
}
ScriptVar ScriptVar::At(const String& key) const
{
return m_p->val_table.At(key);
}
ScriptVar ScriptVar::At(const char* key) const
{
return m_p->val_table.At(String::FromUtf8(key));
}
ScriptVar ScriptVar::At(double key) const
{
return m_p->val_table.At(key);
}
uint32_t ScriptVar::Count() const
{
return m_p->val_table.Count();
}
uint32_t ScriptVar::Size() const
{
return m_p->val_table.Count();
}
ScriptVar ScriptVar::GetKey(uint32_t index) const
{
return m_p->val_table.GetKey(index);
}
ScriptVar ScriptVar::GetValue(uint32_t index) const
{
return m_p->val_table.GetValue(index);
}
void script_global_dbg_dump_var(const String& var_name)
{
lua_init();
lua_getglobal(g_lua_state, var_name.C_Str());
ScriptVar r = ScriptVar::ReadVar(-1, false);
r.DbgPrint(0);
lua_pop(g_lua_state, 1);
}
ScriptVar GlobalGetVar(const String& var_name)
{
lua_init();
lua_getglobal(g_lua_state, var_name.C_Str());
ScriptVar ret = ScriptVar::ReadVar(-1, false);
lua_pop(g_lua_state, 1);
return ret;
}
ScriptVar GlobalGetVarWithParent(const String& var_name)
{
lua_init();
lua_getglobal(g_lua_state, var_name.C_Str());
ScriptVar ret = ScriptVar::ReadVar(-1, true);
lua_pop(g_lua_state, 1);
return ret;
}
void SetErrMsg(const String& msg)
{
lua_init();
if (g_lua_error == nullptr)
{
g_lua_error = new String();
}
*g_lua_error = msg;
}
const String& GetErrMsg()
{
lua_init();
if (g_lua_error == nullptr)
{
g_lua_error = new String();
}
return *g_lua_error;
}
void ResetErrMsg()
{
lua_init();
delete g_lua_error;
g_lua_error = nullptr;
}
ScriptVar ArgGetVar(int index)
{
lua_init();
ScriptVar ret = ScriptVar::ReadVar(index + 1, false);
return ret;
}
ScriptVar ArgGetVarWithParent(int index)
{
lua_init();
ScriptVar ret = ScriptVar::ReadVar(index + 1, true);
return ret;
}
void PushString(const String& str)
{
lua_init();
lua_pushstring(g_lua_state, str.C_Str());
}
void PushDouble(double d)
{
lua_init();
lua_pushnumber(g_lua_state, static_cast<lua_Number>(d));
}
#if KYTY_LUA_VER == KYTY_LUA_5_3
void PushInteger(int64_t i)
{
lua_init();
lua_pushinteger(g_lua_state, i);
}
#else
void PushInteger(int32_t i)
{
lua_init();
PushDouble(double(i));
}
#endif
#if KYTY_LUA_VER == KYTY_LUA_5_3
ScriptVar ScriptTable::At(int64_t m_key) const
{
lua_init();
FOR (i, keys)
{
const ScriptVar& v = keys.At(i);
if (v.IsInteger() && v.ToInteger() == m_key)
{
return values.At(i);
}
}
return ScriptVar();
}
#endif
ScriptVar ScriptTable::At(const String& key) const
{
lua_init();
// FOR(i, keys)
for (const auto& p: m_pairs)
{
const ScriptVar& v = p.GetKey();
if (v.IsString() && v.ToString() == key)
{
return p.GetValue();
}
}
return ScriptVar();
}
ScriptVar ScriptTable::At(double key) const
{
lua_init();
// FOR(i, keys)
for (const auto& p: m_pairs)
{
const ScriptVar& v = p.GetKey();
if (v.IsDouble() && v.ToDouble() == key)
{
return p.GetValue();
}
}
return ScriptVar();
}
void ScriptTable::DbgPrint(int depth) const
{
lua_init();
// FOR(k, keys)
int k = 0;
for (const auto& p: m_pairs)
{
for (int i = 0; i < depth; i++)
{
printf(" ");
}
printf("--- %d ---\n", k++);
for (int i = 0; i < depth; i++)
{
printf(" ");
}
printf("Key:\n");
p.GetKey().DbgPrint(depth);
for (int i = 0; i < depth; i++)
{
printf(" ");
}
printf("Value:\n");
p.GetValue().DbgPrint(depth);
}
}
void ScriptTable::Add(const ScriptVar& k, const ScriptVar& v)
{
lua_init();
ScriptPair p(k, v);
m_pairs.Add(p);
// keys.Add(k);
// values.Add(v);
}
ScriptVar ScriptTable::GetKey(uint32_t index) const
{
lua_init();
return m_pairs.At(index).GetKey();
}
ScriptVar ScriptTable::GetValue(uint32_t index) const
{
lua_init();
return m_pairs.At(index).GetValue();
}
void ScriptFuncResult::SetError(const String& msg)
{
m_ok = false;
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
strncpy_s(m_msg, SCRIPT_FUNC_ERR_SIZE + 1, msg.C_Str(), SCRIPT_FUNC_ERR_SIZE);
#else
strncpy(m_msg, msg.C_Str(), SCRIPT_FUNC_ERR_SIZE);
#endif
m_msg[SCRIPT_FUNC_ERR_SIZE] = '\0';
}
void ScriptFuncResult::ThrowError()
{
lua_init();
if (!m_ok)
{
lua_pushstring(g_lua_state, m_msg);
lua_error(g_lua_state);
}
}
void ScriptFunction::LoadFromStack()
{
lua_init();
#if KYTY_LUA_VER == KYTY_LUA_5_3
lua_dump(g_lua_state, reinterpret_cast<lua_Writer>(LuaWriter), this, 0);
#else
lua_dump(g_lua_state, reinterpret_cast<lua_Writer>(LuaWriter), this);
#endif
}
String ScriptFunction::ToDbgString() const
{
lua_init();
String str;
FOR (i, m_dump)
{
str += String::FromPrintf("%02" PRIx8 "", m_dump.At(i));
}
return str;
}
int ScriptFunction::LuaWriter(LuaState* /*LS*/, const void* p, size_t sz, void* ud)
{
lua_init();
auto* f = static_cast<ScriptFunction*>(ud);
EXIT_IF(sizeof(size_t) > 4 && (static_cast<uint64_t>(sz) >> 32u) > 0);
f->m_dump.Add(static_cast<const uint8_t*>(p), static_cast<uint32_t>(sz));
return 0;
}
ScriptPair::ScriptPair(const ScriptVar& key, const ScriptVar& value): m_key(new ScriptVar(key)), m_value(new ScriptVar(value)) {}
ScriptPair::~ScriptPair()
{
Delete(m_key);
Delete(m_value);
}
ScriptPair::ScriptPair(const ScriptPair& src): m_key(new ScriptVar(*src.m_key)), m_value(new ScriptVar(*src.m_value)) {}
} // namespace Kyty::Scripts
+126
View File
@@ -0,0 +1,126 @@
#include "Kyty/Core/String.h"
#include "Kyty/Scripts/LuaCpp.h"
#include "Kyty/Scripts/Scripts.h"
namespace Kyty::Scripts {
// void script_dbg_dump_stack(const String &prefix);
static String create_error(ScriptError err, const String& msg1, const String& msg2)
{
String err_str = String::FromPrintf("%s %s\n", msg1.C_Str(), msg2.C_Str());
switch (err)
{
case ScriptError::FileError: err_str += String::FromPrintf("file error\n"); break;
case ScriptError::SyntaxError: err_str += String::FromPrintf("syntax error:\n%s\n", GetErrMsg().C_Str()); break;
case ScriptError::RunError: err_str += String::FromPrintf("run error:\n%s\n", GetErrMsg().C_Str()); break;
case ScriptError::UnknownError:
default: err_str += String::FromPrintf("unknown error\n");
}
return err_str;
}
KYTY_STATIC_SCRIPT_FUNC(dofile)
{
KYTY_SCRIPT_FUNC_BEGIN();
if (Scripts::ArgGetVarCount() != 1)
{
KYTY_SCRIPT_THROW_ERROR(U"invalid args\n");
}
String file_name = Scripts::ArgGetVar(0).ToString();
ScriptError err = RunFile(file_name);
if (err != ScriptError::Ok)
{
KYTY_SCRIPT_THROW_ERROR(create_error(err, U"can't run file", file_name));
}
return Scripts::ArgGetVarCount() - 1;
}
KYTY_STATIC_SCRIPT_FUNC(require)
{
KYTY_SCRIPT_FUNC_BEGIN();
if (Scripts::ArgGetVarCount() != 1)
{
KYTY_SCRIPT_THROW_ERROR(U"invalid args\n");
}
// s: name
String file_name = Scripts::ArgGetVar(0).ToString();
String::Utf8 name = file_name.utf8_str();
lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); // s: name, _LOADED
lua_getfield(L, 2, name.GetData()); // s: name, _LOADED, module
if (lua_toboolean(L, -1) != 0)
{ /* is it there? */
return 1; /* package is already loaded */
}
/* else must load package */
lua_pop(L, 1); // s: name, _LOADED
lua_getfield(L, LUA_REGISTRYINDEX, "_PRELOAD"); // s: name, _LOADED, _PRELOADED
lua_getfield(L, 3, name.GetData()); // s: name, _LOADED, _PRELOADED, loader
if (!lua_isnil(L, -1))
{
lua_pop(L, 2); // s: name, _LOADED
String rs = String::FromPrintf(R"(return package.preload["%s"]("%s"))", name.GetData(), name.GetData());
ScriptError err = RunString(rs); // s: name, _LOADED, module
if (err != ScriptError::Ok)
{
KYTY_SCRIPT_THROW_ERROR(create_error(err, U"can't run", rs));
}
} else
{
file_name = file_name.ReplaceChar(U'.', U'/') + U".lua";
ScriptError err = RunFile(file_name); // s: name, _LOADED, module
if (err != ScriptError::Ok)
{
KYTY_SCRIPT_THROW_ERROR(create_error(err, U"can't run file", file_name));
}
}
if (lua_gettop(L) == 2)
{
lua_pushnil(L); // s: name, _LOADED, nil
}
if (!lua_isnil(L, -1))
{ /* non-nil return? */
lua_setfield(L, 2, name.GetData()); /* _LOADED[name] = module */ // s: name, _LOADED
}
lua_getfield(L, 2, name.GetData()); // s: name, _LOADED, module
if (lua_isnil(L, -1))
{ /* module set no value? */
lua_pushboolean(L, 1); /* use true as result */ // s: name, _LOADED, module, true
lua_pushvalue(L, -1); /* extra copy to be returned */ // s: name, _LOADED, true, true
lua_setfield(L, 2, name.GetData()); /* _LOADED[name] = true */ // s: name, _LOADED, true
}
return 1;
}
void scripts_file_lib_reg()
{
RegisterSystemFunc("dofile", dofile);
RegisterSystemFunc("require", require);
}
} // namespace Kyty::Scripts
+77
View File
@@ -0,0 +1,77 @@
#include "Kyty/Scripts/ScriptsLoader.h"
#include "Kyty/Core/MemoryAlloc.h"
#include "Kyty/Scripts/Scripts.h"
namespace Kyty::Scripts {
static String* g_load_err = nullptr;
void SetLoadError(const String& err)
{
Core::mem_tracker_disable();
if (g_load_err == nullptr)
{
g_load_err = new String;
}
String s = err.IsEmpty() ? U"" : String::FromPrintf("%s", err.C_Str());
if (g_load_err->IsEmpty())
{
*g_load_err = s;
} else
{
*g_load_err = *g_load_err + U"\n" + s;
}
Core::mem_tracker_enable();
}
void ResetLoadError()
{
Core::mem_tracker_disable();
if (g_load_err == nullptr)
{
g_load_err = new String;
}
*g_load_err = U"";
Core::mem_tracker_enable();
}
String GetLoadError()
{
return ((g_load_err != nullptr) && !g_load_err->IsEmpty()) ? String::FromPrintf("%s", (*g_load_err).C_Str()) : U"";
}
bool RunScript(const String& lua_file_name)
{
Scripts::ScriptError err = Scripts::RunFile(lua_file_name);
if (err != Scripts::ScriptError::Ok)
{
String err_str = String::FromPrintf("can't run file %s\n", lua_file_name.C_Str());
switch (err)
{
case Scripts::ScriptError::FileError: err_str += String::FromPrintf("file error\n"); break;
case Scripts::ScriptError::SyntaxError:
err_str += String::FromPrintf("syntax error:\n%s\n", Scripts::GetErrMsg().C_Str());
break;
case Scripts::ScriptError::RunError:
err_str += String::FromPrintf("run error:\n%s\n", Scripts::GetErrMsg().C_Str());
break;
case Scripts::ScriptError::UnknownError:
default: err_str += U"unknown error\n";
}
SetLoadError(err_str);
return false;
}
return true;
}
} // namespace Kyty::Scripts
+23
View File
@@ -0,0 +1,23 @@
file(GLOB sys_src
"src/*.cpp"
)
add_library(sys_obj OBJECT ${sys_src})
add_library(sys STATIC $<TARGET_OBJECTS:sys_obj>)
target_link_libraries(sys core)
get_property(inc_headers TARGET sys PROPERTY INCLUDE_DIRECTORIES)
target_include_directories(sys_obj PRIVATE ${inc_headers})
list(APPEND check_headers
${CMAKE_SOURCE_DIR}/include
)
clang_tidy_check(sys_obj "" "${check_headers}" "${inc_headers}")
include_what_you_use(sys_obj "${inc_headers}")
+7
View File
@@ -0,0 +1,7 @@
#include "Kyty/Core/Common.h"
#if KYTY_PLATFORM != KYTY_PLATFORM_LINUX
//#error "KYTY_PLATFORM != KYTY_PLATFORM_LINUX"
#else
#endif
+7
View File
@@ -0,0 +1,7 @@
#include "Kyty/Core/Common.h"
#if KYTY_PLATFORM != KYTY_PLATFORM_LINUX
//#error "KYTY_PLATFORM != KYTY_PLATFORM_LINUX"
#else
#endif
+297
View File
@@ -0,0 +1,297 @@
#include "Kyty/Sys/SysWindowsDbg.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Sys/SysDbg.h"
// IWYU pragma: no_include <basetsd.h>
// IWYU pragma: no_include <memoryapi.h>
// IWYU pragma: no_include <minwindef.h>
// IWYU pragma: no_include <processthreadsapi.h>
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
//#error "KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS"
#else
#include <windows.h> // IWYU pragma: keep
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
#include <intrin.h>
#endif
#include <psapi.h> // IWYU pragma: keep
namespace Kyty {
#if KYTY_BITNESS == 32
static thread_local sys_dbg_stack_info_t g_stack = {0};
// uintptr_t g_stack_addr = 0;
// bool g_need_break = false;
#endif
#define KYTY_FRAME_SKIP 1
struct FrameS
{
struct FrameS* next;
void* ret_addr;
};
constexpr DWORD READABLE =
(static_cast<DWORD>(PAGE_EXECUTE_READ) | static_cast<DWORD>(PAGE_EXECUTE_READWRITE) | static_cast<DWORD>(PAGE_EXECUTE_WRITECOPY) |
static_cast<DWORD>(PAGE_READONLY) | static_cast<DWORD>(PAGE_READWRITE) | static_cast<DWORD>(PAGE_WRITECOPY));
constexpr DWORD PROTECTED = (static_cast<DWORD>(PAGE_GUARD) | static_cast<DWORD>(PAGE_NOCACHE) | static_cast<DWORD>(PAGE_NOACCESS));
exception_filter_func_t g_exception_filter_func = nullptr;
bool sys_mem_read_allowed(void* ptr)
{
MEMORY_BASIC_INFORMATION mbi;
size_t s = VirtualQuery(ptr, &mbi, sizeof(mbi));
if (s == 0)
{
EXIT_IF(s == 0);
}
return ((mbi.Protect & PROTECTED) == 0u) && ((mbi.State & static_cast<DWORD>(MEM_COMMIT)) != 0u) &&
((mbi.AllocationProtect & READABLE) != 0u);
}
// LONG WINAPI
// VectoredHandlerSkip(struct _EXCEPTION_POINTERS *ExceptionInfo)
//{
// PCONTEXT Context;
//
// g_need_break = true;
//
// Context = ExceptionInfo->ContextRecord;
//#ifdef _AMD64_
// Context->Rip++;
//#else
// Context->Eip++;
//#endif
// return EXCEPTION_CONTINUE_EXECUTION;
//}
#if KYTY_BITNESS == 32
static void stackwalk(void* ebp, void** stack, int* depth, uintptr_t stack_addr, size_t stack_size)
{
frame_t* frame = (frame_t*)ebp;
int d = *depth;
int i;
// printf("1\n");
for (i = 0; i < KYTY_FRAME_SKIP; i++)
{
// if (uintptr_t(frame) <= 0xffff || (uintptr_t(frame) & 0xf0000000)
// || frame->ret_addr == 0
// || (uintptr_t(frame->ret_addr) & 0xf0000000)
// || (uintptr_t(frame->next) & 0xf0000000)) break;
// if (!sys_mem_read_allowed(&frame->next)) break;
if (!(uintptr_t(frame) >= stack_addr && uintptr_t(frame) < stack_addr + stack_size)) break;
frame = frame->next;
}
// printf("2\n");
for (i = 0; i < d; i++)
{
//#ifdef _MSC_VER
// __try
// {
//#endif
// FILE *f = fopen("_sw", "wt");
// printf("%d, %08x\n", i, (uint32_t)frame);
// fflush(stdout);
// fclose(f);
// printf("%d, %08x, %08x, %08x\n", i, (uint32_t)frame,
// (uint32_t)frame->ret_addr, (uint32_t)frame->next);
// if (uintptr_t(frame) <= 0xffff || (uintptr_t(frame) & 0xf0000000)
// || frame->ret_addr == 0
// || (uintptr_t(frame->ret_addr) & 0xf0000000)
// || (uintptr_t(frame->next) & 0xf0000000)) break;
// if (uintptr_t(frame) == 0 || frame->ret_addr == 0 ) break;
// if (!sys_mem_read_allowed(&frame->next) || !sys_mem_read_allowed(&frame->ret_addr)) break;
if (!(uintptr_t(frame) >= stack_addr && uintptr_t(frame) < stack_addr + stack_size)) break;
// if (g_need_break) break;
stack[i] = frame->ret_addr;
frame = frame->next;
//#ifdef _MSC_VER
// } __except(EXCEPTION_EXECUTE_HANDLER)
// {
// break;
// }
//#endif
}
// printf("3\n");
*depth = i;
}
#endif
#if KYTY_BITNESS == 32
void sys_stack_walk(void** stack, int* depth)
{
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
void* ebp = (size_t*)_AddressOfReturnAddress() - 1;
#else
void* ebp = __builtin_frame_address(0);
#endif
// g_need_break = false;
//#ifndef _MSC_VER
// PVOID p = AddVectoredExceptionHandler(1000, VectoredHandlerSkip);
// if (!g_stack_addr) g_stack_addr = (uintptr_t)&depth;
///#endif
if (g_stack.total_size == 0)
{
sys_stack_usage(g_stack);
}
stackwalk(ebp, stack, depth, g_stack.addr, g_stack.total_size);
//#ifndef _MSC_VER
// RemoveVectoredExceptionHandler(p);
//#endif
}
#else
//#include <unwind.h>
// struct unwind_info_t
//{
// void **stack;
// int depth;
// int max_depth;
//};
//_Unwind_Reason_Code trace_fcn(_Unwind_Context *ctx, void *d)
//{
// unwind_info_t *info = (unwind_info_t*)d;
// //printf("\t#%d: program counter at %08x\n", *depth, _Unwind_GetIP(ctx));
// //(*depth)++;
// if (info->depth < info->max_depth)
// {
// void *ptr = (void*)_Unwind_GetIP(ctx);
// info->stack[info->depth] = ptr;
// info->depth++;
// }
// return _URC_NO_REASON;
//}
int WalkStack(int z_stack_depth, void** z_stack_trace)
{
CONTEXT context;
// KNONVOLATILE_CONTEXT_POINTERS NvContext;
PRUNTIME_FUNCTION runtime_function = nullptr;
PVOID handler_data = nullptr;
ULONG64 establisher_frame = 0;
ULONG64 image_base = 0;
RtlCaptureContext(&context);
int frame = 0;
while (true)
{
if (frame >= z_stack_depth)
{
break;
}
z_stack_trace[frame] = reinterpret_cast<void*>(context.Rip);
frame++;
runtime_function = RtlLookupFunctionEntry(context.Rip, &image_base, nullptr);
if (runtime_function == nullptr)
{
break;
}
// RtlZeroMemory(&NvContext, sizeof(KNONVOLATILE_CONTEXT_POINTERS));
RtlVirtualUnwind(0, image_base, context.Rip, runtime_function, &context, &handler_data, &establisher_frame, nullptr /*&NvContext*/);
if (context.Rip == 0u)
{
break;
}
}
return frame;
}
void sys_stack_walk(void** stack, int* depth)
{
// USHORT n = CaptureStackBackTrace(KYTY_FRAME_SKIP, *depth, stack, 0);
int n = WalkStack(*depth, stack);
*depth = n;
// unwind_info_t info = {stack, 0, *depth};
// _Unwind_Backtrace(&trace_fcn, &info);
// *depth = info.depth;
}
#endif
void sys_stack_usage_print(sys_dbg_stack_info_t& stack)
{
printf("stack: (0x%" PRIx64 ", %" PRIu64 ") + (0x%" PRIx64 ", %" PRIu64 ") + (0x%" PRIx64 ", %" PRIu64 ")\n",
static_cast<uint64_t>(stack.reserved_addr), static_cast<uint64_t>(stack.reserved_size), static_cast<uint64_t>(stack.guard_addr),
static_cast<uint64_t>(stack.guard_size), static_cast<uint64_t>(stack.commited_addr), static_cast<uint64_t>(stack.commited_size));
}
void sys_stack_usage(sys_dbg_stack_info_t& s)
{
MEMORY_BASIC_INFORMATION mbi {};
[[maybe_unused]] size_t ss = VirtualQuery(&mbi, &mbi, sizeof(mbi));
EXIT_IF(ss == 0);
PVOID reserved = mbi.AllocationBase;
ss = VirtualQuery(reserved, &mbi, sizeof(mbi));
EXIT_IF(ss == 0);
size_t reserved_size = mbi.RegionSize;
ss = VirtualQuery(static_cast<char*>(reserved) + reserved_size, &mbi, sizeof(mbi));
EXIT_IF(ss == 0);
void* guard_page = mbi.BaseAddress;
size_t guard_page_size = mbi.RegionSize;
ss = VirtualQuery(static_cast<char*>(guard_page) + guard_page_size, &mbi, sizeof(mbi));
EXIT_IF(ss == 0);
void* commited = mbi.BaseAddress;
size_t commited_size = mbi.RegionSize;
s.reserved_addr = reinterpret_cast<uintptr_t>(reserved);
s.reserved_size = reserved_size;
s.guard_addr = reinterpret_cast<uintptr_t>(guard_page);
s.guard_size = guard_page_size;
s.commited_addr = reinterpret_cast<uintptr_t>(commited);
s.commited_size = commited_size;
s.addr = s.reserved_addr;
s.total_size = s.reserved_size + s.guard_size + s.commited_size;
}
void sys_get_code_info(uintptr_t* addr, size_t* size)
{
MODULEINFO info {};
GetModuleInformation(GetCurrentProcess(), GetModuleHandle(nullptr), &info, sizeof(MODULEINFO));
*addr = reinterpret_cast<uintptr_t>(info.lpBaseOfDll);
*size = static_cast<size_t>(info.SizeOfImage);
}
static LONG WINAPI ExceptionFilter(PEXCEPTION_POINTERS exception)
{
g_exception_filter_func(exception->ExceptionRecord->ExceptionAddress);
return EXCEPTION_EXECUTE_HANDLER;
}
void sys_set_exception_filter(exception_filter_func_t func)
{
g_exception_filter_func = func;
SetUnhandledExceptionFilter(ExceptionFilter);
}
} // namespace Kyty
#endif
+642
View File
@@ -0,0 +1,642 @@
#include "Kyty/Sys/SysWindowsFileIO.h"
#include "Kyty/Core/Common.h"
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
//#error "KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS"
#else
#include "Kyty/Core/MemoryAlloc.h"
#include "Kyty/Core/String.h"
#include "Kyty/Core/Vector.h"
#include "Kyty/Sys/SysFileIO.h"
#include "Kyty/Sys/SysTimer.h"
#include <windows.h>
// IWYU pragma: no_include <fileapi.h>
// IWYU pragma: no_include <handleapi.h>
// IWYU pragma: no_include <minwinbase.h>
// IWYU pragma: no_include <minwindef.h>
namespace Kyty {
using Core::mem_free;
using Core::mem_realloc;
static inline HANDLE KYTY_INVALID_HANDLE_VALUE()
{
return INVALID_HANDLE_VALUE; // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
}
bool sys_file_io_init()
{
return true;
}
static DWORD get_cache_access_type(sys_file_cache_type_t t)
{
if (t == SYS_FILE_CACHE_RANDOM_ACCESS)
{
return FILE_FLAG_RANDOM_ACCESS;
}
if (t == SYS_FILE_CACHE_SEQUENTIAL_SCAN)
{
return SYS_FILE_CACHE_SEQUENTIAL_SCAN;
}
return SYS_FILE_CACHE_AUTO;
}
void sys_file_read(void* data, uint32_t size, sys_file_t& f, uint32_t* bytes_read)
{
if (f.type == SYS_FILE_FILE)
{
DWORD w = 0;
ReadFile(f.handle, data, size, &w, nullptr);
if (bytes_read != nullptr)
{
*bytes_read = w;
}
} else if (f.type == SYS_FILE_MEMORY_STAT)
{
uint32_t s = size;
if (f.buf->size != 0u)
{
uint32_t l = f.buf->size - (f.buf->ptr - f.buf->base);
if (s > l)
{
s = l;
}
}
std::memcpy(data, f.buf->ptr, s);
f.buf->ptr += s;
if (bytes_read != nullptr)
{
*bytes_read = s;
}
} else if (f.type == SYS_FILE_MEMORY_DYN)
{
uint32_t s = size;
if (f.buf->size != 0u)
{
uint32_t l = f.buf->size - (f.buf->ptr - f.buf->base);
if (s > l)
{
s = l;
}
} else
{
s = 0;
}
std::memcpy(data, f.buf->ptr, s);
f.buf->ptr += s;
if (bytes_read != nullptr)
{
*bytes_read = s;
}
}
}
void sys_file_write(const void* data, uint32_t size, sys_file_t& f, uint32_t* bytes_written)
{
if (f.type == SYS_FILE_FILE)
{
DWORD w = 0;
WriteFile(f.handle, data, size, &w, nullptr);
if (bytes_written != nullptr)
{
*bytes_written = w;
}
} else if (f.type == SYS_FILE_MEMORY_STAT)
{
uint32_t s = size;
if (f.buf->size != 0u)
{
uint32_t l = f.buf->size - (f.buf->ptr - f.buf->base);
if (s > l)
{
s = l;
}
}
std::memcpy(f.buf->ptr, data, s);
f.buf->ptr += s;
if (bytes_written != nullptr)
{
*bytes_written = s;
}
} else if (f.type == SYS_FILE_MEMORY_DYN)
{
uint32_t pos = f.buf->ptr - f.buf->base;
if (f.buf->size < pos + size)
{
f.buf->base = static_cast<uint8_t*>(mem_realloc(f.buf->base, pos + size));
f.buf->ptr = f.buf->base + pos;
f.buf->size = pos + size;
}
std::memcpy(f.buf->ptr, data, size);
f.buf->ptr += size;
if (bytes_written != nullptr)
{
*bytes_written = size;
}
}
}
void sys_file_read_r(void* data, uint32_t size, sys_file_t& f)
{
// DWORD w;
// ReadFile(f, data, size, &w, 0);
sys_file_read(data, size, f);
for (uint32_t i = 0; i < size / 2; i++)
{
char t = (static_cast<char*>(data))[i];
(static_cast<char*>(data))[i] = (static_cast<char*>(data))[size - i - 1];
(static_cast<char*>(data))[size - i - 1] = t;
}
}
void sys_file_write_r(const void* data, uint32_t size, sys_file_t& f)
{
char* data_r = new char[size];
for (uint32_t i = 0; i < size; i++)
{
data_r[i] = (static_cast<const char*>(data))[size - i - 1];
}
sys_file_write(data_r, size, f);
delete[] data_r;
}
void sys_file_write(uint32_t n, sys_file_t& f)
{
sys_file_write(&n, 4, f);
}
void sys_file_write_r(uint32_t n, sys_file_t& f)
{
sys_file_write_r(&n, 4, f);
}
sys_file_t* sys_file_create(const String& file_name)
{
auto* ret = new sys_file_t;
HANDLE h_file = nullptr;
h_file = CreateFileW(reinterpret_cast<LPCWSTR>(file_name.utf16_str().GetData()),
static_cast<DWORD>(GENERIC_READ) | static_cast<DWORD>(GENERIC_WRITE), 0, nullptr, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL, nullptr);
ret->handle = h_file;
ret->type = SYS_FILE_FILE;
return ret;
}
sys_file_t* sys_file_open_r(const String& file_name, sys_file_cache_type_t cache_type)
{
auto* ret = new sys_file_t;
HANDLE h_file = nullptr;
h_file = CreateFileW(reinterpret_cast<LPCWSTR>(file_name.utf16_str().GetData()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
get_cache_access_type(cache_type), nullptr);
if (h_file == KYTY_INVALID_HANDLE_VALUE())
{
ret->type = SYS_FILE_ERROR;
} else
{
ret->type = SYS_FILE_FILE;
}
ret->handle = h_file;
return ret;
}
sys_file_t* sys_file_open(uint8_t* buf, uint32_t buf_size)
{
auto* ret = new sys_file_t;
ret->type = SYS_FILE_MEMORY_STAT;
ret->buf = new sys_file_mem_buf_t;
ret->buf->base = buf;
ret->buf->ptr = buf;
ret->buf->size = buf_size;
return ret;
}
sys_file_t* sys_file_create()
{
auto* ret = new sys_file_t;
ret->type = SYS_FILE_MEMORY_DYN;
ret->buf = new sys_file_mem_buf_t;
ret->buf->base = nullptr;
ret->buf->ptr = nullptr;
ret->buf->size = 0;
return ret;
}
sys_file_t* sys_file_open_w(const String& file_name, sys_file_cache_type_t cache_type)
{
auto* ret = new sys_file_t;
HANDLE h_file = nullptr;
h_file = CreateFileW(reinterpret_cast<LPCWSTR>(file_name.utf16_str().GetData()), GENERIC_WRITE, 0, nullptr, OPEN_EXISTING,
get_cache_access_type(cache_type), nullptr);
if (h_file == KYTY_INVALID_HANDLE_VALUE())
{
ret->type = SYS_FILE_ERROR;
} else
{
ret->type = SYS_FILE_FILE;
}
ret->handle = h_file;
return ret;
}
sys_file_t* sys_file_open_rw(const String& file_name, sys_file_cache_type_t cache_type)
{
auto* ret = new sys_file_t;
HANDLE h_file = nullptr;
h_file = CreateFileW(reinterpret_cast<LPCWSTR>(file_name.utf16_str().GetData()),
static_cast<DWORD>(GENERIC_READ) | static_cast<DWORD>(GENERIC_WRITE), 0, nullptr, OPEN_EXISTING,
get_cache_access_type(cache_type), nullptr);
if (h_file == KYTY_INVALID_HANDLE_VALUE())
{
ret->type = SYS_FILE_ERROR;
} else
{
ret->type = SYS_FILE_FILE;
}
ret->handle = h_file;
return ret;
}
void sys_file_close(sys_file_t* f)
{
if (f->type == SYS_FILE_FILE)
{
CloseHandle(f->handle);
} else if (f->type == SYS_FILE_MEMORY_STAT)
{
delete f->buf;
} else if (f->type == SYS_FILE_MEMORY_DYN)
{
mem_free(f->buf->base);
delete f->buf;
}
// f.type = SYS_FILE_ERROR;
delete f;
}
uint64_t sys_file_size(sys_file_t& f)
{
if (f.type == SYS_FILE_FILE)
{
LARGE_INTEGER s;
GetFileSizeEx(f.handle, &s);
return s.QuadPart;
}
if (f.type == SYS_FILE_MEMORY_STAT || f.type == SYS_FILE_MEMORY_DYN)
{
return f.buf->size;
}
return 0;
}
uint64_t sys_file_size(const String& file_name)
{
LARGE_INTEGER s;
WIN32_FILE_ATTRIBUTE_DATA a;
if (GetFileAttributesExW(reinterpret_cast<LPCWSTR>(file_name.utf16_str().GetData()), GetFileExInfoStandard, &a) == 0)
{
return 0;
}
s.HighPart = static_cast<LONG>(a.nFileSizeHigh);
s.LowPart = a.nFileSizeLow;
return s.QuadPart;
}
bool sys_file_truncate(sys_file_t& f, uint64_t size)
{
bool ok = false;
if (f.type == SYS_FILE_FILE)
{
LARGE_INTEGER s {};
LARGE_INTEGER r {};
s.QuadPart = 0;
SetFilePointerEx(f.handle, s, &r, FILE_CURRENT);
s.QuadPart = static_cast<LONGLONG>(size);
ok = (SetFilePointerEx(f.handle, s, nullptr, FILE_BEGIN) != 0 && SetEndOfFile(f.handle) != 0);
SetFilePointerEx(f.handle, r, nullptr, FILE_BEGIN);
}
return ok;
}
bool sys_file_seek(sys_file_t& f, uint64_t offset)
{
bool ok = true;
if (f.type == SYS_FILE_FILE)
{
LARGE_INTEGER s;
s.QuadPart = static_cast<LONGLONG>(offset);
ok = (SetFilePointerEx(f.handle, s, nullptr, FILE_BEGIN) != 0);
// printf("seek: %u\n", offset);
} else if (f.type == SYS_FILE_MEMORY_STAT || f.type == SYS_FILE_MEMORY_DYN)
{
f.buf->ptr = f.buf->base + offset;
}
return ok;
}
uint64_t sys_file_tell(sys_file_t& f)
{
if (f.type == SYS_FILE_FILE)
{
LARGE_INTEGER s {};
LARGE_INTEGER r {};
s.QuadPart = 0;
SetFilePointerEx(f.handle, s, &r, FILE_CURRENT);
// printf("tell: %u\n", r.QuadPart);
return r.QuadPart;
}
if (f.type == SYS_FILE_MEMORY_STAT || f.type == SYS_FILE_MEMORY_DYN)
{
return f.buf->ptr - f.buf->base;
}
return 0;
}
bool sys_file_is_error(sys_file_t& f)
{
return f.type == SYS_FILE_ERROR || (f.type == SYS_FILE_FILE && f.handle == KYTY_INVALID_HANDLE_VALUE());
}
bool sys_file_is_directory_existing(const String& path)
{
DWORD a = GetFileAttributesW(reinterpret_cast<LPCWSTR>(path.utf16_str().GetData()));
return a != INVALID_FILE_ATTRIBUTES && ((a & static_cast<DWORD>(FILE_ATTRIBUTE_DIRECTORY)) != 0u);
}
bool sys_file_is_file_existing(const String& name)
{
DWORD a = GetFileAttributesW(reinterpret_cast<LPCWSTR>(name.utf16_str().GetData()));
return a != INVALID_FILE_ATTRIBUTES && ((a & static_cast<DWORD>(FILE_ATTRIBUTE_DIRECTORY)) == 0u);
}
bool sys_file_create_directory(const String& path)
{
return CreateDirectoryW(reinterpret_cast<LPCWSTR>(path.utf16_str().GetData()), nullptr) != 0;
}
bool sys_file_delete_directory(const String& path)
{
return RemoveDirectoryW(reinterpret_cast<LPCWSTR>(path.utf16_str().GetData())) != 0;
}
bool sys_file_delete_file(const String& name)
{
return DeleteFileW(reinterpret_cast<LPCWSTR>(name.utf16_str().GetData())) != 0;
}
bool sys_file_flush(sys_file_t& f)
{
if (f.type == SYS_FILE_FILE && f.handle != KYTY_INVALID_HANDLE_VALUE())
{
return (FlushFileBuffers(f.handle) != 0);
}
return false;
}
SysFileTimeStruct sys_file_get_last_access_time_utc(const String& name)
{
SysFileTimeStruct r {};
sys_file_t* f = sys_file_open_r(name);
r.is_invalid = (f->type == SYS_FILE_ERROR || (GetFileTime(f->handle, nullptr, &r.time, nullptr) == 0));
sys_file_close(f);
return r;
}
SysFileTimeStruct sys_file_get_last_write_time_utc(const String& name)
{
SysFileTimeStruct r {};
sys_file_t* f = sys_file_open_r(name);
r.is_invalid = (f->type == SYS_FILE_ERROR || (GetFileTime(f->handle, nullptr, nullptr, &r.time) == 0));
sys_file_close(f);
return r;
}
void sys_file_get_last_access_and_write_time_utc(const String& name, SysFileTimeStruct& a, SysFileTimeStruct& w)
{
// TODO() open file with dwDesiredAccess = 0
// TODO() open directory
sys_file_t* f = sys_file_open_r(name);
a.is_invalid = w.is_invalid = (f->type == SYS_FILE_ERROR || (GetFileTime(f->handle, nullptr, &a.time, &w.time) == 0));
sys_file_close(f);
}
void sys_file_get_last_access_and_write_time_utc(sys_file_t& f, SysFileTimeStruct& a, SysFileTimeStruct& w)
{
if (f.type == SYS_FILE_FILE)
{
a.is_invalid = w.is_invalid = (GetFileTime(f.handle, nullptr, &a.time, &w.time) == 0);
} else if (f.type == SYS_FILE_MEMORY_STAT || f.type == SYS_FILE_MEMORY_DYN)
{
SysTimeStruct t {};
sys_get_system_time_utc(t);
sys_system_to_file_time_utc(t, a);
sys_system_to_file_time_utc(t, w);
} else
{
a.is_invalid = w.is_invalid = true;
}
}
bool sys_file_set_last_access_time_utc(const String& name, SysFileTimeStruct& access)
{
if (access.is_invalid)
{
return false;
}
bool ok = true;
sys_file_t* f = sys_file_open_w(name);
ok = !(f->type == SYS_FILE_ERROR || (SetFileTime(f->handle, nullptr, &access.time, nullptr) == 0));
sys_file_close(f);
return ok;
}
bool sys_file_set_last_write_time_utc(const String& name, SysFileTimeStruct& write)
{
if (write.is_invalid)
{
return false;
}
bool ok = true;
sys_file_t* f = sys_file_open_w(name);
ok = !(f->type == SYS_FILE_ERROR || (SetFileTime(f->handle, nullptr, nullptr, &write.time) == 0));
sys_file_close(f);
return ok;
}
bool sys_file_set_last_access_and_write_time_utc(const String& name, SysFileTimeStruct& access, SysFileTimeStruct& write)
{
if (access.is_invalid || write.is_invalid)
{
return false;
}
bool ok = true;
sys_file_t* f = sys_file_open_w(name);
ok = !(f->type == SYS_FILE_ERROR || (SetFileTime(f->handle, nullptr, &access.time, &write.time) == 0));
sys_file_close(f);
return ok;
}
void sys_file_find_files(const String& path, Vector<sys_file_find_t>& out)
{
String real_path = path.ReplaceChar(U'\\', U'/');
if (!real_path.EndsWith(U"/"))
{
real_path += U"/";
}
String pattern = real_path + U"*";
HANDLE h = nullptr;
WIN32_FIND_DATAW data;
h = FindFirstFileW(reinterpret_cast<LPCWSTR>(pattern.utf16_str().GetData()), &data);
if (h == KYTY_INVALID_HANDLE_VALUE())
{
return;
}
do
{
String file_name = String::FromUtf16(reinterpret_cast<char16_t*>(data.cFileName));
if (file_name == U"." || file_name == U"..")
{
continue;
}
if ((data.dwFileAttributes & static_cast<DWORD>(FILE_ATTRIBUTE_DIRECTORY)) != 0u)
{
sys_file_find_files(real_path + file_name, out);
} else
{
sys_file_find_t r {};
r.path_with_name = real_path + file_name;
r.size = (static_cast<uint64_t>(data.nFileSizeHigh) << 32u) + static_cast<uint64_t>(data.nFileSizeLow);
r.last_access_time.is_invalid = false;
r.last_access_time.time = data.ftLastAccessTime;
r.last_write_time.is_invalid = false;
r.last_write_time.time = data.ftLastWriteTime;
out.Add(r);
}
} while (FindNextFileW(h, &data) != 0);
FindClose(h);
}
void sys_file_get_dents(const String& path, Kyty::Vector<sys_dir_entry_t>& out)
{
String real_path = path.ReplaceChar(U'\\', U'/');
if (!real_path.EndsWith(U"/"))
{
real_path += U"/";
}
String pattern = real_path + U"*";
HANDLE h = nullptr;
WIN32_FIND_DATAW data;
h = FindFirstFileW(reinterpret_cast<LPCWSTR>(pattern.utf16_str().GetData()), &data);
if (h == KYTY_INVALID_HANDLE_VALUE())
{
return;
}
do
{
String file_name = String::FromUtf16(reinterpret_cast<char16_t*>(data.cFileName));
sys_dir_entry_t r {};
r.is_file = ((data.dwFileAttributes & static_cast<DWORD>(FILE_ATTRIBUTE_DIRECTORY)) == 0u);
r.name = file_name;
out.Add(r);
} while (FindNextFileW(h, &data) != 0);
FindClose(h);
}
bool sys_file_copy_file(const String& src, const String& dst)
{
return CopyFileW(reinterpret_cast<LPCWSTR>(src.utf16_str().GetData()), reinterpret_cast<LPCWSTR>(dst.utf16_str().GetData()), FALSE) !=
0;
}
bool sys_file_move_file(const String& src, const String& dst)
{
return MoveFileW(reinterpret_cast<LPCWSTR>(src.utf16_str().GetData()), reinterpret_cast<LPCWSTR>(dst.utf16_str().GetData())) != 0;
}
void sys_file_remove_readonly(const String& name)
{
String::Utf16 s = name.utf16_str();
SetFileAttributesW(reinterpret_cast<LPCWSTR>(s.GetData()),
GetFileAttributesW(reinterpret_cast<LPCWSTR>(s.GetData())) & (~static_cast<DWORD>(FILE_ATTRIBUTE_READONLY)));
}
} // namespace Kyty
#endif