stream: STREAM_TYPE_FILE will now read file upfront

No more having to chunk read - if ~stream_init_file~ is used, the
constructor slurps the entire file into the cache.  This pays up front
for a bunch of checks essentially.

~stream_init_pipe~ should be used for chunked reading.
This commit is contained in:
2026-02-09 08:10:44 +00:00
committed by oreodave
parent 6a54c54bfb
commit f56a59ff7a
3 changed files with 24 additions and 16 deletions

View File

@@ -53,6 +53,8 @@ typedef struct
stream_err_t stream_init_string(stream_t *, const char *, sv_t); stream_err_t stream_init_string(stream_t *, const char *, sv_t);
stream_err_t stream_init_pipe(stream_t *, const char *, FILE *); stream_err_t stream_init_pipe(stream_t *, const char *, FILE *);
// NOTE: stream_init_file will attempt to read all content from the FILE
// descriptor. Use with caution.
stream_err_t stream_init_file(stream_t *, const char *, FILE *); stream_err_t stream_init_file(stream_t *, const char *, FILE *);
void stream_stop(stream_t *); void stream_stop(stream_t *);

View File

@@ -84,7 +84,18 @@ stream_err_t stream_init_file(stream_t *stream, const char *name, FILE *pipe)
stream->type = STREAM_TYPE_FILE; stream->type = STREAM_TYPE_FILE;
stream->name = name; stream->name = name;
stream->pipe.file = pipe; stream->pipe.file = NULL;
// NOTE: We're reading all the data from the file descriptor now.
fseek(pipe, 0, SEEK_END);
long size = ftell(pipe);
fseek(pipe, 0, SEEK_SET);
vec_ensure_free(&stream->pipe.cache, size);
int read = fread(vec_data(&stream->pipe.cache), 1, size, pipe);
// These must be equivalent for this function.
assert(read == size);
stream->pipe.cache.size += size;
return STREAM_ERR_OK; return STREAM_ERR_OK;
} }
@@ -99,11 +110,8 @@ void stream_stop(stream_t *stream)
free(stream->string.data); free(stream->string.data);
break; break;
case STREAM_TYPE_FILE: case STREAM_TYPE_FILE:
// ensure we reset the FILE pointer to the start
fseek(stream->pipe.file, 0, SEEK_SET);
// fallthrough
case STREAM_TYPE_PIPE: case STREAM_TYPE_PIPE:
// Must cleanup vector // Must cleanup caching vector
vec_free(&stream->pipe.cache); vec_free(&stream->pipe.cache);
break; break;
} }
@@ -131,10 +139,10 @@ bool stream_eoc(stream_t *stream)
assert(stream); assert(stream);
switch (stream->type) switch (stream->type)
{ {
case STREAM_TYPE_STRING:
return stream->position >= stream->string.size;
case STREAM_TYPE_PIPE:
case STREAM_TYPE_FILE: case STREAM_TYPE_FILE:
case STREAM_TYPE_STRING:
return stream->position >= stream_size(stream);
case STREAM_TYPE_PIPE:
return feof(stream->pipe.file) && return feof(stream->pipe.file) &&
stream->position >= stream->pipe.cache.size; stream->position >= stream->pipe.cache.size;
default: default:
@@ -148,12 +156,11 @@ bool stream_chunk(stream_t *stream)
assert(stream); assert(stream);
switch (stream->type) switch (stream->type)
{ {
case STREAM_TYPE_FILE:
case STREAM_TYPE_STRING: case STREAM_TYPE_STRING:
// nothing to chunk, hence false // nothing to chunk, hence false
return false; return false;
case STREAM_TYPE_PIPE: case STREAM_TYPE_PIPE:
// fallthrough
case STREAM_TYPE_FILE:
{ {
if (feof(stream->pipe.file)) if (feof(stream->pipe.file))
// We can't read anymore. End of the line // We can't read anymore. End of the line
@@ -194,10 +201,10 @@ char stream_peek(stream_t *stream)
switch (stream->type) switch (stream->type)
{ {
case STREAM_TYPE_STRING:
return stream->string.data[stream->position];
case STREAM_TYPE_PIPE:
case STREAM_TYPE_FILE: case STREAM_TYPE_FILE:
case STREAM_TYPE_STRING:
return stream_sv(stream).data[0];
case STREAM_TYPE_PIPE:
{ {
// Cached already? We are done. // Cached already? We are done.
if (stream->position < stream->pipe.cache.size) if (stream->position < stream->pipe.cache.size)
@@ -306,8 +313,8 @@ sv_t stream_sv_abs(stream_t *stream)
case STREAM_TYPE_STRING: case STREAM_TYPE_STRING:
sv = stream->string; sv = stream->string;
break; break;
case STREAM_TYPE_PIPE:
case STREAM_TYPE_FILE: case STREAM_TYPE_FILE:
case STREAM_TYPE_PIPE:
sv = SV((char *)vec_data(&stream->pipe.cache), stream_size(stream)); sv = SV((char *)vec_data(&stream->pipe.cache), stream_size(stream));
break; break;
default: default:

View File

@@ -113,9 +113,8 @@ void stream_test_file(void)
TEST(err == STREAM_ERR_OK, "Expected initialisating to be okay: %s", TEST(err == STREAM_ERR_OK, "Expected initialisating to be okay: %s",
stream_err_to_cstr(err)); stream_err_to_cstr(err));
} }
TEST(stream_size(&stream) == 0, "Stream doesn't read on init: size = %lu",
stream_size(&stream));
TEST(!stream_eoc(&stream), "Stream should not be at the EoC from init."); TEST(!stream_eoc(&stream), "Stream should not be at the EoC from init.");
stream_stop(&stream);
} }
// try to initialise the stream again but against a nonexistent file - we're // try to initialise the stream again but against a nonexistent file - we're