core: stream: add bstr support

despite not being a b_object, the b_stream interface can now detect when
a b_bstr instance has been passed as a stream parameter and use the correct
functions to handle writing to it.

this allows any function that expects a b_stream parameter to be called
with a b_bstr instance instead, allowing complex stream-based I/O operations
to be directed towards bounded character arrays with no heap allocation
required.
This commit is contained in:
2026-02-03 14:36:48 +00:00
parent 23aba2a27f
commit c13b7a7e3a

View File

@@ -1,10 +1,14 @@
#include "printf.h" #include "printf.h"
#include <blue/core/bstr.h>
#include <blue/core/stream.h> #include <blue/core/stream.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#define READ_MAGIC(p) (*(uint64_t *)p)
#define IS_BSTR(p) (READ_MAGIC(p) == B_BSTR_MAGIC)
#define B_TYPE_STDIO_STREAM (b_stdio_stream_get_type()) #define B_TYPE_STDIO_STREAM (b_stdio_stream_get_type())
#define STREAM_DISPATCH_VIRTUAL(func, stream, ...) \ #define STREAM_DISPATCH_VIRTUAL(func, stream, ...) \
@@ -369,6 +373,39 @@ static enum b_status stream_read_line_s(
return status; return status;
} }
static enum b_status stream_read_line_to_bstr(
struct stream_data *src, struct b_bstr *dest)
{
if (!(src->s_cfg->s_mode & B_STREAM_READ)) {
return B_ERR_NOT_SUPPORTED;
}
enum b_status status = B_SUCCESS;
size_t i = 0;
b_wchar c = 0;
while (1) {
status = stream_read_char(src, &c);
if (status != B_SUCCESS) {
break;
}
b_bstr_write_char(dest, c);
i++;
if (c == '\n') {
break;
}
}
if (status == B_ERR_NO_DATA && i > 0) {
status = B_SUCCESS;
}
return status;
}
static enum b_status stream_write_bytes( static enum b_status stream_write_bytes(
struct stream_data *stream, const void *buf, size_t count, size_t *nr_written) struct stream_data *stream, const void *buf, size_t count, size_t *nr_written)
{ {
@@ -469,6 +506,57 @@ static enum b_status stream_read_all_bytes_s(
return status; return status;
} }
static enum b_status stream_read_all_bytes_to_bstr_s(
struct stream_data *src, struct b_bstr *dest,
struct b_stream_buffer_p *buffer, size_t *out_nr_read)
{
if (!(src->s_cfg->s_mode & B_STREAM_READ)) {
return B_ERR_NOT_SUPPORTED;
}
if (!buffer) {
return B_ERR_INVALID_ARGUMENT;
}
if (src->s_ops->s_seek) {
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_bstr_reserve(dest, length);
}
enum b_status status = B_SUCCESS;
size_t nr_read = 0;
while (1) {
size_t r = 0, w = 0;
status = stream_read_bytes(
src, buffer->p_buf, buffer->p_buf_len, &r);
if (status != B_SUCCESS) {
break;
}
status = b_bstr_write_chars(dest, buffer->p_buf, r, &w);
nr_read += w;
if (status != B_SUCCESS || w != buffer->p_buf_len) {
break;
}
}
if (status == B_ERR_NO_DATA && nr_read > 0) {
status = B_SUCCESS;
}
if (out_nr_read) {
*out_nr_read = nr_read;
}
return status;
}
static enum b_status stream_write_string( static enum b_status stream_write_string(
struct stream_data *stream, const char *s, size_t *nr_written) struct stream_data *stream, const char *s, size_t *nr_written)
{ {
@@ -642,6 +730,10 @@ b_stream *b_stream_open_fp(FILE *fp)
enum b_status b_stream_reserve(b_stream *stream, size_t len) enum b_status b_stream_reserve(b_stream *stream, size_t len)
{ {
if (IS_BSTR(stream)) {
return B_ERR_NOT_SUPPORTED;
}
B_CLASS_DISPATCH_VIRTUAL( B_CLASS_DISPATCH_VIRTUAL(
b_stream, B_TYPE_STREAM, B_ERR_NOT_SUPPORTED, s_reserve, stream, b_stream, B_TYPE_STREAM, B_ERR_NOT_SUPPORTED, s_reserve, stream,
len); len);
@@ -650,6 +742,10 @@ enum b_status b_stream_reserve(b_stream *stream, size_t len)
enum b_status b_stream_seek( enum b_status b_stream_seek(
b_stream *stream, long long offset, b_stream_seek_origin origin) b_stream *stream, long long offset, b_stream_seek_origin origin)
{ {
if (IS_BSTR(stream)) {
return B_ERR_NOT_SUPPORTED;
}
B_CLASS_DISPATCH_VIRTUAL( B_CLASS_DISPATCH_VIRTUAL(
b_stream, B_TYPE_STREAM, B_ERR_NOT_SUPPORTED, s_seek, stream, b_stream, B_TYPE_STREAM, B_ERR_NOT_SUPPORTED, s_seek, stream,
offset, origin); offset, origin);
@@ -657,37 +753,65 @@ enum b_status b_stream_seek(
size_t b_stream_cursor(const b_stream *stream) size_t b_stream_cursor(const b_stream *stream)
{ {
if (IS_BSTR(stream)) {
return b_bstr_get_size((b_bstr *)stream);
}
STREAM_DISPATCH_VIRTUAL_0(stream_cursor, stream); STREAM_DISPATCH_VIRTUAL_0(stream_cursor, stream);
} }
enum b_status b_stream_push_indent(b_stream *strp, int indent) enum b_status b_stream_push_indent(b_stream *strp, int indent)
{ {
if (IS_BSTR(strp)) {
return b_bstr_push_indent((b_bstr *)strp, indent);
}
STREAM_DISPATCH_VIRTUAL(stream_push_indent, strp, indent); STREAM_DISPATCH_VIRTUAL(stream_push_indent, strp, indent);
} }
enum b_status b_stream_pop_indent(b_stream *strp) enum b_status b_stream_pop_indent(b_stream *strp)
{ {
if (IS_BSTR(strp)) {
return b_bstr_pop_indent((b_bstr *)strp);
}
STREAM_DISPATCH_VIRTUAL_0(stream_pop_indent, strp); STREAM_DISPATCH_VIRTUAL_0(stream_pop_indent, strp);
} }
enum b_status b_stream_read_char(b_stream *strp, int *c) enum b_status b_stream_read_char(b_stream *strp, int *c)
{ {
if (IS_BSTR(strp)) {
return B_ERR_NOT_SUPPORTED;
}
STREAM_DISPATCH_VIRTUAL(stream_read_char, strp, c); STREAM_DISPATCH_VIRTUAL(stream_read_char, strp, c);
} }
enum b_status b_stream_read_bytes( enum b_status b_stream_read_bytes(
b_stream *strp, void *buf, size_t count, size_t *nr_read) b_stream *strp, void *buf, size_t count, size_t *nr_read)
{ {
if (IS_BSTR(strp)) {
return B_ERR_NOT_SUPPORTED;
}
STREAM_DISPATCH_VIRTUAL(stream_read_bytes, strp, buf, count, 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) enum b_status b_stream_read_line(b_stream *strp, char *s, size_t max)
{ {
if (IS_BSTR(strp)) {
return B_ERR_NOT_SUPPORTED;
}
STREAM_DISPATCH_VIRTUAL(stream_read_line, strp, s, 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 b_stream_read_line_s(b_stream *src, b_stream *dest)
{ {
if (IS_BSTR(src)) {
return B_ERR_NOT_SUPPORTED;
}
enum b_status status; enum b_status status;
struct stream_data src_p, dest_p; struct stream_data src_p, dest_p;
@@ -696,6 +820,10 @@ enum b_status b_stream_read_line_s(b_stream *src, b_stream *dest)
return status; return status;
} }
if (IS_BSTR(dest)) {
return stream_read_line_to_bstr(&src_p, (b_bstr *)dest);
}
status = stream_get_data(dest, &dest_p); status = stream_get_data(dest, &dest_p);
if (!B_OK(status)) { if (!B_OK(status)) {
return status; return status;
@@ -707,12 +835,20 @@ enum b_status b_stream_read_line_s(b_stream *src, b_stream *dest)
enum b_status b_stream_read_all_bytes( enum b_status b_stream_read_all_bytes(
b_stream *stream, void *p, size_t max, size_t *out_nr_read) b_stream *stream, void *p, size_t max, size_t *out_nr_read)
{ {
if (IS_BSTR(stream)) {
return B_ERR_NOT_SUPPORTED;
}
STREAM_DISPATCH_VIRTUAL(stream_read_all_bytes, stream, p, max, 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( enum b_status b_stream_read_all_bytes_s(
b_stream *src, b_stream *dest, b_stream_buffer *buffer, size_t *out_nr_read) b_stream *src, b_stream *dest, b_stream_buffer *buffer, size_t *out_nr_read)
{ {
if (IS_BSTR(src)) {
return B_ERR_NOT_SUPPORTED;
}
enum b_status status; enum b_status status;
struct stream_data src_p, dest_p; struct stream_data src_p, dest_p;
struct b_stream_buffer_p *buffer_p; struct b_stream_buffer_p *buffer_p;
@@ -722,39 +858,66 @@ enum b_status b_stream_read_all_bytes_s(
return 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); buffer_p = b_object_get_private(buffer, B_TYPE_STREAM_BUFFER);
if (!buffer_p) { if (!buffer_p) {
return B_ERR_INVALID_ARGUMENT; return B_ERR_INVALID_ARGUMENT;
} }
if (IS_BSTR(dest)) {
return stream_read_all_bytes_to_bstr_s(
&src_p, (b_bstr *)dest, buffer_p, out_nr_read);
}
status = stream_get_data(dest, &dest_p);
if (!B_OK(status)) {
return status;
}
return stream_read_all_bytes_s(&src_p, &dest_p, buffer_p, out_nr_read); 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, b_wchar c) enum b_status b_stream_write_char(b_stream *stream, b_wchar c)
{ {
if (IS_BSTR(stream)) {
return b_bstr_write_char((b_bstr *)stream, c);
}
STREAM_DISPATCH_VIRTUAL(stream_write_char, stream, c); STREAM_DISPATCH_VIRTUAL(stream_write_char, stream, c);
} }
enum b_status b_stream_write_string( enum b_status b_stream_write_string(
b_stream *stream, const char *s, size_t *nr_written) b_stream *stream, const char *s, size_t *nr_written)
{ {
if (IS_BSTR(stream)) {
return b_bstr_write_cstr((b_bstr *)stream, s, nr_written);
}
STREAM_DISPATCH_VIRTUAL(stream_write_string, stream, s, nr_written); STREAM_DISPATCH_VIRTUAL(stream_write_string, stream, s, nr_written);
} }
enum b_status b_stream_write_bytes( enum b_status b_stream_write_bytes(
b_stream *stream, const void *buf, size_t count, size_t *nr_written) b_stream *stream, const void *buf, size_t count, size_t *nr_written)
{ {
if (IS_BSTR(stream)) {
return b_bstr_write_chars((b_bstr *)stream, buf, count, nr_written);
}
STREAM_DISPATCH_VIRTUAL(stream_write_bytes, stream, buf, count, nr_written); STREAM_DISPATCH_VIRTUAL(stream_write_bytes, stream, buf, count, nr_written);
} }
enum b_status b_stream_write_fmt( enum b_status b_stream_write_fmt(
b_stream *stream, size_t *nr_written, const char *format, ...) b_stream *stream, size_t *nr_written, const char *format, ...)
{ {
if (IS_BSTR(stream)) {
va_list arg;
va_start(arg, format);
b_status w = b_bstr_write_vfmt(
(b_bstr *)stream, nr_written, format, arg);
va_end(arg);
return w;
}
struct stream_data p; struct stream_data p;
enum b_status status = stream_get_data(stream, &p); enum b_status status = stream_get_data(stream, &p);
@@ -773,6 +936,10 @@ enum b_status b_stream_write_fmt(
enum b_status b_stream_write_vfmt( enum b_status b_stream_write_vfmt(
b_stream *stream, size_t *nr_written, const char *format, va_list arg) b_stream *stream, size_t *nr_written, const char *format, va_list arg)
{ {
if (IS_BSTR(stream)) {
return b_bstr_write_vfmt((b_bstr *)stream, nr_written, format, arg);
}
struct stream_data p; struct stream_data p;
enum b_status status = stream_get_data(stream, &p); enum b_status status = stream_get_data(stream, &p);