Initial commit

This commit is contained in:
InoriRus
2021-12-01 19:29:27 +10:00
parent b1e7dcdc5d
commit 43f49c8763
1843 changed files with 1111694 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
file(GLOB core_src
"src/*.cpp"
)
if (MSVC AND CLANG)
set_source_files_properties(${core_src} PROPERTIES COMPILE_FLAGS "-Wno-pragma-pack")
endif()
add_library(core_obj OBJECT ${core_src})
#add_library(core STATIC ${core_src})
add_library(core STATIC $<TARGET_OBJECTS:core_obj>)
target_link_libraries(core sys)
target_link_libraries(core math)
target_link_libraries(core sdl2)
target_link_libraries(core sqlite)
target_link_libraries(core lzma)
target_link_libraries(core zstd)
get_property(inc_headers TARGET core PROPERTY INCLUDE_DIRECTORIES)
list(APPEND inc_headers
${CMAKE_SOURCE_DIR}/3rdparty/sdl2/include
${CMAKE_SOURCE_DIR}/3rdparty/sqlite/include
${CMAKE_SOURCE_DIR}/3rdparty/lzma/include
${CMAKE_SOURCE_DIR}/3rdparty/zstd/lib
)
target_include_directories(core PRIVATE ${inc_headers})
target_include_directories(core_obj PRIVATE ${inc_headers})
list(APPEND check_headers
${CMAKE_SOURCE_DIR}/include
)
clang_tidy_check(core_obj "" "${check_headers}" "${inc_headers}")
include_what_you_use(core_obj "${inc_headers}")
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+35
View File
@@ -0,0 +1,35 @@
#include "Kyty/Core/Core.h"
#include "Kyty/Core/ArrayWrapper.h" // IWYU pragma: associated
#include "Kyty/Core/ByteBuffer.h" // IWYU pragma: associated
#include "Kyty/Core/Common.h" // IWYU pragma: associated
#include "Kyty/Core/Database.h"
#include "Kyty/Core/Debug.h"
#include "Kyty/Core/File.h"
#include "Kyty/Core/Hash.h" // IWYU pragma: associated
#include "Kyty/Core/Language.h"
#include "Kyty/Core/LinkList.h" // IWYU pragma: associated
#include "Kyty/Core/MagicEnum.h" // IWYU pragma: associated
#include "Kyty/Core/MemoryAlloc.h"
#include "Kyty/Core/RefCounter.h" // IWYU pragma: associated
#include "Kyty/Core/SafeDelete.h" // IWYU pragma: associated
#include "Kyty/Core/SimpleArray.h" // IWYU pragma: associated
#include "Kyty/Core/Singleton.h" // IWYU pragma: associated
#include "Kyty/Core/Vector.h" // IWYU pragma: associated
namespace Kyty::Core {
KYTY_SUBSYSTEM_INIT(Core)
{
core_memory_init();
core_file_init();
core_debug_init(parent->GetArgv()[0]);
Language::Init();
Database::Init();
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Core) {}
KYTY_SUBSYSTEM_DESTROY(Core) {}
} // namespace Kyty::Core
File diff suppressed because it is too large Load Diff
+883
View File
@@ -0,0 +1,883 @@
#include "Kyty/Core/DateTime.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Math/MathAll.h"
#include "Kyty/Sys/SysTimer.h"
namespace Kyty::Core {
static void ymd_to_jd(int year, int month, int day, jd_t* jd)
{
jd_t t_year = year;
jd_t t_month = month;
jd_t t_day = day;
if (t_year < 0)
{
t_year++;
}
jd_t a = Math::floordiv<jd_t>(14 - t_month, 12);
jd_t y = t_year + 4800 - a;
jd_t m = t_month + 12 * a - 3;
*jd = t_day + Math::floordiv<jd_t>(153 * m + 2, 5) + 365 * y + Math::floordiv<jd_t>(y, 4) - Math::floordiv<jd_t>(y, 100) +
Math::floordiv<jd_t>(y, 400) - 32045;
}
static void jd_to_ymd(int* year, int* month, int* day, jd_t jd)
{
jd_t a = jd + 32044;
jd_t b = Math::floordiv<jd_t>(4 * a + 3, 146097);
jd_t c = a - Math::floordiv<jd_t>(146097 * b, 4);
jd_t d = Math::floordiv<jd_t>(4 * c + 3, 1461);
jd_t e = c - Math::floordiv<jd_t>(1461 * d, 4);
jd_t m = Math::floordiv<jd_t>(5 * e + 2, 153);
int t_day = e - Math::floordiv<jd_t>(153 * m + 2, 5) + 1;
int t_month = m + 3 - 12 * Math::floordiv<jd_t>(m, 10);
int t_year = 100 * b + d - 4800 + Math::floordiv<jd_t>(m, 10);
if (t_year <= 0)
{
t_year--;
}
if (year != nullptr)
{
*year = t_year;
}
if (month != nullptr)
{
*month = t_month;
}
if (day != nullptr)
{
*day = t_day;
}
}
static void hms_to_ms(int hour, int minute, int second, int msec, int* ms)
{
*ms = hour * 60 * 60 * 1000 + minute * 60 * 1000 + second * 1000 + msec;
}
static void ms_to_hms(int* hour, int* minute, int* second, int* msec, int ms)
{
if (hour != nullptr)
{
*hour = ms / (60 * 60 * 1000);
}
if (minute != nullptr)
{
*minute = (ms % (60 * 60 * 1000)) / (60 * 1000);
}
if (second != nullptr)
{
*second = (ms / 1000) % 60;
}
if (msec != nullptr)
{
*msec = ms % 1000;
}
}
Date::Date(int year, int month, int day)
{
Set(year, month, day);
}
bool Date::IsValid(int year, int month, int day)
{
if (year == 0)
{
return false;
}
return (day >= 1) && (day <= DaysInMonth(month) || (day == 29 && month == 2 && IsLeapYear(year)));
}
int Date::DaysInMonth() const
{
if (IsInvalid())
{
return 0;
}
int year = 0;
int month = 0;
jd_to_ymd(&year, &month, nullptr, m_jd);
if (month == 2 && IsLeapYear(year))
{
return 29;
}
return DaysInMonth(month);
}
bool Date::IsLeapYear() const
{
if (IsInvalid())
{
return false;
}
int year = 0;
jd_to_ymd(&year, nullptr, nullptr, m_jd);
return IsLeapYear(year);
}
int Date::DaysInMonth(int month)
{
static const int days[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month < 1 || month > 12)
{
month = 0;
}
return days[month];
}
void Date::Set(int year, int month, int day)
{
if (IsValid(year, month, day))
{
ymd_to_jd(year, month, day, &m_jd);
} else
{
m_jd = DATE_JD_INVALID;
}
}
void Date::Get(int* year, int* month, int* day) const
{
if (IsInvalid())
{
if (year != nullptr)
{
*year = 0;
}
if (month != nullptr)
{
*month = 0;
}
if (day != nullptr)
{
*day = 0;
}
} else
{
jd_to_ymd(year, month, day, m_jd);
}
}
bool Date::IsLeapYear(int year)
{
if (year < 1)
{
year++;
}
return ((year % 4) == 0 && (year % 100) != 0) || (year % 400) == 0;
}
int Date::Year() const
{
if (IsInvalid())
{
return 0;
}
int year = 0;
jd_to_ymd(&year, nullptr, nullptr, m_jd);
return year;
}
int Date::Month() const
{
if (IsInvalid())
{
return 0;
}
int month = 0;
jd_to_ymd(nullptr, &month, nullptr, m_jd);
return month;
}
int Date::Day() const
{
if (IsInvalid())
{
return 0;
}
int day = 0;
jd_to_ymd(nullptr, nullptr, &day, m_jd);
return day;
}
int Date::DaysInYear() const
{
if (IsInvalid())
{
return 0;
}
return IsLeapYear() ? 366 : 365;
}
int Date::DayOfWeek() const
{
if (IsInvalid())
{
return 0;
}
if (m_jd >= 0)
{
return (m_jd % 7) + 1;
}
return ((m_jd + 1) % 7) + 7;
}
int Date::DayOfYear() const
{
if (IsInvalid())
{
return 0;
}
jd_t d = 0;
ymd_to_jd(Year(), 1, 1, &d);
return m_jd - d + 1;
}
int Date::DaysInYear(int year)
{
return IsLeapYear(year) ? 366 : 365;
}
// NOLINTNEXTLINE(readability-non-const-parameter)
static bool format_date(const Date* d, const String& f, uint32_t* out_i, String* out_r, LanguageId lang_id)
{
auto& i = *out_i;
auto& r = *out_r;
if (f.Mid(i, 4) == U"YYYY")
{
int y = d->Year();
EXIT_IF(y < 0 || y > 9999);
r += String::FromPrintf("%04d", y);
i += 3;
} else if (f.Mid(i, 3) == U"YYY")
{
int y = d->Year();
EXIT_IF(y < 0 || y > 9999);
r += String::FromPrintf("%04d", y).Mid(1);
i += 2;
} else if (f.Mid(i, 2) == U"YY")
{
int y = d->Year();
EXIT_IF(y < 0 || y > 9999);
r += String::FromPrintf("%04d", y).Mid(2);
i += 1;
} else if (f.Mid(i, 1) == U"Y")
{
int y = d->Year();
EXIT_IF(y < 0 || y > 9999);
r += String::FromPrintf("%04d", y).Mid(3);
} else if (f.Mid(i, 1) == U"Q")
{
r += String::FromPrintf("%d", d->QuarterOfYear());
} else if (f.Mid(i, 2) == U"MM")
{
r += String::FromPrintf("%02d", d->Month());
i += 1;
} else if (f.Mid(i, 5) == U"MONTH")
{
r += Language::GetNameOfMonth(d->Month(), lang_id);
i += 4;
} else if (f.Mid(i, 3) == U"MON")
{
r += Language::GetNameOfMonthShort(d->Month(), lang_id);
i += 2;
} else if (f.Mid(i, 3) == U"DDD")
{
r += String::FromPrintf("%03d", d->DayOfYear());
i += 2;
} else if (f.Mid(i, 2) == U"DD")
{
r += String::FromPrintf("%02d", d->Day());
i += 1;
} else if (f.Mid(i, 2) == U"DY")
{
r += Language::GetNameOfDayShort(d->DayOfWeek(), lang_id);
i += 1;
} else if (f.Mid(i, 3) == U"DAY")
{
r += Language::GetNameOfDay(d->DayOfWeek(), lang_id);
i += 2;
} else if (f.Mid(i, 1) == U"D")
{
r += String::FromPrintf("%d", d->DayOfWeek());
} else if (f.Mid(i, 1) == U"J")
{
r += String::FromPrintf("%d", d->JulianDay());
} else
{
return false;
}
return true;
}
/**
* YYYY - 4-digit year
* YYY, YY, Y - Last 3, 2, or 1 digit(s) of year.
* Q - Quarter of year (1, 2, 3, 4; JAN-MAR = 1).
* MM - Month (01-12; JAN = 01).
* MON - Abbreviated name of month.
* MONTH - Name of month
* D - Day of week (1-7).
* DAY - Name of day.
* DY - Abbreviated name of day.
* DD - Day of month (01-31).
* DDD - Day of year (001-366).
* J - Julian day
*/
String Date::ToString(const char* format, LanguageId lang_id) const
{
if (IsInvalid())
{
return U"";
}
String r;
String f = String::FromUtf8(format);
FOR (i, f)
{
if (!format_date(this, f, &i, &r, lang_id))
{
r += f.At(i);
}
}
return r;
}
int Date::QuarterOfYear() const
{
if (IsInvalid())
{
return 0;
}
return (Month() - 1) / 3 + 1;
}
Date Date::FromMacros(const String& date)
{
StringList lst = date.Split(U" ", String::SplitType::SplitNoEmptyParts);
if (lst.Size() != 3)
{
return Date();
}
static const char* month_str[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
int month = -1;
for (int i = 0; i < 12; i++)
{
if (lst.At(0).EqualAscii(month_str[i]))
{
month = i + 1;
break;
}
}
if (month < 0)
{
return Date();
}
return Date(lst.At(2).ToInt32(), month, lst.At(1).ToInt32());
}
Date Date::FromSystem()
{
SysTimeStruct t {};
sys_get_system_time(t);
return Date(t.Year, t.Month, t.Day);
}
Date Date::FromSystemUTC()
{
SysTimeStruct t {};
sys_get_system_time_utc(t);
return Date(t.Year, t.Month, t.Day);
}
Time::Time(int hour, int minute, int second, int msec)
{
Set(hour, minute, second, msec);
}
Time Time::FromSystem()
{
SysTimeStruct t {};
sys_get_system_time(t);
return Time(t.Hour, t.Minute, t.Second, t.Milliseconds);
}
Time Time::FromSystemUTC()
{
SysTimeStruct t {};
sys_get_system_time_utc(t);
return Time(t.Hour, t.Minute, t.Second, t.Milliseconds);
}
void Time::Set(int hour, int minute, int second, int msec)
{
if (IsValid(hour, minute, second, msec))
{
hms_to_ms(hour, minute, second, msec, &m_ms);
} else
{
m_ms = TIME_MS_INVALID;
}
}
void Time::Get(int* hour, int* minute, int* second, int* msec) const
{
if (IsInvalid())
{
if (hour != nullptr)
{
*hour = -1;
}
if (minute != nullptr)
{
*minute = -1;
}
if (second != nullptr)
{
*second = -1;
}
if (msec != nullptr)
{
*msec = -1;
}
} else
{
ms_to_hms(hour, minute, second, msec, m_ms);
}
}
bool Time::IsValid(int hour, int minute, int second, int msec)
{
return (hour >= 0 && hour <= 23) && (minute >= 0 && minute <= 59) && (second >= 0 && second <= 59) && (msec >= 0 && msec <= 999);
}
int Time::Hour24() const
{
if (IsInvalid())
{
return -1;
}
int h = 0;
ms_to_hms(&h, nullptr, nullptr, nullptr, m_ms);
return h;
}
int Time::Hour12() const
{
if (IsInvalid())
{
return -1;
}
int h = 0;
ms_to_hms(&h, nullptr, nullptr, nullptr, m_ms);
h = h % 12;
return h == 0 ? 12 : h;
}
int Time::Minute() const
{
if (IsInvalid())
{
return -1;
}
int m = 0;
ms_to_hms(nullptr, &m, nullptr, nullptr, m_ms);
return m;
}
int Time::Second() const
{
if (IsInvalid())
{
return -1;
}
int s = 0;
ms_to_hms(nullptr, nullptr, &s, nullptr, m_ms);
return s;
}
int Time::Msec() const
{
if (IsInvalid())
{
return -1;
}
int m = 0;
ms_to_hms(nullptr, nullptr, nullptr, &m, m_ms);
return m;
}
bool Time::IsAM() const
{
if (IsInvalid())
{
return false;
}
int h = 0;
ms_to_hms(&h, nullptr, nullptr, nullptr, m_ms);
return h < 12;
}
bool Time::IsPM() const
{
if (IsInvalid())
{
return false;
}
int h = 0;
ms_to_hms(&h, nullptr, nullptr, nullptr, m_ms);
return h >= 12;
}
// NOLINTNEXTLINE(readability-non-const-parameter)
static bool format_time(const Time* t, const String& f, uint32_t* out_i, String* out_r)
{
auto& i = *out_i;
auto& r = *out_r;
if (f.Mid(i, 4) == U"HH24")
{
r += String::FromPrintf("%02d", t->Hour24());
i += 3;
} else if (f.Mid(i, 4) == U"HH12")
{
r += String::FromPrintf("%02d", t->Hour12());
i += 3;
} else if (f.Mid(i, 2) == U"HH")
{
r += String::FromPrintf("%02d", t->Hour12());
i += 1;
} else if (f.Mid(i, 2) == U"MI")
{
r += String::FromPrintf("%02d", t->Minute());
i += 1;
} else if (f.Mid(i, 5) == U"SSSSS")
{
r += String::FromPrintf("%05d", t->MsecTotal() / 1000);
i += 4;
} else if (f.Mid(i, 2) == U"SS")
{
r += String::FromPrintf("%02d", t->Second());
i += 1;
} else if (f.Mid(i, 3) == U"FFF")
{
r += String::FromPrintf("%03d", t->Msec());
i += 2;
} else if (f.Mid(i, 2) == U"AM")
{
r += t->IsAM() ? U"AM" : U"PM";
i += 1;
} else if (f.Mid(i, 4) == U"A.M.")
{
r += t->IsAM() ? U"A.M." : U"P.M.";
i += 3;
} else
{
return false;
}
return true;
}
/**
* HH - Hour of day (1-12)
* HH12 - Hour of day (1-12)
* HH24 - Hour of day (0-23)
* MI - Minute (0-59)
* SS - Second (0-59)
* SSSSS - Seconds past midnight (0-86399)
* FFF - Milliseconds
* AM - AM or PM
* A.M. - A.M. or P.M.
*/
String Time::ToString(const char* format) const
{
if (IsInvalid())
{
return U"";
}
String r;
String f = String::FromUtf8(format);
FOR (i, f)
{
if (!format_time(this, f, &i, &r))
{
r += f.At(i);
}
}
return r;
}
Time Time::operator+(int secs) const
{
int ms_secs = secs * 1000;
EXIT_IF(ms_secs > TIME_MS_IN_DAY || ms_secs < -TIME_MS_IN_DAY);
if (IsInvalid())
{
return Time();
}
int r = m_ms + ms_secs;
if (r < 0)
{
r += TIME_MS_IN_DAY;
}
if (r >= TIME_MS_IN_DAY)
{
r -= TIME_MS_IN_DAY;
}
return Time(r);
}
Time Time::operator-(int secs) const
{
int ms_secs = secs * 1000;
EXIT_IF(ms_secs > TIME_MS_IN_DAY || ms_secs < -TIME_MS_IN_DAY);
if (IsInvalid())
{
return Time();
}
int r = m_ms - ms_secs;
if (r < 0)
{
r += TIME_MS_IN_DAY;
}
if (r >= TIME_MS_IN_DAY)
{
r -= TIME_MS_IN_DAY;
}
return Time(r);
}
Time Time::operator+=(int secs)
{
int ms_secs = secs * 1000;
EXIT_IF(ms_secs > TIME_MS_IN_DAY || ms_secs < -TIME_MS_IN_DAY);
if (!IsInvalid())
{
m_ms += ms_secs;
if (m_ms < 0)
{
m_ms += TIME_MS_IN_DAY;
}
if (m_ms >= TIME_MS_IN_DAY)
{
m_ms -= TIME_MS_IN_DAY;
}
}
return *this;
}
Time Time::operator-=(int secs)
{
int ms_secs = secs * 1000;
EXIT_IF(ms_secs > TIME_MS_IN_DAY || ms_secs < -TIME_MS_IN_DAY);
if (!IsInvalid())
{
m_ms -= ms_secs;
if (m_ms < 0)
{
m_ms += TIME_MS_IN_DAY;
}
if (m_ms >= TIME_MS_IN_DAY)
{
m_ms -= TIME_MS_IN_DAY;
}
}
return *this;
}
DateTime DateTime::FromSystem()
{
SysTimeStruct t {};
sys_get_system_time(t);
if (t.is_invalid)
{
return DateTime();
}
return DateTime(Date(t.Year, t.Month, t.Day), Time(t.Hour, t.Minute, t.Second, t.Milliseconds));
}
DateTime DateTime::FromSystemUTC()
{
SysTimeStruct t {};
sys_get_system_time_utc(t);
if (t.is_invalid)
{
return DateTime();
}
return DateTime(Date(t.Year, t.Month, t.Day), Time(t.Hour, t.Minute, t.Second, t.Milliseconds));
}
/**
* YYYY - 4-digit year
* YYY, YY, Y - Last 3, 2, or 1 digit(s) of year.
* Q - Quarter of year (1, 2, 3, 4; JAN-MAR = 1).
* MM - Month (01-12; JAN = 01).
* MON - Abbreviated name of month.
* MONTH - Name of month
* D - Day of week (1-7).
* DAY - Name of day.
* DY - Abbreviated name of day.
* DD - Day of month (01-31).
* DDD - Day of year (001-366).
* J - Julian day
* HH - Hour of day (1-12)
* HH12 - Hour of day (1-12)
* HH24 - Hour of day (0-23)
* MI - Minute (0-59)
* SS - Second (0-59)
* SSSSS - Seconds past midnight (0-86399)
* FFF - Milliseconds
* AM - AM or PM
* A.M. - A.M. or P.M.
*/
String DateTime::ToString(const char* format, LanguageId lang_id) const
{
if (IsInvalid())
{
return U"";
}
String r;
String f = String::FromUtf8(format);
FOR (i, f)
{
if (!format_date(&m_date, f, &i, &r, lang_id) && !format_time(&m_time, f, &i, &r))
{
r += f.At(i);
}
}
return r;
}
uint64_t DateTime::DistanceMs(const DateTime& other) const
{
EXIT_IF(IsInvalid() || other.IsInvalid());
if (other == *this)
{
return 0;
}
if (other > *this)
{
return other.DistanceMs(*this);
}
jd_t j1 = other.m_date.JulianDay();
jd_t j2 = m_date.JulianDay();
return int64_t(j2 - j1 - 1) * int64_t(TIME_MS_IN_DAY) + (int64_t(TIME_MS_IN_DAY) - int64_t(other.m_time.MsecTotal())) +
int64_t(m_time.MsecTotal());
}
DateTime DateTime::FromSQLiteJulian(double jd)
{
double i = NAN;
double f = modf(jd + 0.5, &i);
return DateTime(Date(jd_t(i)), Time(int(f * double(TIME_MS_IN_DAY))));
}
double DateTime::ToSQLiteJulian() const
{
return -0.5 + double(GetDate().JulianDay()) + double(GetTime().MsecTotal()) / double(TIME_MS_IN_DAY);
}
int64_t DateTime::ToSQLiteJulianInt64() const
{
return -(static_cast<int64_t>(TIME_MS_IN_DAY)) / 2 + int64_t(GetDate().JulianDay()) * (static_cast<int64_t>(TIME_MS_IN_DAY)) +
int64_t(GetTime().MsecTotal());
}
DateTime DateTime::FromUnix(double seconds)
{
return FromSQLiteJulian(2440587.5 + seconds / 86400.0);
}
double DateTime::ToUnix() const
{
return (ToSQLiteJulian() - 2440587.5) * 86400.0;
}
} // namespace Kyty::Core
+84
View File
@@ -0,0 +1,84 @@
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/Debug.h"
#include "Kyty/Core/Subsystems.h"
#include <cstdarg>
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
#include <windows.h> // IWYU pragma: keep
// IWYU pragma: no_include <debugapi.h>
#endif
namespace Kyty::Core {
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BUILD == KYTY_BUILD_DEBUG && KYTY_COMPILER == KYTY_COMPILER_CLANG
constexpr int PRINT_STACK_FROM = 4;
#else
constexpr int PRINT_STACK_FROM = 2;
#endif
void dbg_print_stack()
{
DebugStack s;
DebugStack::Trace(&s);
printf("--- Stack Trace ---\n");
s.Print(PRINT_STACK_FROM);
}
int dbg_assert_handler(char const* expr, char const* file, int line)
{
dbg_print_stack();
KYTY_LOGE("--- Fatal Error ---\n");
KYTY_LOGE("Assertion (%s) failed in %s:%d\n", expr, file, line);
SubsystemsListSingleton::Instance()->ShutdownAll();
return 1;
}
int dbg_exit_if_handler(char const* expr, char const* file, int line)
{
dbg_print_stack();
KYTY_LOGE("--- Fatal Error ---\n");
KYTY_LOGE("Error: condition (%s) is true in %s:%d\n", expr, file, line);
SubsystemsListSingleton::Instance()->ShutdownAll();
return 1;
}
int dbg_not_implemented_handler(char const* expr, char const* file, int line)
{
dbg_print_stack();
KYTY_LOGE("--- Fatal Error ---\n");
KYTY_LOGE("Not implemented (%s) in %s:%d\n", expr, file, line);
SubsystemsListSingleton::Instance()->ShutdownAll();
return 1;
}
int dbg_exit_handler(char const* file, int line, const char* f, ...)
{
va_list args {};
va_start(args, f);
dbg_print_stack();
KYTY_LOGE("--- Error ---\n");
vprintf(f, args);
KYTY_LOGE(" in %s:%d\n", file, line);
SubsystemsListSingleton::Instance()->ShutdownAll();
va_end(args);
return 1;
}
bool dbg_is_debugger_present()
{
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
return !(IsDebuggerPresent() == 0);
#endif
return false;
}
} // namespace Kyty::Core
+917
View File
@@ -0,0 +1,917 @@
#include "Kyty/Core/Debug.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/File.h"
#include "Kyty/Core/Hashmap.h"
#include "Kyty/Core/SafeDelete.h"
#include "Kyty/Core/Vector.h"
#include "Kyty/Sys/SysDbg.h"
#include "Kyty/Sys/SysStdlib.h"
// IWYU pragma: no_include <sec_api/string_s.h>
#if 0 && (KYTY_COMPILER == KYTY_COMPILER_MSVC || KYTY_COMPILER == KYTY_COMPILER_MINGW)
String unDName(const String &mangled,
void* (*memget)(size_t), void (*memfree)(void*),
unsigned short int flags);
#endif
namespace Kyty::Core {
#ifndef KYTY_FINAL
#define DEBUG_MAP_ENABLED
#endif
constexpr bool DBG_PRINTF = false;
static DebugMap* g_dbg_map = nullptr;
static String* g_exe_name = nullptr;
struct DebugFunctionInfo
{
uintptr_t addr;
uintptr_t length;
String::Utf8 name;
String::Utf8 obj;
bool operator<(const DebugFunctionInfo& f) const { return addr < f.addr; }
};
struct DebugFunctionInfo2
{
uintptr_t addr;
uintptr_t length;
const char* name;
const char* obj;
bool operator<(const DebugFunctionInfo2& f) const { return addr < f.addr; }
};
using DebugMapType = Hashmap<uintptr_t, uint32_t>;
using DebugDataType = Vector<DebugFunctionInfo>;
using DebugDataType2 = Vector<DebugFunctionInfo2>;
struct DebugMapPrivate
{
DebugMapPrivate() = default;
~DebugMapPrivate()
{
if (buf != nullptr)
{
DeleteArray(buf);
}
}
void FixBaseAddress();
static const DebugFunctionInfo* FindFunc(DebugMap* map, uintptr_t addr);
KYTY_CLASS_NO_COPY(DebugMapPrivate)
DebugDataType data;
DebugDataType2 data2;
DebugMapType map;
char* buf {nullptr};
};
static void exception_filter(void* /*addr*/)
{
EXIT("Exception!!!");
}
void core_debug_init(const char* app_name)
{
sys_set_exception_filter(exception_filter);
g_exe_name = new String(String::FromUtf8(app_name));
g_dbg_map = new DebugMap;
// g_dbg_map->LoadMap();
g_dbg_map->LoadCsv();
}
DebugMap::DebugMap(): m_p(new DebugMapPrivate) {}
void DebugMap::LoadMap()
{
#ifdef DEBUG_MAP_ENABLED
printf("exe_name = %s\n", g_exe_name->utf8_str().GetData());
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
String linker = Debug::GetLinker();
String map_file =
g_exe_name->FilenameWithoutExtension() + U"_" + Debug::GetCompiler() + U"_" + linker + U"_" + Debug::GetBitness() + U".map";
if (linker == U"ld")
{
LoadGnuLd(map_file, KYTY_BITNESS);
} else if (linker == U"link" || linker == U"lld_link")
{
LoadMsvcLink(map_file, KYTY_BITNESS);
} else if (linker == U"lld")
{
LoadLlvmLld(map_file, KYTY_BITNESS);
} else
{
return;
}
#elif KYTY_PLATFORM == KYTY_PLATFORM_ANDROID
#if KYTY_ABI == KYTY_ABI_ARMEABI
LoadGnuLd(U"armeabi/fc_android.map", 32);
#elif KYTY_ABI == KYTY_ABI_ARM64_V8A
LoadGnuLd(U"arm64-v8a/fc_android.map", 64);
#elif KYTY_ABI == KYTY_ABI_ARMEABI_V7A
LoadGnuLd(U"armeabi-v7a/fc_android.map", 32);
#elif KYTY_ABI == KYTY_ABI_MIPS
LoadGnuLd(U"mips/fc_android.map", 64);
#elif KYTY_ABI == KYTY_ABI_MIPS64
LoadGnuLd(U"mips64/fc_android.map", 64);
#elif KYTY_ABI == KYTY_ABI_X86
LoadGnuLd(U"x86/fc_android.map", 32);
#elif KYTY_ABI == KYTY_ABI_X86_64
LoadGnuLd(U"x86_64/fc_android.map", 64);
#endif
#endif
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
DumpMap(map_file.FilenameWithoutExtension() + U".csv");
if (linker == U"lld")
{
m_p->FixBaseAddress();
}
#elif KYTY_PLATFORM == KYTY_PLATFORM_ANDROID
DumpMap(U"_map.csv");
#endif
// EXIT("1");
#endif
}
void DebugMap::LoadCsv()
{
#ifdef DEBUG_MAP_ENABLED
printf("exe_name = %s\n", g_exe_name->utf8_str().GetData());
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS
String linker = Debug::GetLinker();
String map_file =
g_exe_name->FilenameWithoutExtension() + U"_" + Debug::GetCompiler() + U"_" + linker + U"_" + Debug::GetBitness() + U".csv";
LoadCsv(map_file);
if (linker == U"lld")
{
m_p->FixBaseAddress();
}
#elif KYTY_PLATFORM == KYTY_PLATFORM_ANDROID
#if KYTY_ABI == KYTY_ABI_ARMEABI
LoadCsv(U"armeabi/fc_android.csv");
#elif KYTY_ABI == KYTY_ABI_ARM64_V8A
LoadCsv(U"arm64-v8a/fc_android.csv");
#elif KYTY_ABI == KYTY_ABI_ARMEABI_V7A
LoadCsv(U"armeabi-v7a/fc_android.csv");
#elif KYTY_ABI == KYTY_ABI_MIPS
LoadCsv(U"mips/fc_android.csv");
#elif KYTY_ABI == KYTY_ABI_MIPS64
LoadCsv(U"mips64/fc_android.csv");
#elif KYTY_ABI == KYTY_ABI_X86
LoadCsv(U"x86/fc_android.csv");
#elif KYTY_ABI == KYTY_ABI_X86_64
LoadCsv(U"x86_64/fc_android.csv");
#endif
#endif
// EXIT("1");
#endif
}
DebugMap::~DebugMap()
{
Delete(m_p);
}
KYTY_ARRAY_DEFINE_SWAP(DebugMapSortSwapFunc, DebugFunctionInfo)
{
DebugMapType& map = *static_cast<DebugMapType*>(arg);
map[array[i].addr] = j;
map[array[j].addr] = i;
// DebugMap::DataType &data = *(DebugMap::DataType*)arg;
//
// printf("swap %d, %d\n", i, j);
// for(uint32_t ii = 0; ii < data.Size(); ii++)
// {
// const DebugMap::FunctionInfo& f = data.At(ii);
//
// printf("\t0x%016I64x;%I64u;%s;%s\n", f.addr, f.length, f.name.utf8_str().GetData(), f.obj.utf8_str().GetData());
// }
DebugFunctionInfo t = array[i];
array[i] = array[j];
array[j] = t;
}
void DebugMap::LoadMsvcLink(const String& name, int mode)
{
File pf(name, File::Mode::Read);
if (pf.IsInvalid())
{
return;
}
auto* buf = new uint8_t[pf.Size()];
pf.Read(buf, pf.Size());
File f;
f.OpenInMem(buf, pf.Size());
f.SetEncoding(File::Encoding::Utf8);
pf.Close();
for (;;)
{
if (f.IsEOF())
{
break;
}
String s = f.ReadLine();
s = s.RemoveChar(U'\n');
StringList list = s.Split(U" ");
if (list.Size() >= 5 && list[3] == U"f")
{
EXIT_IF(!((list.Size() == 5 && list[3] == U"f") || (list.Size() == 6 && list[3] == U"f" && list[4] == U"i")));
uintptr_t addr = (mode == 32 ? list[2].ToUint32(16) : list[2].ToUint64(16));
const String func = list.At(1);
const String obj = list.Size() == 5 ? list[4] : list[5];
if (DBG_PRINTF)
{
printf("%016" PRIx64 "; %s; %s\n", static_cast<uint64_t>(addr), func.utf8_str().GetData(), obj.utf8_str().GetData());
fflush(stdout);
}
DebugFunctionInfo inf = {addr, 0, func.utf8_str(), obj.utf8_str()};
if (m_p->map.Contains(addr))
{
String name1(m_p->data.At(m_p->map[addr]).name);
if (name1.StartsWith(U"_"))
{
name1 = name1.Mid(1);
}
// EXIT_IF(data.At(map[addr]).name != func_name);
if (name1 != func && !name1.ContainsStr(func) && !func.ContainsStr(name1))
{
if (DBG_PRINTF)
{
printf("warning: name1: %s, name2: %s\n", name1.utf8_str().GetData(), func.utf8_str().GetData());
}
// exit(1);
}
continue;
}
// if (mem_alloc_obj.Contains(inf.obj, String::CASE_INSENSITIVE)
// && func.ContainsAny(mem_alloc_names))
// {
// inf.is_mem_alloc = true;
// }
m_p->data.Add(inf);
m_p->map.Put(addr, m_p->data.Size() - 1);
} else
{
EXIT_IF(list.Size() >= 5 && list[0].ContainsStr(U":") && list[3] != U"f");
}
}
f.Close();
DeleteArray(buf);
m_p->data.Sort(DebugMapSortSwapFunc, &m_p->map);
for (uint32_t i = 0; i < m_p->data.Size(); i++)
{
if (i > 0)
{
m_p->data[i - 1].length = m_p->data[i].addr - m_p->data[i - 1].addr;
}
// UNDNAME_COMPLETE
#if 0 && (KYTY_COMPILER == KYTY_COMPILER_MSVC || KYTY_COMPILER == KYTY_COMPILER_MINGW)
if (String(data[i].name).ContainsStr("?"))
{
String n = String(data[i].name).Mid(String(data[i].name).FindIndex("?"));
//char name[1024*16];
//char name_all[1024*16];
//UnDecorateSymbolName(data[i].name.utf8_str().GetData(), name, sizeof(name) - 1, UNDNAME_COMPLETE | UNDNAME_32_BIT_DECODE | UNDNAME_TYPE_ONLY);
String name = unDName(n, malloc, free, 0x1000);
String name_all = unDName(n, malloc, free, 0);
//UnDecorateSymbolName(n.utf8_str().GetData(), name, sizeof(name) - 1, 0x1000);
//UnDecorateSymbolName(n.utf8_str().GetData(), name_all, sizeof(name_all) - 1, 0);
if (name.At(0) == '?' || name_all.At(0) == '?')
{
n = n.Left(n.FindLastIndex("$"));
name = unDName(n, malloc, free, 0x1000);
name_all = unDName(n, malloc, free, 0);
//UnDecorateSymbolName(n.utf8_str().GetData(), name, sizeof(name) - 1, 0x1000);
//UnDecorateSymbolName(n.utf8_str().GetData(), name_all, sizeof(name_all) - 1, 0);
}
//String name_str = String(name);
//String name_all_str = String(name_all);
//if (name_all_str.Contains(name_str))
{
uint32_t first = name_all.FindIndex("(");
uint32_t last = name_all.FindLastIndex(")");
//printf("%08x, %u, %u, %s, %s, %s\n", (uint32_t)data[i].addr, first, last, name_str.utf8_str().GetData(), name_all_str.utf8_str().GetData(), n.utf8_str().GetData());
data[i].name = (name + name_all.Mid(first, last - first + 1)).utf8_str();
}// else
//{
// data[i].name = String(name_all);
//}
}
#endif
}
}
void DebugMap::LoadLlvmLld(const String& name, int bitness)
{
if (bitness != 64)
{
return;
}
File pf(name, File::Mode::Read);
if (pf.IsInvalid())
{
return;
}
auto* buf = new uint8_t[pf.Size()];
pf.Read(buf, pf.Size());
File f;
f.OpenInMem(buf, pf.Size());
f.SetEncoding(File::Encoding::Utf8);
pf.Close();
String header = f.ReadLine().RemoveChar(U'\n');
String in;
if (header == U"Address Size Align Out In Symbol")
{
uint32_t prev_addr = 0;
for (;;)
{
if (f.IsEOF())
{
break;
}
String s = f.ReadLine().RemoveChar(U'\n');
uint32_t address = 0;
if (s.At(8) == U' ')
{
address = s.Mid(0, 8).ToUint32(16);
} else
{
break;
}
if (address != prev_addr)
{
if (s.At(24) != U' ')
{
if (!s.Mid(24).StartsWith(U".text"))
{
break;
}
continue;
}
if (s.At(32) != U' ')
{
in = s.Mid(32);
in = in.FixFilenameSlash().FilenameWithoutDirectory();
in = in.Mid(0, in.FindIndex(U':'));
} else
{
String sym = s.Mid(40);
DebugFunctionInfo inf = {address, 0, sym.utf8_str(), in.utf8_str()};
m_p->data.Add(inf);
m_p->map.Put(address, m_p->data.Size() - 1);
prev_addr = address;
}
}
}
}
f.Close();
DeleteArray(buf);
m_p->data.Sort(DebugMapSortSwapFunc, &m_p->map);
for (uint32_t i = 0; i < m_p->data.Size(); i++)
{
if (i > 0 && m_p->data[i - 1].length == 0)
{
m_p->data[i - 1].length = m_p->data[i].addr - m_p->data[i - 1].addr;
}
}
}
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
void DebugMap::LoadGnuLd(const String& name, int bitness)
{
File pf(name, File::Mode::Read);
if (pf.IsInvalid())
{
return;
}
auto* buf = new uint8_t[pf.Size()];
pf.Read(buf, pf.Size());
File f;
f.OpenInMem(buf, pf.Size());
f.SetEncoding(File::Encoding::Utf8);
pf.Close();
int mode = 0;
String object_name;
uintptr_t object_addr = 0;
uintptr_t object_size = 0;
bool object_start = false;
bool object_unsorted = false;
for (;;)
{
if (f.IsEOF())
{
break;
}
String s = f.ReadLine();
s = s.RemoveChar(U'\n');
if (mode == 1)
{
if (s.StartsWith(U" "))
{
uintptr_t addr = bitness == 32 ? s.Mid(16, 10).ToUint32(16) : s.Mid(16, 18).ToUint64(16);
String func_name = bitness == 32 ? s.Mid(42) : s.Mid(50);
if (DBG_PRINTF)
{
printf("\t%08" PRIx32 ", %s\n", static_cast<uint32_t>(addr), func_name.utf8_str().GetData());
}
DebugFunctionInfo inf = {addr, object_size, func_name.utf8_str(), object_name.utf8_str()};
if (m_p->map.Contains(addr))
{
String name1(m_p->data.At(m_p->map[addr]).name);
if (name1.StartsWith(U"_"))
{
name1 = name1.Mid(1);
}
// EXIT_IF(data.At(map[addr]).name != func_name);
if (name1 != func_name && !name1.ContainsStr(func_name) && !func_name.ContainsStr(name1))
{
if (DBG_PRINTF)
{
printf("warning: name1: %s, name2: %s\n", name1.utf8_str().GetData(), func_name.utf8_str().GetData());
}
// exit(1);
}
continue;
}
if (object_unsorted)
{
inf.length = 0;
} else if (m_p->data.Size() > 0 && String(m_p->data.At(m_p->data.Size() - 1).obj) == object_name && !object_start)
{
// EXIT_IF(addr <= data.At(data.Size() - 1).addr);
if (addr <= m_p->data.At(m_p->data.Size() - 1).addr)
{
object_unsorted = true;
m_p->data[m_p->data.Size() - 1].length = 0;
inf.length = 0;
} else
{
uintptr_t l = addr - m_p->data.At(m_p->data.Size() - 1).addr;
m_p->data[m_p->data.Size() - 1].length = l;
EXIT_IF(object_size < l);
object_size -= l;
inf.length -= l;
}
const DebugFunctionInfo& func = m_p->data.At(m_p->data.Size() - 1);
if (DBG_PRINTF)
{
printf(" -- > %016" PRIx64 "; %" PRIu64 "; %s; %s\n", static_cast<uint64_t>(func.addr),
static_cast<uint64_t>(func.length), func.name.GetData(), func.obj.GetData());
}
} else if (inf.addr != object_addr)
{
// EXIT_IF(inf.addr < object_addr);
if (inf.addr < object_addr)
{
object_unsorted = true;
inf.length = 0;
} else
{
uintptr_t l = addr - object_addr;
object_size -= l;
inf.length -= l;
}
}
// if (mem_alloc_obj.Contains(inf.obj, String::CASE_INSENSITIVE)
// && func_name.ContainsAny(mem_alloc_names))
// {
// inf.is_mem_alloc = true;
// }
m_p->data.Add(inf);
m_p->map.Put(addr, m_p->data.Size() - 1);
const DebugFunctionInfo& func = m_p->data.At(m_p->data.Size() - 1);
if (DBG_PRINTF)
{
printf(" -- > %016" PRIx64 "; %" PRIu64 "; %s; %s\n", static_cast<uint64_t>(func.addr),
static_cast<uint64_t>(func.length), func.name.GetData(), func.obj.GetData());
}
object_start = false;
} else
{
mode = 0;
}
}
if (mode == 2)
{
StringList l = s.Split(U" ");
if (l.Size() == 3 && l[0].StartsWith(U"0x"))
{
s = U".text " + s;
mode = 0;
}
}
if (mode == 0)
{
if (s.TrimLeft().StartsWith(U".text"))
{
s = s.RemoveChar(U'\n');
StringList l = s.Split(U" ");
if (l.Size() == 4 && l[0] == U".text")
{
mode = 1;
object_name = l[3].Mid(l[3].FindLastIndex(U"/") + 1);
object_addr = l[1].ToUint64(16);
object_size = l[2].ToUint64(16);
object_start = true;
object_unsorted = false;
if (DBG_PRINTF)
{
printf("%08" PRIx32 ", %08" PRIx32 ", %s\n", static_cast<uint32_t>(object_addr), static_cast<uint32_t>(object_size),
object_name.utf8_str().GetData());
}
} else if (l.Size() == 1 && l[0] != U".text")
{
mode = 2;
}
}
}
}
f.Close();
DeleteArray(buf);
m_p->data.Sort(DebugMapSortSwapFunc, &m_p->map);
// data.Sort();
for (uint32_t i = 0; i < m_p->data.Size(); i++)
{
if (i > 0 && m_p->data[i - 1].length == 0)
{
m_p->data[i - 1].length = m_p->data[i].addr - m_p->data[i - 1].addr;
}
}
}
void DebugMap::DumpMap(const String& name)
{
File::CreateDirectories(name.DirectoryWithoutFilename());
File file(name);
if (file.IsInvalid())
{
return;
}
uint32_t size = m_p->data.Size();
file.Printf("Addr;Size;Func;Obj\n");
for (uint32_t i = 0; i < size; i++)
{
const DebugFunctionInfo& f = m_p->data.At(i);
file.Printf("0x%016" PRIx64 ";%" PRIu64 ";%s;%s\n", static_cast<uint64_t>(f.addr), static_cast<uint64_t>(f.length),
f.name.GetData(), f.obj.GetData());
}
file.Close();
}
static const DebugFunctionInfo* GetFunc(const DebugStack& s, int i)
{
uintptr_t addr = s.GetAddr(i);
if (g_dbg_map != nullptr)
{
return DebugMapPrivate::FindFunc(g_dbg_map, addr);
}
return nullptr;
}
template <class T>
static const T* find_info(const T* data, uintptr_t addr, uint32_t low, uint32_t high)
{
// AGAIN:
for (;;)
{
if (low == high)
{
const T* f = data + low;
if (addr >= f->addr && addr < f->addr + f->length)
{
return f;
}
} else
{
uint32_t mid = (low + high) >> 1u;
const T* f = data + mid;
if (addr < f->addr)
{
// return find_info(data, addr, low, mid);
high = mid;
// goto AGAIN;
continue;
}
if (addr >= f->addr + f->length)
{
// return find_info(data, addr, mid + 1, high);
low = mid + 1;
// goto AGAIN;
continue;
}
return f;
}
break;
}
return nullptr;
}
void DebugMapPrivate::FixBaseAddress()
{
uintptr_t code_base = 0;
size_t code_size = 0;
sys_get_code_info(&code_base, &code_size);
if (data.Size() != 0)
{
for (auto& p: data)
{
p.addr += code_base;
}
}
if (data2.Size() != 0)
{
for (auto& p: data2)
{
p.addr += code_base;
}
}
}
const DebugFunctionInfo* DebugMapPrivate::FindFunc(DebugMap* map, uintptr_t addr)
{
static DebugFunctionInfo s {};
if (map->m_p->data.Size() == 0)
{
if (map->m_p->data2.Size() == 0)
{
return nullptr;
}
const DebugFunctionInfo2* f2 = find_info(map->m_p->data2.GetData(), addr, 0, map->m_p->data2.Size() - 1);
if (f2 != nullptr)
{
s.addr = f2->addr;
s.length = f2->length;
s.name = String::FromUtf8(f2->name).utf8_str();
s.obj = String::FromUtf8(f2->obj).utf8_str();
return &s;
}
return nullptr;
}
return find_info(map->m_p->data.GetData(), addr, 0, map->m_p->data.Size() - 1);
}
#if KYTY_PLATFORM != KYTY_PLATFORM_WINDOWS
static char* strtok_s(char* _Str, const char* _Delim, char** /*_Context*/)
{
return strtok(_Str, _Delim);
}
#endif
void DebugMap::LoadCsv(const String& name)
{
File pf(name, File::Mode::Read);
if (pf.IsInvalid())
{
return;
}
uint32_t size = pf.Size();
m_p->buf = new char[size + 1];
pf.Read(m_p->buf, size);
pf.Close();
m_p->buf[size] = 0;
char* context = nullptr;
[[maybe_unused]] char* p = strtok_s(m_p->buf, "\r\n;", &context);
EXIT_IF(String::FromUtf8(p) != U"Addr");
p = strtok_s(nullptr, "\r\n;", &context);
EXIT_IF(String::FromUtf8(p) != U"Size");
p = strtok_s(nullptr, "\r\n;", &context);
EXIT_IF(String::FromUtf8(p) != U"Func");
p = strtok_s(nullptr, "\r\n;", &context);
EXIT_IF(String::FromUtf8(p) != U"Obj");
for (;;)
{
char* s1 = strtok_s(nullptr, "\r\n;", &context);
char* s2 = strtok_s(nullptr, "\r\n;", &context);
char* s3 = strtok_s(nullptr, "\r\n;", &context);
char* s4 = strtok_s(nullptr, "\r\n;", &context);
if ((s1 == nullptr) || (s2 == nullptr) || (s3 == nullptr) || (s4 == nullptr))
{
break;
}
DebugFunctionInfo2 inf = {static_cast<uintptr_t>(sys_strtoui64(s1 + 2, nullptr, 16)),
static_cast<uintptr_t>(sys_strtoui64(s2, nullptr, 10)), s3, s4};
m_p->data2.Add(inf);
}
}
void DebugStack::Print(int from, bool with_name) const
{
for (int i = from; i < depth; i++)
{
const DebugFunctionInfo* f = with_name ? GetFunc(*this, i) : nullptr;
if (sizeof(uintptr_t) == 4)
{
printf("[%d] %08" PRIx32 ", %08" PRIx32 ", %s, %s\n", i - from, static_cast<uint32_t>(GetAddr(i)),
f != nullptr ? static_cast<uint32_t>(f->addr) : 0, f != nullptr ? f->obj.GetData() : "unknown",
f != nullptr ? f->name.GetData() : "unknown");
} else
{
printf("[%d] %016" PRIx64 ", %016" PRIx64 ", %s, %s\n", i - from, static_cast<uint64_t>(GetAddr(i)),
f != nullptr ? static_cast<uint64_t>(f->addr) : 0, f != nullptr ? f->obj.GetData() : "unknown",
f != nullptr ? f->name.GetData() : "unknown");
}
}
}
void DebugStack::Trace(DebugStack* stack)
{
stack->depth = DEBUG_MAX_STACK_DEPTH;
sys_stack_walk(stack->stack, &stack->depth);
}
void DebugStack::PrintAndroid(int from, bool with_name) const
{
KYTY_LOGI("---stack---\n");
for (int i = from; i < depth; i++)
{
const DebugFunctionInfo* f = with_name ? GetFunc(*this, i) : nullptr;
if (sizeof(uintptr_t) == 4)
{
KYTY_LOGI("[%d] %08" PRIx32 ", %08" PRIx32 ", %s, %s\n", i - from, static_cast<uint32_t>(GetAddr(i)),
f != nullptr ? static_cast<uint32_t>(f->addr) : 0, f != nullptr ? f->obj.GetData() : "unknown",
f != nullptr ? f->name.GetData() : "unknown");
} else
{
KYTY_LOGI("[%d] %016" PRIx64 ", %016" PRIx64 ", %s, %s\n", i - from, static_cast<uint64_t>(GetAddr(i)),
f != nullptr ? static_cast<uint64_t>(f->addr) : 0, f != nullptr ? f->obj.GetData() : "unknown",
f != nullptr ? f->name.GetData() : "unknown");
}
}
}
String Debug::GetCompiler()
{
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
return U"msvc";
#elif KYTY_COMPILER == KYTY_COMPILER_CLANG
return U"clang";
#elif KYTY_COMPILER == KYTY_COMPILER_MINGW
return U"mingw";
#elif KYTY_COMPILER == KYTY_COMPILER_GCC
return U"gcc";
#else
return U"????";
#endif
}
String Debug::GetLinker()
{
#if KYTY_LINKER == KYTY_LINKER_LD
return U"ld";
#elif KYTY_LINKER == KYTY_LINKER_LLD
return U"lld";
#elif KYTY_LINKER == KYTY_LINKER_LINK
return U"link";
#elif KYTY_LINKER == KYTY_LINKER_LLD_LINK
return U"lld_link";
#else
return U"??";
#endif
}
String Debug::GetBitness()
{
#if KYTY_BITNESS == 32
return U"32";
#elif KYTY_BITNESS == 64
return U"64";
#else
return U"??";
#endif
}
} // namespace Kyty::Core
File diff suppressed because it is too large Load Diff
+507
View File
@@ -0,0 +1,507 @@
#include "Kyty/Core/Hashmap.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/Hash.h"
#include "Kyty/Core/SafeDelete.h"
#include <cstring>
namespace Kyty::Core {
constexpr uint32_t HASH_MAX_KEY_SIZE = 32;
constexpr uint32_t HASH_MAX_VALUE_SIZE = 16;
#define KYTY_HASH_DEFINE_INT(type) \
KYTY_HASH_DEFINE_CALC(type) \
{ \
switch (sizeof(type)) \
{ \
case 8: return hash64(uint64_t(*key)); \
case 4: return hash32(uint32_t(*key)); \
case 2: return hash16(uint16_t(*key)); \
case 1: return hash8(uint8_t(*key)); \
} \
return hash((void*)key, sizeof(type)); \
} \
KYTY_HASH_DEFINE_EQUALS(type) { return (*key_a) == (*key_b); }
#define KYTY_HASH_DEFINE_PTR(type) \
KYTY_HASH_DEFINE_CALC(type) \
{ \
switch (sizeof(type)) \
{ \
case 8: return hash64(uint64_t(*key)); \
case 4: return hash32(uint32_t(uintptr_t(*key))); \
} \
return hash((void*)key, sizeof(type)); \
} \
KYTY_HASH_DEFINE_EQUALS(type) { return (*key_a) == (*key_b); }
KYTY_HASH_DEFINE_INT(int8_t);
KYTY_HASH_DEFINE_INT(uint8_t);
KYTY_HASH_DEFINE_INT(int16_t);
KYTY_HASH_DEFINE_INT(uint16_t);
KYTY_HASH_DEFINE_INT(int32_t);
KYTY_HASH_DEFINE_INT(uint32_t);
KYTY_HASH_DEFINE_INT(int64_t);
KYTY_HASH_DEFINE_INT(uint64_t);
KYTY_HASH_DEFINE_INT(char16_t);
KYTY_HASH_DEFINE_INT(char32_t);
KYTY_HASH_DEFINE_PTR(int8_t*);
KYTY_HASH_DEFINE_PTR(uint8_t*);
KYTY_HASH_DEFINE_PTR(int16_t*);
KYTY_HASH_DEFINE_PTR(uint16_t*);
KYTY_HASH_DEFINE_PTR(int32_t*);
KYTY_HASH_DEFINE_PTR(uint32_t*);
KYTY_HASH_DEFINE_PTR(int64_t*);
KYTY_HASH_DEFINE_PTR(uint64_t*);
KYTY_HASH_DEFINE_PTR(char16_t*);
KYTY_HASH_DEFINE_PTR(char32_t*);
KYTY_HASH_DEFINE_PTR(void*);
class HashmapPrivate
{
public:
struct Entry
{
uint8_t key[HASH_MAX_KEY_SIZE];
uint32_t hash;
uint8_t value[HASH_MAX_VALUE_SIZE];
Entry* next;
};
KYTY_CLASS_NO_COPY(HashmapPrivate);
HashmapPrivate(uint32_t initial_capacity, uint32_t key_size, uint32_t value_size, hash_calc_func_t hash, hash_key_equals_func_t equals,
hash_key_copy_func_t key_copy, hash_value_copy_func_t value_copy, hash_key_free_func_t key_free,
hash_value_free_func_t value_free)
: bucket_count_initial(initial_capacity), m_bucket_count(bucket_count_initial), size_max((m_bucket_count * 3) / 4),
m_key_size(key_size), m_value_size(value_size), hash_func(hash), equals_func(equals), key_copy_func(key_copy),
value_copy_func(value_copy), key_free_func(key_free), value_free_func(value_free)
{
EXIT_IF(hash == nullptr);
EXIT_IF(equals == nullptr);
EXIT_IF(key_copy == nullptr);
EXIT_IF(value_copy == nullptr);
EXIT_IF(key_free == nullptr);
EXIT_IF(value_free == nullptr);
EXIT_IF(initial_capacity & (initial_capacity - 1));
EXIT_IF(key_size > HASH_MAX_KEY_SIZE);
EXIT_IF(value_size > HASH_MAX_VALUE_SIZE);
}
virtual ~HashmapPrivate() { Clear(); }
void Clear()
{
if (size > 0)
{
for (uint32_t i = 0; i < m_bucket_count; i++)
{
Entry* entry = buckets[i];
while (entry != nullptr)
{
Entry* next = entry->next;
value_free_func(entry->value);
key_free_func(entry->key);
Delete(entry);
entry = next;
}
}
DeleteArray(buckets);
m_bucket_count = bucket_count_initial;
size_max = (m_bucket_count * 3) / 4;
buckets = nullptr;
size = 0;
}
}
inline uint32_t HashKey(const void* key) const
{
auto h = hash_func(key);
return h;
}
inline bool EqualKeys(const void* key_a, uint32_t hash_a, const void* key_b, uint32_t hash_b) const
{
if (key_a == key_b)
{
return true;
}
if (hash_a != hash_b)
{
return false;
}
return equals_func(key_a, key_b);
}
static inline uint32_t CalcIndex(uint32_t bucket_count, uint32_t hash) { return hash & (bucket_count - 1); }
uint32_t Size() const { return size; }
Entry* CreateEntry(const void* key, uint32_t hash, const void* value) const
{
auto* entry = new Entry;
key_copy_func(entry->key, key);
entry->hash = hash;
value_copy_func(entry->value, value);
entry->next = nullptr;
return entry;
}
void ExpandIfNecessary()
{
if (size > size_max)
{
uint32_t new_bucket_count = m_bucket_count << 1u;
auto** new_buckets = new Entry*[new_bucket_count];
// NOLINTNEXTLINE(bugprone-sizeof-expression)
std::memset(new_buckets, 0, sizeof(Entry*) * new_bucket_count);
// Move over existing entries.
for (uint32_t i = 0; i < m_bucket_count; i++)
{
Entry* entry = buckets[i];
while (entry != nullptr)
{
Entry* next = entry->next;
size_t index = CalcIndex(new_bucket_count, entry->hash);
entry->next = new_buckets[index];
new_buckets[index] = entry;
entry = next;
}
}
// Copy over internals.
DeleteArray(buckets);
buckets = new_buckets;
m_bucket_count = new_bucket_count;
size_max = (m_bucket_count * 3) / 4;
}
}
void Put(const void* key, const void* value)
{
uint32_t hash = HashKey(key);
uint32_t index = CalcIndex(m_bucket_count, hash);
if (buckets == nullptr)
{
buckets = new Entry*[m_bucket_count];
// NOLINTNEXTLINE(bugprone-sizeof-expression)
std::memset(buckets, 0, sizeof(Entry*) * m_bucket_count);
}
Entry** p = &(buckets[index]);
for (;;)
{
Entry* current = *p;
// Add a new entry.
if (current == nullptr)
{
*p = CreateEntry(key, hash, value);
size++;
ExpandIfNecessary();
return;
}
// Replace existing entry.
if (EqualKeys(current->key, current->hash, key, hash))
{
value_free_func(current->value);
value_copy_func(current->value, value);
return;
}
// Move to next entry.
p = &current->next;
}
}
const void* Get(const void* key) const
{
if (size == 0)
{
return nullptr;
}
uint32_t hash = HashKey(key);
uint32_t index = CalcIndex(m_bucket_count, hash);
Entry* entry = buckets[index];
while (entry != nullptr)
{
if (EqualKeys(entry->key, entry->hash, key, hash))
{
return entry->value;
}
entry = entry->next;
}
return nullptr;
}
void* OperatorSqBr(const void* key, const void* default_value)
{
uint32_t hash = HashKey(key);
uint32_t index = CalcIndex(m_bucket_count, hash);
if (buckets == nullptr)
{
buckets = new Entry*[m_bucket_count];
// NOLINTNEXTLINE(bugprone-sizeof-expression)
std::memset(buckets, 0, sizeof(Entry*) * m_bucket_count);
}
Entry** p = &(buckets[index]);
for (;;)
{
Entry* current = *p;
// Add a new entry.
if (current == nullptr)
{
current = *p = CreateEntry(key, hash, default_value);
size++;
ExpandIfNecessary();
return current->value;
}
// Replace existing entry.
if (EqualKeys(current->key, current->hash, key, hash))
{
return current->value;
}
// Move to next entry.
p = &current->next;
}
return nullptr;
}
void Remove(const void* key)
{
if (size == 0)
{
return;
}
uint32_t hash = HashKey(key);
uint32_t index = CalcIndex(m_bucket_count, hash);
// Pointer to the current entry.
Entry** p = &(buckets[index]);
Entry* current = nullptr;
while ((current = *p) != nullptr)
{
if (EqualKeys(current->key, current->hash, key, hash))
{
key_free_func(current->key);
value_free_func(current->value);
*p = current->next;
Delete(current);
size--;
if (size == 0)
{
DeleteArray(buckets);
m_bucket_count = bucket_count_initial;
size_max = (m_bucket_count * 3) / 4;
buckets = nullptr;
}
return;
}
p = &current->next;
}
}
void Start(uint32_t start_from) const
{
loop_entry = nullptr;
if (size == 0)
{
return;
}
for (loop_index = start_from; loop_index < m_bucket_count; loop_index++)
{
loop_entry = buckets[loop_index];
if (loop_entry != nullptr)
{
break;
}
}
}
bool End() const { return loop_entry == nullptr || loop_index >= m_bucket_count; }
void Next() const
{
EXIT_IF(loop_entry == nullptr);
loop_entry = loop_entry->next;
if (loop_entry == nullptr)
{
Start(loop_index + 1);
}
}
const void* Value() const
{
EXIT_IF(loop_entry == nullptr);
return loop_entry->value;
}
const void* Key() const
{
EXIT_IF(loop_entry == nullptr);
return loop_entry->key;
}
void ForEach(hash_callback_func_t callback, void* arg) const
{
for (Start(0); !End(); Next())
{
if (!callback(Key(), Value(), arg))
{
break;
}
}
}
uint32_t CollisionsCount() const
{
if (size == 0)
{
return 0;
}
uint32_t collisions = 0;
for (uint32_t i = 0; i < m_bucket_count; i++)
{
Entry* entry = buckets[i];
while (entry != nullptr)
{
if (entry->next != nullptr)
{
collisions++;
}
entry = entry->next;
}
}
return collisions;
}
mutable uint32_t loop_index = 0;
mutable Entry* loop_entry = nullptr;
Entry** buckets = nullptr;
uint32_t bucket_count_initial;
uint32_t m_bucket_count;
uint32_t size = 0;
uint32_t size_max;
uint32_t m_key_size;
uint32_t m_value_size;
hash_calc_func_t hash_func;
hash_key_equals_func_t equals_func;
hash_key_copy_func_t key_copy_func;
hash_value_copy_func_t value_copy_func;
hash_key_free_func_t key_free_func;
hash_value_free_func_t value_free_func;
};
HashmapBase::HashmapBase(uint32_t key_size, uint32_t value_size, hash_calc_func_t hash, hash_key_equals_func_t equals,
hash_key_copy_func_t key_copy, hash_value_copy_func_t value_copy, hash_key_free_func_t key_free,
hash_value_free_func_t value_free)
: m_p(new HashmapPrivate(8, key_size, value_size, hash, equals, key_copy, value_copy, key_free, value_free))
{
}
HashmapBase::~HashmapBase()
{
Delete(m_p);
}
uint32_t HashmapBase::Size() const
{
return m_p->Size();
}
void HashmapBase::Put(const void* key, const void* value)
{
m_p->Put(key, value);
}
const void* HashmapBase::Get(const void* key) const
{
return m_p->Get(key);
}
void HashmapBase::Remove(const void* key)
{
m_p->Remove(key);
}
void HashmapBase::Start() const
{
m_p->Start(0);
}
bool HashmapBase::End() const
{
return m_p->End();
}
void HashmapBase::Next() const
{
m_p->Next();
}
const void* HashmapBase::Value() const
{
return m_p->Value();
}
const void* HashmapBase::Key() const
{
return m_p->Key();
}
void HashmapBase::ForEach(hash_callback_func_t callback, void* arg) const
{
m_p->ForEach(callback, arg);
}
void* HashmapBase::OperatorSquareBrackets(const void* key, const void* default_value)
{
void* r = m_p->OperatorSqBr(key, default_value);
EXIT_IF(r == nullptr);
return r;
}
void HashmapBase::Clear()
{
m_p->Clear();
}
uint32_t HashmapBase::CollisionsCount() const
{
return m_p->CollisionsCount();
}
} // namespace Kyty::Core
+477
View File
@@ -0,0 +1,477 @@
#include "Kyty/Core/JsonReader.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/SafeDelete.h"
namespace Kyty::Core {
static String* g_json_error = nullptr;
static Json* g_null = nullptr;
static void set_error(const String& str)
{
if (g_json_error == nullptr)
{
g_json_error = new String;
}
*g_json_error = str;
}
static const char32_t* skip(const char32_t* in)
{
if (in == nullptr)
{
return nullptr;
}
while ((*in != 0u) && Char::IsSpace(*in))
{
in++;
}
return in;
}
static const char32_t* skip_number(const char32_t* in)
{
if (in == nullptr)
{
return nullptr;
}
while ((*in != 0u) && (Char::IsDecimal(*in) || *in == U'-' || *in == U'+' || *in == U'e' || *in == U'E' || *in == U'.'))
{
in++;
}
return in;
}
const char32_t* Json::parse_value(const char32_t* value)
{
EXIT_IF(!value);
switch (*value)
{
case U'n':
{
if (Char::EqualAsciiN(value, "null", 4))
{
m_type = JsonNULL;
return value + 4;
}
break;
}
case U'f':
{
if (Char::EqualAsciiN(value, "false", 5))
{
m_type = JsonBool;
return value + 5;
}
break;
}
case U't':
{
if (Char::EqualAsciiN(value, "true", 4))
{
m_type = JsonBool;
m_value_bool = true;
m_value_int = 1;
return value + 4;
}
break;
}
case U'\"': return parse_string(value);
case U'[': return parse_array(value);
case U'{': return parse_object(value);
case U'-':
case U'0':
case U'1':
case U'2':
case U'3':
case U'4':
case U'5':
case U'6':
case U'7':
case U'8':
case U'9': return parse_number(value);
default: return value; break;
}
set_error(value);
return nullptr;
}
const char32_t* Json::parse_array(const char32_t* value)
{
if (*value != U'[')
{
set_error(value);
return nullptr;
}
m_type = JsonArray;
value = skip(value + 1);
if (*value == U']')
{
return value + 1;
}
Json* child = new Json();
value = skip(child->parse_value(skip(value)));
if (value == nullptr)
{
Delete(child);
return nullptr;
}
m_list.Add(child);
while (*value == U',')
{
child = new Json();
value = skip(child->parse_value(skip(value + 1)));
if (value == nullptr)
{
Delete(child);
return nullptr;
}
m_list.Add(child);
}
if (*value == ']')
{
return value + 1;
}
set_error(value);
return nullptr;
}
const char32_t* Json::parse_object(const char32_t* value)
{
if (*value != U'{')
{
set_error(value);
return nullptr;
}
m_type = JsonObject;
value = skip(value + 1);
if (*value == U'}')
{
return value + 1;
}
Json* child = new Json();
value = skip(child->parse_value(skip(value)));
if (value == nullptr)
{
Delete(child);
return nullptr;
}
child->m_name = child->m_value_string;
child->m_value_string = U"";
if (*value != U':')
{
Delete(child);
set_error(value);
return nullptr;
}
value = skip(child->parse_value(skip(value + 1)));
if (value == nullptr)
{
Delete(child);
return nullptr;
}
m_list.Add(child);
while (*value == U',')
{
child = new Json();
value = skip(child->parse_value(skip(value + 1)));
if (value == nullptr)
{
Delete(child);
return nullptr;
}
child->m_name = child->m_value_string;
child->m_value_string = U"";
if (*value != U':')
{
Delete(child);
set_error(value);
return nullptr;
}
value = skip(child->parse_value(skip(value + 1)));
if (value == nullptr)
{
Delete(child);
return nullptr;
}
m_list.Add(child);
}
if (*value == U'}')
{
return value + 1;
}
set_error(value);
return nullptr;
}
const char32_t* Json::parse_number(const char32_t* value)
{
const char32_t* end = skip_number(value);
uint32_t len = end - value;
if (len != 0u)
{
String str(U' ', len);
std::memcpy(str.GetData(), value, len * sizeof(char32_t));
m_type = JsonNumber;
m_value_double = str.ToDouble();
m_value_int = static_cast<int64_t>(m_value_double);
m_value_string = str;
return end;
}
set_error(value);
return nullptr;
}
const char32_t* Json::parse_string(const char32_t* str)
{
if (*str != U'\"')
{
set_error(str);
return nullptr;
}
const char32_t* ptr = str + 1;
uint32_t len = 0;
for (;; len++, ptr++)
{
if (*ptr == U'\"' || *ptr == 0)
{
break;
}
if (*ptr == U'\\')
{
ptr++;
}
}
String out(' ', len);
ptr = str + 1;
char32_t* ptr2 = out.GetData();
while (*ptr != '\"' && *ptr != 0)
{
if (*ptr != '\\')
{
*ptr2++ = *ptr++;
} else
{
ptr++;
switch (*ptr)
{
case U'b': *ptr2++ = U'\b'; break;
case U'f': *ptr2++ = U'\f'; break;
case U'n': *ptr2++ = U'\n'; break;
case U'r': *ptr2++ = U'\r'; break;
case U't': *ptr2++ = U'\t'; break;
case U'u':
set_error(ptr);
return nullptr;
break;
default: *ptr2++ = *ptr; break;
}
ptr++;
}
}
if (*ptr == U'\"')
{
ptr++;
}
len = ptr2 - out.GetDataConst();
m_value_string = out.Left(len);
m_type = JsonString;
return ptr;
}
// Json::Json()
//{
// m_value_int = 0;
// m_value_double = 0.0;
// m_value_bool = false;
// m_type = JsonNULL;
//}
Json::~Json()
{
FOR (i, m_list)
{
Delete(m_list[i]);
}
}
const Json* Json::Create(const String& str)
{
Json* c = new Json();
const char32_t* value = c->parse_value(skip(str.GetDataConst()));
if (value == nullptr)
{
Delete(c);
return nullptr;
}
return c;
}
String Json::GetError()
{
if (g_json_error != nullptr)
{
return *g_json_error;
}
return U"";
}
void Json::Init()
{
if (g_null == nullptr)
{
g_null = new Json();
}
}
// const Json* Json::GetItem(const String& string) const
const Json* Json::GetItem(const char* string) const
{
FOR (i, m_list)
{
const Json* n = m_list.At(i);
if (n->m_name.EqualAsciiNoCase(string))
{
return n;
}
}
Init();
return g_null;
}
// String Json::GetString(const String& name, const String& default_value) const
String Json::GetString(const char* name, const String& default_value) const
{
const Json* n = GetItem(name);
if (!n->IsNull())
{
return n->m_value_string;
}
return default_value;
}
String Json::GetString(const char* name) const
{
const Json* n = GetItem(name);
if (!n->IsNull())
{
return n->m_value_string;
}
return U"";
}
// double Json::GetFloat(const String& name, double default_value) const
double Json::GetFloat(const char* name, double default_value) const
{
const Json* n = GetItem(name);
if (!n->IsNull())
{
return n->m_value_double;
}
return default_value;
}
// int64_t Json::GetInt(const String& name, int64_t default_value) const
int64_t Json::GetInt(const char* name, int64_t default_value) const
{
const Json* n = GetItem(name);
if (!n->IsNull())
{
return n->m_value_int;
}
return default_value;
}
bool Json::GetBool(const char* name, bool default_value) const
{
const Json* n = GetItem(name);
if (!n->IsNull())
{
return n->m_value_bool;
}
return default_value;
}
StringList Json::DbgCheckList(const StringList& required, const StringList& optional) const
{
StringList errors;
FOR (i, required)
{
if (GetItem(required.At(i).C_Str())->m_name.IsEmpty())
{
errors.Add(U"missing: " + required.At(i));
}
}
FOR (i, m_list)
{
const Json* n = m_list.At(i);
if (!n->m_name.IsEmpty() && !(required.Contains(n->m_name) || optional.Contains(n->m_name)))
{
errors.Add(U"unknown: " + n->m_name);
}
}
return errors;
}
} // namespace Kyty::Core
+342
View File
@@ -0,0 +1,342 @@
#include "Kyty/Core/Language.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/Hashmap.h"
#include "Kyty/Core/Vector.h"
namespace Kyty::Core {
static const char32_t* g_list_numeric = U"0123456789";
static const char32_t* g_list_punctuation = U" !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~";
static const char32_t* g_list_punctuation_2 = U"ªº¡¿";
static const char32_t* g_alphabet_english_1 = U"ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const char32_t* g_alphabet_english_2 = U"abcdefghijklmnopqrstuvwxyz";
static const char32_t* g_alphabet_russian_1 = U"АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ";
static const char32_t* g_alphabet_russian_2 = U"абвгдеёжзийклмнопрстуфхцчшщьыъэюя";
static const char32_t* g_alphabet_german_1 = U"ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÜẞ";
static const char32_t* g_alphabet_german_2 = U"abcdefghijklmnopqrstuvwxyzäöüß";
static const char32_t* g_alphabet_french_1 = U"ABCDEFGHIJKLMNOPQRSTUVWXYZÀÂÆÈÉÊËÎÏÔŒÙÛÜŸÇ";
static const char32_t* g_alphabet_french_2 = U"abcdefghijklmnopqrstuvwxyzàâæèéêëîïôœùûüÿç";
static const char32_t* g_alphabet_italian_1 = U"ABCDEFGHILMNOPQRSTUVÀÈÉÌÍÏÒÓÙÚ";
static const char32_t* g_alphabet_italian_2 = U"abcdefghilmnopqrstuvàèéìíïòóùú";
static const char32_t* g_alphabet_spanish_1 = U"ABCDEFGHIJKLMNÑOPQRSTUVWXYZÁÉÍÓÚÜ";
static const char32_t* g_alphabet_spanish_2 = U"abcdefghijklmnñopqrstuvwxyzáéíóúü";
static const char32_t* g_alphabet_portuguese_1 = U"ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÉÊÍÒÓÔÕÚÜÇ";
static const char32_t* g_alphabet_portuguese_2 = U"abcdefghijklmnopqrstuvwxyzàáâãéêíòóôõúüç";
static const char32_t* g_short_month_english[] = {U"Jan", U"Feb", U"Mar", U"Apr", U"May", U"Jun",
U"Jul", U"Aug", U"Sep", U"Oct", U"Nov", U"Dec"};
static const char32_t* g_month_english[] = {U"January", U"February", U"March", U"April", U"May", U"June",
U"July", U"August", U"September", U"October", U"November", U"December"};
static const char32_t* g_short_month_russian[] = {U"Янв", U"Фев", U"Мар", U"Апр", U"Май", U"Июн",
U"Июл", U"Авг", U"Сен", U"Окт", U"Ноя", U"Дек"};
static const char32_t* g_month_russian[] = {U"Январь", U"Февраль", U"Март", U"Апрель", U"Май", U"Июнь",
U"Июль", U"Август", U"Сентябрь", U"Октябрь", U"Ноябрь", U"Декабрь"};
static const char32_t* g_short_day_english[] = {U"Mon", U"Tue", U"Wed", U"Thu", U"Fri", U"Sat", U"Sun"};
static const char32_t* g_day_english[] = {U"Monday", U"Tuesday", U"Wednesday", U"Thursday", U"Friday", U"Saturday", U"Sunday"};
static const char32_t* g_short_day_russian[] = {U"Пнд", U"Втн", U"Срд", U"Чтв", U"Птн", U"Сбт", U"Вск"};
static const char32_t* g_day_russian[] = {U"Понедельник", U"Вторник", U"Среда", U"Четверг", U"Пятница", U"Суббота", U"Воскресенье"};
Hashmap<String, LanguageId>* g_lang_map = nullptr;
void Language::Init()
{
g_lang_map = new Hashmap<String, LanguageId>;
g_lang_map->Put(U"de", LanguageId::German);
g_lang_map->Put(U"en", LanguageId::English);
g_lang_map->Put(U"fr", LanguageId::French);
g_lang_map->Put(U"it", LanguageId::Italian);
g_lang_map->Put(U"pt", LanguageId::Portuguese);
g_lang_map->Put(U"ru", LanguageId::Russian);
g_lang_map->Put(U"es", LanguageId::Spanish);
}
static String string_remove_duplicates(const String& s)
{
Vector<char32_t> r;
for (auto ch: s)
{
if (!r.Contains(ch))
{
r.Add(ch);
}
}
r.Sort();
r.Add(U'\0');
return r.GetDataConst();
}
String Language::GetLettersList(LanguageId lang_id)
{
String ret = U"";
switch (lang_id)
{
case LanguageId::English:
ret += g_alphabet_english_1;
ret += g_alphabet_english_2;
break;
case LanguageId::Russian:
ret += g_alphabet_russian_1;
ret += g_alphabet_russian_2;
break;
case LanguageId::German:
ret += g_alphabet_german_1;
ret += g_alphabet_german_2;
break;
case LanguageId::French:
ret += g_alphabet_french_1;
ret += g_alphabet_french_2;
break;
case LanguageId::Italian:
ret += g_alphabet_italian_1;
ret += g_alphabet_italian_2;
break;
case LanguageId::Spanish:
ret += g_alphabet_spanish_1;
ret += g_alphabet_spanish_2;
break;
case LanguageId::Portuguese:
ret += g_alphabet_portuguese_1;
ret += g_alphabet_portuguese_2;
break;
case LanguageId::Unknown: EXIT("unknown language\n");
}
return string_remove_duplicates(ret);
}
String Language::GetLettersList(const String& id)
{
return GetLettersList(GetId(id));
}
String Language::GetNumericList(const String& /*id*/)
{
String ret = U"";
ret += g_list_numeric;
return string_remove_duplicates(ret);
}
String Language::GetPunctuationList(const String& id)
{
String ret = U"";
ret += g_list_punctuation;
if (GetId(id) == LanguageId::Spanish)
{
ret += g_list_punctuation_2;
}
return string_remove_duplicates(ret);
}
String Language::GetCharList(const String& id)
{
String ret = U"";
ret += g_list_numeric;
ret += g_list_punctuation;
switch (GetId(id))
{
case LanguageId::English:
ret += g_alphabet_english_1;
ret += g_alphabet_english_2;
break;
case LanguageId::Russian:
ret += g_alphabet_russian_1;
ret += g_alphabet_russian_2;
break;
case LanguageId::German:
ret += g_alphabet_german_1;
ret += g_alphabet_german_2;
break;
case LanguageId::French:
ret += g_alphabet_french_1;
ret += g_alphabet_french_2;
break;
case LanguageId::Italian:
ret += g_alphabet_italian_1;
ret += g_alphabet_italian_2;
break;
case LanguageId::Spanish:
ret += g_list_punctuation_2;
ret += g_alphabet_spanish_1;
ret += g_alphabet_spanish_2;
break;
case LanguageId::Portuguese:
ret += g_alphabet_portuguese_1;
ret += g_alphabet_portuguese_2;
break;
case LanguageId::Unknown: EXIT("unknown language\n");
}
return string_remove_duplicates(ret);
}
String Language::GetCharListAll()
{
String ret = U"";
ret += g_list_numeric;
ret += g_list_punctuation;
ret += g_list_punctuation_2;
ret += g_alphabet_english_1;
ret += g_alphabet_english_2;
ret += g_alphabet_russian_1;
ret += g_alphabet_russian_2;
ret += g_alphabet_german_1;
ret += g_alphabet_german_2;
ret += g_alphabet_french_1;
ret += g_alphabet_french_2;
ret += g_alphabet_italian_1;
ret += g_alphabet_italian_2;
ret += g_alphabet_spanish_1;
ret += g_alphabet_spanish_2;
ret += g_alphabet_portuguese_1;
ret += g_alphabet_portuguese_2;
return string_remove_duplicates(ret);
}
String Language::GetNameOfMonth(int month, LanguageId lang_id)
{
EXIT_IF(month < 1 || month > 12);
switch (lang_id)
{
case LanguageId::English: return g_month_english[month - 1]; break;
case LanguageId::Russian: return g_month_russian[month - 1]; break;
case LanguageId::Unknown:
case LanguageId::German:
case LanguageId::French:
case LanguageId::Italian:
case LanguageId::Portuguese:
case LanguageId::Spanish: EXIT("unknown language\n");
}
return U"";
}
String Language::GetNameOfMonthShort(int month, LanguageId lang_id)
{
EXIT_IF(month < 1 || month > 12);
switch (lang_id)
{
case LanguageId::English: return g_short_month_english[month - 1]; break;
case LanguageId::Russian: return g_short_month_russian[month - 1]; break;
case LanguageId::Unknown:
case LanguageId::German:
case LanguageId::French:
case LanguageId::Italian:
case LanguageId::Portuguese:
case LanguageId::Spanish: EXIT("unknown language\n");
}
return U"";
}
String Language::GetNameOfDay(int day, LanguageId lang_id)
{
EXIT_IF(day < 1 || day > 7);
switch (lang_id)
{
case LanguageId::English: return g_day_english[day - 1]; break;
case LanguageId::Russian: return g_day_russian[day - 1]; break;
case LanguageId::Unknown:
case LanguageId::German:
case LanguageId::French:
case LanguageId::Italian:
case LanguageId::Portuguese:
case LanguageId::Spanish: EXIT("unknown language\n");
}
return U"";
}
LanguageId Language::GetId(const String& id)
{
return g_lang_map->Get(id, LanguageId::Unknown);
}
StringList Language::GetLanguages()
{
StringList ret;
FOR_HASH (*g_lang_map)
{
ret.Add(g_lang_map->Key());
}
return ret;
}
String Language::GetNameOfDayShort(int day, LanguageId lang_id)
{
EXIT_IF(day < 1 || day > 7);
switch (lang_id)
{
case LanguageId::English: return g_short_day_english[day - 1]; break;
case LanguageId::Russian: return g_short_day_russian[day - 1]; break;
case LanguageId::Unknown:
case LanguageId::German:
case LanguageId::French:
case LanguageId::Italian:
case LanguageId::Portuguese:
case LanguageId::Spanish: EXIT("unknown language\n");
}
return U"";
}
} // namespace Kyty::Core
+619
View File
@@ -0,0 +1,619 @@
#include "Kyty/Core/MemoryAlloc.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/DateTime.h" // IWYU pragma: keep
#include "Kyty/Core/Debug.h" // IWYU pragma: keep
#include "Kyty/Core/Hashmap.h" // IWYU pragma: keep
#include "Kyty/Sys/SysHeap.h"
#include "Kyty/Sys/SysSync.h"
#include <cstdlib>
#include <new>
namespace Kyty::Core {
#if !defined(KYTY_FINAL) && !defined(KYTY_SHARED_DLL)
#define MEM_TRACKER
#endif
#define MEM_ALLOC_ALIGNED
#ifdef MEM_ALLOC_ALIGNED
#if KYTY_PLATFORM == KYTY_PLATFORM_ANDROID
constexpr int MEM_ALLOC_ALIGN = 8;
#else
constexpr int MEM_ALLOC_ALIGN = 16;
#endif
#endif
#if KYTY_PLATFORM == KYTY_PLATFORM_WINDOWS && KYTY_BITNESS == 64
[[maybe_unused]] constexpr int STACK_CHECK_FROM = 5;
#elif KYTY_PLATFORM == KYTY_PLATFORM_ANDROID
[[maybe_unused]] constexpr int STACK_CHECK_FROM = 4;
#else
[[maybe_unused]] constexpr int STACK_CHECK_FROM = 2;
#endif
static SysCS* g_mem_cs = nullptr;
static bool g_mem_initialized = false;
static sys_heap_id_t g_default_heap = nullptr;
static size_t g_mem_max_size = 0;
#ifdef MEM_TRACKER
using pattern_t = uint32_t;
constexpr size_t PATTERN_SIZE = (sizeof(pattern_t));
constexpr size_t PATTERNS_NUM = 4;
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init,hicpp-member-init)
struct MemBlockInfoT
{
uintptr_t addr;
size_t size;
int state;
DebugStack stack;
pattern_t left_pattern;
pattern_t right_pattern;
};
thread_local int g_mem_depth = 0;
thread_local bool g_mem_tracker_enabled = true;
static Hashmap<uintptr_t, MemBlockInfoT*>* g_mem_map = nullptr;
static int g_mem_state = 0;
static size_t g_total_allocated = 0;
#define KYTY_MDBG(str, ptr) \
{ \
}
#endif
class MemLock
{
public:
MemLock()
{
g_mem_cs->Enter();
#ifdef MEM_TRACKER
g_mem_depth++;
#endif
}
~MemLock()
{
#ifdef MEM_TRACKER
g_mem_depth--;
#endif
g_mem_cs->Leave();
}
KYTY_CLASS_NO_COPY(MemLock);
#ifdef MEM_TRACKER
[[nodiscard]] bool IsRecursive() const { return g_mem_depth > 1; } // NOLINT(readability-convert-member-functions-to-static)
#endif
};
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
#pragma code_seg(push)
#endif
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
#pragma code_seg(".mem_a")
#endif
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
#pragma code_seg(".mem_b")
#endif
#ifdef MEM_TRACKER
static const Array<pattern_t, 4> g_mem_patterns = {0xAAAAAAAA, 0xCCCCCCCC, 0x55555555, 0x33333333};
static int g_pattern_id = 0;
static pattern_t pattern_next()
{
return g_mem_patterns[g_pattern_id++ % g_mem_patterns.Size()];
}
static void pattern_write(MemBlockInfoT* info)
{
auto* ptr = reinterpret_cast<uint8_t*>(info->addr);
for (int i = 0; i < 4; i++)
{
(reinterpret_cast<pattern_t*>(ptr - PATTERN_SIZE * PATTERNS_NUM))[i] = info->left_pattern;
(reinterpret_cast<pattern_t*>(ptr + info->size))[i] = info->right_pattern;
}
}
static bool pattern_check(MemBlockInfoT* info)
{
auto* ptr = reinterpret_cast<uint8_t*>(info->addr);
for (int i = 0; i < 4; i++)
{
if ((reinterpret_cast<pattern_t*>(ptr - PATTERN_SIZE * PATTERNS_NUM))[i] != info->left_pattern ||
(reinterpret_cast<pattern_t*>(ptr + info->size))[i] != info->right_pattern)
{
return false;
}
}
return true;
}
#endif
static void mem_init()
{
if (g_mem_initialized)
{
return;
}
g_mem_initialized = true;
#ifdef MEM_TRACKER
srand(DateTime::FromSystemUTC().GetTime().MsecTotal());
g_pattern_id = static_cast<int>(rand() % g_mem_patterns.Size()); // NOLINT(cert-msc30-c,cert-msc50-cpp)
#endif
#ifdef MEM_TRACKER
g_mem_depth++;
#endif
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
g_mem_cs = new (std::malloc(sizeof(SysCS))) SysCS;
g_mem_cs->Init();
#ifdef MEM_TRACKER
g_mem_map = new Hashmap<uintptr_t, MemBlockInfoT*>;
#endif
g_default_heap = sys_heap_create();
#ifdef MEM_TRACKER
g_mem_depth--;
#endif
}
void core_memory_init()
{
mem_init();
}
void* mem_alloc_check_alignment(void* ptr)
{
#ifdef MEM_ALLOC_ALIGNED
if ((uintptr_t(ptr) & uintptr_t(MEM_ALLOC_ALIGN - 1)) != 0u)
{
EXIT("mem alloc not aligned!\n");
}
#endif
return ptr;
}
void* mem_alloc(size_t size)
{
if (size == 0)
{
EXIT("size == 0\n");
}
if ((g_mem_max_size != 0u) && size > g_mem_max_size)
{
EXIT("mem_alloc(): size(%" PRIu64 ") > max(%" PRIu64 ")\n", uint64_t(size), uint64_t(g_mem_max_size));
}
mem_init();
MemLock lock;
#ifdef MEM_TRACKER
DebugStack stack;
DebugStack::Trace(&stack);
if (lock.IsRecursive() || !g_mem_tracker_enabled)
{
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
void* r = std::malloc(size);
KYTY_MDBG("- std alloc -", r);
return mem_alloc_check_alignment(r);
}
#endif
#ifdef MEM_TRACKER
auto* ptr_p = static_cast<pattern_t*>(sys_heap_alloc(g_default_heap, size + PATTERN_SIZE * PATTERNS_NUM * 2));
void* ptr = ptr_p + PATTERNS_NUM;
#else
void* ptr = sys_heap_alloc(g_default_heap, size);
#endif
if (ptr == nullptr)
{
EXIT("mem_alloc(): can't alloc %" PRIu64 " bytes\n", uint64_t(size));
}
#ifdef MEM_TRACKER
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
auto* info = static_cast<MemBlockInfoT*>(std::malloc(sizeof(MemBlockInfoT)));
info->addr = reinterpret_cast<uintptr_t>(ptr);
info->size = size;
info->state = g_mem_state;
stack.CopyTo(&info->stack);
info->left_pattern = pattern_next();
info->right_pattern = pattern_next();
pattern_write(info);
g_mem_map->Put(reinterpret_cast<uintptr_t>(ptr), info);
g_total_allocated += size;
KYTY_MDBG("- mem_alloc -", ptr);
#endif
return mem_alloc_check_alignment(ptr);
}
void* mem_realloc(void* ptr, size_t size)
{
EXIT_IF(size == 0);
if ((g_mem_max_size != 0u) && size > g_mem_max_size)
{
EXIT("mem_realloc(): size(%" PRIu64 ") > max(%" PRIu64 ")\n", uint64_t(size), uint64_t(g_mem_max_size));
}
mem_init();
MemLock lock;
#ifdef MEM_TRACKER
DebugStack stack;
DebugStack::Trace(&stack);
if (lock.IsRecursive() || !g_mem_tracker_enabled)
{
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
void* ptr2 = std::realloc(ptr, size);
KYTY_MDBG("- std realloc old -", ptr);
KYTY_MDBG("- std realloc new -", ptr2);
return mem_alloc_check_alignment(ptr2);
}
#endif
#ifdef MEM_TRACKER
auto* ptr2_b = static_cast<pattern_t*>(sys_heap_realloc(
g_default_heap, ptr != nullptr ? (static_cast<pattern_t*>(ptr)) - PATTERNS_NUM : nullptr, size + PATTERN_SIZE * PATTERNS_NUM * 2));
void* ptr2 = ptr2_b + PATTERNS_NUM;
#else
void* ptr2 = sys_heap_realloc(g_default_heap, ptr, size);
#endif
if (ptr2 == nullptr)
{
EXIT("mem_realloc(): can't alloc %" PRIu64 " bytes\n", uint64_t(size));
}
#ifdef MEM_TRACKER
if (ptr != nullptr)
{
MemBlockInfoT* const* info_p = g_mem_map->Find(reinterpret_cast<uintptr_t>(ptr));
// EXIT_IF(info == 0);
if (info_p == nullptr)
{
printf("error %016" PRIx64 "\n", reinterpret_cast<uint64_t>(ptr));
EXIT_IF(info_p == nullptr);
}
MemBlockInfoT* info = *info_p;
g_total_allocated -= info->size;
g_total_allocated += size;
if (ptr == ptr2)
{
info->size = size;
info->state = g_mem_state;
stack.CopyTo(&info->stack);
} else
{
info->addr = reinterpret_cast<uintptr_t>(ptr2);
info->size = size;
info->state = g_mem_state;
stack.CopyTo(&info->stack);
g_mem_map->Put(reinterpret_cast<uintptr_t>(ptr2), info);
g_mem_map->Remove(reinterpret_cast<uintptr_t>(ptr));
}
pattern_write(info);
} else
{
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
auto* info = static_cast<MemBlockInfoT*>(std::malloc(sizeof(MemBlockInfoT)));
info->addr = reinterpret_cast<uintptr_t>(ptr2);
info->size = size;
info->state = g_mem_state;
stack.CopyTo(&info->stack);
info->left_pattern = pattern_next();
info->right_pattern = pattern_next();
pattern_write(info);
g_total_allocated += size;
g_mem_map->Put(reinterpret_cast<uintptr_t>(ptr2), info);
}
KYTY_MDBG("- mem_realloc old -", ptr);
KYTY_MDBG("- mem_realloc new -", ptr2);
#endif
// g_mem_cs->Leave();
return mem_alloc_check_alignment(ptr2);
}
void mem_free(void* ptr)
{
EXIT_IF(!g_mem_initialized);
MemLock lock;
#ifdef MEM_TRACKER
MemBlockInfoT* const* info = g_mem_map->Find(reinterpret_cast<uintptr_t>(ptr));
if (info != nullptr)
{
if (!pattern_check(*info))
{
EXIT("memory overflow\n");
}
g_total_allocated -= (*info)->size;
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
std::free(*info);
g_mem_map->Remove(reinterpret_cast<uintptr_t>(ptr));
// printf("heap_free: %x\n", uintptr_t(ptr));
#endif
#ifdef MEM_TRACKER
sys_heap_free(g_default_heap, ptr != nullptr ? (static_cast<pattern_t*>(ptr)) - PATTERNS_NUM : nullptr);
#else
sys_heap_free(g_default_heap, ptr);
#endif
#ifdef MEM_TRACKER
} else
{
if (ptr != nullptr)
{
// printf("free: %x\n", uintptr_t(ptr));
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
std::free(ptr);
}
}
KYTY_MDBG("- mem_free -", ptr);
#endif
}
bool mem_check([[maybe_unused]] const void* ptr)
{
#ifdef MEM_TRACKER
EXIT_IF(!g_mem_initialized);
MemLock lock;
MemBlockInfoT* const* info = g_mem_map->Find(reinterpret_cast<uintptr_t>(ptr));
return info != nullptr && pattern_check(*info);
#else
return true;
#endif
}
#ifdef MEM_TRACKER
static bool KYTY_HASH_CALL mem_map_print_callback(const uintptr_t* /*key*/, MemBlockInfoT* const* value, void* arg)
{
int state = static_cast<int>(reinterpret_cast<intptr_t>(arg));
if (state <= (*value)->state)
{
if (sizeof(uintptr_t) == 4)
{
printf("\n%08" PRIx32 ", %" PRIu32 ", %d\n", static_cast<uint32_t>((*value)->addr), static_cast<uint32_t>((*value)->size),
(*value)->state);
(*value)->stack.Print(STACK_CHECK_FROM);
} else
{
printf("\n%016" PRIx64 ", %" PRIu64 ", %d\n", static_cast<uint64_t>((*value)->addr), static_cast<uint64_t>((*value)->size),
(*value)->state);
(*value)->stack.Print(STACK_CHECK_FROM);
}
}
return true;
}
static bool KYTY_HASH_CALL mem_map_stat_callback(const uintptr_t* /*key*/, MemBlockInfoT* const* value, void* arg)
{
auto* s = static_cast<MemStats*>(arg);
if (s->state <= (*value)->state)
{
s->total_allocated += (*value)->size;
s->blocks_num++;
}
return true;
}
#endif
void mem_get_stat(MemStats* s)
{
#ifdef MEM_TRACKER
if (!g_mem_initialized)
{
#endif
s->total_allocated = 0;
s->blocks_num = 0;
#ifdef MEM_TRACKER
return;
}
MemLock lock;
if (s->state == 0)
{
s->total_allocated = g_total_allocated;
s->blocks_num = g_mem_map->Size();
} else
{
s->total_allocated = 0;
s->blocks_num = 0;
g_mem_map->ForEach(mem_map_stat_callback, static_cast<void*>(s));
}
#endif
}
int mem_new_state()
{
#ifdef MEM_TRACKER
if (!g_mem_initialized)
{
#endif
return 0;
#ifdef MEM_TRACKER
}
MemLock lock;
g_mem_state++;
return g_mem_state;
#endif
}
void mem_print(int from_state)
{
#ifdef MEM_TRACKER
if (!g_mem_initialized)
{
return;
}
MemLock lock;
intptr_t s = from_state;
g_mem_map->ForEach(mem_map_print_callback, reinterpret_cast<void*>(s));
#endif
}
} // namespace Kyty::Core
#ifndef KYTY_SHARED_DLL
void* operator new(size_t size)
{
return Kyty::Core::mem_alloc(size);
}
void* operator new(std::size_t size, const std::nothrow_t& /*nothrow_value*/) noexcept
{
return Kyty::Core::mem_alloc(size);
}
void* operator new[](size_t size)
{
return Kyty::Core::mem_alloc(size);
}
void* operator new[](std::size_t size, const std::nothrow_t& /*nothrow_value*/) noexcept
{
return Kyty::Core::mem_alloc(size);
}
void operator delete(void* block) noexcept
{
Kyty::Core::mem_free(block);
}
void operator delete[](void* block) noexcept
{
Kyty::Core::mem_free(block);
}
#else
//#error "haha"
#endif
extern "C" {
void* mem_alloc_c(size_t size)
{
return Kyty::Core::mem_alloc(size);
}
void* mem_realloc_c(void* ptr, size_t size)
{
return Kyty::Core::mem_realloc(ptr, size);
}
void mem_free_c(void* ptr)
{
Kyty::Core::mem_free(ptr);
}
}
namespace Kyty::Core {
bool mem_tracker_enabled()
{
#ifdef MEM_TRACKER
return g_mem_tracker_enabled;
#else
return false;
#endif
}
void mem_tracker_enable()
{
#ifdef MEM_TRACKER
g_mem_tracker_enabled = true;
#endif
}
void mem_tracker_disable()
{
#ifdef MEM_TRACKER
g_mem_tracker_enabled = false;
#endif
}
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
#pragma code_seg(".mem_c")
#endif
#if KYTY_COMPILER == KYTY_COMPILER_MSVC
#pragma code_seg(pop)
#endif
void mem_set_max_size(size_t size)
{
g_mem_max_size = size;
}
} // namespace Kyty::Core
+71
View File
@@ -0,0 +1,71 @@
#include "Kyty/Core/SDLSubsystem.h"
#include "Kyty/Core/Common.h"
#include "Kyty/Core/MemoryAlloc.h"
#include <cstring>
// IWYU pragma: no_include <intrin.h>
// IWYU pragma: no_include "SDL_error.h"
// IWYU pragma: no_include "SDL_platform.h"
// IWYU pragma: no_include "SDL_stdinc.h"
#include "SDL.h"
namespace Kyty::Core {
void* SDLCALL game_malloc_func(size_t size)
{
return Core::mem_alloc(size);
}
void* SDLCALL game_calloc_func(size_t nmemb, size_t size)
{
void* p = Core::mem_alloc(nmemb * size);
std::memset(p, 0, nmemb * size);
return p;
}
void* SDLCALL game_realloc_func(void* mem, size_t size)
{
return Core::mem_realloc(mem, size);
}
void SDLCALL game_free_func(void* mem)
{
Core::mem_free(mem);
}
KYTY_SUBSYSTEM_INIT(SDL)
{
int sdl_alloc = SDL_GetNumAllocations();
if (sdl_alloc != 0)
{
printf("warning: SDL static alloc: %d blocks\n", sdl_alloc);
} else
{
if (SDL_SetMemoryFunctions(game_malloc_func, game_calloc_func, game_realloc_func, game_free_func) != 0)
{
KYTY_SUBSYSTEM_FAIL("%s\n", SDL_GetError());
}
}
#if SDL_DYNAMIC_API != 0
#error "SDL_DYNAMIC_API"
#endif
if (SDL_Init(0) < 0)
{
KYTY_SUBSYSTEM_FAIL("%s\n", SDL_GetError());
}
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(SDL) {}
KYTY_SUBSYSTEM_DESTROY(SDL)
{
SDL_Quit();
}
} // namespace Kyty::Core
File diff suppressed because it is too large Load Diff
+370
View File
@@ -0,0 +1,370 @@
#include "Kyty/Core/Subsystems.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/SafeDelete.h"
#include "Kyty/Sys/SysStdio.h"
#include <cstdarg>
#include <cstdlib>
#include <cstring>
#include <new>
namespace Kyty::Core {
class SubsystemPrivate
{
public:
SubsystemPrivate() = default;
virtual ~SubsystemPrivate()
{
if (fail_msg != nullptr)
{
DeleteArray(fail_msg);
}
}
KYTY_CLASS_NO_COPY(SubsystemPrivate);
bool failed {false};
char* fail_msg {nullptr};
};
class SubsystemsListPrivate
{
public:
explicit SubsystemsListPrivate(SubsystemsList* p): parent(p) {}
virtual ~SubsystemsListPrivate()
{
SubsListStruct* n = list;
for (;;)
{
if (n == nullptr)
{
break;
}
SubsListStruct* nn = n;
DepsListStruct* dl = n->deps;
for (;;)
{
if (dl == nullptr)
{
break;
}
DepsListStruct* ddl = dl;
dl = dl->next;
Delete(ddl);
}
n = n->next;
Delete(nn);
}
}
void SetArgs(int argc, char** argv)
{
this->m_argc = argc;
this->m_argv = argv;
}
void Add(Subsystem* s, std::initializer_list<Subsystem*> deps)
{
EXIT_IF(!s);
const char* name = s->Id();
auto* nl = new SubsListStruct;
nl->s = s;
nl->name = name;
nl->deps = nullptr;
nl->next = list;
nl->prev_init = nullptr;
list = nl;
for (auto* dep: deps)
{
const char* str = dep->Id();
auto* l = new DepsListStruct;
l->dep_name = str;
l->next = nl->deps;
nl->deps = l;
}
nl->initialized = false;
}
bool InitAll(bool print_msg)
{
for (;;)
{
SubsListStruct* n = FindNextToInitialize();
if (n == nullptr)
{
break;
}
n->s->Init(parent);
if (n->s->m_p->failed)
{
fail_msg = n->s->m_p->fail_msg;
fail_name = n->name;
return false;
}
if (print_msg)
{
printf("Initialized: %s\n", n->name);
}
n->initialized = true;
SubsListStruct* last = last_init;
last_init = n;
n->prev_init = last;
}
return true;
}
void DestroyAll(bool print_msg)
{
SubsListStruct* n = last_init;
for (;;)
{
if (n == nullptr)
{
break;
}
n->s->Destroy(parent);
n->initialized = false;
if (print_msg)
{
printf("Destroyed: %s\n", n->name);
}
n = n->prev_init;
}
last_init = nullptr;
}
void ShutdownAll()
{
SubsListStruct* n = last_init;
for (;;)
{
if (n == nullptr)
{
break;
}
n->s->UnexpectedShutdown(parent);
n->initialized = false;
n = n->prev_init;
}
last_init = nullptr;
}
struct DepsListStruct
{
const char* dep_name;
DepsListStruct* next;
};
struct SubsListStruct
{
Subsystem* s;
const char* name;
DepsListStruct* deps;
SubsListStruct* next;
SubsListStruct* prev_init;
bool initialized;
};
[[nodiscard]] SubsListStruct* FindByName(const char* name) const
{
SubsListStruct* n = list;
for (;;)
{
if ((n == nullptr) || std::strcmp(n->name, name) == 0)
{
break;
}
n = n->next;
}
return n;
}
[[nodiscard]] SubsListStruct* FindNextToInitialize() const
{
SubsListStruct* n = list;
for (;;)
{
if (n == nullptr)
{
break;
}
if (!n->initialized)
{
DepsListStruct* d = n->deps;
for (;;)
{
if (d == nullptr)
{
break;
}
SubsListStruct* s = FindByName(d->dep_name);
if ((s == nullptr) || !s->initialized)
{
break;
}
d = d->next;
}
if (d == nullptr)
{
return n;
}
}
n = n->next;
}
return nullptr;
}
KYTY_CLASS_NO_COPY(SubsystemsListPrivate);
SubsListStruct* list = nullptr;
SubsListStruct* last_init = nullptr;
int m_argc = 0;
char** m_argv = nullptr;
const char* fail_msg = nullptr;
const char* fail_name = nullptr;
SubsystemsList* parent;
};
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
SubsystemsList::SubsystemsList(): m_p(static_cast<SubsystemsListPrivate*>(std::malloc(sizeof(SubsystemsListPrivate))))
{
new (m_p) SubsystemsListPrivate(this);
}
SubsystemsList::~SubsystemsList()
{
m_p->~SubsystemsListPrivate();
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
std::free(m_p);
// delete p;
}
void SubsystemsList::Add(Subsystem* s, std::initializer_list<Subsystem*> deps)
{
m_p->Add(s, deps);
}
bool SubsystemsList::InitAll(bool print_msg)
{
return m_p->InitAll(print_msg);
}
void SubsystemsList::DestroyAll(bool print_msg)
{
m_p->DestroyAll(print_msg);
}
int* SubsystemsList::GetArgc()
{
return &m_p->m_argc;
}
char** SubsystemsList::GetArgv()
{
return m_p->m_argv;
}
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
Subsystem::Subsystem(): m_p(static_cast<SubsystemPrivate*>(std::malloc(sizeof(SubsystemPrivate))))
{
new (m_p) SubsystemPrivate;
}
Subsystem::~Subsystem()
{
m_p->~SubsystemPrivate();
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
std::free(m_p);
// delete p;
}
void Subsystem::Fail(const char* format, ...)
{
va_list args {};
va_start(args, format);
uint32_t len = sys_vscprintf(format, args);
if (len != 0)
{
// NOLINTNEXTLINE(cppcoreguidelines-no-malloc,hicpp-no-malloc)
char* d = static_cast<char*>(std::malloc(len + 1));
std::memset(d, 0, len + 1);
/*len = */ sys_vsnprintf(d, len, format, args);
m_p->fail_msg = d;
m_p->failed = true;
}
va_end(args);
}
const char* SubsystemsList::GetFailName() const
{
return m_p->fail_name;
}
const char* SubsystemsList::GetFailMsg() const
{
return m_p->fail_msg;
}
void SubsystemsList::SetArgs(int argc, char* argv[])
{
m_p->SetArgs(argc, argv);
}
void SubsystemsList::ShutdownAll()
{
m_p->ShutdownAll();
}
} // namespace Kyty::Core
+305
View File
@@ -0,0 +1,305 @@
#include "Kyty/Core/Threads.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Core/SafeDelete.h"
#include "Kyty/Core/String.h"
//#define THREADS_SDL
#ifdef THREADS_SDL
#include "SDL.h"
#else
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#endif
namespace Kyty::Core {
#ifdef THREADS_SDL
typedef uint64_t thread_id_t;
#else
using thread_id_t = std::thread::id;
#endif
struct Thread::ThreadPrivate
{
ThreadPrivate(thread_func_t func, void* arg)
: finished(false), auto_delete(false),
#ifdef THREADS_SDL
m_func(func), m_arg(arg)
{
sdl = SDL_CreateThread(thread_run, "sdl_thread", this);
}
#else
m_thread(func, arg)
{
}
#endif
bool finished;
bool auto_delete;
#ifdef THREADS_SDL
SDL_Thread* sdl;
thread_func_t m_func;
void* m_arg;
static int thread_run(void* data)
{
ThreadPrivate* t = (ThreadPrivate*)data;
t->m_func(t->m_arg);
return 0;
}
#else
std::thread m_thread;
#endif
};
struct Mutex::MutexPrivate
{
#ifdef THREADS_SDL
SDL_mutex* sdl;
#else
std::recursive_mutex m_mutex;
#endif
};
struct CondVar::CondVarPrivate
{
#ifdef THREADS_SDL
SDL_cond* sdl;
#else
std::condition_variable_any m_cv;
#endif
};
static thread_id_t g_main_thread;
static int g_main_thread_int;
static std::atomic<int> g_thread_counter = 0;
KYTY_SUBSYSTEM_INIT(Threads)
{
#ifdef THREADS_SDL
g_main_thread = SDL_ThreadID();
#else
g_main_thread = std::this_thread::get_id();
g_main_thread_int = Thread::GetThreadIdUnique();
#endif
}
KYTY_SUBSYSTEM_UNEXPECTED_SHUTDOWN(Threads) {}
KYTY_SUBSYSTEM_DESTROY(Threads) {}
Thread::Thread(thread_func_t func, void* arg): m_thread(new ThreadPrivate(func, arg)) // @suppress("Symbol is not resolved")
{
}
Thread::~Thread()
{
EXIT_IF(!m_thread->finished && !m_thread->auto_delete);
Delete(m_thread);
}
void Thread::Join()
{
EXIT_IF(m_thread->finished || m_thread->auto_delete);
#ifdef THREADS_SDL
int status = -1;
SDL_WaitThread(m_thread->sdl, &status);
EXIT_IF(status != 0);
#else
m_thread->m_thread.join();
#endif
m_thread->finished = true;
}
void Thread::Detach()
{
EXIT_IF(m_thread->finished || m_thread->auto_delete);
m_thread->auto_delete = true;
#ifdef THREADS_SDL
SDL_DetachThread(m_thread->sdl);
#else
m_thread->m_thread.detach();
#endif
}
void Thread::Sleep(uint32_t millis)
{
#ifdef THREADS_SDL
SDL_Delay(millis);
#else
std::this_thread::sleep_for(std::chrono::milliseconds(millis));
#endif
}
void Thread::SleepMicro(uint32_t micros)
{
#ifdef THREADS_SDL
SDL_Delay(micros / 1000);
#else
std::this_thread::sleep_for(std::chrono::microseconds(micros));
#endif
}
void Thread::SleepNano(uint64_t nanos)
{
#ifdef THREADS_SDL
SDL_Delay(nanos / 1000000);
#else
std::this_thread::sleep_for(std::chrono::nanoseconds(nanos));
#endif
}
bool Thread::IsMainThread()
{
#ifdef THREADS_SDL
return g_main_thread == thread_id_t(SDL_ThreadID());
#else
return g_main_thread == std::this_thread::get_id();
#endif
}
String Thread::GetId() const
{
#ifdef THREADS_SDL
return String::FromPrintf("%" PRIu64, (uint64_t)SDL_GetThreadID(m_thread->sdl));
#else
std::stringstream ss;
ss << m_thread->m_thread.get_id();
return String::FromUtf8(ss.str().c_str());
#endif
}
String Thread::GetThreadId()
{
#ifdef THREADS_SDL
return String::FromPrintf("%" PRIu64, (uint64_t)SDL_ThreadID());
#else
std::stringstream ss;
ss << std::this_thread::get_id();
return String::FromUtf8(ss.str().c_str());
#endif
}
Mutex::Mutex(): m_mutex(new MutexPrivate)
{
#ifdef THREADS_SDL
m_mutex->sdl = SDL_CreateMutex();
EXIT_IF(!m_mutex->sdl);
#endif
}
Mutex::~Mutex()
{
#ifdef THREADS_SDL
SDL_DestroyMutex(m_mutex->sdl);
#endif
Delete(m_mutex);
}
void Mutex::Lock()
{
#ifdef THREADS_SDL
SDL_LockMutex(m_mutex->sdl);
#else
m_mutex->m_mutex.lock();
#endif
}
void Mutex::Unlock()
{
#ifdef THREADS_SDL
SDL_UnlockMutex(m_mutex->sdl);
#else
m_mutex->m_mutex.unlock();
#endif
}
bool Mutex::TryLock()
{
#ifdef THREADS_SDL
int status = SDL_TryLockMutex(m_mutex->sdl);
if (status == 0)
{
return true;
}
return false;
#else
return m_mutex->m_mutex.try_lock();
#endif
}
CondVar::CondVar(): m_cond_var(new CondVarPrivate)
{
#ifdef THREADS_SDL
m_cond_var->sdl = SDL_CreateCond();
EXIT_IF(!m_cond_var->sdl);
#endif
}
CondVar::~CondVar()
{
#ifdef THREADS_SDL
SDL_DestroyCond(m_cond_var->sdl);
#endif
Delete(m_cond_var);
}
void CondVar::Wait(Mutex* mutex)
{
#ifdef THREADS_SDL
SDL_CondWait(m_cond_var->sdl, mutex->m_mutex->sdl);
#else
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
m_cond_var->m_cv.wait(cpp_lock);
cpp_lock.release();
#endif
}
void CondVar::WaitFor(Mutex* mutex, uint32_t micros)
{
#ifdef THREADS_SDL
SDL_CondWaitTimeout(m_cond_var->sdl, mutex->m_mutex->sdl, (micros < 1000 ? 1 : micros / 1000));
#else
std::unique_lock<std::recursive_mutex> cpp_lock(mutex->m_mutex->m_mutex, std::adopt_lock_t());
m_cond_var->m_cv.wait_for(cpp_lock, std::chrono::microseconds(micros));
cpp_lock.release();
#endif
}
void CondVar::Signal()
{
#ifdef THREADS_SDL
SDL_CondSignal(m_cond_var->sdl);
#else
m_cond_var->m_cv.notify_one();
#endif
}
void CondVar::SignalAll()
{
#ifdef THREADS_SDL
SDL_CondBroadcast(m_cond_var->sdl);
#else
m_cond_var->m_cv.notify_all();
#endif
}
int Thread::GetThreadIdUnique()
{
static thread_local int tid = ++g_thread_counter;
return tid;
}
} // namespace Kyty::Core
+106
View File
@@ -0,0 +1,106 @@
#include "Kyty/Core/Timer.h"
#include "Kyty/Core/DbgAssert.h"
#include "Kyty/Sys/SysTimer.h"
namespace Kyty::Core {
Timer::Timer() noexcept
{
sys_query_performance_frequency(&m_Frequency);
}
void Timer::Start()
{
sys_query_performance_counter(&m_StartTime);
m_is_paused = false;
}
void Timer::Pause()
{
EXIT_IF(m_is_paused);
sys_query_performance_counter(&m_PauseTime);
m_is_paused = true;
}
void Timer::Resume()
{
EXIT_IF(!m_is_paused);
uint64_t current_time = 0;
sys_query_performance_counter(&current_time);
m_StartTime += current_time - m_PauseTime;
m_is_paused = false;
}
bool Timer::IsPaused() const
{
return m_is_paused;
}
// return time in milliseconds
double Timer::GetTimeMs() const
{
if (m_is_paused)
{
return 1000.0 * (static_cast<double>(m_PauseTime - m_StartTime)) / static_cast<double>(m_Frequency);
}
uint64_t current_time = 0;
sys_query_performance_counter(&current_time);
return 1000.0 * (static_cast<double>(current_time - m_StartTime)) / static_cast<double>(m_Frequency);
}
// return time in seconds
double Timer::GetTimeS() const
{
if (m_is_paused)
{
return (static_cast<double>(m_PauseTime - m_StartTime)) / static_cast<double>(m_Frequency);
}
uint64_t current_time = 0;
sys_query_performance_counter(&current_time);
return (static_cast<double>(current_time - m_StartTime)) / static_cast<double>(m_Frequency);
}
// return time in ticks
uint64_t Timer::GetTicks() const
{
if (m_is_paused)
{
return (m_PauseTime - m_StartTime);
}
uint64_t current_time = 0;
sys_query_performance_counter(&current_time);
return (current_time - m_StartTime);
}
// return ticks frequency
uint64_t Timer::GetFrequency() const
{
return m_Frequency;
}
uint64_t Timer::QueryPerformanceFrequency()
{
uint64_t ret = 0;
sys_query_performance_frequency(&ret);
return ret;
}
uint64_t Timer::QueryPerformanceCounter()
{
uint64_t ret = 0;
sys_query_performance_counter(&ret);
return ret;
}
} // namespace Kyty::Core