mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-09-14 07:18:13 +00:00
io: rework how we set SDL_IOStream status.
This now relies on the implementation to set these flags on short reads/writes instead of the higher level checking if SDL_SetError() was called. Additionally (and crucially), this now sets ERROR or EOF on all short reads, across all backends, not just when we get a zero-byte return value. Fixes #13720.
This commit is contained in:
@@ -111,7 +111,7 @@ typedef struct SDL_IOStreamInterface
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Read up to `size` bytes from the data stream to the area pointed
|
* Read up to `size` bytes from the data stream to the area pointed
|
||||||
* at by `ptr`.
|
* at by `ptr`. `size` will always be > 0.
|
||||||
*
|
*
|
||||||
* On an incomplete read, you should set `*status` to a value from the
|
* On an incomplete read, you should set `*status` to a value from the
|
||||||
* SDL_IOStatus enum. You do not have to explicitly set this on
|
* SDL_IOStatus enum. You do not have to explicitly set this on
|
||||||
@@ -123,7 +123,7 @@ typedef struct SDL_IOStreamInterface
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Write exactly `size` bytes from the area pointed at by `ptr`
|
* Write exactly `size` bytes from the area pointed at by `ptr`
|
||||||
* to data stream.
|
* to data stream. `size` will always be > 0.
|
||||||
*
|
*
|
||||||
* On an incomplete write, you should set `*status` to a value from the
|
* On an incomplete write, you should set `*status` to a value from the
|
||||||
* SDL_IOStatus enum. You do not have to explicitly set this on
|
* SDL_IOStatus enum. You do not have to explicitly set this on
|
||||||
@@ -580,6 +580,10 @@ extern SDL_DECLSPEC Sint64 SDLCALL SDL_TellIO(SDL_IOStream *context);
|
|||||||
* the stream is not at EOF, SDL_GetIOStatus() will return a different error
|
* the stream is not at EOF, SDL_GetIOStatus() will return a different error
|
||||||
* value and SDL_GetError() will offer a human-readable message.
|
* value and SDL_GetError() will offer a human-readable message.
|
||||||
*
|
*
|
||||||
|
* A request for zero bytes on a valid stream will return zero immediately
|
||||||
|
* without accessing the stream, so the stream status (EOF, err, etc) will
|
||||||
|
* not change.
|
||||||
|
*
|
||||||
* \param context a pointer to an SDL_IOStream structure.
|
* \param context a pointer to an SDL_IOStream structure.
|
||||||
* \param ptr a pointer to a buffer to read data into.
|
* \param ptr a pointer to a buffer to read data into.
|
||||||
* \param size the number of bytes to read from the data source.
|
* \param size the number of bytes to read from the data source.
|
||||||
@@ -609,6 +613,10 @@ extern SDL_DECLSPEC size_t SDLCALL SDL_ReadIO(SDL_IOStream *context, void *ptr,
|
|||||||
* recoverable, such as a non-blocking write that can simply be retried later,
|
* recoverable, such as a non-blocking write that can simply be retried later,
|
||||||
* or a fatal error.
|
* or a fatal error.
|
||||||
*
|
*
|
||||||
|
* A request for zero bytes on a valid stream will return zero immediately
|
||||||
|
* without accessing the stream, so the stream status (EOF, err, etc) will
|
||||||
|
* not change.
|
||||||
|
*
|
||||||
* \param context a pointer to an SDL_IOStream structure.
|
* \param context a pointer to an SDL_IOStream structure.
|
||||||
* \param ptr a pointer to a buffer containing data to write.
|
* \param ptr a pointer to a buffer containing data to write.
|
||||||
* \param size the number of bytes to write.
|
* \param size the number of bytes to write.
|
||||||
|
@@ -1827,7 +1827,10 @@ size_t Android_JNI_FileRead(void *userdata, void *buffer, size_t size, SDL_IOSta
|
|||||||
const int bytes = AAsset_read((AAsset *)userdata, buffer, size);
|
const int bytes = AAsset_read((AAsset *)userdata, buffer, size);
|
||||||
if (bytes < 0) {
|
if (bytes < 0) {
|
||||||
SDL_SetError("AAsset_read() failed");
|
SDL_SetError("AAsset_read() failed");
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
return 0;
|
return 0;
|
||||||
|
} else if (bytes < size) {
|
||||||
|
*status = SDL_IO_STATUS_EOF;
|
||||||
}
|
}
|
||||||
return (size_t)bytes;
|
return (size_t)bytes;
|
||||||
}
|
}
|
||||||
@@ -1835,6 +1838,7 @@ size_t Android_JNI_FileRead(void *userdata, void *buffer, size_t size, SDL_IOSta
|
|||||||
size_t Android_JNI_FileWrite(void *userdata, const void *buffer, size_t size, SDL_IOStatus *status)
|
size_t Android_JNI_FileWrite(void *userdata, const void *buffer, size_t size, SDL_IOStatus *status)
|
||||||
{
|
{
|
||||||
SDL_SetError("Cannot write to Android package filesystem");
|
SDL_SetError("Cannot write to Android package filesystem");
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -220,15 +220,17 @@ static size_t SDLCALL windows_file_read(void *userdata, void *ptr, size_t size,
|
|||||||
switch (error) {
|
switch (error) {
|
||||||
case ERROR_BROKEN_PIPE:
|
case ERROR_BROKEN_PIPE:
|
||||||
case ERROR_HANDLE_EOF:
|
case ERROR_HANDLE_EOF:
|
||||||
|
*status = SDL_IO_STATUS_EOF;
|
||||||
break;
|
break;
|
||||||
case ERROR_NO_DATA:
|
case ERROR_NO_DATA:
|
||||||
*status = SDL_IO_STATUS_NOT_READY;
|
*status = SDL_IO_STATUS_NOT_READY;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
WIN_SetError("Error reading from datastream");
|
WIN_SetError("Error reading from datastream");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0; // !!! FIXME: this should return the bytes read from any readahead we finished out before this (the `iodata->left > 0` code above). In that case, fail on the next read.
|
||||||
}
|
}
|
||||||
read_ahead = SDL_min(total_need, bytes);
|
read_ahead = SDL_min(total_need, bytes);
|
||||||
SDL_memcpy(ptr, iodata->data, read_ahead);
|
SDL_memcpy(ptr, iodata->data, read_ahead);
|
||||||
@@ -241,15 +243,17 @@ static size_t SDLCALL windows_file_read(void *userdata, void *ptr, size_t size,
|
|||||||
switch (error) {
|
switch (error) {
|
||||||
case ERROR_BROKEN_PIPE:
|
case ERROR_BROKEN_PIPE:
|
||||||
case ERROR_HANDLE_EOF:
|
case ERROR_HANDLE_EOF:
|
||||||
|
*status = SDL_IO_STATUS_EOF;
|
||||||
break;
|
break;
|
||||||
case ERROR_NO_DATA:
|
case ERROR_NO_DATA:
|
||||||
*status = SDL_IO_STATUS_NOT_READY;
|
*status = SDL_IO_STATUS_NOT_READY;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
WIN_SetError("Error reading from datastream");
|
WIN_SetError("Error reading from datastream");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0; // !!! FIXME: this should return the bytes read from any readahead we finished out before this (the `iodata->left > 0` code above). In that case, fail on the next read.
|
||||||
}
|
}
|
||||||
total_read += bytes;
|
total_read += bytes;
|
||||||
}
|
}
|
||||||
@@ -263,6 +267,7 @@ static size_t SDLCALL windows_file_write(void *userdata, const void *ptr, size_t
|
|||||||
|
|
||||||
if (iodata->left) {
|
if (iodata->left) {
|
||||||
if (!SetFilePointer(iodata->h, -(LONG)iodata->left, NULL, FILE_CURRENT)) {
|
if (!SetFilePointer(iodata->h, -(LONG)iodata->left, NULL, FILE_CURRENT)) {
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
WIN_SetError("Error seeking in datastream");
|
WIN_SetError("Error seeking in datastream");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -274,16 +279,17 @@ static size_t SDLCALL windows_file_write(void *userdata, const void *ptr, size_t
|
|||||||
LARGE_INTEGER windowsoffset;
|
LARGE_INTEGER windowsoffset;
|
||||||
windowsoffset.QuadPart = 0;
|
windowsoffset.QuadPart = 0;
|
||||||
if (!SetFilePointerEx(iodata->h, windowsoffset, &windowsoffset, FILE_END)) {
|
if (!SetFilePointerEx(iodata->h, windowsoffset, &windowsoffset, FILE_END)) {
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
WIN_SetError("Error seeking in datastream");
|
WIN_SetError("Error seeking in datastream");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!WriteFile(iodata->h, ptr, (DWORD)size, &bytes, NULL)) {
|
if (!WriteFile(iodata->h, ptr, (DWORD)size, &bytes, NULL)) {
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
WIN_SetError("Error writing to datastream");
|
WIN_SetError("Error writing to datastream");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
} else if (bytes == 0 && size > 0) {
|
||||||
if (bytes == 0 && size > 0) {
|
|
||||||
*status = SDL_IO_STATUS_NOT_READY;
|
*status = SDL_IO_STATUS_NOT_READY;
|
||||||
}
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
@@ -421,9 +427,12 @@ static size_t SDLCALL fd_read(void *userdata, void *ptr, size_t size, SDL_IOStat
|
|||||||
if (errno == EAGAIN) {
|
if (errno == EAGAIN) {
|
||||||
*status = SDL_IO_STATUS_NOT_READY;
|
*status = SDL_IO_STATUS_NOT_READY;
|
||||||
} else {
|
} else {
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
SDL_SetError("Error reading from datastream: %s", strerror(errno));
|
SDL_SetError("Error reading from datastream: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
bytes = 0;
|
bytes = 0;
|
||||||
|
} else if (bytes < size) {
|
||||||
|
*status = SDL_IO_STATUS_EOF;
|
||||||
}
|
}
|
||||||
return (size_t)bytes;
|
return (size_t)bytes;
|
||||||
}
|
}
|
||||||
@@ -440,6 +449,7 @@ static size_t SDLCALL fd_write(void *userdata, const void *ptr, size_t size, SDL
|
|||||||
if (errno == EAGAIN) {
|
if (errno == EAGAIN) {
|
||||||
*status = SDL_IO_STATUS_NOT_READY;
|
*status = SDL_IO_STATUS_NOT_READY;
|
||||||
} else {
|
} else {
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
SDL_SetError("Error writing to datastream: %s", strerror(errno));
|
SDL_SetError("Error writing to datastream: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
bytes = 0;
|
bytes = 0;
|
||||||
@@ -606,13 +616,19 @@ static size_t SDLCALL stdio_read(void *userdata, void *ptr, size_t size, SDL_IOS
|
|||||||
{
|
{
|
||||||
IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
|
IOStreamStdioData *iodata = (IOStreamStdioData *) userdata;
|
||||||
const size_t bytes = fread(ptr, 1, size, iodata->fp);
|
const size_t bytes = fread(ptr, 1, size, iodata->fp);
|
||||||
if (bytes == 0 && ferror(iodata->fp)) {
|
if (bytes < size) {
|
||||||
|
if (ferror(iodata->fp)) {
|
||||||
if (errno == EAGAIN) {
|
if (errno == EAGAIN) {
|
||||||
*status = SDL_IO_STATUS_NOT_READY;
|
*status = SDL_IO_STATUS_NOT_READY;
|
||||||
clearerr(iodata->fp);
|
clearerr(iodata->fp);
|
||||||
} else {
|
} else {
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
SDL_SetError("Error reading from datastream: %s", strerror(errno));
|
SDL_SetError("Error reading from datastream: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
SDL_assert(feof(iodata->fp));
|
||||||
|
*status = SDL_IO_STATUS_EOF;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
@@ -626,6 +642,7 @@ static size_t SDLCALL stdio_write(void *userdata, const void *ptr, size_t size,
|
|||||||
*status = SDL_IO_STATUS_NOT_READY;
|
*status = SDL_IO_STATUS_NOT_READY;
|
||||||
clearerr(iodata->fp);
|
clearerr(iodata->fp);
|
||||||
} else {
|
} else {
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
SDL_SetError("Error writing to datastream: %s", strerror(errno));
|
SDL_SetError("Error writing to datastream: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -769,13 +786,22 @@ static size_t mem_io(void *userdata, void *dst, const void *src, size_t size)
|
|||||||
static size_t SDLCALL mem_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
|
static size_t SDLCALL mem_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
|
||||||
{
|
{
|
||||||
IOStreamMemData *iodata = (IOStreamMemData *) userdata;
|
IOStreamMemData *iodata = (IOStreamMemData *) userdata;
|
||||||
return mem_io(userdata, ptr, iodata->here, size);
|
const size_t retval = mem_io(userdata, ptr, iodata->here, size);
|
||||||
|
if ((retval < size) && (iodata->stop == iodata->here)) {
|
||||||
|
*status = SDL_IO_STATUS_EOF;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t SDLCALL mem_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
|
static size_t SDLCALL mem_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
|
||||||
{
|
{
|
||||||
IOStreamMemData *iodata = (IOStreamMemData *) userdata;
|
IOStreamMemData *iodata = (IOStreamMemData *) userdata;
|
||||||
return mem_io(userdata, iodata->here, ptr, size);
|
const size_t retval = mem_io(userdata, iodata->here, ptr, size);
|
||||||
|
if ((retval < size) && (iodata->stop == iodata->here)) {
|
||||||
|
SDL_SetError("Memory buffer is full");
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool SDLCALL mem_close(void *userdata)
|
static bool SDLCALL mem_close(void *userdata)
|
||||||
@@ -1027,7 +1053,11 @@ static Sint64 SDLCALL dynamic_mem_seek(void *userdata, Sint64 offset, SDL_IOWhen
|
|||||||
static size_t SDLCALL dynamic_mem_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
|
static size_t SDLCALL dynamic_mem_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
|
||||||
{
|
{
|
||||||
IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
|
IOStreamDynamicMemData *iodata = (IOStreamDynamicMemData *) userdata;
|
||||||
return mem_io(&iodata->data, ptr, iodata->data.here, size);
|
const size_t retval = mem_io(&iodata->data, ptr, iodata->data.here, size);
|
||||||
|
if ((retval < size) && (iodata->data.stop == iodata->data.here)) {
|
||||||
|
*status = SDL_IO_STATUS_EOF;
|
||||||
|
}
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool dynamic_mem_realloc(IOStreamDynamicMemData *iodata, size_t size)
|
static bool dynamic_mem_realloc(IOStreamDynamicMemData *iodata, size_t size)
|
||||||
@@ -1060,12 +1090,15 @@ static size_t SDLCALL dynamic_mem_write(void *userdata, const void *ptr, size_t
|
|||||||
if (size > (size_t)(iodata->data.stop - iodata->data.here)) {
|
if (size > (size_t)(iodata->data.stop - iodata->data.here)) {
|
||||||
if (size > (size_t)(iodata->end - iodata->data.here)) {
|
if (size > (size_t)(iodata->end - iodata->data.here)) {
|
||||||
if (!dynamic_mem_realloc(iodata, size)) {
|
if (!dynamic_mem_realloc(iodata, size)) {
|
||||||
|
*status = SDL_IO_STATUS_ERROR;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
iodata->data.stop = iodata->data.here + size;
|
iodata->data.stop = iodata->data.here + size;
|
||||||
}
|
}
|
||||||
return mem_io(&iodata->data, iodata->data.here, ptr, size);
|
const size_t retval = mem_io(&iodata->data, iodata->data.here, ptr, size);
|
||||||
|
SDL_assert(retval == size); // we should have allocated enough to cover this!
|
||||||
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool SDLCALL dynamic_mem_close(void *userdata)
|
static bool SDLCALL dynamic_mem_close(void *userdata)
|
||||||
@@ -1333,8 +1366,6 @@ Sint64 SDL_TellIO(SDL_IOStream *context)
|
|||||||
|
|
||||||
size_t SDL_ReadIO(SDL_IOStream *context, void *ptr, size_t size)
|
size_t SDL_ReadIO(SDL_IOStream *context, void *ptr, size_t size)
|
||||||
{
|
{
|
||||||
size_t bytes;
|
|
||||||
|
|
||||||
if (!context) {
|
if (!context) {
|
||||||
SDL_InvalidParamError("context");
|
SDL_InvalidParamError("context");
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1342,30 +1373,18 @@ size_t SDL_ReadIO(SDL_IOStream *context, void *ptr, size_t size)
|
|||||||
context->status = SDL_IO_STATUS_WRITEONLY;
|
context->status = SDL_IO_STATUS_WRITEONLY;
|
||||||
SDL_Unsupported();
|
SDL_Unsupported();
|
||||||
return 0;
|
return 0;
|
||||||
|
} else if (size == 0) {
|
||||||
|
return 0; // context->status doesn't change for this.
|
||||||
}
|
}
|
||||||
|
|
||||||
context->status = SDL_IO_STATUS_READY;
|
context->status = SDL_IO_STATUS_READY;
|
||||||
SDL_ClearError();
|
SDL_ClearError();
|
||||||
|
|
||||||
if (size == 0) {
|
return context->iface.read(context->userdata, ptr, size, &context->status);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes = context->iface.read(context->userdata, ptr, size, &context->status);
|
|
||||||
if (bytes == 0 && context->status == SDL_IO_STATUS_READY) {
|
|
||||||
if (*SDL_GetError()) {
|
|
||||||
context->status = SDL_IO_STATUS_ERROR;
|
|
||||||
} else {
|
|
||||||
context->status = SDL_IO_STATUS_EOF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t SDL_WriteIO(SDL_IOStream *context, const void *ptr, size_t size)
|
size_t SDL_WriteIO(SDL_IOStream *context, const void *ptr, size_t size)
|
||||||
{
|
{
|
||||||
size_t bytes;
|
|
||||||
|
|
||||||
if (!context) {
|
if (!context) {
|
||||||
SDL_InvalidParamError("context");
|
SDL_InvalidParamError("context");
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1373,20 +1392,14 @@ size_t SDL_WriteIO(SDL_IOStream *context, const void *ptr, size_t size)
|
|||||||
context->status = SDL_IO_STATUS_READONLY;
|
context->status = SDL_IO_STATUS_READONLY;
|
||||||
SDL_Unsupported();
|
SDL_Unsupported();
|
||||||
return 0;
|
return 0;
|
||||||
|
} else if (size == 0) {
|
||||||
|
return 0; // context->status doesn't change for this.
|
||||||
}
|
}
|
||||||
|
|
||||||
context->status = SDL_IO_STATUS_READY;
|
context->status = SDL_IO_STATUS_READY;
|
||||||
SDL_ClearError();
|
SDL_ClearError();
|
||||||
|
|
||||||
if (size == 0) {
|
return context->iface.write(context->userdata, ptr, size, &context->status);
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes = context->iface.write(context->userdata, ptr, size, &context->status);
|
|
||||||
if ((bytes == 0) && (context->status == SDL_IO_STATUS_READY)) {
|
|
||||||
context->status = SDL_IO_STATUS_ERROR;
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t SDL_IOprintf(SDL_IOStream *context, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
|
size_t SDL_IOprintf(SDL_IOStream *context, SDL_PRINTF_FORMAT_STRING const char *fmt, ...)
|
||||||
|
Reference in New Issue
Block a user