From 9240c25c999dc9d69912ce3c1d5d875ff6449520 Mon Sep 17 00:00:00 2001 From: Bill Hails Date: Sat, 9 Nov 2024 15:18:43 +0000 Subject: [PATCH] new memstream filehandle type --- docs/generated/builtins.md | 3 ++ fn/ioutils.fn | 24 +++++++++++ src/builtin_io.c | 86 ++++++++++++++++++++++++++++++++++---- src/builtin_io.h | 1 + src/builtins.yaml | 8 ++++ src/memory.c | 2 + src/primitives.yaml | 4 ++ tests/fn/test_hygiene.fn | 14 ------- tests/fn/test_tostring.fn | 4 ++ 9 files changed, 123 insertions(+), 23 deletions(-) delete mode 100644 tests/fn/test_hygiene.fn create mode 100644 tests/fn/test_tostring.fn diff --git a/docs/generated/builtins.md b/docs/generated/builtins.md index a30ae33..d638e26 100644 --- a/docs/generated/builtins.md +++ b/docs/generated/builtins.md @@ -4,12 +4,15 @@ Support for declaring builtins ```mermaid flowchart TD +BuiltInMemBufHash --entries--> entries BuiltIn --name--> HashSymbol BuiltIn --result--> TcType BuiltIn --args--> BuiltInArgs BuiltIn --implementation--> void_ptr BuiltInImplementation --implementation--> void_ptr BuiltInImplementation --nargs--> int +BuiltInMemBuf --buffer--> string +BuiltInMemBuf --size--> size BuiltInArgs["BuiltInArgs[]"] --entries--> TcType BuiltIns["BuiltIns[]"] --entries--> BuiltIn ``` diff --git a/fn/ioutils.fn b/fn/ioutils.fn index ebb2abe..797aece 100644 --- a/fn/ioutils.fn +++ b/fn/ioutils.fn @@ -29,3 +29,27 @@ fn with_input_from(filename, handler) { } } } + +fn with_buffer(handler) { + switch (openmem()) { + (success(filehandle)) { + let data = some(handler(filehandle)); + in + close(filehandle); + data; + } + (failure(errmsg)) { + print(errmsg); + nothing; + } + } +} + +fn to_string(data) { + unsafe switch (with_buffer(fn (buf) { + fputv(buf, data); + fgets(buf); + })) { + (some(result)) { result } + } +} diff --git a/src/builtin_io.c b/src/builtin_io.c index a4b9877..29b5cde 100644 --- a/src/builtin_io.c +++ b/src/builtin_io.c @@ -54,6 +54,7 @@ static void registerGets(BuiltIns *registry); static void registerFGets(BuiltIns *registry); static void registerOpen(BuiltIns *registry); +static void registerOpenMemstream(BuiltIns *registry); static void registerClose(BuiltIns *registry); static void registerOpenDir(BuiltIns *registry); @@ -62,6 +63,21 @@ static void registerCloseDir(BuiltIns *registry); static void registerFType(BuiltIns *registry); +static BuiltInMemBufHash *memBufs = NULL; + +void markMemBufs() { + if (memBufs != NULL) { + markHashTable((HashTable *) memBufs); + } +} + +static BuiltInMemBufHash *getMemBufs(void) { + if (memBufs == NULL) { + memBufs = newBuiltInMemBufHash(); + } + return memBufs; +} + void registerIO(BuiltIns *registry) { registerPutc(registry); registerFPutc(registry); @@ -76,6 +92,7 @@ void registerIO(BuiltIns *registry) { registerGets(registry); registerFGets(registry); registerOpen(registry); + registerOpenMemstream(registry); registerClose(registry); registerOpenDir(registry); registerReadDir(registry); @@ -157,12 +174,25 @@ static Value builtin_puts(Vec *args) { #define IO_MODE_WRITE 1 #define IO_MODE_APPEND 2 +static HashSymbol *fileHandleToKey(FILE *file) { + static char buf[128]; + sprintf(buf, "%p", file); + return newSymbol(buf); +} static void opaque_io_close(Opaque *data) { if (data == NULL) return; if (data->data == NULL) return; DEBUG("closing io %p", data->data); fclose(data->data); + HashSymbol *key = fileHandleToKey(data->data); + BuiltInMemBuf *memBuf = NULL; + if (getBuiltInMemBufHash(getMemBufs(), key, &memBuf)) { + if (memBuf->buffer != NULL) { + free(memBuf->buffer); + memBuf->buffer = NULL; + } + } data->data = NULL; } @@ -204,6 +234,21 @@ static Value builtin_open(Vec *args) { return result; } +static Value builtin_open_memstream(Vec *args __attribute__((unused))) { + BuiltInMemBuf *memBuf = newBuiltInMemBuf(); + int save = PROTECT(memBuf); + FILE *file = open_memstream(&memBuf->buffer, &memBuf->size); + BuiltInMemBufHash *memBufs = getMemBufs(); + HashSymbol *key = fileHandleToKey(file); + setBuiltInMemBufHash(memBufs, key, memBuf); + Opaque *wrapper = newOpaque(file, opaque_io_close, NULL); + Value opaque = value_Opaque(wrapper); + protectValue(opaque); + Value result = makeTryResult(1, opaque); + UNPROTECT(save); + return result; +} + static Value builtin_opendir(Vec *args) { char *dirname = listToUtf8(args->entries[0]); DIR *dir = opendir(dirname); @@ -364,7 +409,7 @@ void fputValue(FILE *fh, Value x) { fputVec(fh, x.val.vec); break; default: - cant_happen("unrecognised value type in fputValue"); + cant_happen("unrecognised value type %s", valueTypeName(x.type)); } } @@ -390,13 +435,24 @@ void putVec(Vec *x) { static Value private_fgets(FILE *fh) { ByteArray *bytes = newByteArray(); int save = PROTECT(bytes); - int c; - while ((c = fgetc(fh)) != EOF) { - if (c == '\n') break; - if (c == 0) break; - pushByteArray(bytes, (Byte) c); + HashSymbol *key = fileHandleToKey(fh); + BuiltInMemBuf *buf = NULL; + if (getBuiltInMemBufHash(getMemBufs(), key, &buf)) { + fflush(fh); + if (buf->buffer == NULL) { + cant_happen("fgets on null memstream"); + } + char *b = buf->buffer; + do { pushByteArray(bytes, (Byte) *b); } while (*(b++)); + } else { + int c; + while ((c = fgetc(fh)) != EOF) { + if (c == '\n') break; + if (c == 0) break; + pushByteArray(bytes, (Byte) c); + } + pushByteArray(bytes, 0); } - pushByteArray(bytes, 0); Value string = utf8ToList((char *) bytes->entries); UNPROTECT(save); return string; @@ -441,8 +497,8 @@ static Value builtin_fputv(Vec *args) { if (data == NULL || data->data == NULL) { cant_happen("fput on closed file handle"); } - fputValue((FILE *) data->data, args->entries[0]); - return args->entries[0]; + fputValue((FILE *) data->data, args->entries[1]); + return args->entries[1]; } static TcType *pushFileArg(BuiltInArgs *args) { @@ -572,6 +628,18 @@ static void registerOpen(BuiltIns *registry) { UNPROTECT(save); } +// try(string, opaque(file)) +static void registerOpenMemstream(BuiltIns *registry) { + BuiltInArgs *args = newBuiltInArgs(); + int save = PROTECT(args); + TcType *stringType = makeStringType(); + PROTECT(stringType); + TcType *tryFileType = makeTryFileType(stringType); + PROTECT(tryFileType); + pushNewBuiltIn(registry, "openmem", tryFileType, args, (void *)builtin_open_memstream); + UNPROTECT(save); +} + // string -> try(string, opaque(dir)) static void registerOpenDir(BuiltIns *registry) { BuiltInArgs *args = newBuiltInArgs(); diff --git a/src/builtin_io.h b/src/builtin_io.h index 6e38b55..5ee9cc8 100644 --- a/src/builtin_io.h +++ b/src/builtin_io.h @@ -21,6 +21,7 @@ # include # include "builtins.h" +void markMemBufs(void); void registerIO(BuiltIns *registry); void putValue(Value x); void fputValue(FILE *fh, Value x); diff --git a/src/builtins.yaml b/src/builtins.yaml index cfdf033..dfb021b 100644 --- a/src/builtins.yaml +++ b/src/builtins.yaml @@ -36,6 +36,14 @@ structs: implementation: void_ptr nargs: int + BuiltInMemBuf: + buffer: string=NULL + size: size=0 + +hashes: + BuiltInMemBufHash: + entries: BuiltInMemBuf + arrays: BuiltInArgs: dimension: 1 diff --git a/src/memory.c b/src/memory.c index 29264b0..383f80d 100644 --- a/src/memory.c +++ b/src/memory.c @@ -32,6 +32,7 @@ #include "symbol.h" #include "arithmetic.h" #include "opaque.h" +#include "builtin_io.h" static int bytesAllocated = 0; static int nextGC = 0; @@ -381,6 +382,7 @@ static void mark() { markProtected(); markArithmetic(); markNamespaces(); + markMemBufs(); #ifdef DEBUG_LOG_GC eprintf("starting markVarTable\n"); #endif diff --git a/src/primitives.yaml b/src/primitives.yaml index 075f52f..13c3a80 100644 --- a/src/primitives.yaml +++ b/src/primitives.yaml @@ -120,3 +120,7 @@ file: printf: "%p" valued: true +size: + cname: size_t + printf: "%zu" + valued: true diff --git a/tests/fn/test_hygiene.fn b/tests/fn/test_hygiene.fn deleted file mode 100644 index deb1ed6..0000000 --- a/tests/fn/test_hygiene.fn +++ /dev/null @@ -1,14 +0,0 @@ -let - fn now() { 1 } - - macro time(expression) { - let start = now(); - in { - let result = expression; - in - print(now() - start); - result - } - } -in - time(time(true)) diff --git a/tests/fn/test_tostring.fn b/tests/fn/test_tostring.fn new file mode 100644 index 0000000..8750ecf --- /dev/null +++ b/tests/fn/test_tostring.fn @@ -0,0 +1,4 @@ +let + link "ioutils.fn" as io; +in + assert(io.to_string(1234 + 5i) == "(1234+5i)")