From 96919c37c9c30c951f0e0c44f2484e679a171e07 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Mon, 8 Jun 2026 11:01:20 -0400
Subject: [PATCH] stdlib: Add SDL_wcstoul(), SDL_wcstoll(), and SDL_wcstoull()
Includes the appropriate stdlib checks and automated tests.
---
CMakeLists.txt | 2 +-
build-scripts/check_stdlib_usage.py | 5 +
cmake/PreseedDOSCache.cmake | 3 +
cmake/PreseedEmscriptenCache.cmake | 3 +
cmake/PreseedMSVCCache.cmake | 3 +
cmake/PreseedNokiaNGageCache.cmake | 3 +
include/SDL3/SDL_stdinc.h | 81 +++++++++++++
include/build_config/SDL_build_config.h.cmake | 3 +
src/dynapi/SDL_dynapi.exports | 3 +
src/dynapi/SDL_dynapi.sym | 3 +
src/dynapi/SDL_dynapi_overrides.h | 3 +
src/dynapi/SDL_dynapi_procs.h | 3 +
src/stdlib/SDL_string.c | 106 +++++++++++++++++-
test/testautomation_stdlib.c | 104 +++++++++++------
14 files changed, 288 insertions(+), 37 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 51542b8ba617a..45892a81f745e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1149,7 +1149,7 @@ if(SDL_LIBC)
tan tanf trunc truncf
unsetenv
vsnprintf vsscanf
- wcsnlen wcscmp wcsdup wcslcat wcslcpy wcslen wcsncmp wcsstr wcstol
+ wcsnlen wcscmp wcsdup wcslcat wcslcpy wcslen wcsncmp wcsstr wcstol wcstoll wcstoul wcstoull
)
if(WINDOWS OR CYGWIN)
list(APPEND symbols_to_check
diff --git a/build-scripts/check_stdlib_usage.py b/build-scripts/check_stdlib_usage.py
index 8f843d3aea48d..a0b6e64e6806c 100755
--- a/build-scripts/check_stdlib_usage.py
+++ b/build-scripts/check_stdlib_usage.py
@@ -123,6 +123,7 @@
'strtol',
'strtoll',
'strtoul',
+ 'strtoull',
'strupr',
'tan',
'tanf',
@@ -147,6 +148,10 @@
'wcsncasecmp',
'wcsncmp',
'wcsstr',
+ 'wcstol',
+ 'wcstoll',
+ 'wcstoul',
+ 'wcstoull',
)
RE_STDLIB_SYMBOL = re.compile(rf"(?<!->)\b(?P<symbol>{'|'.join(STDLIB_SYMBOLS)})\b\(")
diff --git a/cmake/PreseedDOSCache.cmake b/cmake/PreseedDOSCache.cmake
index 1b2a70aab41bd..e35fe25a75777 100644
--- a/cmake/PreseedDOSCache.cmake
+++ b/cmake/PreseedDOSCache.cmake
@@ -154,6 +154,9 @@ if(CMAKE_SYSTEM_NAME STREQUAL "DOS")
set(LIBC_HAS_WCSNLEN "" CACHE INTERNAL "Have symbol wcsnlen")
set(LIBC_HAS_WCSSTR "" CACHE INTERNAL "Have symbol wcsstr")
set(LIBC_HAS_WCSTOL "" CACHE INTERNAL "Have symbol wcstol")
+ set(LIBC_HAS_WCSTOLL "" CACHE INTERNAL "Have symbol wcstoll")
+ set(LIBC_HAS_WCSTOUL "" CACHE INTERNAL "Have symbol wcstoul")
+ set(LIBC_HAS_WCSTOULL "" CACHE INTERNAL "Have symbol wcstoull")
set(LIBC_HAS__EXIT "1" CACHE INTERNAL "Have symbol _Exit")
set(LIBC_HAS__I64TOA "" CACHE INTERNAL "Have symbol _i64toa")
set(LIBC_HAS__LTOA "" CACHE INTERNAL "Have symbol _ltoa")
diff --git a/cmake/PreseedEmscriptenCache.cmake b/cmake/PreseedEmscriptenCache.cmake
index 773ae123b7b53..41ccb569dea85 100644
--- a/cmake/PreseedEmscriptenCache.cmake
+++ b/cmake/PreseedEmscriptenCache.cmake
@@ -127,6 +127,9 @@ if(EMSCRIPTEN)
set(LIBC_HAS_WCSNLEN "1" CACHE INTERNAL "Have symbol wcsnlen")
set(LIBC_HAS_WCSSTR "1" CACHE INTERNAL "Have symbol wcsstr")
set(LIBC_HAS_WCSTOL "1" CACHE INTERNAL "Have symbol wcstol")
+ set(LIBC_HAS_WCSTOLL "1" CACHE INTERNAL "Have symbol wcstoll")
+ set(LIBC_HAS_WCSTOUL "1" CACHE INTERNAL "Have symbol wcstoul")
+ set(LIBC_HAS_WCSTOULL "1" CACHE INTERNAL "Have symbol wcstoull")
set(LIBC_HAS__EXIT "1" CACHE INTERNAL "Have symbol _Exit")
set(LIBC_HAS__I64TOA "" CACHE INTERNAL "Have symbol _i64toa")
set(LIBC_HAS__LTOA "" CACHE INTERNAL "Have symbol _ltoa")
diff --git a/cmake/PreseedMSVCCache.cmake b/cmake/PreseedMSVCCache.cmake
index f93d2b8ff67da..7456d50c0f7ba 100644
--- a/cmake/PreseedMSVCCache.cmake
+++ b/cmake/PreseedMSVCCache.cmake
@@ -149,6 +149,9 @@ if(MSVC)
set(LIBC_HAS_WCSNLEN "1" CACHE INTERNAL "Have symbol wcsnlen")
set(LIBC_HAS_WCSSTR "1" CACHE INTERNAL "Have symbol wcsstr")
set(LIBC_HAS_WCSTOL "1" CACHE INTERNAL "Have symbol wcstol")
+ set(LIBC_HAS_WCSTOLL "1" CACHE INTERNAL "Have symbol wcstoll")
+ set(LIBC_HAS_WCSTOUL "1" CACHE INTERNAL "Have symbol wcstoul")
+ set(LIBC_HAS_WCSTOULL "1" CACHE INTERNAL "Have symbol wcstoull")
set(LIBC_HAS__EXIT "1" CACHE INTERNAL "Have symbol _Exit")
set(LIBC_HAS__I64TOA "1" CACHE INTERNAL "Have symbol _i64toa")
set(LIBC_HAS__LTOA "1" CACHE INTERNAL "Have symbol _ltoa")
diff --git a/cmake/PreseedNokiaNGageCache.cmake b/cmake/PreseedNokiaNGageCache.cmake
index e404785319549..3ce363947510f 100644
--- a/cmake/PreseedNokiaNGageCache.cmake
+++ b/cmake/PreseedNokiaNGageCache.cmake
@@ -130,6 +130,9 @@ if(NGAGESDK)
set(LIBC_HAS_WCSNLEN "" CACHE INTERNAL "Have symbol wcsnlen")
set(LIBC_HAS_WCSSTR "" CACHE INTERNAL "Have symbol wcsstr")
set(LIBC_HAS_WCSTOL "" CACHE INTERNAL "Have symbol wcstol")
+ set(LIBC_HAS_WCSTOLL "" CACHE INTERNAL "Have symbol wcstoll")
+ set(LIBC_HAS_WCSTOUL "" CACHE INTERNAL "Have symbol wcstoul")
+ set(LIBC_HAS_WCSTOULL "" CACHE INTERNAL "Have symbol wcstoull")
set(LIBC_HAS__EXIT "" CACHE INTERNAL "Have symbol _Exit")
set(LIBC_HAS__I64TOA "" CACHE INTERNAL "Have symbol _i64toa")
set(LIBC_HAS__LTOA "" CACHE INTERNAL "Have symbol _ltoa")
diff --git a/include/SDL3/SDL_stdinc.h b/include/SDL3/SDL_stdinc.h
index 49d424c0c15ec..c3bcada73027e 100644
--- a/include/SDL3/SDL_stdinc.h
+++ b/include/SDL3/SDL_stdinc.h
@@ -3054,6 +3054,87 @@ extern SDL_DECLSPEC int SDLCALL SDL_wcsncasecmp(const wchar_t *str1, const wchar
*/
extern SDL_DECLSPEC long SDLCALL SDL_wcstol(const wchar_t *str, wchar_t **endp, int base);
+/**
+ * Parse an `unsigned long` from a wide string.
+ *
+ * If `str` starts with whitespace, then those whitespace characters are
+ * skipped before attempting to parse the number.
+ *
+ * If the parsed number does not fit inside an `unsigned long`, the result is clamped to
+ * the minimum and maximum representable `unsigned long` values.
+ *
+ * \param str The null-terminated wide string to read. Must not be NULL.
+ * \param endp If not NULL, the address of the first invalid wide character
+ * (i.e. the next character after the parsed number) will be
+ * written to this pointer.
+ * \param base The base of the integer to read. Supported values are 0 and 2
+ * to 36 inclusive. If 0, the base will be inferred from the
+ * number's prefix (0x for hexadecimal, 0 for octal, decimal
+ * otherwise).
+ * \returns the parsed `unsigned long`, or 0 if no number could be parsed.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.6.0.
+ *
+ * \sa SDL_strtoul
+ */
+extern SDL_DECLSPEC unsigned long SDLCALL SDL_wcstoul(const wchar_t *str, wchar_t **endp, int base);
+
+/**
+ * Parse a `long long` from a wide string.
+ *
+ * If `str` starts with whitespace, then those whitespace characters are
+ * skipped before attempting to parse the number.
+ *
+ * If the parsed number does not fit inside a `long long`, the result is clamped to
+ * the minimum and maximum representable `long long` values.
+ *
+ * \param str The null-terminated wide string to read. Must not be NULL.
+ * \param endp If not NULL, the address of the first invalid wide character
+ * (i.e. the next character after the parsed number) will be
+ * written to this pointer.
+ * \param base The base of the integer to read. Supported values are 0 and 2
+ * to 36 inclusive. If 0, the base will be inferred from the
+ * number's prefix (0x for hexadecimal, 0 for octal, decimal
+ * otherwise).
+ * \returns the parsed `long long`, or 0 if no number could be parsed.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.6.0.
+ *
+ * \sa SDL_strtoll
+ */
+extern SDL_DECLSPEC long long SDLCALL SDL_wcstoll(const wchar_t *str, wchar_t **endp, int base);
+
+/**
+ * Parse an `unsigned long long` from a wide string.
+ *
+ * If `str` starts with whitespace, then those whitespace characters are
+ * skipped before attempting to parse the number.
+ *
+ * If the parsed number does not fit inside an `unsigned long long`, the result is clamped to
+ * the minimum and maximum representable `unsigned long long` values.
+ *
+ * \param str The null-terminated wide string to read. Must not be NULL.
+ * \param endp If not NULL, the address of the first invalid wide character
+ * (i.e. the next character after the parsed number) will be
+ * written to this pointer.
+ * \param base The base of the integer to read. Supported values are 0 and 2
+ * to 36 inclusive. If 0, the base will be inferred from the
+ * number's prefix (0x for hexadecimal, 0 for octal, decimal
+ * otherwise).
+ * \returns the parsed `unsigned long long`, or 0 if no number could be parsed.
+ *
+ * \threadsafety It is safe to call this function from any thread.
+ *
+ * \since This function is available since SDL 3.6.0.
+ *
+ * \sa SDL_strtoull
+ */
+extern SDL_DECLSPEC unsigned long long SDLCALL SDL_wcstoull(const wchar_t *str, wchar_t **endp, int base);
+
/**
* This works exactly like strlen() but doesn't require access to a C runtime.
*
diff --git a/include/build_config/SDL_build_config.h.cmake b/include/build_config/SDL_build_config.h.cmake
index 7b608323dcc3d..341b9e689efce 100644
--- a/include/build_config/SDL_build_config.h.cmake
+++ b/include/build_config/SDL_build_config.h.cmake
@@ -92,6 +92,9 @@
#cmakedefine HAVE_WCSCMP 1
#cmakedefine HAVE_WCSNCMP 1
#cmakedefine HAVE_WCSTOL 1
+#cmakedefine HAVE_WCSTOLL 1
+#cmakedefine HAVE_WCSTOUL 1
+#cmakedefine HAVE_WCSTOULL 1
#cmakedefine HAVE_STRLEN 1
#cmakedefine HAVE_STRNLEN 1
#cmakedefine HAVE_STRLCPY 1
diff --git a/src/dynapi/SDL_dynapi.exports b/src/dynapi/SDL_dynapi.exports
index 4eb5b02e62f6b..b8bf43232587b 100644
--- a/src/dynapi/SDL_dynapi.exports
+++ b/src/dynapi/SDL_dynapi.exports
@@ -1291,3 +1291,6 @@ _SDL_HasSVE2
_SDL_GamepadHasCapSense
_SDL_GetGamepadCapSense
_SDL_aligned_alloc_zero
+_SDL_wcstoul
+_SDL_wcstoll
+_SDL_wcstoull
diff --git a/src/dynapi/SDL_dynapi.sym b/src/dynapi/SDL_dynapi.sym
index de5e49896f12e..92ccf55be5a17 100644
--- a/src/dynapi/SDL_dynapi.sym
+++ b/src/dynapi/SDL_dynapi.sym
@@ -1292,6 +1292,9 @@ SDL3_0.0.0 {
SDL_GamepadHasCapSense;
SDL_GetGamepadCapSense;
SDL_aligned_alloc_zero;
+ SDL_wcstoul;
+ SDL_wcstoll;
+ SDL_wcstoull;
# extra symbols go here (don't modify this line)
local: *;
};
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index e471dac703108..47841e3862c40 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -1318,3 +1318,6 @@
#define SDL_GamepadHasCapSense SDL_GamepadHasCapSense_REAL
#define SDL_GetGamepadCapSense SDL_GetGamepadCapSense_REAL
#define SDL_aligned_alloc_zero SDL_aligned_alloc_zero_REAL
+#define SDL_wcstoul SDL_wcstoul_REAL
+#define SDL_wcstoll SDL_wcstoll_REAL
+#define SDL_wcstoull SDL_wcstoull_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index d2a28315ca2bf..1ad817ec0db4c 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -1326,3 +1326,6 @@ SDL_DYNAPI_PROC(bool,SDL_HasSVE2,(void),(),return)
SDL_DYNAPI_PROC(bool,SDL_GamepadHasCapSense,(SDL_Gamepad *a,SDL_GamepadCapSenseType b),(a,b),return)
SDL_DYNAPI_PROC(bool,SDL_GetGamepadCapSense,(SDL_Gamepad *a,SDL_GamepadCapSenseType b),(a,b),return)
SDL_DYNAPI_PROC(void*,SDL_aligned_alloc_zero,(size_t a,size_t b),(a,b),return)
+SDL_DYNAPI_PROC(unsigned long,SDL_wcstoul,(const wchar_t *a,wchar_t **b,int c),(a,b,c),return)
+SDL_DYNAPI_PROC(long long,SDL_wcstoll,(const wchar_t *a,wchar_t **b,int c),(a,b,c),return)
+SDL_DYNAPI_PROC(unsigned long long,SDL_wcstoull,(const wchar_t *a,wchar_t **b,int c),(a,b,c),return)
diff --git a/src/stdlib/SDL_string.c b/src/stdlib/SDL_string.c
index 6c75b40ac5666..c0525ef928005 100644
--- a/src/stdlib/SDL_string.c
+++ b/src/stdlib/SDL_string.c
@@ -432,7 +432,7 @@ static size_t SDL_ScanUnsignedLongLongInternal(const char *text, int count, int
}
#endif
-#ifndef HAVE_WCSTOL
+#if !defined(HAVE_WCSTOL) || !defined (HAVE_WCSTOLL) || !defined(HAVE_WCSTOUL) || !defined(HAVE_WCSTOULL)
// SDL_ScanUnsignedLongLongInternalW assumes that wchar_t can be converted to int without truncating bits
SDL_COMPILE_TIME_ASSERT(wchar_t_int, sizeof(wchar_t) <= sizeof(int));
@@ -591,6 +591,29 @@ static size_t SDL_ScanUnsignedLong(const char *text, int count, int radix, unsig
}
#endif
+#ifndef HAVE_WCSTOUL
+static size_t SDL_ScanUnsignedLongW(const wchar_t *text, int count, int radix, unsigned long *valuep)
+{
+ const unsigned long ulong_max = ~0UL;
+ unsigned long long value;
+ bool negative;
+ size_t len = SDL_ScanUnsignedLongLongInternalW(text, count, radix, &value, &negative);
+ if (negative) {
+ if (value == 0 || value > ulong_max) {
+ value = ulong_max;
+ } else if (value == ulong_max) {
+ value = 1;
+ } else {
+ value = 0ULL - value;
+ }
+ } else if (value > ulong_max) {
+ value = ulong_max;
+ }
+ *valuep = (unsigned long)value;
+ return len;
+}
+#endif
+
#ifndef HAVE_VSSCANF
static size_t SDL_ScanUintPtrT(const char *text, uintptr_t *valuep)
{
@@ -636,6 +659,28 @@ static size_t SDL_ScanLongLong(const char *text, int count, int radix, long long
}
#endif
+#ifndef HAVE_WCSTOLL
+static size_t SDL_ScanLongLongW(const wchar_t *text, int count, int radix, long long *valuep)
+{
+ const unsigned long long llong_max = (~0ULL) >> 1;
+ unsigned long long value;
+ bool negative;
+ size_t len = SDL_ScanUnsignedLongLongInternalW(text, count, radix, &value, &negative);
+ if (negative) {
+ const unsigned long long abs_llong_min = llong_max + 1;
+ if (value == 0 || value > abs_llong_min) {
+ value = 0ULL - abs_llong_min;
+ } else {
+ value = 0ULL - value;
+ }
+ } else if (value > llong_max) {
+ value = llong_max;
+ }
+ *valuep = value;
+ return len;
+}
+#endif
+
#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOULL) || !defined(HAVE_STRTOD)
static size_t SDL_ScanUnsignedLongLong(const char *text, int count, int radix, unsigned long long *valuep)
{
@@ -653,6 +698,23 @@ static size_t SDL_ScanUnsignedLongLong(const char *text, int count, int radix, u
}
#endif
+#ifndef HAVE_WCSTOULL
+static size_t SDL_ScanUnsignedLongLongW(const wchar_t *text, int count, int radix, unsigned long long *valuep)
+{
+ const unsigned long long ullong_max = ~0ULL;
+ bool negative;
+ size_t len = SDL_ScanUnsignedLongLongInternalW(text, count, radix, valuep, &negative);
+ if (negative) {
+ if (*valuep == 0) {
+ *valuep = ullong_max;
+ } else {
+ *valuep = 0ULL - *valuep;
+ }
+ }
+ return len;
+}
+#endif
+
#if !defined(HAVE_VSSCANF) || !defined(HAVE_STRTOD)
static size_t SDL_ScanFloat(const char *text, double *valuep)
{
@@ -923,6 +985,48 @@ long SDL_wcstol(const wchar_t *string, wchar_t **endp, int base)
#endif // HAVE_WCSTOL
}
+unsigned long SDL_wcstoul(const wchar_t *string, wchar_t **endp, int base)
+{
+#ifdef HAVE_WCSTOUL
+ return wcstoul(string, endp, base);
+#else
+ unsigned long value = 0;
+ size_t len = SDL_ScanUnsignedLongW(string, 0, base, &value);
+ if (endp) {
+ *endp = (wchar_t *)string + len;
+ }
+ return value;
+#endif // HAVE_WCSTOUL
+}
+
+long long SDL_wcstoll(const wchar_t *string, wchar_t **endp, int base)
+{
+#ifdef HAVE_WCSTOLL
+ return wcstoll(string, endp, base);
+#else
+ long long value = 0;
+ size_t len = SDL_ScanLongLongW(string, 0, base, &value);
+ if (endp) {
+ *endp = (wchar_t *)string + len;
+ }
+ return value;
+#endif // HAVE_WCSTOLL
+}
+
+unsigned long long SDL_wcstoull(const wchar_t *string, wchar_t **endp, int base)
+{
+#ifdef HAVE_WCSTOULL
+ return wcstoull(string, endp, base);
+#else
+ unsigned long long value = 0;
+ size_t len = SDL_ScanUnsignedLongLongW(string, 0, base, &value);
+ if (endp) {
+ *endp = (wchar_t *)string + len;
+ }
+ return value;
+#endif // HAVE_WCSTOULL
+}
+
size_t SDL_strlcpy(SDL_OUT_Z_CAP(maxlen) char *dst, const char *src, size_t maxlen)
{
#ifdef HAVE_STRLCPY
diff --git a/test/testautomation_stdlib.c b/test/testautomation_stdlib.c
index d3f5c54cd8644..ad4e7f969f3bc 100644
--- a/test/testautomation_stdlib.c
+++ b/test/testautomation_stdlib.c
@@ -1289,54 +1289,88 @@ stdlib_strpbrk(void *arg)
return TEST_COMPLETED;
}
-static int SDLCALL stdlib_wcstol(void *arg)
+static int SDLCALL stdlib_wcstox(void *arg)
{
- const long long_max = (~0UL) >> 1;
- const long long_min = ((~0UL) >> 1) + 1UL;
-
-#define WCSTOL_TEST_CASE(str, base, expected_result, expected_endp_offset) do { \
- const wchar_t *s = str; \
- long r, expected_r = expected_result; \
- wchar_t *ep, *expected_ep = (wchar_t *)s + expected_endp_offset; \
- r = SDL_wcstol(s, &ep, base); \
- SDLTest_AssertPass("Call to SDL_wcstol(" #str ", &endp, " #base ")"); \
- SDLTest_AssertCheck(r == expected_r, "Check result value, expected: %ld, got: %ld", expected_r, r); \
- SDLTest_AssertCheck(ep == expected_ep, "Check endp value, expected: %p, got: %p", expected_ep, ep); \
+ const unsigned long long ullong_max = ~0ULL;
+
+#define WCSTOX_TEST_CASE(func_name, type, format_spec, str, base, expected_result, expected_endp_offset) \
+ do { \
+ const wchar_t *s = str; \
+ type r, expected_r = expected_result; \
+ wchar_t *ep, *expected_ep = (wchar_t *)s + expected_endp_offset; \
+ r = func_name(s, &ep, base); \
+ SDLTest_AssertPass("Call to " #func_name "(" #str ", &endp, " #base ")"); \
+ SDLTest_AssertCheck(r == expected_r, "Check result value, expected: " format_spec ", got: " format_spec, expected_r, r); \
+ SDLTest_AssertCheck(ep == expected_ep, "Check endp value, expected: %p, got: %p", expected_ep, ep); \
} while (0)
// infer decimal
- WCSTOL_TEST_CASE(L"\t 123abcxyz", 0, 123, 6); // skip leading space
- WCSTOL_TEST_CASE(L"+123abcxyz", 0, 123, 4);
- WCSTOL_TEST_CASE(L"-123abcxyz", 0, -123, 4);
- WCSTOL_TEST_CASE(L"99999999999999999999abcxyz", 0, long_max, 20);
- WCSTOL_TEST_CASE(L"-99999999999999999999abcxyz", 0, long_min, 21);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"\t 123abcxyz", 0, 123, 6); // skip leading space
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"+123abcxyz", 0, 123, 4);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"+123abcxyz", 0, 123, 4);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"-123abcxyz", 0, -123, 4);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"9999999999999999999999999999999999999999abcxyz", 0, ullong_max, 40);
// infer hexadecimal
- WCSTOL_TEST_CASE(L"0x123abcxyz", 0, 0x123abc, 8);
- WCSTOL_TEST_CASE(L"0X123ABCXYZ", 0, 0x123abc, 8); // uppercase X
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"0x123abcxyz", 0, 0x123abc, 8);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"0X123ABCXYZ", 0, 0x123abc, 8); // uppercase X
// infer octal
- WCSTOL_TEST_CASE(L"0123abcxyz", 0, 0123, 4);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"0123abcxyz", 0, 0123, 4);
// arbitrary bases
- WCSTOL_TEST_CASE(L"00110011", 2, 51, 8);
- WCSTOL_TEST_CASE(L"-uvwxyz", 32, -991, 3);
- WCSTOL_TEST_CASE(L"ZzZzZzZzZzZzZ", 36, long_max, 13);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"00110011", 2, 51, 8);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"-uvwxyz", 32, -991, 3);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"ZzZzZzZzZzZzZzZzZzZzZzZzZ", 36, ullong_max, 25);
+
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"0", 0, 0, 1);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"0", 10, 0, 1);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"-0", 0, 0, 2);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L"-0", 10, 0, 2);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLu, L" - 1", 0, 0, 0); // invalid input
- WCSTOL_TEST_CASE(L"-0", 10, 0, 2);
- WCSTOL_TEST_CASE(L" - 1", 0, 0, 0); // invalid input
+ // We know that SDL_wcstol, SDL_wcstoul and SDL_wcstoll share the same code path as SDL_wcstoull under the hood,
+ // so the most interesting test cases are those close to the bounds of the integer type.
+
+ // For simplicity, we only run long/long long tests when they are 32-bit/64-bit, respectively.
+ // Suppressing warnings would be difficult otherwise.
+ // Since the CI runs the tests against a variety of targets, this should be fine in practice.
- // values near the bounds of the type
const bool long_is_32bit = sizeof(long) == 4;
if (long_is_32bit) {
- WCSTOL_TEST_CASE(L"2147483647", 10, 2147483647, 10);
- WCSTOL_TEST_CASE(L"2147483648", 10, 2147483647, 10);
- WCSTOL_TEST_CASE(L"-2147483648", 10, -2147483647L - 1, 11);
- WCSTOL_TEST_CASE(L"-2147483649", 10, -2147483647L - 1, 11);
- WCSTOL_TEST_CASE(L"-9999999999999999999999999999999999999999", 10, -2147483647L - 1, 41);
+ WCSTOX_TEST_CASE(SDL_wcstol, long, "%ld", L"0", 0, 0, 1);
+ WCSTOX_TEST_CASE(SDL_wcstol, long, "%ld", L"0", 10, 0, 1);
+ WCSTOX_TEST_CASE(SDL_wcstol, long, "%ld", L"-0", 0, 0, 2);
+ WCSTOX_TEST_CASE(SDL_wcstol, long, "%ld", L"-0", 10, 0, 2);
+ WCSTOX_TEST_CASE(SDL_wcstol, long, "%ld", L"2147483647", 10, 2147483647, 10);
+ WCSTOX_TEST_CASE(SDL_wcstol, long, "%ld", L"2147483648", 10, 2147483647, 10);
+ WCSTOX_TEST_CASE(SDL_wcstol, long, "%ld", L"-2147483648", 10, -2147483647L - 1, 11);
+ WCSTOX_TEST_CASE(SDL_wcstol, long, "%ld", L"-2147483649", 10, -2147483647L - 1, 11);
+ WCSTOX_TEST_CASE(SDL_wcstol, long, "%ld", L"-9999999999999999999999999999999999999999", 10, -2147483647L - 1, 41);
+
+ WCSTOX_TEST_CASE(SDL_wcstoul, unsigned long, "%lu", L"4294967295", 10, 4294967295UL, 10);
+ WCSTOX_TEST_CASE(SDL_wcstoul, unsigned long, "%lu", L"4294967296", 10, 4294967295UL, 10);
+ WCSTOX_TEST_CASE(SDL_wcstoul, unsigned long, "%lu", L"-4294967295", 10, 1, 11);
+ }
+
+ const bool long_long_is_64bit = sizeof(long long) == 8;
+ if (long_long_is_64bit) {
+ WCSTOX_TEST_CASE(SDL_wcstoll, long long, FMT_PRILLd, L"0", 0, 0LL, 1);
+ WCSTOX_TEST_CASE(SDL_wcstoll, long long, FMT_PRILLd, L"0", 10, 0LL, 1);
+ WCSTOX_TEST_CASE(SDL_wcstoll, long long, FMT_PRILLd, L"-0", 0, 0LL, 2);
+ WCSTOX_TEST_CASE(SDL_wcstoll, long long, FMT_PRILLd, L"-0", 10, 0LL, 2);
+ WCSTOX_TEST_CASE(SDL_wcstoll, long long, FMT_PRILLd, L"9223372036854775807", 10, 9223372036854775807LL, 19);
+ WCSTOX_TEST_CASE(SDL_wcstoll, long long, FMT_PRILLd, L"9223372036854775808", 10, 9223372036854775807LL, 19);
+ WCSTOX_TEST_CASE(SDL_wcstoll, long long, FMT_PRILLd, L"-9223372036854775808", 10, -9223372036854775807LL - 1, 20);
+ WCSTOX_TEST_CASE(SDL_wcstoll, long long, FMT_PRILLd, L"-9223372036854775809", 10, -9223372036854775807LL - 1, 20);
+ WCSTOX_TEST_CASE(SDL_wcstoll, long long, FMT_PRILLd, L"-9999999999999999999999999999999999999999", 10, -9223372036854775807LL - 1, 41);
+
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLd, L"18446744073709551615", 10, 18446744073709551615ULL, 20);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLd, L"18446744073709551616", 10, 18446744073709551615ULL, 20);
+ WCSTOX_TEST_CASE(SDL_wcstoull, unsigned long long, FMT_PRILLd, L"-18446744073709551615", 10, 1, 21);
}
-#undef WCSTOL_TEST_CASE
+#undef WCSTOX_TEST_CASE
return TEST_COMPLETED;
}
@@ -1494,8 +1528,8 @@ static const SDLTest_TestCaseReference stdlibTest_strpbrk = {
stdlib_strpbrk, "stdlib_strpbrk", "Calls to SDL_strpbrk", TEST_ENABLED
};
-static const SDLTest_TestCaseReference stdlibTest_wcstol = {
- stdlib_wcstol, "stdlib_wcstol", "Calls to SDL_wcstol", TEST_ENABLED
+static const SDLTest_TestCaseReference stdlibTest_wcstox = {
+ stdlib_wcstox, "stdlib_wcstox", "Calls to SDL_wcstol, SDL_wcstoul, SDL_wcstoll, and SDL_wcstoull", TEST_ENABLED
};
static const SDLTest_TestCaseReference stdlibTest_strtox = {
@@ -1519,7 +1553,7 @@ static const SDLTest_TestCaseReference *stdlibTests[] = {
&stdlibTestOverflow,
&stdlibTest_iconv,
&stdlibTest_strpbrk,
- &stdlibTest_wcstol,
+ &stdlibTest_wcstox,
&stdlibTest_strtox,
&stdlibTest_strtod,
NULL