mirror of
https://github.com/InoriRus/Kyty.git
synced 2026-08-28 05:06:40 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
add_subdirectory(Core)
|
||||
add_subdirectory(Math)
|
||||
add_subdirectory(Scripts)
|
||||
add_subdirectory(Sys)
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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 = ¤t->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 = ¤t->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 = ¤t->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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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(¤t_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(¤t_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(¤t_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(¤t_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
|
||||
@@ -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}")
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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}")
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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}")
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_LINUX
|
||||
//#error "KYTY_PLATFORM != KYTY_PLATFORM_LINUX"
|
||||
#else
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "Kyty/Core/Common.h"
|
||||
|
||||
#if KYTY_PLATFORM != KYTY_PLATFORM_LINUX
|
||||
//#error "KYTY_PLATFORM != KYTY_PLATFORM_LINUX"
|
||||
#else
|
||||
|
||||
#endif
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user