From fd4f60e37f17702c67beece7d49e58463aabb48b Mon Sep 17 00:00:00 2001 From: Max Wash Date: Fri, 24 Oct 2025 12:32:16 +0100 Subject: [PATCH] core: stream: convert to a b_object interface and base class --- core/include/blue/core/stream.h | 75 ++- core/stream.c | 998 +++++++++++++++++++++----------- 2 files changed, 690 insertions(+), 383 deletions(-) diff --git a/core/include/blue/core/stream.h b/core/include/blue/core/stream.h index d55040c..ff69f6c 100644 --- a/core/include/blue/core/stream.h +++ b/core/include/blue/core/stream.h @@ -1,26 +1,23 @@ -#ifndef BLUELIB_CORE_STREAM_H_ -#define BLUELIB_CORE_STREAM_H_ +#ifndef BLUE_CORE_STREAM_H_ +#define BLUE_CORE_STREAM_H_ +#include #include #include #include #include -#define b_stdin (z__b_stream_get_stdin()) -#define b_stdout (z__b_stream_get_stdout()) -#define b_stderr (z__b_stream_get_stderr()) +B_DECLS_BEGIN; -typedef enum b_stream_pipeline_flags { - B_STREAM_PIPELINE_F_NONE = 0x00u, - B_STREAM_PIPELINE_F_DYNAMIC = 0x01u, - B_STREAM_PIPELINE_F_BUF_DYNAMIC = 0x02u, -} b_stream_pipeline_flags; +#define b_stdin (z__b_stream_get_stdin()) +#define b_stdout (z__b_stream_get_stdout()) +#define b_stderr (z__b_stream_get_stderr()) -typedef struct b_stream_pipeline { - b_stream_pipeline_flags p_flags; - void *p_buf; - size_t p_buf_len; -} b_stream_pipeline; +#define B_TYPE_STREAM (b_stream_get_type()) +#define B_TYPE_STREAM_BUFFER (b_stream_buffer_get_type()) + +B_DECLARE_TYPE(b_stream); +B_DECLARE_TYPE(b_stream_buffer); typedef enum b_stream_mode { B_STREAM_READ = 0x01u, @@ -35,40 +32,35 @@ typedef enum b_stream_seek_origin { B_STREAM_SEEK_END = 0x03u, } b_stream_seek_origin; -typedef struct b_stream { +typedef struct b_stream_cfg { b_stream_mode s_mode; - size_t s_cursor; - int *s_istack; - int s_add_indent; - size_t s_istack_ptr, s_istack_size; - void *s_ptr0, *s_ptr1; +} b_stream_cfg; - b_status (*s_close)(struct b_stream *); - b_status (*s_seek)(struct b_stream *, long long, b_stream_seek_origin); - b_status (*s_tell)(const struct b_stream *, size_t *); - b_status (*s_getc)(struct b_stream *, int *); - b_status (*s_read)(struct b_stream *, unsigned char *, size_t, size_t *); - b_status (*s_write)( - struct b_stream *, const unsigned char *, size_t, size_t *); - b_status (*s_reserve)(struct b_stream *, size_t); -} b_stream; +B_TYPE_CLASS_DECLARATION_BEGIN(b_stream) + b_status (*s_close)(b_stream *); + b_status (*s_seek)(b_stream *, long long, b_stream_seek_origin); + b_status (*s_tell)(const b_stream *, size_t *); + b_status (*s_getc)(b_stream *, int *); + b_status (*s_read)(b_stream *, unsigned char *, size_t, size_t *); + b_status (*s_write)(b_stream *, const unsigned char *, size_t, size_t *); + b_status (*s_reserve)(b_stream *, size_t); +B_TYPE_CLASS_DECLARATION_END(b_stream) + +B_TYPE_CLASS_DECLARATION_BEGIN(b_stream_buffer) +B_TYPE_CLASS_DECLARATION_END(b_stream_buffer) + +BLUE_API b_type b_stream_get_type(); +BLUE_API b_type b_stream_buffer_get_type(); BLUE_API b_stream *z__b_stream_get_stdin(void); BLUE_API b_stream *z__b_stream_get_stdout(void); BLUE_API b_stream *z__b_stream_get_stderr(void); -BLUE_API b_status b_stream_pipeline_create( - size_t buffer_size, b_stream_pipeline **out); - -BLUE_API b_status b_stream_pipeline_create( - size_t buffer_size, b_stream_pipeline **out); -BLUE_API b_status b_stream_pipeline_init( - void *p, size_t len, b_stream_pipeline *out); -BLUE_API b_status b_stream_pipeline_destroy(b_stream_pipeline *pipeline); +BLUE_API b_stream_buffer *b_stream_buffer_create(void *p, size_t len); +BLUE_API b_stream_buffer *b_stream_buffer_create_dynamic(size_t buffer_size); BLUE_API b_stream *b_stream_open_fp(FILE *fp); -BLUE_API b_status b_stream_close(b_stream *stream); BLUE_API b_status b_stream_reserve(b_stream *stream, size_t len); BLUE_API b_status b_stream_seek( b_stream *stream, long long offset, b_stream_seek_origin origin); @@ -88,8 +80,7 @@ BLUE_API b_status b_stream_read_line_s(b_stream *src, b_stream *dest); BLUE_API b_status b_stream_read_all_bytes( b_stream *stream, void *p, size_t max, size_t *nr_read); BLUE_API b_status b_stream_read_all_bytes_s( - b_stream *src, b_stream *dest, b_stream_pipeline *pipeline, - size_t *nr_read); + b_stream *src, b_stream *dest, b_stream_buffer *buffer, size_t *nr_read); BLUE_API b_status b_stream_write_char(b_stream *stream, char c); BLUE_API b_status b_stream_write_string( @@ -103,4 +94,6 @@ BLUE_API b_status b_stream_write_fmt( BLUE_API b_status b_stream_write_vfmt( b_stream *stream, size_t *nr_written, const char *format, va_list arg); +B_DECLS_END; + #endif diff --git a/core/stream.c b/core/stream.c index 36ff70a..6f99528 100644 --- a/core/stream.c +++ b/core/stream.c @@ -5,82 +5,72 @@ #include #include +#define B_TYPE_STDIO_STREAM (b_stdio_stream_get_type()) + +#define STREAM_DISPATCH_VIRTUAL(func, stream, ...) \ + do { \ + struct stream_data _stream; \ + enum b_status status = stream_get_data(stream, &_stream); \ + if (!B_OK(status)) { \ + return status; \ + } \ + return func(&_stream, __VA_ARGS__); \ + } while (0) +#define STREAM_DISPATCH_VIRTUAL_0(func, stream) \ + do { \ + struct stream_data _stream; \ + enum b_status status = stream_get_data(stream, &_stream); \ + if (!B_OK(status)) { \ + return status; \ + } \ + return func(&_stream); \ + } while (0) + #define IDX_STDIN 0 #define IDX_STDOUT 1 #define IDX_STDERR 2 -static enum b_status stdio_read( - struct b_stream *stream, unsigned char *out, size_t max, size_t *nr_read) -{ - FILE *fp = stream->s_ptr0; - enum b_status status = B_SUCCESS; +B_DECLARE_TYPE(b_stdio_stream); - size_t count = fread(out, 1, max, fp); +B_TYPE_CLASS_DECLARATION_BEGIN(b_stdio_stream) +B_TYPE_CLASS_DECLARATION_END(b_stdio_stream) - if (ferror(fp)) { - status = B_ERR_IO_FAILURE; - } +/*** PRIVATE DATA *************************************************************/ - *nr_read = count; - return status; -} +typedef enum b_stream_buffer_flags { + B_STREAM_BUFFER_F_NONE = 0x00u, + B_STREAM_BUFFER_F_DYNAMIC = 0x01u, +} b_stream_buffer_flags; -static enum b_status stdio_write( - struct b_stream *stream, const unsigned char *data, size_t count, - size_t *nr_written) -{ - FILE *fp = stream->s_ptr0; - enum b_status status = B_SUCCESS; - size_t w = fwrite(data, 1, count, fp); +struct b_stream_p { + int *s_istack; + int s_add_indent; + size_t s_istack_ptr, s_istack_size; + void *s_ptr0, *s_ptr1; +}; - if (ferror(fp)) { - status = B_ERR_IO_FAILURE; - } +struct b_stdio_stream_p { + FILE *s_fp; +}; - *nr_written = w; - return status; -} +struct b_stream_buffer_p { + b_stream_buffer_flags p_flags; + void *p_buf; + size_t p_buf_len; +}; -static enum b_status stdio_seek( - struct b_stream *stream, long long offset, b_stream_seek_origin origin) -{ - FILE *fp = stream->s_ptr0; - int whence = 0; - switch (origin) { - case B_STREAM_SEEK_START: - whence = SEEK_SET; - break; - case B_STREAM_SEEK_CURRENT: - whence = SEEK_CUR; - break; - case B_STREAM_SEEK_END: - whence = SEEK_END; - break; - default: - return B_ERR_INVALID_ARGUMENT; - } +struct stream_data { + b_stream *s_obj; + struct b_stream_p *s_private; + struct b_stream_cfg *s_cfg; + b_stream_class *s_ops; +}; - int ret = fseek(fp, offset, whence); - if (ret != 0) { - return B_ERR_NOT_SUPPORTED; - } - - return B_SUCCESS; -} - -static enum b_status stdio_tell(const struct b_stream *stream, size_t *cursor) -{ - FILE *fp = stream->s_ptr0; - long pos = ftell(fp); - if (pos == -1L) { - return B_ERR_NOT_SUPPORTED; - } - - *cursor = (size_t)pos; - return B_SUCCESS; -} - -static struct b_stream stdio[] = { +static b_stream *stdio[] = { + [IDX_STDIN] = NULL, + [IDX_STDOUT] = NULL, + [IDX_STDERR] = NULL, +#if 0 [IDX_STDIN] = { .s_mode = B_STREAM_READ, .s_read = stdio_read, @@ -102,210 +92,83 @@ static struct b_stream stdio[] = { .s_tell = stdio_tell, .s_ptr0 = NULL, /* set to stderr (stdio.h) at runtime */ }, +#endif }; -struct b_stream *z__b_stream_get_stdin(void) +/*** PRIVATE FUNCTIONS ********************************************************/ + +b_type b_stdio_stream_get_type(void); +static enum b_status stdio_read(b_stream *, unsigned char *, size_t, size_t *); +static enum b_status stdio_write( + b_stream *, const unsigned char *, size_t, size_t *); +static enum b_status stdio_seek(b_stream *, long long, b_stream_seek_origin); +static enum b_status stdio_tell(const b_stream *, size_t *); + +static enum b_status stream_get_data(const b_stream *strp, struct stream_data *out) { - stdio[IDX_STDIN].s_ptr0 = stdin; - return &stdio[IDX_STDIN]; + out->s_obj = (b_stream *)strp; + return b_object_get_data( + strp, B_TYPE_STREAM, (void **)&out->s_private, + (void **)&out->s_cfg, (void **)&out->s_ops); } -struct b_stream *z__b_stream_get_stdout(void) +static int current_indent(struct stream_data *stream) { - stdio[IDX_STDOUT].s_ptr0 = stdout; - return &stdio[IDX_STDOUT]; -} - -struct b_stream *z__b_stream_get_stderr(void) -{ - stdio[IDX_STDERR].s_ptr0 = stderr; - return &stdio[IDX_STDERR]; -} - -enum b_status b_stream_pipeline_create( - size_t buffer_size, struct b_stream_pipeline **out) -{ - struct b_stream_pipeline *pipeline = malloc(sizeof *pipeline); - if (!pipeline) { - return B_ERR_NO_MEMORY; - } - - memset(pipeline, 0x0, sizeof *pipeline); - - pipeline->p_buf = malloc(buffer_size); - if (!pipeline->p_buf) { - free(pipeline); - return B_ERR_NO_MEMORY; - } - - pipeline->p_buf_len = buffer_size; - pipeline->p_flags - = B_STREAM_PIPELINE_F_DYNAMIC | B_STREAM_PIPELINE_F_BUF_DYNAMIC; - - *out = pipeline; - return B_SUCCESS; -} - -enum b_status b_stream_pipeline_init( - void *p, size_t len, struct b_stream_pipeline *out) -{ - memset(out, 0x0, sizeof *out); - - out->p_buf = p; - out->p_buf_len = len; - - return B_SUCCESS; -} - -enum b_status b_stream_pipeline_destroy(struct b_stream_pipeline *pipeline) -{ - if (pipeline->p_flags & B_STREAM_PIPELINE_F_BUF_DYNAMIC) { - free(pipeline->p_buf); - } - - if (pipeline->p_flags & B_STREAM_PIPELINE_F_DYNAMIC) { - free(pipeline); - } - - return B_SUCCESS; -} - -struct b_stream *b_stream_open_fp(FILE *fp) -{ - struct b_stream *stream = malloc(sizeof *stream); - if (!stream) { - return NULL; - } - - memset(stream, 0x0, sizeof *stream); - - stream->s_mode = B_STREAM_READ | B_STREAM_WRITE; - stream->s_ptr0 = fp; - stream->s_read = stdio_read; - stream->s_write = stdio_write; - stream->s_seek = stdio_seek; - stream->s_tell = stdio_tell; - - return stream; -} - -enum b_status b_stream_close(b_stream *stream) -{ - if (stream->s_istack) { - free(stream->s_istack); - } - - if (stream->s_close) { - stream->s_close(stream); - } - - if (!(stream->s_mode & Z__B_STREAM_STATIC)) { - free(stream); - } - - return B_SUCCESS; -} - -enum b_status b_stream_reserve(b_stream *stream, size_t len) -{ - if (!stream->s_reserve) { - return B_ERR_NOT_SUPPORTED; - } - - return stream->s_reserve(stream, len); -} - -enum b_status b_stream_seek( - b_stream *stream, long long offset, b_stream_seek_origin origin) -{ - if (!stream->s_seek) { - return B_ERR_NOT_SUPPORTED; - } - - return stream->s_seek(stream, offset, origin); -} - -size_t b_stream_cursor(const b_stream *stream) -{ - if (!stream->s_tell) { + if (!stream->s_private->s_istack || !stream->s_private->s_istack_size) { return 0; } - size_t cursor = 0; - enum b_status status = stream->s_tell(stream, &cursor); + return stream->s_private->s_istack[stream->s_private->s_istack_ptr]; +} + +static size_t stream_cursor(const struct stream_data *stream) +{ + if (!stream || !stream->s_ops || !stream->s_ops->s_tell) { + return B_NPOS; + } + + size_t p = B_NPOS; + b_status status = stream->s_ops->s_tell(stream->s_obj, &p); if (!B_OK(status)) { - return 0; + return B_NPOS; } - return cursor; + return p; } -enum b_status b_stream_push_indent(b_stream *stream, int indent) +static enum b_status stream_seek( + struct stream_data *stream, long long offset, b_stream_seek_origin origin) { - if (!(stream->s_mode & B_STREAM_WRITE)) { + if (!stream || !stream->s_ops || !stream->s_ops->s_seek) { return B_ERR_NOT_SUPPORTED; } - if (stream->s_mode & B_STREAM_BINARY) { - return B_ERR_NOT_SUPPORTED; - } - - if (!stream->s_istack) { - stream->s_istack = calloc(4, sizeof(int)); - stream->s_istack_size = 4; - stream->s_istack_ptr = 0; - } - - if (stream->s_istack_ptr + 1 >= stream->s_istack_size) { - int *buf = realloc( - stream->s_istack, - (stream->s_istack_size + 4) * sizeof(int)); - if (!buf) { - return B_ERR_NO_MEMORY; - } - - stream->s_istack = buf; - stream->s_istack_size += 4; - } - - int cur_indent = stream->s_istack[stream->s_istack_ptr]; - stream->s_istack[++stream->s_istack_ptr] = cur_indent + indent; - - return B_SUCCESS; + return stream->s_ops->s_seek(stream->s_obj, offset, origin); } -enum b_status b_stream_pop_indent(b_stream *stream) +static enum b_status stream_reserve(struct stream_data *stream, size_t len) { - if (!(stream->s_mode & B_STREAM_WRITE)) { + if (!stream || !stream->s_ops || !stream->s_ops->s_reserve) { return B_ERR_NOT_SUPPORTED; } - if (stream->s_mode & B_STREAM_BINARY) { - return B_ERR_NOT_SUPPORTED; - } - - if (!stream->s_istack || !stream->s_istack_size || !stream->s_istack_ptr) { - return B_SUCCESS; - } - - stream->s_istack_ptr--; - return B_SUCCESS; + return stream->s_ops->s_reserve(stream->s_obj, len); } -enum b_status b_stream_read_char(struct b_stream *stream, int *c) +static enum b_status stream_read_char(struct stream_data *stream, int *c) { - if (!(stream->s_mode & B_STREAM_READ)) { + if (!(stream->s_cfg->s_mode & B_STREAM_READ)) { return B_ERR_NOT_SUPPORTED; } enum b_status status = B_ERR_NOT_SUPPORTED; - if (stream->s_getc) { - status = stream->s_getc(stream, c); - } else if (stream->s_read) { + if (stream->s_ops->s_getc) { + status = stream->s_ops->s_getc(stream->s_obj, c); + } else if (stream->s_ops->s_read) { size_t r; unsigned char v = 0; - status = stream->s_read(stream, &v, 1, &r); + status = stream->s_ops->s_read(stream->s_obj, &v, 1, &r); *c = v; @@ -317,25 +180,77 @@ enum b_status b_stream_read_char(struct b_stream *stream, int *c) return status; } -enum b_status b_stream_read_bytes( - struct b_stream *stream, void *buf, size_t count, size_t *nr_read) +static enum b_status __write_char(struct stream_data *stream, char c) { - if (!(stream->s_mode & B_STREAM_READ)) { + size_t w; + enum b_status status = stream->s_ops->s_write( + stream->s_obj, (unsigned char *)&c, 1, &w); + + if (status == B_SUCCESS && w < 1) { + status = B_ERR_IO_FAILURE; + } + + return status; +} + +static enum b_status stream_write_char(struct stream_data *stream, char c) +{ + if (!(stream->s_cfg->s_mode & B_STREAM_WRITE)) { return B_ERR_NOT_SUPPORTED; } enum b_status status = B_ERR_NOT_SUPPORTED; - if (!stream->s_read) { + if (!stream->s_ops->s_write) { + return B_ERR_NOT_SUPPORTED; + } + + if (c == '\n') { + stream->s_private->s_add_indent = 1; + } + + if (!stream->s_private->s_istack_size) { + return __write_char(stream, c); + } + + if (stream->s_private->s_add_indent && c != '\n') { + int indent = current_indent(stream); + for (int i = 0; i < indent; i++) { + __write_char(stream, ' '); + __write_char(stream, ' '); + } + + stream->s_private->s_add_indent = 0; + } + + __write_char(stream, c); + + if (c == '\n') { + stream->s_private->s_add_indent = 1; + } + + return B_SUCCESS; +} + +static enum b_status stream_read_bytes( + struct stream_data *stream, void *buf, size_t count, size_t *nr_read) +{ + if (!(stream->s_cfg->s_mode & B_STREAM_READ)) { + return B_ERR_NOT_SUPPORTED; + } + + enum b_status status = B_ERR_NOT_SUPPORTED; + + if (!stream->s_ops->s_read) { return status; } - return stream->s_read(stream, buf, count, nr_read); + return stream->s_ops->s_read(stream->s_obj, buf, count, nr_read); } -enum b_status b_stream_read_line(struct b_stream *stream, char *s, size_t max) +static enum b_status stream_read_line(struct stream_data *stream, char *s, size_t max) { - if (!(stream->s_mode & B_STREAM_READ)) { + if (!(stream->s_cfg->s_mode & B_STREAM_READ)) { return B_ERR_NOT_SUPPORTED; } @@ -349,7 +264,7 @@ enum b_status b_stream_read_line(struct b_stream *stream, char *s, size_t max) break; } - status = b_stream_read_char(stream, &c); + status = stream_read_char(stream, &c); if (status != B_SUCCESS) { break; } @@ -365,13 +280,14 @@ enum b_status b_stream_read_line(struct b_stream *stream, char *s, size_t max) return B_SUCCESS; } -enum b_status b_stream_read_line_s(struct b_stream *src, b_stream *dest) +static enum b_status stream_read_line_s( + struct stream_data *src, struct stream_data *dest) { - if (!(src->s_mode & B_STREAM_READ)) { + if (!(src->s_cfg->s_mode & B_STREAM_READ)) { return B_ERR_NOT_SUPPORTED; } - if (!(dest->s_mode & B_STREAM_WRITE)) { + if (!(dest->s_cfg->s_mode & B_STREAM_WRITE)) { return B_ERR_NOT_SUPPORTED; } @@ -381,12 +297,12 @@ enum b_status b_stream_read_line_s(struct b_stream *src, b_stream *dest) int c = 0; while (1) { - status = b_stream_read_char(src, &c); + status = stream_read_char(src, &c); if (status != B_SUCCESS) { break; } - b_stream_write_char(dest, c); + stream_write_char(dest, c); i++; if (c == '\n') { @@ -401,10 +317,26 @@ enum b_status b_stream_read_line_s(struct b_stream *src, b_stream *dest) return status; } -enum b_status b_stream_read_all_bytes( - struct b_stream *stream, void *p, size_t max, size_t *out_nr_read) +static enum b_status stream_write_bytes( + struct stream_data *stream, const void *buf, size_t count, size_t *nr_written) { - if (!(stream->s_mode & B_STREAM_READ)) { + if (!(stream->s_cfg->s_mode & B_STREAM_WRITE)) { + return B_ERR_NOT_SUPPORTED; + } + + enum b_status status = B_ERR_NOT_SUPPORTED; + + if (!stream->s_ops->s_write) { + return status; + } + + return stream->s_ops->s_write(stream->s_obj, buf, count, nr_written); +} + +static enum b_status stream_read_all_bytes( + struct stream_data *stream, void *p, size_t max, size_t *out_nr_read) +{ + if (!(stream->s_cfg->s_mode & B_STREAM_READ)) { return B_ERR_NOT_SUPPORTED; } @@ -414,7 +346,7 @@ enum b_status b_stream_read_all_bytes( while (nr_read < max) { int c; - status = b_stream_read_char(stream, &c); + status = stream_read_char(stream, &c); if (status != B_SUCCESS) { break; } @@ -430,29 +362,29 @@ enum b_status b_stream_read_all_bytes( return status; } -enum b_status b_stream_read_all_bytes_s( - struct b_stream *src, struct b_stream *dest, - struct b_stream_pipeline *pipeline, size_t *out_nr_read) +static enum b_status stream_read_all_bytes_s( + struct stream_data *src, struct stream_data *dest, + struct b_stream_buffer_p *buffer, size_t *out_nr_read) { - if (!(src->s_mode & B_STREAM_READ)) { + if (!(src->s_cfg->s_mode & B_STREAM_READ)) { return B_ERR_NOT_SUPPORTED; } - if (!(dest->s_mode & B_STREAM_WRITE)) { + if (!(dest->s_cfg->s_mode & B_STREAM_WRITE)) { return B_ERR_NOT_SUPPORTED; } - if (!pipeline) { + if (!buffer) { return B_ERR_INVALID_ARGUMENT; } - if (src->s_seek && dest->s_reserve) { - size_t offset = b_stream_cursor(src); - b_stream_seek(src, 0, B_STREAM_SEEK_END); - size_t length = b_stream_cursor(src); - b_stream_seek(src, offset, B_STREAM_SEEK_START); + if (src->s_ops->s_seek && dest->s_ops->s_reserve) { + size_t offset = stream_cursor(src); + stream_seek(src, 0, B_STREAM_SEEK_END); + size_t length = stream_cursor(src); + stream_seek(src, offset, B_STREAM_SEEK_START); - b_stream_reserve(dest, length); + stream_reserve(dest, length); } enum b_status status = B_SUCCESS; @@ -460,16 +392,16 @@ enum b_status b_stream_read_all_bytes_s( while (1) { size_t r = 0, w = 0; - status = b_stream_read_bytes( - src, pipeline->p_buf, pipeline->p_buf_len, &r); + status = stream_read_bytes( + src, buffer->p_buf, buffer->p_buf_len, &r); if (status != B_SUCCESS) { break; } - status = b_stream_write_bytes(dest, pipeline->p_buf, r, &w); + status = stream_write_bytes(dest, buffer->p_buf, r, &w); nr_read += w; - if (status != B_SUCCESS || w != pipeline->p_buf_len) { + if (status != B_SUCCESS || w != buffer->p_buf_len) { break; } } @@ -485,74 +417,14 @@ enum b_status b_stream_read_all_bytes_s( return status; } -static enum b_status __write_char(struct b_stream *stream, char c) -{ - size_t w; - enum b_status status = stream->s_write(stream, (unsigned char *)&c, 1, &w); - - if (status == B_SUCCESS && w < 1) { - status = B_ERR_IO_FAILURE; - } - - return status; -} - -static int current_indent(struct b_stream *stream) -{ - if (!stream->s_istack || !stream->s_istack_size) { - return 0; - } - - return stream->s_istack[stream->s_istack_ptr]; -} - -enum b_status b_stream_write_char(struct b_stream *stream, char c) -{ - if (!(stream->s_mode & B_STREAM_WRITE)) { - return B_ERR_NOT_SUPPORTED; - } - - enum b_status status = B_ERR_NOT_SUPPORTED; - - if (!stream->s_write) { - return B_ERR_NOT_SUPPORTED; - } - - if (c == '\n') { - stream->s_add_indent = 1; - } - - if (!stream->s_istack_size) { - return __write_char(stream, c); - } - - if (stream->s_add_indent && c != '\n') { - int indent = current_indent(stream); - for (int i = 0; i < indent; i++) { - __write_char(stream, ' '); - __write_char(stream, ' '); - } - - stream->s_add_indent = 0; - } - - __write_char(stream, c); - - if (c == '\n') { - stream->s_add_indent = 1; - } - - return B_SUCCESS; -} - -enum b_status b_stream_write_string( - b_stream *stream, const char *s, size_t *nr_written) +static enum b_status stream_write_string( + struct stream_data *stream, const char *s, size_t *nr_written) { size_t i; enum b_status status = B_SUCCESS; for (i = 0; s[i]; i++) { - status = b_stream_write_char(stream, s[i]); + status = stream_write_char(stream, s[i]); if (!B_OK(status)) { break; @@ -566,34 +438,292 @@ enum b_status b_stream_write_string( return status; } -enum b_status b_stream_write_bytes( - struct b_stream *stream, const void *buf, size_t count, size_t *nr_written) +static b_stream *init_stdio_stream(FILE *fp, b_stream_mode mode) { - if (!(stream->s_mode & B_STREAM_WRITE)) { + b_stdio_stream *stream = b_object_create(B_TYPE_STDIO_STREAM); + if (!stream) { + return NULL; + } + + struct b_stdio_stream_p *p + = b_object_get_private(stream, B_TYPE_STDIO_STREAM); + b_stream_cfg *cfg = b_object_get_protected(stream, B_TYPE_STREAM); + p->s_fp = fp; + cfg->s_mode = mode; + + return stream; +} + +static enum b_status stream_push_indent(struct stream_data *stream, int indent) +{ + if (!(stream->s_cfg->s_mode & B_STREAM_WRITE)) { return B_ERR_NOT_SUPPORTED; } - enum b_status status = B_ERR_NOT_SUPPORTED; - - if (!stream->s_write) { - return status; + if (stream->s_cfg->s_mode & B_STREAM_BINARY) { + return B_ERR_NOT_SUPPORTED; } - return stream->s_write(stream, buf, count, nr_written); + if (!stream->s_private->s_istack) { + stream->s_private->s_istack = calloc(4, sizeof(int)); + stream->s_private->s_istack_size = 4; + stream->s_private->s_istack_ptr = 0; + } + + if (stream->s_private->s_istack_ptr + 1 >= stream->s_private->s_istack_size) { + int *buf = realloc( + stream->s_private->s_istack, + (stream->s_private->s_istack_size + 4) * sizeof(int)); + if (!buf) { + return B_ERR_NO_MEMORY; + } + + stream->s_private->s_istack = buf; + stream->s_private->s_istack_size += 4; + } + + int cur_indent + = stream->s_private->s_istack[stream->s_private->s_istack_ptr]; + stream->s_private->s_istack[++stream->s_private->s_istack_ptr] + = cur_indent + indent; + + return B_SUCCESS; +} + +static enum b_status stream_pop_indent(struct stream_data *stream) +{ + if (!(stream->s_cfg->s_mode & B_STREAM_WRITE)) { + return B_ERR_NOT_SUPPORTED; + } + + if (stream->s_cfg->s_mode & B_STREAM_BINARY) { + return B_ERR_NOT_SUPPORTED; + } + + if (!stream->s_private->s_istack || !stream->s_private->s_istack_size + || !stream->s_private->s_istack_ptr) { + return B_SUCCESS; + } + + stream->s_private->s_istack_ptr--; + return B_SUCCESS; } static void fctprintf_callback(char c, void *p) { - struct b_stream *stream = p; - b_stream_write_char(stream, c); + struct stream_data *stream = p; + stream_write_char(stream, c); +} + +/*** PUBLIC FUNCTIONS *********************************************************/ + +b_stream *z__b_stream_get_stdin(void) +{ + if (!stdio[IDX_STDIN]) { + stdio[IDX_STDIN] = init_stdio_stream(stdin, B_STREAM_READ); + } + + return stdio[IDX_STDIN]; +} + +b_stream *z__b_stream_get_stdout(void) +{ + if (!stdio[IDX_STDOUT]) { + stdio[IDX_STDOUT] = init_stdio_stream(stdout, B_STREAM_WRITE); + } + + return stdio[IDX_STDOUT]; +} + +b_stream *z__b_stream_get_stderr(void) +{ + if (!stdio[IDX_STDERR]) { + stdio[IDX_STDERR] = init_stdio_stream(stderr, B_STREAM_WRITE); + } + + return stdio[IDX_STDERR]; +} + +b_stream_buffer *b_stream_buffer_create_dynamic(size_t buffer_size) +{ + b_stream_buffer *buffer = b_object_create(B_TYPE_STREAM_BUFFER); + if (!buffer) { + return NULL; + } + + struct b_stream_buffer_p *p + = b_object_get_private(buffer, B_TYPE_STREAM_BUFFER); + + p->p_buf = malloc(buffer_size); + if (!p->p_buf) { + b_stream_buffer_unref(buffer); + return NULL; + } + + p->p_buf_len = buffer_size; + p->p_flags = B_STREAM_BUFFER_F_DYNAMIC; + + return buffer; +} + +b_stream_buffer *b_stream_buffer_create(void *buf, size_t len) +{ + b_stream_buffer *buffer = b_object_create(B_TYPE_STREAM_BUFFER); + if (!buffer) { + return NULL; + } + + struct b_stream_buffer_p *p + = b_object_get_private(buffer, B_TYPE_STREAM_BUFFER); + + p->p_buf = buf; + p->p_buf_len = len; + p->p_flags = 0; + + return buffer; +} + +b_stream *b_stream_open_fp(FILE *fp) +{ + b_stream *stream = b_object_create(B_TYPE_STREAM); + if (!stream) { + return NULL; + } + + struct stream_data p; + stream_get_data(stream, &p); + + p.s_cfg->s_mode = B_STREAM_READ | B_STREAM_WRITE; + p.s_private->s_ptr0 = fp; + p.s_ops->s_read = stdio_read; + p.s_ops->s_write = stdio_write; + p.s_ops->s_seek = stdio_seek; + p.s_ops->s_tell = stdio_tell; + + return stream; +} + +enum b_status b_stream_reserve(b_stream *stream, size_t len) +{ + B_CLASS_DISPATCH_VIRTUAL( + b_stream, B_TYPE_STREAM, B_ERR_NOT_SUPPORTED, s_reserve, stream, + len); +} + +enum b_status b_stream_seek( + b_stream *stream, long long offset, b_stream_seek_origin origin) +{ + B_CLASS_DISPATCH_VIRTUAL( + b_stream, B_TYPE_STREAM, B_ERR_NOT_SUPPORTED, s_seek, stream, + offset, origin); +} + +size_t b_stream_cursor(const b_stream *stream) +{ + STREAM_DISPATCH_VIRTUAL_0(stream_cursor, stream); +} + +enum b_status b_stream_push_indent(b_stream *strp, int indent) +{ + STREAM_DISPATCH_VIRTUAL(stream_push_indent, strp, indent); +} + +enum b_status b_stream_pop_indent(b_stream *strp) +{ + STREAM_DISPATCH_VIRTUAL_0(stream_pop_indent, strp); +} + +enum b_status b_stream_read_char(b_stream *strp, int *c) +{ + STREAM_DISPATCH_VIRTUAL(stream_read_char, strp, c); +} + +enum b_status b_stream_read_bytes( + b_stream *strp, void *buf, size_t count, size_t *nr_read) +{ + STREAM_DISPATCH_VIRTUAL(stream_read_bytes, strp, buf, count, nr_read); +} + +enum b_status b_stream_read_line(b_stream *strp, char *s, size_t max) +{ + STREAM_DISPATCH_VIRTUAL(stream_read_line, strp, s, max); +} + +enum b_status b_stream_read_line_s(b_stream *src, b_stream *dest) +{ + enum b_status status; + struct stream_data src_p, dest_p; + + status = stream_get_data(src, &src_p); + if (!B_OK(status)) { + return status; + } + + status = stream_get_data(dest, &dest_p); + if (!B_OK(status)) { + return status; + } + + return stream_read_line_s(&src_p, &dest_p); +} + +enum b_status b_stream_read_all_bytes( + b_stream *stream, void *p, size_t max, size_t *out_nr_read) +{ + STREAM_DISPATCH_VIRTUAL(stream_read_all_bytes, stream, p, max, out_nr_read); +} + +enum b_status b_stream_read_all_bytes_s( + b_stream *src, b_stream *dest, b_stream_buffer *buffer, size_t *out_nr_read) +{ + enum b_status status; + struct stream_data src_p, dest_p; + struct b_stream_buffer_p *buffer_p; + + status = stream_get_data(src, &src_p); + if (!B_OK(status)) { + return status; + } + + status = stream_get_data(dest, &dest_p); + if (!B_OK(status)) { + return status; + } + + buffer_p = b_object_get_private(buffer, B_TYPE_STREAM_BUFFER); + if (!buffer_p) { + return B_ERR_INVALID_ARGUMENT; + } + + return stream_read_all_bytes_s(&src_p, &dest_p, buffer_p, out_nr_read); +} + +enum b_status b_stream_write_char(b_stream *stream, char c) +{ + STREAM_DISPATCH_VIRTUAL(stream_write_char, stream, c); +} + +enum b_status b_stream_write_string( + b_stream *stream, const char *s, size_t *nr_written) +{ + STREAM_DISPATCH_VIRTUAL(stream_write_string, stream, s, nr_written); +} + +enum b_status b_stream_write_bytes( + b_stream *stream, const void *buf, size_t count, size_t *nr_written) +{ + STREAM_DISPATCH_VIRTUAL(stream_write_bytes, stream, buf, count, nr_written); } enum b_status b_stream_write_fmt( b_stream *stream, size_t *nr_written, const char *format, ...) { + struct stream_data p; + enum b_status status = stream_get_data(stream, &p); + va_list arg; va_start(arg, format); - int w = z__b_fctprintf(fctprintf_callback, stream, format, arg); + int w = z__b_fctprintf(fctprintf_callback, &p, format, arg); va_end(arg); if (nr_written) { @@ -606,7 +736,10 @@ enum b_status b_stream_write_fmt( enum b_status b_stream_write_vfmt( b_stream *stream, size_t *nr_written, const char *format, va_list arg) { - int w = z__b_fctprintf(fctprintf_callback, stream, format, arg); + struct stream_data p; + enum b_status status = stream_get_data(stream, &p); + + int w = z__b_fctprintf(fctprintf_callback, &p, format, arg); if (nr_written) { *nr_written = w; @@ -614,3 +747,184 @@ enum b_status b_stream_write_vfmt( return B_SUCCESS; } + +/*** VIRTUAL FUNCTIONS ********************************************************/ + +static void stream_init(b_object *obj, void *priv) +{ + struct b_stream_p *stream = priv; +} + +static void stream_fini(b_object *obj, void *priv) +{ + struct b_stream_p *stream = priv; + b_stream_class *ops = b_object_get_interface(obj, B_TYPE_STREAM); + + if (stream->s_istack) { + free(stream->s_istack); + } + + if (ops->s_close) { + ops->s_close(obj); + } +} + +static void stream_buffer_init(b_object *obj, void *priv) +{ + struct b_stream_buffer_p *buffer = priv; +} + +static void stream_buffer_fini(b_object *obj, void *priv) +{ + struct b_stream_buffer_p *buffer = priv; + if (buffer->p_flags & B_STREAM_BUFFER_F_DYNAMIC) { + free(buffer->p_buf); + } +} + +static void stdio_stream_init(b_object *obj, void *priv) +{ + struct b_stdio_stream_p *stream = priv; +} + +static void stdio_stream_fini(b_object *obj, void *priv) +{ + struct b_stdio_stream_p *stream = priv; +} + +static enum b_status stdio_read( + b_stream *stream, unsigned char *out, size_t max, size_t *nr_read) +{ + struct b_stdio_stream_p *p + = b_object_get_private(stream, B_TYPE_STDIO_STREAM); + + enum b_status status = B_SUCCESS; + + size_t count = fread(out, 1, max, p->s_fp); + + if (ferror(p->s_fp)) { + status = B_ERR_IO_FAILURE; + } + + *nr_read = count; + return status; +} + +static enum b_status stdio_write( + b_stream *stream, const unsigned char *data, size_t count, size_t *nr_written) +{ + struct b_stdio_stream_p *p + = b_object_get_private(stream, B_TYPE_STDIO_STREAM); + + enum b_status status = B_SUCCESS; + size_t w = fwrite(data, 1, count, p->s_fp); + + if (ferror(p->s_fp)) { + status = B_ERR_IO_FAILURE; + } + + *nr_written = w; + return status; +} + +static enum b_status stdio_seek( + b_stream *stream, long long offset, b_stream_seek_origin origin) +{ + struct b_stdio_stream_p *p + = b_object_get_private(stream, B_TYPE_STDIO_STREAM); + + int whence = 0; + switch (origin) { + case B_STREAM_SEEK_START: + whence = SEEK_SET; + break; + case B_STREAM_SEEK_CURRENT: + whence = SEEK_CUR; + break; + case B_STREAM_SEEK_END: + whence = SEEK_END; + break; + default: + return B_ERR_INVALID_ARGUMENT; + } + + int ret = fseek(p->s_fp, offset, whence); + if (ret != 0) { + return B_ERR_NOT_SUPPORTED; + } + + return B_SUCCESS; +} + +static enum b_status stdio_tell(const b_stream *stream, size_t *cursor) +{ + struct b_stdio_stream_p *p + = b_object_get_private(stream, B_TYPE_STDIO_STREAM); + + long pos = ftell(p->s_fp); + if (pos == -1L) { + return B_ERR_NOT_SUPPORTED; + } + + *cursor = (size_t)pos; + return B_SUCCESS; +} + +/*** CLASS DEFINITION *********************************************************/ + +// ---- b_stream DEFINITION +B_TYPE_CLASS_DEFINITION_BEGIN(b_stream) + B_TYPE_CLASS_INTERFACE_BEGIN(b_object, B_TYPE_OBJECT) + B_INTERFACE_ENTRY(to_string) = NULL; + B_TYPE_CLASS_INTERFACE_END(b_object, B_TYPE_OBJECT) +B_TYPE_CLASS_DEFINITION_END(b_stream) + +B_TYPE_DEFINITION_BEGIN(b_stream) + B_TYPE_ID(0xa2c98988, 0x30e5, 0x47c7, 0x88cd, 0x6c8ea79f69cd); + B_TYPE_CLASS(b_stream_class); + B_TYPE_INSTANCE_PRIVATE(struct b_stream_p); + B_TYPE_INSTANCE_PROTECTED(b_stream_cfg); + B_TYPE_INSTANCE_INIT(stream_init); + B_TYPE_INSTANCE_FINI(stream_fini); +B_TYPE_DEFINITION_END(b_stream) + +// ---- b_stream_buffer DEFINITION +B_TYPE_CLASS_DEFINITION_BEGIN(b_stream_buffer) + B_TYPE_CLASS_INTERFACE_BEGIN(b_object, B_TYPE_OBJECT) + B_INTERFACE_ENTRY(to_string) = NULL; + B_TYPE_CLASS_INTERFACE_END(b_object, B_TYPE_OBJECT) +B_TYPE_CLASS_DEFINITION_END(b_stream_buffer) + +B_TYPE_DEFINITION_BEGIN(b_stream_buffer) + B_TYPE_ID(0x575c7be1, 0x665f, 0x41f8, 0xbfed, 0x6269a2985be0); + B_TYPE_CLASS(b_stream_buffer_class); + B_TYPE_INSTANCE_PRIVATE(struct b_stream_buffer_p); + B_TYPE_INSTANCE_INIT(stream_buffer_init); + B_TYPE_INSTANCE_FINI(stream_buffer_fini); +B_TYPE_DEFINITION_END(b_stream_buffer) + +// ---- b_stdio_stream DEFINITION +B_TYPE_CLASS_DEFINITION_BEGIN(b_stdio_stream) + B_TYPE_CLASS_INTERFACE_BEGIN(b_object, B_TYPE_OBJECT) + B_INTERFACE_ENTRY(to_string) = NULL; + B_TYPE_CLASS_INTERFACE_END(b_object, B_TYPE_OBJECT) + + B_TYPE_CLASS_INTERFACE_BEGIN(b_stream, B_TYPE_STREAM) + B_INTERFACE_ENTRY(s_close) = NULL; + B_INTERFACE_ENTRY(s_seek) = stdio_seek; + B_INTERFACE_ENTRY(s_tell) = stdio_tell; + B_INTERFACE_ENTRY(s_getc) = NULL; + B_INTERFACE_ENTRY(s_read) = stdio_read; + B_INTERFACE_ENTRY(s_write) = stdio_write; + B_INTERFACE_ENTRY(s_reserve) = NULL; + B_TYPE_CLASS_INTERFACE_END(b_stream, B_TYPE_STREAM) +B_TYPE_CLASS_DEFINITION_END(b_stdio_stream) + +B_TYPE_DEFINITION_BEGIN(b_stdio_stream) + B_TYPE_ID(0x67678926, 0xd0b7, 0x4f99, 0xb83c, 0x790927597645); + B_TYPE_EXTENDS(B_TYPE_STREAM); + B_TYPE_CLASS(b_stdio_stream_class); + B_TYPE_INSTANCE_PRIVATE(struct b_stdio_stream_p); + B_TYPE_INSTANCE_INIT(stdio_stream_init); + B_TYPE_INSTANCE_FINI(stdio_stream_fini); +B_TYPE_DEFINITION_END(b_stdio_stream)