Skip to content

Commit 7c9ae67

Browse files
authored
Add write buffering to Windows SDL_IOStream (#12424) (#14794)
1 parent dd52dd8 commit 7c9ae67

File tree

1 file changed

+130
-21
lines changed

1 file changed

+130
-21
lines changed

src/io/SDL_iostream.c

Lines changed: 130 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ typedef struct IOStreamWindowsData
7070
void *data;
7171
size_t size;
7272
size_t left;
73+
void *write_data;
74+
size_t write_pos;
75+
bool writable;
7376
bool append;
7477
bool autoclose;
7578
} IOStreamWindowsData;
@@ -80,7 +83,8 @@ typedef struct IOStreamWindowsData
8083
#define INVALID_SET_FILE_POINTER 0xFFFFFFFF
8184
#endif
8285

83-
#define READAHEAD_BUFFER_SIZE 1024
86+
#define READAHEAD_BUFFER_SIZE 1024
87+
#define WRITEBEHIND_BUFFER_SIZE 1024
8488

8589
static HANDLE SDLCALL windows_file_open(const char *filename, const char *mode)
8690
{
@@ -150,6 +154,41 @@ static HANDLE SDLCALL windows_file_open(const char *filename, const char *mode)
150154
return h;
151155
}
152156

157+
static bool windows_flush_write_buffer(IOStreamWindowsData *iodata,
158+
SDL_IOStatus *status)
159+
{
160+
if (iodata->write_pos == 0) {
161+
return true; // Nothing to flush
162+
}
163+
164+
// In append mode, seek to EOF before writing
165+
if (iodata->append) {
166+
LARGE_INTEGER windowsoffset;
167+
windowsoffset.QuadPart = 0;
168+
if (!SetFilePointerEx(iodata->h, windowsoffset,
169+
&windowsoffset, FILE_END)) {
170+
if (status) {
171+
*status = SDL_IO_STATUS_ERROR;
172+
}
173+
WIN_SetError("Error seeking in datastream");
174+
return false;
175+
}
176+
}
177+
178+
DWORD bytes;
179+
if (!WriteFile(iodata->h, iodata->write_data,
180+
(DWORD)iodata->write_pos, &bytes, NULL)) {
181+
if (status) {
182+
*status = SDL_IO_STATUS_ERROR;
183+
}
184+
WIN_SetError("Error writing to datastream");
185+
return false;
186+
}
187+
188+
iodata->write_pos = 0;
189+
return true;
190+
}
191+
153192
static Sint64 SDLCALL windows_file_size(void *userdata)
154193
{
155194
IOStreamWindowsData *iodata = (IOStreamWindowsData *) userdata;
@@ -168,6 +207,10 @@ static Sint64 SDLCALL windows_file_seek(void *userdata, Sint64 offset, SDL_IOWhe
168207
DWORD windowswhence;
169208
LARGE_INTEGER windowsoffset;
170209

210+
if (!windows_flush_write_buffer(iodata, NULL)) {
211+
return -1;
212+
}
213+
171214
// FIXME: We may be able to satisfy the seek within buffered data
172215
if ((whence == SDL_IO_SEEK_CUR) && (iodata->left)) {
173216
offset -= iodata->left;
@@ -204,6 +247,10 @@ static size_t SDLCALL windows_file_read(void *userdata, void *ptr, size_t size,
204247
size_t read_ahead;
205248
DWORD bytes;
206249

250+
if (!windows_flush_write_buffer(iodata, status)) {
251+
return 0;
252+
}
253+
207254
if (iodata->left > 0) {
208255
void *data = (char *)iodata->data +
209256
iodata->size -
@@ -270,42 +317,87 @@ static size_t SDLCALL windows_file_read(void *userdata, void *ptr, size_t size,
270317

271318
static size_t SDLCALL windows_file_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
272319
{
273-
IOStreamWindowsData *iodata = (IOStreamWindowsData *) userdata;
274-
DWORD bytes;
320+
IOStreamWindowsData *iodata = (IOStreamWindowsData *)userdata;
321+
const Uint8 *src = (const Uint8 *)ptr;
322+
size_t remaining = size;
323+
size_t total_written = 0;
275324

325+
if (!iodata->writable) {
326+
*status = SDL_IO_STATUS_READONLY;
327+
return 0;
328+
}
329+
330+
// Invalidate read-ahead buffer if it has data
276331
if (iodata->left) {
277-
if (!SetFilePointer(iodata->h, -(LONG)iodata->left, NULL, FILE_CURRENT)) {
332+
if (!SetFilePointer(iodata->h, -(LONG)iodata->left,
333+
NULL, FILE_CURRENT)) {
278334
*status = SDL_IO_STATUS_ERROR;
279335
WIN_SetError("Error seeking in datastream");
280336
return 0;
281337
}
282338
iodata->left = 0;
283339
}
284340

285-
// if in append mode, we must go to the EOF before write
286-
if (iodata->append) {
287-
LARGE_INTEGER windowsoffset;
288-
windowsoffset.QuadPart = 0;
289-
if (!SetFilePointerEx(iodata->h, windowsoffset, &windowsoffset, FILE_END)) {
341+
// For large writes, flush buffer and write directly
342+
if (size >= WRITEBEHIND_BUFFER_SIZE) {
343+
if (!windows_flush_write_buffer(iodata, status)) {
344+
return 0;
345+
}
346+
347+
// In append mode, seek to EOF before direct write
348+
if (iodata->append) {
349+
LARGE_INTEGER windowsoffset;
350+
windowsoffset.QuadPart = 0;
351+
if (!SetFilePointerEx(iodata->h, windowsoffset,
352+
&windowsoffset, FILE_END)) {
353+
*status = SDL_IO_STATUS_ERROR;
354+
WIN_SetError("Error seeking in datastream");
355+
return 0;
356+
}
357+
}
358+
359+
DWORD bytes;
360+
if (!WriteFile(iodata->h, ptr, (DWORD)size, &bytes, NULL)) {
290361
*status = SDL_IO_STATUS_ERROR;
291-
WIN_SetError("Error seeking in datastream");
362+
WIN_SetError("Error writing to datastream");
292363
return 0;
364+
} else if (bytes == 0 && size > 0) {
365+
*status = SDL_IO_STATUS_NOT_READY;
293366
}
367+
return bytes;
294368
}
295369

296-
if (!WriteFile(iodata->h, ptr, (DWORD)size, &bytes, NULL)) {
297-
*status = SDL_IO_STATUS_ERROR;
298-
WIN_SetError("Error writing to datastream");
299-
return 0;
300-
} else if (bytes == 0 && size > 0) {
301-
*status = SDL_IO_STATUS_NOT_READY;
370+
// Buffer small writes
371+
while (remaining > 0) {
372+
size_t space = WRITEBEHIND_BUFFER_SIZE - iodata->write_pos;
373+
size_t to_buffer = SDL_min(remaining, space);
374+
375+
SDL_memcpy((char *)iodata->write_data + iodata->write_pos,
376+
src, to_buffer);
377+
iodata->write_pos += to_buffer;
378+
src += to_buffer;
379+
remaining -= to_buffer;
380+
total_written += to_buffer;
381+
382+
if (iodata->write_pos == WRITEBEHIND_BUFFER_SIZE) {
383+
if (!windows_flush_write_buffer(iodata, status)) {
384+
return total_written;
385+
}
386+
}
302387
}
303-
return bytes;
388+
389+
return total_written;
304390
}
305391

306392
static bool SDLCALL windows_file_flush(void *userdata, SDL_IOStatus *status)
307393
{
308-
IOStreamWindowsData *iodata = (IOStreamWindowsData *) userdata;
394+
IOStreamWindowsData *iodata = (IOStreamWindowsData *)userdata;
395+
396+
if (!windows_flush_write_buffer(iodata, status)) {
397+
return false;
398+
}
399+
400+
// Sync to disk
309401
if (!FlushFileBuffers(iodata->h)) {
310402
return WIN_SetError("Error flushing datastream");
311403
}
@@ -314,16 +406,23 @@ static bool SDLCALL windows_file_flush(void *userdata, SDL_IOStatus *status)
314406

315407
static bool SDLCALL windows_file_close(void *userdata)
316408
{
317-
IOStreamWindowsData *iodata = (IOStreamWindowsData *) userdata;
409+
IOStreamWindowsData *iodata = (IOStreamWindowsData *)userdata;
410+
bool result = true;
411+
412+
if (!windows_flush_write_buffer(iodata, NULL)) {
413+
result = false;
414+
}
415+
318416
if (iodata->h != INVALID_HANDLE_VALUE) {
319417
if (iodata->autoclose) {
320418
CloseHandle(iodata->h);
321419
}
322-
iodata->h = INVALID_HANDLE_VALUE; // to be sure
420+
iodata->h = INVALID_HANDLE_VALUE;
323421
}
324422
SDL_free(iodata->data);
423+
SDL_free(iodata->write_data);
325424
SDL_free(iodata);
326-
return true;
425+
return result;
327426
}
328427

329428
SDL_IOStream *SDL_IOFromHandle(HANDLE handle, const char *mode, bool autoclose)
@@ -348,6 +447,9 @@ SDL_IOStream *SDL_IOFromHandle(HANDLE handle, const char *mode, bool autoclose)
348447
iface.close = windows_file_close;
349448

350449
iodata->h = handle;
450+
iodata->writable = (SDL_strchr(mode, 'w') != NULL) ||
451+
(SDL_strchr(mode, 'a') != NULL) ||
452+
(SDL_strchr(mode, '+') != NULL);
351453
iodata->append = (SDL_strchr(mode, 'a') != NULL);
352454
iodata->autoclose = autoclose;
353455

@@ -357,6 +459,13 @@ SDL_IOStream *SDL_IOFromHandle(HANDLE handle, const char *mode, bool autoclose)
357459
return NULL;
358460
}
359461

462+
iodata->write_data = (char *)SDL_malloc(WRITEBEHIND_BUFFER_SIZE);
463+
if (!iodata->write_data) {
464+
iface.close(iodata);
465+
return NULL;
466+
}
467+
iodata->write_pos = 0;
468+
360469
SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
361470
if (!iostr) {
362471
iface.close(iodata);

0 commit comments

Comments
 (0)