/* * Minimal bootstrap RTL (runtime library) - Reader (input) functions. * * Copyright © 2026 Samuel Lidén Borell * * SPDX-License-Identifier: EUPL-1.2+ OR LGPL-2.1-or-later */ #include #include #include "rtl.h" #include "internal.h" #define DEFAULT_BUFFERSIZE 128 /**< Maximum of "non-unsigned" (31 bit range) integer. */ #define PLAIN_SLULINT_MAX 0x7FFFFFFF #define NO_LIMIT SLUL_INT_MAX struct Reader { FILE *file; /** * Filename for use in error messages. * Note that the filename should NOT be exposed to the application. */ const char *filename; SlulInt limit_left; SlulInt lookahead_size; unsigned char *lookahead_buffer; unsigned char *lookahead; }; struct Reader *Reader__from_file(FILE *f, const char *filename) { struct Reader *rd = malloc(sizeof(struct Reader)); OOM_CHECK(rd); assert(f != NULL); assert(filename != NULL); rd->file = f; rd->filename = filename; rd->limit_left = NO_LIMIT; rd->lookahead_size = 0; rd->lookahead_buffer = NULL; return rd; } struct Reader *Reader__from_filename(const char *filename) { FILE *f; f = fopen(filename, "rb"); if (!f) { perror(filename); exit(EXIT_FAILURE); } return Reader__from_file(f, filename); } static SLUL_NORETURN void file_perror(struct Reader *rd) { perror(rd->filename); exit(EXIT_FAILURE); } static SLUL_NORETURN void file_error(struct Reader *rd, const char *errmsg) { fprintf(stderr, "error: %s: %s\n", rd->filename, errmsg); exit(EXIT_FAILURE); } static int readbyte(struct Reader *rd) { assert(rd != NULL); if (rd->limit_left != NO_LIMIT) { if (rd->limit_left-- == 0) { file_error(rd, "Exceeded size limit"); } } if (rd->lookahead_size) { int byte = *(rd->lookahead++); if (--rd->lookahead_size == 0) { free(rd->lookahead_buffer); } return byte; } else { return getc(rd->file); } } static SlulInt readbytes(struct Reader *rd, SlulInt size, unsigned char *out) { SlulInt ls; SlulInt numread = 0; SlulInt maxread = size; assert(rd != NULL); assert(size <= PLAIN_SLULINT_MAX); assert(out != NULL); if (size == 0) { return 0; } /* Handle remaining limit, if a limit has been set */ { SlulInt limit = rd->limit_left; if (limit != NO_LIMIT) { if (size > limit) { size = limit+1; maxread = limit; limit = 0; } else { limit -= size; } rd->limit_left = limit; } } /* Read from lookahead buffer, if lookahead was done before */ ls = rd->lookahead_size; if (ls) { ls = (ls <= size ? ls : size); memcpy(out, rd->lookahead, (size_t)ls); rd->lookahead += ls; if ((rd->lookahead_size -= ls) == 0) { free(rd->lookahead_buffer); rd->lookahead_buffer = NULL; } out += ls; size -= ls; numread += ls; } /* Read from underlying file (unless satisfied by lookahead buffer) */ if (size) { numread += (SlulInt)fread(out, 1, (size_t)size, rd->file); } if (numread > maxread) { file_error(rd, "Exceeded size limit"); } return numread; } static SLUL_NORETURN void handle_eof(struct Reader *rd) { if (ferror(rd->file)) { file_perror(rd); } else { file_error(rd, "Unexpected end of file"); } } static void try_read(struct Reader *rd, SlulInt len, unsigned char *buffer) { if (readbytes(rd, len, buffer) != len) { handle_eof(rd); } } SlulInt Reader_read_u8(struct Reader *rd) { int byte = readbyte(rd); if (byte == EOF) { handle_eof(rd); } assert(byte >= 0); return (SlulInt)byte; } SlulInt Reader_read_u16_le(struct Reader *rd) { unsigned char bytes[2]; try_read(rd, 2, bytes); return (bytes[0] & 0xFFU) | ((bytes[1] & 0xFFU) << 8U); } SlulInt Reader_read_u32_le(struct Reader *rd) { unsigned char bytes[4]; try_read(rd, 4, bytes); return (bytes[0] & 0xFFU) | ((bytes[1] & 0xFFU) << 8U) | ((bytes[2] & 0xFFU) << 16U) | ((bytes[3] & 0xFFU) << 24U); } void Reader_expect(struct Reader *rd, const String *s) { const unsigned char *sp; SlulInt len; assert(s != NULL); sp = SLUL__decode_string(s, &len); while (len--) { if (Reader_read_u8(rd) != *(sp++)) { file_error(rd, "Input does not contain expected string"); } } } void Reader_expect_repeated(struct Reader *rd, const String *s, SlulInt repeat_count) { SlulInt i; assert(s != NULL); for (i = repeat_count; i != 0; i--) { Reader_expect(rd, s); } }