diff --git a/core/libs/prx/libSceLibcInternal/Export.cpp b/core/libs/prx/libSceLibcInternal/Export.cpp index 6597f5f5b..49699b6ca 100644 --- a/core/libs/prx/libSceLibcInternal/Export.cpp +++ b/core/libs/prx/libSceLibcInternal/Export.cpp @@ -1,7 +1,14 @@ +#include "prx/libc/include/General.hpp" #include "prx/libc/include/HeapDiagnostics.hpp" extern "C" { +int Need_sceLibcInternal_nid_postfix = 1; + +void APS5_VABI __cxa_finalize_nid_postfix(void* dsoHandle) { + CxaFinalize_nid_no_patch(dsoHandle); +} + void APS5_VABI sceLibcHeapGetTraceInfo_nid_postfix(Info* info) { LibcHeapTraceInfo_nid_no_patch(info); } diff --git a/core/libs/prx/libc/include/General.hpp b/core/libs/prx/libc/include/General.hpp index f1ed6ca20..35c9126a5 100644 --- a/core/libs/prx/libc/include/General.hpp +++ b/core/libs/prx/libc/include/General.hpp @@ -9,6 +9,7 @@ #include "general/ExportMacros.hpp" extern "C" void NotImplemented_nid_no_patch(const char* funcName); +extern "C" void CxaFinalize_nid_no_patch(void* dsoHandle); extern "C" std::filesystem::path ResolvePath_nid_no_patch(const char* path); // Guest path aliases: a guest prefix such as "/_sm/0" (a save-data mount point) resolves to a host diff --git a/core/libs/prx/libc/include/specifics/gcc/AtomicOps.hpp b/core/libs/prx/libc/include/specifics/gcc/AtomicOps.hpp index 37a0e544c..bfeef359d 100644 --- a/core/libs/prx/libc/include/specifics/gcc/AtomicOps.hpp +++ b/core/libs/prx/libc/include/specifics/gcc/AtomicOps.hpp @@ -9,4 +9,12 @@ inline unsigned int GccAtomicFetchSub(volatile unsigned int* target, unsigned in return __atomic_fetch_sub(target, value, __ATOMIC_SEQ_CST); } +inline bool GccAtomicCompareExchangeWeak(volatile unsigned int* target, unsigned int* expected, unsigned int desired) { + return __atomic_compare_exchange_n(target, expected, desired, true, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +} + +inline unsigned int GccAtomicLoad(volatile unsigned int* target) { + return __atomic_load_n(target, __ATOMIC_SEQ_CST); +} + #endif diff --git a/core/libs/prx/libc/src/FileAndHeap.cpp b/core/libs/prx/libc/src/FileAndHeap.cpp index b3e3ed183..be4ac1c83 100644 --- a/core/libs/prx/libc/src/FileAndHeap.cpp +++ b/core/libs/prx/libc/src/FileAndHeap.cpp @@ -63,6 +63,14 @@ FileStream* APS5_VABI fopen_nid_postfix(const char* filename, const char* mode) return stream.release(); } +int APS5_VABI fopen_s_nid_postfix(FileStream** result, const char* filename, const char* mode) { + constexpr int GuestEinval = 22; + constexpr int GuestEnoent = 2; + if (!result || !filename || !mode) return GuestEinval; + *result = fopen_nid_postfix(filename, mode); + return *result ? 0 : GuestEnoent; +} + int APS5_VABI fclose_nid_postfix(FileStream* stream) { GetNativeStream(stream); std::unique_ptr owner(stream->IsDynamic() ? stream : nullptr); @@ -121,6 +129,17 @@ int APS5_VABI fseek_nid_postfix(FileStream* stream, std::int64_t offset, int ori std::int64_t APS5_VABI ftell_nid_postfix(FileStream* stream) { return ftello_nid_postfix(stream); } +int APS5_VABI fgetpos_nid_postfix(FileStream* stream, std::int64_t* position) { + if (!position) throw std::invalid_argument("fgetpos: null position"); + *position = ftello_nid_postfix(stream); + return 0; +} + +int APS5_VABI fsetpos_nid_postfix(FileStream* stream, const std::int64_t* position) { + if (!position) throw std::invalid_argument("fsetpos: null position"); + return fseeko_nid_postfix(stream, *position, SEEK_SET); +} + int APS5_VABI fputs_nid_postfix(const char* str, FileStream* stream) { if (!str) throw std::runtime_error("fputs: null string"); const int result = std::fputs(str, GetNativeStream(stream)); @@ -149,6 +168,15 @@ void* APS5_VABI memalign_nid_postfix(size_t alignment, size_t size) { return ApplicationHeapAlign_nid_no_patch(alignment, size); } +void* APS5_VABI aligned_alloc_nid_postfix(size_t alignment, size_t size) { + return ApplicationHeapAlign_nid_no_patch(alignment, size); +} + +void* APS5_VABI reallocalign_nid_postfix(void* ptr, size_t size, size_t alignment) { + if (ptr != nullptr) NotImplemented_nid_no_patch("reallocalign of an existing block"); + return ApplicationHeapAlign_nid_no_patch(alignment, size); +} + void* APS5_VABI calloc_nid_postfix(size_t count, size_t size) { return ApplicationHeapCalloc_nid_no_patch(count, size); } diff --git a/core/libs/prx/libc/src/Formatting.cpp b/core/libs/prx/libc/src/Formatting.cpp index 207069071..bac3baaf8 100644 --- a/core/libs/prx/libc/src/Formatting.cpp +++ b/core/libs/prx/libc/src/Formatting.cpp @@ -169,6 +169,56 @@ int APS5_VABI vsnprintf_nid_postfix(char* str, size_t size, const char* format, #endif } +#ifdef _WIN32 + +int APS5_VABI snprintf_s_nid_postfix(char* buffer, size_t size, const char* format, ...) { + __builtin_sysv_va_list args; + __builtin_sysv_va_start(args, format); + const int result = LibcDetail::FormatWindows(buffer, size, format, args); + __builtin_sysv_va_end(args); + return result; +} + +int APS5_VABI printf_s_nid_postfix(const char* format, ...) { + __builtin_sysv_va_list args; + __builtin_sysv_va_start(args, format); + const int result = LibcDetail::PrintWindows(format, args); + __builtin_sysv_va_end(args); + return result; +} + +#else + +int APS5_VABI vsprintf_s_nid_postfix(char* buffer, size_t size, const char* format, VaList* args) { + return std::vsnprintf(buffer, size, format, *reinterpret_cast(args)); +} + +int APS5_VABI sprintf_s_nid_postfix(char* buffer, size_t size, const char* format, ...) { + std::va_list args; + va_start(args, format); + const int result = std::vsnprintf(buffer, size, format, args); + va_end(args); + return result; +} + +int APS5_VABI snprintf_s_nid_postfix(char* buffer, size_t size, const char* format, ...) { + std::va_list args; + va_start(args, format); + const int result = std::vsnprintf(buffer, size, format, args); + va_end(args); + return result; +} + +int APS5_VABI printf_s_nid_postfix(const char* format, ...) { + std::va_list args; + va_start(args, format); + const int result = std::vprintf(format, args); + va_end(args); + return result; +} + +#endif + int APS5_VABI puts_nid_postfix(const char* s) { return std::puts(s); } diff --git a/core/libs/prx/libc/src/LocaleSupport.cpp b/core/libs/prx/libc/src/LocaleSupport.cpp index 4c52e8ad2..4bc5eb94e 100644 --- a/core/libs/prx/libc/src/LocaleSupport.cpp +++ b/core/libs/prx/libc/src/LocaleSupport.cpp @@ -143,6 +143,9 @@ int APS5_VABI tolower_nid_postfix(int c) { return ConvertCharacter(c, false); } std::uint64_t _ZNSt5ctypeIcE2idE_nid_postfix = 0; std::uint64_t _ZNSt5ctypeIwE2idE_nid_postfix = 0; std::uint64_t _ZNSt7collateIwE2idE_nid_postfix = 0; +std::uint64_t _ZNSt7collateIcE2idE_nid_postfix = 0; +std::uint64_t _ZNSt7codecvtIcc9_MbstatetE2idE_nid_postfix = 0; +std::uintptr_t _ZTVSt7codecvtIcc9_MbstatetE_nid_postfix[16] {}; std::uint64_t _ZNSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE2idE_nid_postfix = 0; std::uintptr_t _ZTVSt7num_putIcSt19ostreambuf_iteratorIcSt11char_traitsIcEEE_nid_postfix[12] {}; @@ -185,6 +188,10 @@ void APS5_VABI _ZNSt7collateIwE7_GetcatEPPKNSt6locale5facetEPKS1__nid_postfix(Gu NotImplemented_nid_no_patch(__func__); } +void APS5_VABI _ZNSt7collateIcE7_GetcatEPPKNSt6locale5facetEPKS1__nid_postfix(GuestLocale::Facet**, const GuestLocale::Implementation*) { + NotImplemented_nid_no_patch(__func__); +} + void APS5_VABI _ZNSt8_LocinfoC1EPKc_nid_postfix(GuestLocale::LocinfoStorage* self, const char* localeName) { if (self == nullptr || localeName == nullptr || std::strcmp(localeName, "C") != 0) throw std::invalid_argument("_Locinfo: only the C locale is supported"); new (self) GuestLocale::LocinfoStorage{}; diff --git a/core/libs/prx/libc/src/RuntimeSupport.cpp b/core/libs/prx/libc/src/RuntimeSupport.cpp index c2772ac47..bf1ada3eb 100644 --- a/core/libs/prx/libc/src/RuntimeSupport.cpp +++ b/core/libs/prx/libc/src/RuntimeSupport.cpp @@ -1,4 +1,6 @@ +#include #include +#include #include #include #include @@ -6,6 +8,9 @@ #include #include #include +#include +#include +#include #include "prx/libc/include/General.hpp" #include "prx/libc/include/specifics/gcc/AtomicOps.hpp" @@ -15,6 +20,16 @@ namespace { std::recursive_mutex g_sysLock; +struct ExitDestructor { + void (*func)(void*); + void* arg; + void* dsoHandle; +}; + +std::mutex g_exitMutex; +std::vector g_exitDestructors; +bool g_exitRunnerRegistered = false; + } extern "C" { @@ -24,21 +39,31 @@ FileStream _Stdout_nid_postfix{stdout}; FileStream _Stdin_nid_postfix{stdin}; int APS5_VABI __cxa_atexit_nid_postfix(void (*func)(void*), void* arg, void* dsoHandle) { - (void)dsoHandle; - static std::vector> destructors; - static bool runnerRegistered = false; - destructors.emplace_back(func, arg); - if (!runnerRegistered) { - runnerRegistered = true; - std::atexit([] { - for (auto it = destructors.rbegin(); it != destructors.rend(); ++it) { - it->first(it->second); - } - }); + std::lock_guard lock(g_exitMutex); + g_exitDestructors.push_back({func, arg, dsoHandle}); + if (!g_exitRunnerRegistered) { + g_exitRunnerRegistered = true; + std::atexit([] { CxaFinalize_nid_no_patch(nullptr); }); } return 0; } +void CxaFinalize_nid_no_patch(void* dsoHandle) { + for (;;) { + ExitDestructor destructor{}; + { + std::lock_guard lock(g_exitMutex); + const auto found = std::find_if(g_exitDestructors.rbegin(), g_exitDestructors.rend(), [&](const ExitDestructor& entry) { + return dsoHandle == nullptr || entry.dsoHandle == dsoHandle; + }); + if (found == g_exitDestructors.rend()) return; + destructor = *found; + g_exitDestructors.erase(std::next(found).base()); + } + destructor.func(destructor.arg); + } +} + unsigned int APS5_VABI _Atomic_fetch_add_4_nid_postfix(volatile unsigned int* target, unsigned int value, int memoryOrder) { (void)memoryOrder; return GccAtomicFetchAdd(target, value); @@ -49,6 +74,50 @@ unsigned int APS5_VABI _Atomic_fetch_sub_4_nid_postfix(volatile unsigned int* ta return GccAtomicFetchSub(target, value); } +int APS5_VABI _Atomic_compare_exchange_weak_4_nid_postfix(volatile unsigned int* target, unsigned int* expected, unsigned int desired, int successOrder, int failureOrder) { + (void)successOrder; + (void)failureOrder; + return GccAtomicCompareExchangeWeak(target, expected, desired) ? 1 : 0; +} + +unsigned int APS5_VABI _Atomic_load_4_nid_postfix(volatile unsigned int* target, int memoryOrder) { + (void)memoryOrder; + return GccAtomicLoad(target); +} + +std::uintmax_t APS5_VABI strtoumax_nid_postfix(const char* str, char** endptr, int base) { + return std::strtoumax(str, endptr, base); +} + +unsigned int APS5_VABI _ZSt14_Random_devicev_nid_postfix() { + static std::random_device device; + static std::mutex mutex; + std::lock_guard lock(mutex); + return device(); +} + +[[noreturn]] void APS5_VABI _ZSt14_Throw_C_errori_nid_postfix(int code) { + throw std::runtime_error("_Throw_C_error: C11 thread error " + std::to_string(code)); +} + +void APS5_VABI _Lockfilelock_nid_postfix(FileStream* stream) { + if (stream == nullptr) throw std::invalid_argument("_Lockfilelock: null stream"); +#ifdef _WIN32 + _lock_file(GetNativeStream(stream)); +#else + flockfile(GetNativeStream(stream)); +#endif +} + +void APS5_VABI _Unlockfilelock_nid_postfix(FileStream* stream) { + if (stream == nullptr) throw std::invalid_argument("_Unlockfilelock: null stream"); +#ifdef _WIN32 + _unlock_file(GetNativeStream(stream)); +#else + funlockfile(GetNativeStream(stream)); +#endif +} + unsigned long APS5_VABI _Stoul_nid_postfix(const char* str, char** endptr, int base) { return std::strtoul(str, endptr, base); } diff --git a/core/libs/prx/libc/src/StdioExtras.cpp b/core/libs/prx/libc/src/StdioExtras.cpp index 90a21b057..d2aabc81d 100644 --- a/core/libs/prx/libc/src/StdioExtras.cpp +++ b/core/libs/prx/libc/src/StdioExtras.cpp @@ -6,6 +6,7 @@ #include "prx/libc/include/FileStream.hpp" #include "prx/libc/include/GuestHeap.hpp" +#include "prx/libc/include/ApplicationHeap.hpp" #include "prx/libc/include/General.hpp" #include "SceTypes.hpp" @@ -19,6 +20,10 @@ using GuestNewHandler = void (APS5_VABI*)(); GuestNewHandler g_newHandler = nullptr; +void* Allocate(std::size_t size) { + return ApplicationHeapAllocate_nid_no_patch(size == 0 ? 1 : size); +} + } extern "C" { @@ -58,6 +63,44 @@ GuestNewHandler APS5_VABI _ZSt15set_new_handlerPFvvE_nid_postfix(GuestNewHandler return previous; } +GuestNewHandler APS5_VABI _ZSt15get_new_handlerv_nid_postfix() { + return g_newHandler; +} + +unsigned char _ZSt7nothrow_nid_postfix = 0; + +void* APS5_VABI _Znwm_nid_postfix(std::size_t size) { + return Allocate(size); +} + +void* APS5_VABI _Znam_nid_postfix(std::size_t size) { + return Allocate(size); +} + +void* APS5_VABI _ZnwmRKSt9nothrow_t_nid_postfix(std::size_t size, const void*) { + return Allocate(size); +} + +void* APS5_VABI _ZnamRKSt9nothrow_t_nid_postfix(std::size_t size, const void*) { + return Allocate(size); +} + +void APS5_VABI _ZdlPv_nid_postfix(void* pointer) { + if (pointer != nullptr) ApplicationHeapFree_nid_no_patch(pointer); +} + +void APS5_VABI _ZdaPv_nid_postfix(void* pointer) { + if (pointer != nullptr) ApplicationHeapFree_nid_no_patch(pointer); +} + +void APS5_VABI _ZdlPvm_nid_postfix(void* pointer, std::size_t) { + if (pointer != nullptr) ApplicationHeapFree_nid_no_patch(pointer); +} + +void APS5_VABI _ZdaPvm_nid_postfix(void* pointer, std::size_t) { + if (pointer != nullptr) ApplicationHeapFree_nid_no_patch(pointer); +} + void APS5_VABI _ZdlPvSt11align_val_t_nid_postfix(void* pointer, std::size_t alignment) { (void)alignment; GuestHeap::GuestHeapFree_nid_postfix(pointer); diff --git a/core/libs/prx/libc/src/Strings.cpp b/core/libs/prx/libc/src/Strings.cpp index c3fdec2d6..1f70ddc44 100644 --- a/core/libs/prx/libc/src/Strings.cpp +++ b/core/libs/prx/libc/src/Strings.cpp @@ -188,6 +188,79 @@ int APS5_VABI strncpy_s_nid_postfix(char* dest, size_t destsz, const char* src, return 0; } +int APS5_VABI strcpy_s_nid_postfix(char* dest, size_t destsz, const char* src) { + return strncpy_s_nid_postfix(dest, destsz, src, static_cast(-1)); +} + +int APS5_VABI strncat_s_nid_postfix(char* dest, size_t destsz, const char* src, size_t count) { + constexpr int GuestEinval = 22; + constexpr int GuestErange = 34; + if (!dest || destsz == 0) return GuestEinval; + size_t used = 0; + while (used < destsz && dest[used] != '\0') ++used; + if (used == destsz || !src) { + dest[0] = '\0'; + return used == destsz ? GuestErange : GuestEinval; + } + size_t length = 0; + while (length < count && src[length] != '\0') ++length; + if (length >= destsz - used) { + dest[0] = '\0'; + return GuestErange; + } + std::memcpy(dest + used, src, length); + dest[used + length] = '\0'; + return 0; +} + +int APS5_VABI strcat_s_nid_postfix(char* dest, size_t destsz, const char* src) { + return strncat_s_nid_postfix(dest, destsz, src, static_cast(-1)); +} + +int APS5_VABI memcpy_s_nid_postfix(void* dest, size_t destsz, const void* src, size_t count) { + constexpr int GuestEinval = 22; + constexpr int GuestErange = 34; + if (!dest) return GuestEinval; + if (!src || count > destsz) { + std::memset(dest, 0, destsz); + return src ? GuestErange : GuestEinval; + } + std::memcpy(dest, src, count); + return 0; +} + +int APS5_VABI memmove_s_nid_postfix(void* dest, size_t destsz, const void* src, size_t count) { + constexpr int GuestEinval = 22; + constexpr int GuestErange = 34; + if (!dest) return GuestEinval; + if (!src || count > destsz) { + std::memset(dest, 0, destsz); + return src ? GuestErange : GuestEinval; + } + std::memmove(dest, src, count); + return 0; +} + +int APS5_VABI memset_s_nid_postfix(void* dest, size_t destsz, int value, size_t count) { + constexpr int GuestEinval = 22; + constexpr int GuestErange = 34; + if (!dest) return GuestEinval; + const auto length = count > destsz ? destsz : count; + auto* bytes = static_cast(dest); + for (size_t index = 0; index < length; ++index) bytes[index] = static_cast(value); + return count > destsz ? GuestErange : 0; +} + +char* APS5_VABI strnstr_nid_postfix(const char* haystack, const char* needle, size_t length) { + const size_t needleLength = std::strlen(needle); + if (needleLength == 0) return const_cast(haystack); + for (size_t index = 0; index < length && haystack[index] != '\0'; ++index) { + if (needleLength > length - index) break; + if (std::strncmp(haystack + index, needle, needleLength) == 0) return const_cast(haystack + index); + } + return nullptr; +} + unsigned long long APS5_VABI _Stoull_nid_postfix(const char* str, char** endptr, int base) { return std::strtoull(str, endptr, base); } diff --git a/core/libs/prx/libc/src/Time.cpp b/core/libs/prx/libc/src/Time.cpp index 6d03d5fb1..64bc20ddb 100644 --- a/core/libs/prx/libc/src/Time.cpp +++ b/core/libs/prx/libc/src/Time.cpp @@ -89,6 +89,10 @@ size_t APS5_VABI libc_strftime_nid_postfix(char* str, size_t count, const char* return std::strftime(str, count, format, timeptr); } +char* APS5_VABI asctime_nid_postfix(const std::tm* timeptr) { + return std::asctime(timeptr); +} + size_t APS5_VABI strftime_nid_postfix(char* str, size_t count, const char* format, const std::tm* timeptr) { return std::strftime(str, count, format, timeptr); } diff --git a/core/libs/tests/ApplicationHeap.cpp b/core/libs/tests/ApplicationHeap.cpp index 48009f88e..781827460 100644 --- a/core/libs/tests/ApplicationHeap.cpp +++ b/core/libs/tests/ApplicationHeap.cpp @@ -7,6 +7,13 @@ #include #include +extern "C" { +void* APS5_VABI _Znwm_nid_postfix(std::size_t); +void* APS5_VABI _ZnamRKSt9nothrow_t_nid_postfix(std::size_t, const void*); +void APS5_VABI _ZdlPv_nid_postfix(void*); +void APS5_VABI _ZdaPv_nid_postfix(void*); +} + namespace { alignas(64) std::array storage{}; @@ -117,7 +124,15 @@ int main(int argc, char**) { require(ApplicationHeapPosixAlign_nid_no_patch(&pointer, 64, 128) == 0 && pointer == storage.data() && lastAlignment == 64); reject([] { ApplicationHeapAlign_nid_no_patch(3, 64); }); reject([] { ApplicationHeapCalloc_nid_no_patch(2, std::numeric_limits::max()); }); + require(_Znwm_nid_postfix(0) == storage.data() && lastSize == 1); + _ZdlPv_nid_postfix(storage.data()); + _ZdlPv_nid_postfix(nullptr); + require(frees == 3); + require(_ZnamRKSt9nothrow_t_nid_postfix(24, nullptr) == storage.data() && lastSize == 24); + _ZdaPv_nid_postfix(storage.data()); + require(frees == 4); fail = true; + reject([] { _Znwm_nid_postfix(8); }); reject([] { ApplicationHeapAlign_nid_no_patch(4, 64); }); reject([] { ApplicationHeapAllocate_nid_no_patch(64); }); void* unchanged = storage.data(); diff --git a/core/libs/tests/GuestStrings.cpp b/core/libs/tests/GuestStrings.cpp index cc8eefab6..c10a9f3bb 100644 --- a/core/libs/tests/GuestStrings.cpp +++ b/core/libs/tests/GuestStrings.cpp @@ -15,6 +15,14 @@ std::size_t APS5_VABI strlcat_nid_postfix(char*, const char*, std::size_t); char* APS5_VABI strtok_r_nid_postfix(char*, const char*, char**); char* APS5_VABI strtok_nid_postfix(char*, const char*); char* APS5_VABI strcasestr_nid_postfix(const char*, const char*); +int APS5_VABI strcpy_s_nid_postfix(char*, std::size_t, const char*); +int APS5_VABI strcat_s_nid_postfix(char*, std::size_t, const char*); +int APS5_VABI strncat_s_nid_postfix(char*, std::size_t, const char*, std::size_t); +int APS5_VABI memcpy_s_nid_postfix(void*, std::size_t, const void*, std::size_t); +int APS5_VABI memmove_s_nid_postfix(void*, std::size_t, const void*, std::size_t); +int APS5_VABI memset_s_nid_postfix(void*, std::size_t, int, std::size_t); +char* APS5_VABI strnstr_nid_postfix(const char*, const char*, std::size_t); +int APS5_VABI snprintf_s_nid_postfix(char*, std::size_t, const char*, ...); } static void Require(bool condition) { @@ -24,7 +32,31 @@ static void Require(bool condition) { } } +static void CheckBoundsCheckedFunctions() { + char small[4] = "zz"; + Require(strcpy_s_nid_postfix(small, sizeof(small), "abc") == 0 && std::strcmp(small, "abc") == 0); + Require(strcpy_s_nid_postfix(small, sizeof(small), "abcd") == 34 && small[0] == '\0'); + Require(strcpy_s_nid_postfix(nullptr, 4, "a") == 22); + char joined[8] = "ab"; + Require(strcat_s_nid_postfix(joined, sizeof(joined), "cd") == 0 && std::strcmp(joined, "abcd") == 0); + Require(strncat_s_nid_postfix(joined, sizeof(joined), "efgh", 2) == 0 && std::strcmp(joined, "abcdef") == 0); + Require(strcat_s_nid_postfix(joined, sizeof(joined), "gh") == 34 && joined[0] == '\0'); + char bytes[4] = {1, 2, 3, 4}; + const char source[4] = {5, 6, 7, 8}; + Require(memcpy_s_nid_postfix(bytes, sizeof(bytes), source, 2) == 0 && bytes[0] == 5 && bytes[2] == 3); + Require(memcpy_s_nid_postfix(bytes, 2, source, 4) == 34 && bytes[0] == 0 && bytes[1] == 0 && bytes[2] == 3); + char overlap[6] = "abcde"; + Require(memmove_s_nid_postfix(overlap + 1, 5, overlap, 3) == 0 && std::strcmp(overlap, "aabce") == 0); + Require(memset_s_nid_postfix(bytes, sizeof(bytes), 9, 8) == 34 && bytes[3] == 9); + Require(strnstr_nid_postfix("haystack", "st", 4) == nullptr); + const char haystack[] = "haystack"; + Require(strnstr_nid_postfix(haystack, "st", 6) == haystack + 3); + char formatted[8]; + Require(snprintf_s_nid_postfix(formatted, sizeof(formatted), "%d-%s", 42, "x") == 4 && std::strcmp(formatted, "42-x") == 0); +} + int main() { + CheckBoundsCheckedFunctions(); Require(std::strcmp(basename_nid_postfix(nullptr), ".") == 0); Require(std::strcmp(basename_nid_postfix(""), ".") == 0); Require(std::strcmp(basename_nid_postfix("////"), "/") == 0);