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_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 *);
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->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;
}
@@ -99,11 +110,8 @@ void stream_stop(stream_t *stream)
free(stream->string.data);
break;
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:
// Must cleanup vector
// Must cleanup caching vector
vec_free(&stream->pipe.cache);
break;
}
@@ -131,10 +139,10 @@ bool stream_eoc(stream_t *stream)
assert(stream);
switch (stream->type)
{
case STREAM_TYPE_STRING:
return stream->position >= stream->string.size;
case STREAM_TYPE_PIPE:
case STREAM_TYPE_FILE:
case STREAM_TYPE_STRING:
return stream->position >= stream_size(stream);
case STREAM_TYPE_PIPE:
return feof(stream->pipe.file) &&
stream->position >= stream->pipe.cache.size;
default:
@@ -148,12 +156,11 @@ bool stream_chunk(stream_t *stream)
assert(stream);
switch (stream->type)
{
case STREAM_TYPE_FILE:
case STREAM_TYPE_STRING:
// nothing to chunk, hence false
return false;
case STREAM_TYPE_PIPE:
// fallthrough
case STREAM_TYPE_FILE:
{
if (feof(stream->pipe.file))
// We can't read anymore. End of the line
@@ -194,10 +201,10 @@ char stream_peek(stream_t *stream)
switch (stream->type)
{
case STREAM_TYPE_STRING:
return stream->string.data[stream->position];
case STREAM_TYPE_PIPE:
case STREAM_TYPE_FILE:
case STREAM_TYPE_STRING:
return stream_sv(stream).data[0];
case STREAM_TYPE_PIPE:
{
// Cached already? We are done.
if (stream->position < stream->pipe.cache.size)
@@ -306,8 +313,8 @@ sv_t stream_sv_abs(stream_t *stream)
case STREAM_TYPE_STRING:
sv = stream->string;
break;
case STREAM_TYPE_PIPE:
case STREAM_TYPE_FILE:
case STREAM_TYPE_PIPE:
sv = SV((char *)vec_data(&stream->pipe.cache), stream_size(stream));
break;
default:

View File

@@ -113,9 +113,8 @@ void stream_test_file(void)
TEST(err == STREAM_ERR_OK, "Expected initialisating to be okay: %s",
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.");
stream_stop(&stream);
}
// try to initialise the stream again but against a nonexistent file - we're