From 2c8c2d72b501a945e35097f6056a7e4d54065e3f Mon Sep 17 00:00:00 2001 From: ProgramGamer Date: Sat, 27 Sep 2025 13:36:30 -0300 Subject: [PATCH] Allow in-memory IOStreams to be of length 0 (#13840) --- src/io/SDL_iostream.c | 12 +-- test/testautomation_iostream.c | 190 +++++++++++++++++++++++++++++++-- 2 files changed, 183 insertions(+), 19 deletions(-) diff --git a/src/io/SDL_iostream.c b/src/io/SDL_iostream.c index b47b4da4bb..cc435da2fb 100644 --- a/src/io/SDL_iostream.c +++ b/src/io/SDL_iostream.c @@ -1023,14 +1023,10 @@ SDL_IOStream *SDL_IOFromFile(const char *file, const char *mode) SDL_IOStream *SDL_IOFromMem(void *mem, size_t size) { - CHECK_PARAM(!mem) { + CHECK_PARAM(size && !mem) { SDL_InvalidParamError("mem"); return NULL; } - CHECK_PARAM(!size) { - SDL_InvalidParamError("size"); - return NULL; - } IOStreamMemData *iodata = (IOStreamMemData *) SDL_calloc(1, sizeof (*iodata)); if (!iodata) { @@ -1065,14 +1061,10 @@ SDL_IOStream *SDL_IOFromMem(void *mem, size_t size) SDL_IOStream *SDL_IOFromConstMem(const void *mem, size_t size) { - CHECK_PARAM(!mem) { + CHECK_PARAM(size && !mem) { SDL_InvalidParamError("mem"); return NULL; } - CHECK_PARAM(!size) { - SDL_InvalidParamError("size"); - return NULL; - } IOStreamMemData *iodata = (IOStreamMemData *) SDL_calloc(1, sizeof (*iodata)); if (!iodata) { diff --git a/test/testautomation_iostream.c b/test/testautomation_iostream.c index 6099923faf..85bfc71498 100644 --- a/test/testautomation_iostream.c +++ b/test/testautomation_iostream.c @@ -198,6 +198,110 @@ static void testGenericIOStreamValidations(SDL_IOStream *rw, bool write) (int)i); } +/** + * Makes sure parameters work properly. Local helper function. + * + * \sa SDL_SeekIO + * \sa SDL_ReadIO + */ +static void testEmptyIOStreamValidations(SDL_IOStream *rw, bool write) +{ + char con[sizeof(IOStreamHelloWorldTestString)]; + char buf[sizeof(IOStreamHelloWorldTestString)]; + Sint64 i; + size_t s; + int seekPos = SDLTest_RandomIntegerInRange(4, 8); + + /* Clear control & buffer */ + SDL_zeroa(con); + SDL_zeroa(buf); + + /* Set to start. */ + i = SDL_SeekIO(rw, 0, SDL_IO_SEEK_SET); + SDLTest_AssertPass("Call to SDL_SeekIO succeeded"); + SDLTest_AssertCheck(i == (Sint64)0, "Verify seek to 0 with SDL_SeekIO (SDL_IO_SEEK_SET), expected 0, got %" SDL_PRIs64, i); + + /* Test write */ + s = SDL_WriteIO(rw, IOStreamHelloWorldTestString, sizeof(IOStreamHelloWorldTestString) - 1); + SDLTest_AssertPass("Call to SDL_WriteIO succeeded"); + if (write) { + SDLTest_AssertCheck(s == 0, "Verify result of writing with SDL_WriteIO, expected 0, got %i", (int)s); + } else { + SDLTest_AssertCheck(s == 0, "Verify result of writing with SDL_WriteIO, expected 0, got %i", (int)s); + } + + /* Test seek to random position */ + i = SDL_SeekIO(rw, seekPos, SDL_IO_SEEK_SET); + SDLTest_AssertPass("Call to SDL_SeekIO succeeded"); + SDLTest_AssertCheck(i == 0, "Verify seek to %i with SDL_SeekIO (SDL_IO_SEEK_SET), expected 0, got %" SDL_PRIs64, seekPos, i); + + /* Test seek back to start */ + i = SDL_SeekIO(rw, 0, SDL_IO_SEEK_SET); + SDLTest_AssertPass("Call to SDL_SeekIO succeeded"); + SDLTest_AssertCheck(i == (Sint64)0, "Verify seek to 0 with SDL_SeekIO (SDL_IO_SEEK_SET), expected 0, got %" SDL_PRIs64, i); + + /* Test read */ + s = SDL_ReadIO(rw, buf, sizeof(IOStreamHelloWorldTestString) - 1); + SDLTest_AssertPass("Call to SDL_ReadIO succeeded"); + SDLTest_AssertCheck(s == 0, "Verify result from SDL_ReadIO, expected 0, got %i", (int)s); + SDLTest_AssertCheck( + SDL_memcmp(buf, con, sizeof(IOStreamHelloWorldTestString) - 1) == 0, + "Verify that buffer remains unchanged, expected '%s', got '%s'", con, buf); + + /* Test seek back to start */ + i = SDL_SeekIO(rw, 0, SDL_IO_SEEK_SET); + SDLTest_AssertPass("Call to SDL_SeekIO succeeded"); + SDLTest_AssertCheck(i == (Sint64)0, "Verify seek to 0 with SDL_SeekIO (SDL_IO_SEEK_SET), expected 0, got %" SDL_PRIs64, i); + + /* Test printf */ + s = SDL_IOprintf(rw, "%s", IOStreamHelloWorldTestString); + SDLTest_AssertPass("Call to SDL_IOprintf succeeded"); + if (write) { + SDLTest_AssertCheck(s == 0, "Verify result of writing with SDL_IOprintf, expected 0, got %i", (int)s); + } else { + SDLTest_AssertCheck(s == 0, "Verify result of writing with SDL_WriteIO, expected 0, got %i", (int)s); + } + + /* Test seek back to start */ + i = SDL_SeekIO(rw, 0, SDL_IO_SEEK_SET); + SDLTest_AssertPass("Call to SDL_SeekIO succeeded"); + SDLTest_AssertCheck(i == (Sint64)0, "Verify seek to 0 with SDL_SeekIO (SDL_IO_SEEK_SET), expected 0, got %" SDL_PRIs64, i); + + /* Test read */ + s = SDL_ReadIO(rw, buf, sizeof(IOStreamHelloWorldTestString) - 1); + SDLTest_AssertPass("Call to SDL_ReadIO succeeded"); + SDLTest_AssertCheck( + s == 0, + "Verify result from SDL_ReadIO, expected 0, got %i", + (int)s); + SDLTest_AssertCheck( + SDL_memcmp(buf, con, sizeof(IOStreamHelloWorldTestString) - 1) == 0, + "Verify that buffer remains unchanged, expected '%s', got '%s'", con, buf); + + /* More seek tests. */ + i = SDL_SeekIO(rw, -4, SDL_IO_SEEK_CUR); + SDLTest_AssertPass("Call to SDL_SeekIO(...,-4,SDL_IO_SEEK_CUR) succeeded"); + SDLTest_AssertCheck( + i == 0, + "Verify seek to -4 with SDL_SeekIO (SDL_IO_SEEK_CUR), expected 0, got %i", + (int)i); + + i = SDL_SeekIO(rw, -1, SDL_IO_SEEK_END); + SDLTest_AssertPass("Call to SDL_SeekIO(...,-1,SDL_IO_SEEK_END) succeeded"); + SDLTest_AssertCheck( + i == 0, + "Verify seek to -1 with SDL_SeekIO (SDL_IO_SEEK_END), expected 0, got %i", + (int)i); + + /* Invalid whence seek */ + i = SDL_SeekIO(rw, 0, (SDL_IOWhence)999); + SDLTest_AssertPass("Call to SDL_SeekIO(...,0,invalid_whence) succeeded"); + SDLTest_AssertCheck( + i == (Sint64)(-1), + "Verify seek with SDL_SeekIO (invalid_whence); expected: -1, got %i", + (int)i); +} + /** * Negative test for SDL_IOFromFile parameters * @@ -233,14 +337,6 @@ static int SDLCALL iostrm_testParamNegative(void *arg) SDLTest_AssertPass("Call to SDL_IOFromMem(NULL, 10) succeeded"); SDLTest_AssertCheck(iostrm == NULL, "Verify SDL_IOFromMem(NULL, 10) returns NULL"); - iostrm = SDL_IOFromMem((void *)IOStreamAlphabetString, 0); - SDLTest_AssertPass("Call to SDL_IOFromMem(data, 0) succeeded"); - SDLTest_AssertCheck(iostrm == NULL, "Verify SDL_IOFromMem(data, 0) returns NULL"); - - iostrm = SDL_IOFromConstMem((const void *)IOStreamAlphabetString, 0); - SDLTest_AssertPass("Call to SDL_IOFromConstMem(data, 0) succeeded"); - SDLTest_AssertCheck(iostrm == NULL, "Verify SDL_IOFromConstMem(data, 0) returns NULL"); - return TEST_COMPLETED; } @@ -312,6 +408,74 @@ static int SDLCALL iostrm_testConstMem(void *arg) return TEST_COMPLETED; } +/** + * Tests opening nothing. + * + * \sa SDL_IOFromMem + * \sa SDL_CloseIO + */ +static int SDLCALL iostrm_testMemEmpty(void *arg) +{ + char mem[sizeof(IOStreamHelloWorldTestString)]; + SDL_IOStream *rw; + int result; + + /* Clear buffer */ + SDL_zeroa(mem); + + /* Open empty */ + rw = SDL_IOFromMem(mem, 0); + SDLTest_AssertPass("Call to SDL_IOFromMem() succeeded"); + SDLTest_AssertCheck(rw != NULL, "Verify opening memory with SDL_IOFromMem does not return NULL"); + + /* Bail out if NULL */ + if (rw == NULL) { + return TEST_ABORTED; + } + + /* Run generic tests */ + testEmptyIOStreamValidations(rw, true); + + /* Close */ + result = SDL_CloseIO(rw); + SDLTest_AssertPass("Call to SDL_CloseIO() succeeded"); + SDLTest_AssertCheck(result == true, "Verify result value is true; got: %d", result); + + return TEST_COMPLETED; +} + +/** + * Tests opening nothing. + * + * \sa SDL_IOFromMem + * \sa SDL_CloseIO + */ +static int SDLCALL iostrm_testConstMemEmpty(void *arg) +{ + SDL_IOStream *rw; + int result; + + /* Open handle */ + rw = SDL_IOFromConstMem(IOStreamHelloWorldCompString, 0); + SDLTest_AssertPass("Call to SDL_IOFromConstMem() succeeded"); + SDLTest_AssertCheck(rw != NULL, "Verify opening memory with SDL_IOFromConstMem does not return NULL"); + + /* Bail out if NULL */ + if (rw == NULL) { + return TEST_ABORTED; + } + + /* Run generic tests */ + testEmptyIOStreamValidations(rw, false); + + /* Close handle */ + result = SDL_CloseIO(rw); + SDLTest_AssertPass("Call to SDL_CloseIO() succeeded"); + SDLTest_AssertCheck(result == true, "Verify result value is true; got: %d", result); + + return TEST_COMPLETED; +} + static int free_call_count; void SDLCALL test_free(void* mem) { free_call_count++; @@ -736,10 +900,18 @@ static const SDLTest_TestCaseReference iostrmTest10 = { iostrm_testMemWithFree, "iostrm_testMemWithFree", "Tests opening from memory with free on close", TEST_ENABLED }; +static const SDLTest_TestCaseReference iostrmTest11 = { + iostrm_testMemEmpty, "iostrm_testMemEmpty", "Tests opening empty memory stream", TEST_ENABLED +}; + +static const SDLTest_TestCaseReference iostrmTest12 = { + iostrm_testConstMemEmpty, "iostrm_testConstMemEmpty", "Tests opening empty (const) memory stream", TEST_ENABLED +}; + /* Sequence of IOStream test cases */ static const SDLTest_TestCaseReference *iostrmTests[] = { &iostrmTest1, &iostrmTest2, &iostrmTest3, &iostrmTest4, &iostrmTest5, &iostrmTest6, - &iostrmTest7, &iostrmTest8, &iostrmTest9, &iostrmTest10, NULL + &iostrmTest7, &iostrmTest8, &iostrmTest9, &iostrmTest10, &iostrmTest11, &iostrmTest12, NULL }; /* IOStream test suite (global) */