Compare commits
13 Commits
a6d3d861b7
...
fe727d75e4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe727d75e4 | ||
|
|
06a4eafbb9 | ||
|
|
fc5a6eb8fb | ||
|
|
5c7fb0fabd | ||
|
|
f164427b47 | ||
|
|
2238f348e1 | ||
|
|
f56a59ff7a | ||
|
|
6a54c54bfb | ||
|
|
2855536bdd | ||
|
|
8426f04734 | ||
|
|
459f434e5d | ||
|
|
9ee8955908 | ||
|
|
477abc9aa1 |
@@ -53,8 +53,11 @@ 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_reset(stream_t *);
|
||||||
|
void stream_free(stream_t *);
|
||||||
|
|
||||||
// End of Content (i.e. we've consumed all cached content/file)
|
// End of Content (i.e. we've consumed all cached content/file)
|
||||||
bool stream_eoc(stream_t *);
|
bool stream_eoc(stream_t *);
|
||||||
@@ -70,6 +73,12 @@ u64 stream_seek(stream_t *, i64);
|
|||||||
u64 stream_seek_forward(stream_t *, u64);
|
u64 stream_seek_forward(stream_t *, u64);
|
||||||
u64 stream_seek_backward(stream_t *, u64);
|
u64 stream_seek_backward(stream_t *, u64);
|
||||||
|
|
||||||
|
// Return a string view to stream data relative to the current position of the
|
||||||
|
// stream
|
||||||
|
sv_t stream_sv(stream_t *);
|
||||||
|
// Return a string view to data from the start of the stream
|
||||||
|
sv_t stream_sv_abs(stream_t *);
|
||||||
|
|
||||||
// Return a relative substring of a given size
|
// Return a relative substring of a given size
|
||||||
sv_t stream_substr(stream_t *, u64);
|
sv_t stream_substr(stream_t *, u64);
|
||||||
// Return an absolute substring at given index and of given size.
|
// Return an absolute substring at given index and of given size.
|
||||||
|
|||||||
@@ -17,13 +17,10 @@ const char *read_err_to_cstr(read_err_t err)
|
|||||||
{
|
{
|
||||||
case READ_ERR_OK:
|
case READ_ERR_OK:
|
||||||
return "OK";
|
return "OK";
|
||||||
break;
|
|
||||||
case READ_ERR_EOF:
|
case READ_ERR_EOF:
|
||||||
return "EOF";
|
return "EOF";
|
||||||
break;
|
|
||||||
case READ_ERR_UNKNOWN_CHAR:
|
case READ_ERR_UNKNOWN_CHAR:
|
||||||
return "UNKNOWN_CHAR";
|
return "UNKNOWN_CHAR";
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
FAIL("Unreachable");
|
FAIL("Unreachable");
|
||||||
}
|
}
|
||||||
|
|||||||
313
src/stream.c
313
src/stream.c
@@ -11,6 +11,8 @@
|
|||||||
|
|
||||||
#include <alisp/base.h>
|
#include <alisp/base.h>
|
||||||
#include <alisp/stream.h>
|
#include <alisp/stream.h>
|
||||||
|
#include <alisp/sv.h>
|
||||||
|
#include <alisp/vec.h>
|
||||||
|
|
||||||
const char *stream_err_to_cstr(stream_err_t err)
|
const char *stream_err_to_cstr(stream_err_t err)
|
||||||
{
|
{
|
||||||
@@ -18,19 +20,14 @@ const char *stream_err_to_cstr(stream_err_t err)
|
|||||||
{
|
{
|
||||||
case STREAM_ERR_INVALID_PTR:
|
case STREAM_ERR_INVALID_PTR:
|
||||||
return "INVALID PTR";
|
return "INVALID PTR";
|
||||||
break;
|
|
||||||
case STREAM_ERR_FILE_NONEXISTENT:
|
case STREAM_ERR_FILE_NONEXISTENT:
|
||||||
return "FILE NONEXISTENT";
|
return "FILE NONEXISTENT";
|
||||||
break;
|
|
||||||
case STREAM_ERR_FILE_READ:
|
case STREAM_ERR_FILE_READ:
|
||||||
return "FILE READ";
|
return "FILE READ";
|
||||||
break;
|
|
||||||
case STREAM_ERR_PIPE_NONEXISTENT:
|
case STREAM_ERR_PIPE_NONEXISTENT:
|
||||||
return "PIPE NONEXISTENT";
|
return "PIPE NONEXISTENT";
|
||||||
break;
|
|
||||||
case STREAM_ERR_OK:
|
case STREAM_ERR_OK:
|
||||||
return "OK";
|
return "OK";
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
FAIL("Unreachable");
|
FAIL("Unreachable");
|
||||||
}
|
}
|
||||||
@@ -65,8 +62,6 @@ stream_err_t stream_init_pipe(stream_t *stream, const char *name, FILE *pipe)
|
|||||||
stream->name = name;
|
stream->name = name;
|
||||||
stream->pipe.file = pipe;
|
stream->pipe.file = pipe;
|
||||||
|
|
||||||
vec_init(&stream->pipe.cache, STREAM_DEFAULT_CHUNK);
|
|
||||||
|
|
||||||
return STREAM_ERR_OK;
|
return STREAM_ERR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,12 +77,30 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void stream_stop(stream_t *stream)
|
void stream_reset(stream_t *stream)
|
||||||
|
{
|
||||||
|
if (!stream)
|
||||||
|
return;
|
||||||
|
stream->position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void stream_free(stream_t *stream)
|
||||||
{
|
{
|
||||||
if (!stream)
|
if (!stream)
|
||||||
return;
|
return;
|
||||||
@@ -97,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;
|
||||||
}
|
}
|
||||||
@@ -124,31 +134,15 @@ u64 stream_size(stream_t *stream)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool stream_eos(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:
|
|
||||||
return feof(stream->pipe.file);
|
|
||||||
default:
|
|
||||||
FAIL("Unreachable");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool stream_eoc(stream_t *stream)
|
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:
|
||||||
@@ -160,23 +154,20 @@ bool stream_eoc(stream_t *stream)
|
|||||||
bool stream_chunk(stream_t *stream)
|
bool stream_chunk(stream_t *stream)
|
||||||
{
|
{
|
||||||
assert(stream);
|
assert(stream);
|
||||||
u64 to_read = STREAM_DEFAULT_CHUNK;
|
|
||||||
switch (stream->type)
|
switch (stream->type)
|
||||||
{
|
{
|
||||||
case STREAM_TYPE_STRING:
|
|
||||||
// vacuously true
|
|
||||||
return true;
|
|
||||||
case STREAM_TYPE_PIPE:
|
|
||||||
to_read = 1;
|
|
||||||
// fallthrough
|
|
||||||
case STREAM_TYPE_FILE:
|
case STREAM_TYPE_FILE:
|
||||||
|
case STREAM_TYPE_STRING:
|
||||||
|
// nothing to chunk, hence false
|
||||||
|
return false;
|
||||||
|
case STREAM_TYPE_PIPE:
|
||||||
{
|
{
|
||||||
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
|
||||||
return false;
|
return false;
|
||||||
vec_ensure_free(&stream->pipe.cache, to_read);
|
vec_ensure_free(&stream->pipe.cache, STREAM_DEFAULT_CHUNK);
|
||||||
int read = fread(vec_data(&stream->pipe.cache) + stream->pipe.cache.size, 1,
|
int read = fread(vec_data(&stream->pipe.cache) + stream->pipe.cache.size, 1,
|
||||||
to_read, stream->pipe.file);
|
STREAM_DEFAULT_CHUNK, stream->pipe.file);
|
||||||
|
|
||||||
// If we read something it's a good thing
|
// If we read something it's a good thing
|
||||||
if (read > 0)
|
if (read > 0)
|
||||||
@@ -185,8 +176,10 @@ bool stream_chunk(stream_t *stream)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
FAIL("Unreachable");
|
FAIL("Unreachable");
|
||||||
return 0;
|
return 0;
|
||||||
@@ -202,24 +195,20 @@ char stream_next(stream_t *stream)
|
|||||||
|
|
||||||
char stream_peek(stream_t *stream)
|
char stream_peek(stream_t *stream)
|
||||||
{
|
{
|
||||||
// If we've reached end of stream, and end of content, there's really nothing
|
// End of the line? We're done.
|
||||||
// to check here.
|
|
||||||
if (stream_eoc(stream))
|
if (stream_eoc(stream))
|
||||||
return '\0';
|
return '\0';
|
||||||
|
|
||||||
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)
|
||||||
{
|
return stream_sv(stream).data[0];
|
||||||
const char *const str = (char *)vec_data(&stream->pipe.cache);
|
|
||||||
return str[stream->position];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to read chunks in till we've reached it or we're at the end of the
|
// Try to read chunks in till we've reached it or we're at the end of the
|
||||||
// file.
|
// file.
|
||||||
@@ -231,7 +220,7 @@ char stream_peek(stream_t *stream)
|
|||||||
// Same principle as the stream_eos(stream) check.
|
// Same principle as the stream_eos(stream) check.
|
||||||
if (stream->position >= stream->pipe.cache.size)
|
if (stream->position >= stream->pipe.cache.size)
|
||||||
return '\0';
|
return '\0';
|
||||||
return ((char *)vec_data(&stream->pipe.cache))[stream->position];
|
return stream_sv(stream).data[0];
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
FAIL("Unreachable");
|
FAIL("Unreachable");
|
||||||
@@ -246,36 +235,38 @@ u64 stream_seek(stream_t *stream, i64 offset)
|
|||||||
else if (offset > 0)
|
else if (offset > 0)
|
||||||
return stream_seek_forward(stream, offset);
|
return stream_seek_forward(stream, offset);
|
||||||
else
|
else
|
||||||
// vacuously successful
|
return 0;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 stream_seek_forward(stream_t *stream, u64 offset)
|
u64 stream_seek_forward(stream_t *stream, u64 offset)
|
||||||
{
|
{
|
||||||
if (stream_eoc(stream))
|
if (stream_eoc(stream))
|
||||||
return 0;
|
return 0;
|
||||||
|
else if (stream->position + offset < stream_size(stream))
|
||||||
|
{
|
||||||
|
stream->position += offset;
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: The only case not caught by the above branches is exact-to-end
|
||||||
|
// movement (i.e. offset puts us exactly at the end of the stream) or movement
|
||||||
|
// beyond what we've cached.
|
||||||
switch (stream->type)
|
switch (stream->type)
|
||||||
{
|
{
|
||||||
|
case STREAM_TYPE_FILE:
|
||||||
case STREAM_TYPE_STRING:
|
case STREAM_TYPE_STRING:
|
||||||
{
|
{
|
||||||
if (stream->position + offset >= stream->string.size)
|
// Clamp in the case of FILE and STRING movement since they're already
|
||||||
return 0;
|
// fully cached.
|
||||||
|
if (stream->position + offset >= stream_size(stream))
|
||||||
|
offset = stream_size(stream) - stream->position;
|
||||||
stream->position += offset;
|
stream->position += offset;
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
case STREAM_TYPE_PIPE:
|
case STREAM_TYPE_PIPE:
|
||||||
case STREAM_TYPE_FILE:
|
|
||||||
{
|
{
|
||||||
// Similar principle as stream_peek really...
|
// Pipes may have data remaining that hasn't been cached - we need to chunk
|
||||||
|
// before we can be sure to stop.
|
||||||
// Cached already? We are done.
|
|
||||||
if (stream->position + offset < stream->pipe.cache.size)
|
|
||||||
{
|
|
||||||
stream->position += offset;
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to read chunks in till we've reached it or we're at the end of the
|
// Try to read chunks in till we've reached it or we're at the end of the
|
||||||
// file.
|
// file.
|
||||||
@@ -284,32 +275,58 @@ u64 stream_seek_forward(stream_t *stream, u64 offset)
|
|||||||
read_chunk = stream_chunk(stream))
|
read_chunk = stream_chunk(stream))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Same principle as the stream_eoc(stream) check.
|
// NOTE: We've read everything from the pipe, but the offset is greater. We
|
||||||
if (stream->position + offset > stream->pipe.cache.size)
|
// must clamp here.
|
||||||
{
|
if (stream->position + offset > stream_size(stream))
|
||||||
offset = stream->pipe.cache.size - stream->position;
|
offset = stream_size(stream) - stream->position;
|
||||||
}
|
|
||||||
stream->position += offset;
|
stream->position += offset;
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
FAIL("Unreachable");
|
FAIL("Unreachable");
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 stream_seek_backward(stream_t *stream, u64 offset)
|
u64 stream_seek_backward(stream_t *stream, u64 offset)
|
||||||
{
|
{
|
||||||
assert(stream);
|
if (!stream)
|
||||||
|
return 0;
|
||||||
if (stream->position < offset)
|
if (stream->position < offset)
|
||||||
{
|
|
||||||
offset = stream->position;
|
offset = stream->position;
|
||||||
}
|
|
||||||
|
|
||||||
stream->position -= offset;
|
stream->position -= offset;
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sv_t stream_sv(stream_t *stream)
|
||||||
|
{
|
||||||
|
sv_t sv = stream_sv_abs(stream);
|
||||||
|
return sv_chop_left(sv, stream->position);
|
||||||
|
}
|
||||||
|
|
||||||
|
sv_t stream_sv_abs(stream_t *stream)
|
||||||
|
{
|
||||||
|
if (!stream)
|
||||||
|
return SV(NULL, 0);
|
||||||
|
sv_t sv = {0};
|
||||||
|
switch (stream->type)
|
||||||
|
{
|
||||||
|
case STREAM_TYPE_STRING:
|
||||||
|
sv = stream->string;
|
||||||
|
break;
|
||||||
|
case STREAM_TYPE_FILE:
|
||||||
|
case STREAM_TYPE_PIPE:
|
||||||
|
sv = SV((char *)vec_data(&stream->pipe.cache), stream_size(stream));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
FAIL("Unreachable");
|
||||||
|
return SV(NULL, 0);
|
||||||
|
}
|
||||||
|
return sv;
|
||||||
|
}
|
||||||
|
|
||||||
sv_t stream_substr(stream_t *stream, u64 size)
|
sv_t stream_substr(stream_t *stream, u64 size)
|
||||||
{
|
{
|
||||||
if (stream_eoc(stream))
|
if (stream_eoc(stream))
|
||||||
@@ -324,104 +341,130 @@ sv_t stream_substr(stream_t *stream, u64 size)
|
|||||||
if (successful != size)
|
if (successful != size)
|
||||||
return SV(NULL, 0);
|
return SV(NULL, 0);
|
||||||
|
|
||||||
char *ptr = NULL;
|
sv_t sv = stream_sv(stream);
|
||||||
switch (stream->type)
|
sv = sv_truncate(sv, size);
|
||||||
{
|
return sv;
|
||||||
case STREAM_TYPE_STRING:
|
|
||||||
ptr = stream->string.data;
|
|
||||||
break;
|
|
||||||
case STREAM_TYPE_PIPE:
|
|
||||||
case STREAM_TYPE_FILE:
|
|
||||||
ptr = (char *)vec_data(&stream->pipe.cache);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
FAIL("Unreachable");
|
|
||||||
return SV(NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SV(ptr + stream->position, size);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sv_t stream_substr_abs(stream_t *stream, u64 index, u64 size)
|
sv_t stream_substr_abs(stream_t *stream, u64 index, u64 size)
|
||||||
{
|
{
|
||||||
switch (stream->type)
|
switch (stream->type)
|
||||||
{
|
{
|
||||||
case STREAM_TYPE_STRING:
|
|
||||||
if (index + size <= stream_size(stream))
|
|
||||||
return SV(stream->string.data + index, size);
|
|
||||||
return SV(NULL, 0);
|
|
||||||
case STREAM_TYPE_PIPE:
|
case STREAM_TYPE_PIPE:
|
||||||
case STREAM_TYPE_FILE:
|
|
||||||
{
|
{
|
||||||
if (index + size <= stream_size(stream))
|
if (index + size > stream_size(stream))
|
||||||
return SV((char *)vec_data(&stream->pipe.cache) + index, size);
|
{
|
||||||
// (index + size > stream_size(stream)) => try reading chunks
|
// => try reading chunks till either we drop or we have enough space
|
||||||
for (bool read_chunk = stream_chunk(stream);
|
for (bool read_chunk = stream_chunk(stream);
|
||||||
read_chunk && index + size >= stream->pipe.cache.size;
|
read_chunk && index + size >= stream->pipe.cache.size;
|
||||||
read_chunk = stream_chunk(stream))
|
read_chunk = stream_chunk(stream))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (index + size > stream_size(stream))
|
|
||||||
return SV(NULL, 0);
|
|
||||||
return SV((char *)vec_data(&stream->pipe.cache) + index, size);
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case STREAM_TYPE_STRING:
|
||||||
|
case STREAM_TYPE_FILE:
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
assert("Unreachable");
|
FAIL("Unreachable");
|
||||||
return SV(NULL, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sv_t sv = stream_sv_abs(stream);
|
||||||
|
sv = sv_chop_left(sv, index);
|
||||||
|
sv = sv_truncate(sv, size);
|
||||||
|
return sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
sv_t stream_till(stream_t *stream, const char *str)
|
sv_t stream_till(stream_t *stream, const char *str)
|
||||||
{
|
{
|
||||||
if (stream_eoc(stream))
|
if (stream_eoc(stream))
|
||||||
return SV(NULL, 0);
|
return SV(NULL, 0);
|
||||||
u64 current_position = stream->position;
|
|
||||||
for (char c = stream_peek(stream); c != '\0' && strchr(str, c) == NULL;
|
sv_t cur_sv = stream_sv(stream);
|
||||||
c = stream_next(stream))
|
sv_t sv = sv_till(cur_sv, str);
|
||||||
continue;
|
stream_seek_forward(stream, sv.size);
|
||||||
u64 size = stream->position - current_position;
|
switch (stream->type)
|
||||||
if (size == 0)
|
{
|
||||||
return SV(NULL, 0);
|
case STREAM_TYPE_FILE:
|
||||||
return stream_substr_abs(stream, current_position, size - 1);
|
case STREAM_TYPE_STRING:
|
||||||
|
return sv;
|
||||||
|
case STREAM_TYPE_PIPE:
|
||||||
|
{
|
||||||
|
if (cur_sv.size > sv.size)
|
||||||
|
return sv;
|
||||||
|
|
||||||
|
// Build a substring by hand while chunking data.
|
||||||
|
u64 index, size;
|
||||||
|
for (index = stream->position - sv.size, size = sv.size;
|
||||||
|
cur_sv.size == sv.size; size += sv.size)
|
||||||
|
{
|
||||||
|
cur_sv = stream_sv(stream);
|
||||||
|
sv = sv_till(cur_sv, str);
|
||||||
|
stream_seek_forward(stream, sv.size);
|
||||||
|
if (sv.size == 0)
|
||||||
|
// Must stop if this has happened; nothing else to pick up.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return stream_substr_abs(stream, index, size);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
FAIL("Unreachable");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sv_t stream_while(stream_t *stream, const char *str)
|
sv_t stream_while(stream_t *stream, const char *str)
|
||||||
{
|
{
|
||||||
if (stream_eoc(stream))
|
if (stream_eoc(stream))
|
||||||
return SV(NULL, 0);
|
return SV(NULL, 0);
|
||||||
u64 current_position = stream->position;
|
|
||||||
for (char c = stream_peek(stream); c != '\0' && strchr(str, c);
|
sv_t cur_sv = stream_sv(stream);
|
||||||
c = stream_next(stream))
|
sv_t sv = sv_while(cur_sv, str);
|
||||||
continue;
|
stream_seek_forward(stream, sv.size);
|
||||||
u64 size = stream->position - current_position;
|
switch (stream->type)
|
||||||
if (size == 0)
|
{
|
||||||
return SV(NULL, 0);
|
case STREAM_TYPE_FILE:
|
||||||
return stream_substr_abs(stream, current_position, size - 1);
|
case STREAM_TYPE_STRING:
|
||||||
|
return sv;
|
||||||
|
case STREAM_TYPE_PIPE:
|
||||||
|
{
|
||||||
|
if (cur_sv.size > sv.size)
|
||||||
|
return sv;
|
||||||
|
|
||||||
|
// Build a substring by hand while chunking data.
|
||||||
|
u64 index, size;
|
||||||
|
for (index = stream->position - sv.size, size = sv.size;
|
||||||
|
cur_sv.size == sv.size; size += sv.size)
|
||||||
|
{
|
||||||
|
cur_sv = stream_sv(stream);
|
||||||
|
sv = sv_while(cur_sv, str);
|
||||||
|
stream_seek_forward(stream, sv.size);
|
||||||
|
if (sv.size == 0)
|
||||||
|
// Must stop if this has happened; nothing else to pick up.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return stream_substr_abs(stream, index, size);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
FAIL("Unreachable");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void stream_line_col(stream_t *stream, u64 *line, u64 *col)
|
void stream_line_col(stream_t *stream, u64 *line, u64 *col)
|
||||||
{
|
{
|
||||||
if (!stream || !line || !col)
|
if (!stream || !line || !col)
|
||||||
return;
|
return;
|
||||||
// Go through the cache, byte by byte.
|
|
||||||
char *cache = NULL;
|
// Generate a string view from the stream of exactly the content /upto/
|
||||||
u64 size = 0;
|
// stream.postion.
|
||||||
if (stream->type == STREAM_TYPE_STRING)
|
sv_t sv = stream_sv_abs(stream);
|
||||||
{
|
sv = sv_truncate(sv, stream->position + 1);
|
||||||
cache = stream->string.data;
|
|
||||||
size = stream->string.size;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cache = (char *)vec_data(&stream->pipe.cache);
|
|
||||||
size = stream->pipe.cache.size;
|
|
||||||
}
|
|
||||||
|
|
||||||
*line = 1;
|
*line = 1;
|
||||||
*col = 0;
|
*col = 0;
|
||||||
for (u64 i = 0; i < size; ++i)
|
// TODO: Could this be faster? Does it matter?
|
||||||
|
for (u64 i = 0; i < sv.size; ++i)
|
||||||
{
|
{
|
||||||
char c = cache[i];
|
char c = sv.data[i];
|
||||||
if (c == '\n')
|
if (c == '\n')
|
||||||
{
|
{
|
||||||
*line += 1;
|
*line += 1;
|
||||||
|
|||||||
4
src/sv.c
4
src/sv.c
@@ -60,7 +60,7 @@ sv_t sv_till(sv_t sv, const char *reject)
|
|||||||
|
|
||||||
if (offset == sv.size)
|
if (offset == sv.size)
|
||||||
return sv;
|
return sv;
|
||||||
return sv_chop_right(sv, sv.size - offset);
|
return sv_truncate(sv, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
sv_t sv_while(sv_t sv, const char *accept)
|
sv_t sv_while(sv_t sv, const char *accept)
|
||||||
@@ -75,7 +75,7 @@ sv_t sv_while(sv_t sv, const char *accept)
|
|||||||
|
|
||||||
if (offset == sv.size)
|
if (offset == sv.size)
|
||||||
return sv;
|
return sv;
|
||||||
return sv_chop_right(sv, sv.size - offset);
|
return sv_truncate(sv, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Copyright (C) 2025, 2026 Aryadev Chavali
|
/* Copyright (C) 2025, 2026 Aryadev Chavali
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ void stream_test_string(void)
|
|||||||
test_strings[i].size);
|
test_strings[i].size);
|
||||||
TEST(!stream_eoc(&stream), "Not end of content already");
|
TEST(!stream_eoc(&stream), "Not end of content already");
|
||||||
|
|
||||||
stream_stop(&stream);
|
stream_free(&stream);
|
||||||
TEST(strncmp(copy.data, test_strings[i].data, copy.size) == 0,
|
TEST(strncmp(copy.data, test_strings[i].data, copy.size) == 0,
|
||||||
"Freeing a stream does not free the underlying memory it was derived "
|
"Freeing a stream does not free the underlying memory it was derived "
|
||||||
"from");
|
"from");
|
||||||
@@ -96,7 +96,7 @@ void stream_test_string(void)
|
|||||||
stream_err_to_cstr(err));
|
stream_err_to_cstr(err));
|
||||||
TEST(stream_size(&stream) == 0, "NULL stream size is 0");
|
TEST(stream_size(&stream) == 0, "NULL stream size is 0");
|
||||||
TEST(stream_eoc(&stream), "NULL stream is always at end of content");
|
TEST(stream_eoc(&stream), "NULL stream is always at end of content");
|
||||||
stream_stop(&stream);
|
stream_free(&stream);
|
||||||
|
|
||||||
TEST_END();
|
TEST_END();
|
||||||
}
|
}
|
||||||
@@ -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_free(&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
|
||||||
@@ -164,7 +163,7 @@ void stream_test_peek_next(void)
|
|||||||
"(%c)",
|
"(%c)",
|
||||||
c3, c2);
|
c3, c2);
|
||||||
|
|
||||||
stream_stop(&stream);
|
stream_free(&stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invalid streams
|
// Invalid streams
|
||||||
@@ -181,7 +180,7 @@ void stream_test_peek_next(void)
|
|||||||
"Next on an invalid stream should not affect position (%lu -> %lu)",
|
"Next on an invalid stream should not affect position (%lu -> %lu)",
|
||||||
old_position, stream.position);
|
old_position, stream.position);
|
||||||
|
|
||||||
stream_stop(&stream);
|
stream_free(&stream);
|
||||||
}
|
}
|
||||||
TEST_END();
|
TEST_END();
|
||||||
}
|
}
|
||||||
@@ -210,7 +209,7 @@ void stream_test_seek(void)
|
|||||||
"stream (%lu -> %lu)",
|
"stream (%lu -> %lu)",
|
||||||
old_position, stream.position);
|
old_position, stream.position);
|
||||||
|
|
||||||
stream_stop(&stream);
|
stream_free(&stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid streams
|
// Valid streams
|
||||||
@@ -268,7 +267,7 @@ void stream_test_seek(void)
|
|||||||
"above.",
|
"above.",
|
||||||
stream.position);
|
stream.position);
|
||||||
|
|
||||||
stream_stop(&stream);
|
stream_free(&stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_END();
|
TEST_END();
|
||||||
@@ -302,7 +301,7 @@ void stream_test_substr(void)
|
|||||||
position, size);
|
position, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_stop(&stream);
|
stream_free(&stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Taking substrings of valid streams
|
// Taking substrings of valid streams
|
||||||
@@ -377,7 +376,7 @@ void stream_test_substr(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stream_stop(&stream);
|
stream_free(&stream);
|
||||||
}
|
}
|
||||||
TEST_END();
|
TEST_END();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user