From cc56a2ee2b5703f9ea5ac63a86870af188845c30 Mon Sep 17 00:00:00 2001 From: Aryadev Chavali Date: Thu, 28 Aug 2025 22:55:41 +0100 Subject: Still got some failures, but a basic stream implementation Need to fix what's going on with the example in main.c using stdin. --- alisp.h | 59 ++++++++++++ build.sh | 2 +- main.c | 17 +++- stream.c | 296 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ symtable.c | 2 +- 5 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 stream.c diff --git a/alisp.h b/alisp.h index e9694d9..dba0485 100644 --- a/alisp.h +++ b/alisp.h @@ -17,14 +17,18 @@ #include #include +#include #include #include +#include /// The bare fucking minimum #define MAX(A, B) ((A) > (B) ? (A) : (B)) #define MIN(A, B) ((A) < (B) ? (A) : (B)) #define ARRSIZE(A) (sizeof(A) / sizeof((A)[0])) #define NTH_BYTE(X, N) (((X) >> (8 * N)) & ((1 << 8) - 1)) +#define FAIL(MSG) assert(false && "FAIL: " #MSG) +#define TODO(MSG) assert(false && "TODO: " #MSG) typedef uint8_t u8; typedef uint16_t u16; @@ -91,6 +95,61 @@ void sym_table_init(sym_table_t *); char *sym_table_find(sym_table_t *, sv_t); void sym_table_cleanup(sym_table_t *); +/// Streams +typedef enum +{ + STREAM_TYPE_STRING, + STREAM_TYPE_FILE, +} stream_type_t; + +typedef enum +{ + STREAM_ERR_INVALID_PTR = -4, + STREAM_ERR_FILE_NONEXISTENT = -3, + STREAM_ERR_FILE_READ = -2, + STREAM_ERR_PIPE_NONEXISTENT = -1, + STREAM_ERR_OK = 0, +} stream_err_t; + +typedef struct +{ + vec_t cache; + FILE *file; +} stream_pipe_t; + +typedef struct +{ + stream_type_t type; + char *name; + u64 position; + union + { + sv_t string; + stream_pipe_t pipe; + }; +} stream_t; + +#define STREAM_DEFAULT_CHUNK 64 + +stream_err_t stream_init_string(stream_t *, char *, sv_t); +stream_err_t stream_init_file(stream_t *, char *, FILE *); +void stream_stop(stream_t *); + +// end of stream +bool stream_eos(stream_t *); +// end of cache +bool stream_eoc(stream_t *); +u64 stream_size(stream_t *); + +bool stream_chunk(stream_t *); +char stream_next(stream_t *); +char stream_peek(stream_t *); +bool stream_seek(stream_t *, i64); +bool stream_seek_forward(stream_t *, u64); +bool stream_seek_backward(stream_t *, u64); +sv_t stream_substr(stream_t *, u64); +sv_t stream_substr_abs(stream_t *, u64, u64); + /// Basic defintions for a Lisp #define NIL 0 diff --git a/build.sh b/build.sh index 544f623..6e42066 100644 --- a/build.sh +++ b/build.sh @@ -4,7 +4,7 @@ set -xe CFLAGS="-Wall -Wextra -std=c11 -ggdb -fsanitize=address -fsanitize=undefined" LINK="" -LIB="sv.c vec.c symtable.c tag.c constructor.c sys.c" +LIB="sv.c vec.c symtable.c tag.c constructor.c stream.c sys.c" OUT="alisp.out" build() { diff --git a/main.c b/main.c index f7f290f..98f2878 100644 --- a/main.c +++ b/main.c @@ -20,6 +20,21 @@ int main(void) { - puts("Watch this space\n"); + stream_t stream = {0}; + // const char data[] = "Hello, world!"; + // const sv_t sv = SV(data, ARRSIZE(data) - 1); + // stream_init_string(&stream, NULL, sv); + + // stream_init_file(&stream, "test.txt"); + + stream_init_file(&stream, "stdin", stdin); + printf("[debug]: setup stream pipe\n"); + do + { + printf("%s[%lu]: `%c`\n", stream.name, stream.position, + stream_next(&stream)); + } while (!stream_eoc(&stream)); + printf("%lu/%lu\n", stream.position, stream_size(&stream)); + stream_stop(&stream); return 0; } diff --git a/stream.c b/stream.c new file mode 100644 index 0000000..a7fb72e --- /dev/null +++ b/stream.c @@ -0,0 +1,296 @@ +/* Copyright (C) 2025 Aryadev Chavali + + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the Unlicense for details. + + * You may distribute and modify this code under the terms of the Unlicense, + * which you should have received a copy of along with this program. If not, + * please go to . + + * Created: 2025-08-26 + * Description: Stream implementation + */ + +#include + +#include "./alisp.h" + +stream_err_t stream_init_string(stream_t *stream, char *name, sv_t contents) +{ + if (!stream) + return STREAM_ERR_INVALID_PTR; + name = name ? name : ""; + memset(stream, 0, sizeof(*stream)); + + stream->type = STREAM_TYPE_STRING; + stream->name = name; + stream->string = contents; + + return STREAM_ERR_OK; +} + +stream_err_t stream_init_file(stream_t *stream, char *name, FILE *pipe) +{ + if (!stream || !pipe) + return STREAM_ERR_INVALID_PTR; + name = name ? name : ""; + memset(stream, 0, sizeof(*stream)); + + stream->type = STREAM_TYPE_FILE; + stream->name = name; + stream->pipe.file = pipe; + + vec_init(&stream->pipe.cache, STREAM_DEFAULT_CHUNK); + + return STREAM_ERR_OK; +} + +void stream_stop(stream_t *stream) +{ + if (!stream) + return; + switch (stream->type) + { + case STREAM_TYPE_STRING: + // Nothing to do, all dealt with outside of stream + break; + case STREAM_TYPE_FILE: + // Must cleanup vector + vec_free(&stream->pipe.cache); + break; + } + memset(stream, 0, sizeof(*stream)); +} + +u64 stream_size(stream_t *stream) +{ + assert(stream); + switch (stream->type) + { + case STREAM_TYPE_STRING: + return stream->string.size; + case STREAM_TYPE_FILE: + return stream->pipe.cache.size; + default: + FAIL("Unreachable"); + return 0; + } +} + +bool stream_eos(stream_t *stream) +{ + assert(stream); + switch (stream->type) + { + case STREAM_TYPE_STRING: + return stream->position >= stream->string.size; + case STREAM_TYPE_FILE: + return feof(stream->pipe.file); + default: + FAIL("Unreachable"); + return 0; + } +} + +bool stream_eoc(stream_t *stream) +{ + assert(stream); + switch (stream->type) + { + case STREAM_TYPE_STRING: + return stream->position >= stream->string.size; + case STREAM_TYPE_FILE: + return stream->position >= stream->pipe.cache.size; + default: + FAIL("Unreachable"); + return 0; + } +} + +bool stream_chunk(stream_t *stream) +{ + assert(stream); + switch (stream->type) + { + case STREAM_TYPE_STRING: + // vacuously true + return true; + case STREAM_TYPE_FILE: + { + if (feof(stream->pipe.file)) + return false; + vec_ensure_free(&stream->pipe.cache, STREAM_DEFAULT_CHUNK); + int read = fread(vec_data(&stream->pipe.cache), 1, STREAM_DEFAULT_CHUNK, + stream->pipe.file); + stream->pipe.cache.size += read; + return true; + } + default: + FAIL("Unreachable"); + return 0; + } +} + +char stream_next(stream_t *stream) +{ + char c = stream_peek(stream); + if (c != '\0') + ++stream->position; + return c; +} + +char stream_peek(stream_t *stream) +{ + if (stream_eos(stream)) + return '\0'; + + switch (stream->type) + { + case STREAM_TYPE_STRING: + return stream->string.data[stream->position]; + case STREAM_TYPE_FILE: + { + // Cached already? We are done. + if (stream->position < stream->pipe.cache.size) + return ((char *)vec_data(&stream->pipe.cache))[stream->position]; + + // Try to read chunks in till we've reached it or we're at the end of the + // file. + for (bool read_chunk = stream_chunk(stream); + read_chunk && stream->position >= stream->pipe.cache.size; + read_chunk = stream_chunk(stream)) + continue; + + // Same principle as the stream_eos(stream) check. + if (stream->position >= stream->pipe.cache.size) + return '\0'; + return ((char *)vec_data(&stream->pipe.cache))[stream->position]; + } + default: + FAIL("Unreachable"); + return 0; + } +} + +bool stream_seek(stream_t *stream, i64 offset) +{ + if (offset < 0) + return stream_seek_backward(stream, offset * -1); + else if (offset > 0) + return stream_seek_forward(stream, offset); + else + // vacuously successful + return true; +} + +bool stream_seek_forward(stream_t *stream, u64 offset) +{ + if (stream_eos(stream)) + return false; + switch (stream->type) + { + case STREAM_TYPE_STRING: + { + if (stream->position + offset < stream->string.size) + { + stream->position += offset; + return true; + } + return false; + } + case STREAM_TYPE_FILE: + { + // Similar principle as stream_peek really... + + // Cached already? We are done. + if (stream->position + offset < stream->pipe.cache.size) + { + stream->position += offset; + return true; + } + + // Try to read chunks in till we've reached it or we're at the end of the + // file. + for (bool read_chunk = stream_chunk(stream); + read_chunk && stream->position + offset >= stream->pipe.cache.size; + read_chunk = stream_chunk(stream)) + continue; + + // Same principle as the stream_eos(stream) check. + if (stream->position + offset >= stream->pipe.cache.size) + return false; + stream->position += offset; + return true; + } + default: + FAIL("Unreachable"); + return 0; + } +} + +bool stream_seek_backward(stream_t *stream, u64 offset) +{ + assert(stream); + if (stream->position < offset) + return false; + stream->position -= offset; + return true; +} + +sv_t stream_substr(stream_t *stream, u64 size) +{ + if (stream_eos(stream)) + return SV(NULL, 0); + u64 current_position = stream->position; + bool successful = stream_seek_forward(stream, size); + // In case we did happen to move forward + stream->position = current_position; + if (!successful) + return SV(NULL, 0); + + char *ptr = NULL; + switch (stream->type) + { + case STREAM_TYPE_STRING: + ptr = stream->string.data; + break; + case STREAM_TYPE_FILE: + ptr = 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) +{ + + 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_FILE: + { + if (index + size < stream_size(stream)) + return SV(vec_data(&stream->pipe.cache) + index, size); + // stream_size(stream) <= index + size => try reading chunks + for (bool read_chunk = stream_chunk(stream); + read_chunk && index + size >= stream->pipe.cache.size; + read_chunk = stream_chunk(stream)) + continue; + + if (index + size >= stream_size(stream)) + return SV(NULL, 0); + return SV(vec_data(&stream->pipe.cache) + index, size); + } + default: + assert("Unreachable"); + return SV(NULL, 0); + } +} diff --git a/symtable.c b/symtable.c index 15c6cd0..39139eb 100644 --- a/symtable.c +++ b/symtable.c @@ -38,7 +38,7 @@ char *sym_table_find(sym_table_t *table, sv_t sv) if (table->entries.capacity == 0) sym_table_init(table); - // WIP: Deal with resizing this when table->count > table->size / 2 + // TODO: Deal with resizing this when table->count > table->size / 2 u64 index = djb2(sv) & (table->capacity - 1); for (sv_t comp = VEC_GET(&table->entries, sv_t)[index]; comp.data; index += 1, -- cgit v1.2.3-13-gbd6f