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:
177
core/stream.c
177
core/stream.c
@@ -1,10 +1,14 @@
|
||||
#include "printf.h"
|
||||
|
||||
#include <blue/core/bstr.h>
|
||||
#include <blue/core/stream.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.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 STREAM_DISPATCH_VIRTUAL(func, stream, ...) \
|
||||
@@ -369,6 +373,39 @@ static enum b_status stream_read_line_s(
|
||||
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(
|
||||
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;
|
||||
}
|
||||
|
||||
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(
|
||||
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)
|
||||
{
|
||||
if (IS_BSTR(stream)) {
|
||||
return B_ERR_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
B_CLASS_DISPATCH_VIRTUAL(
|
||||
b_stream, B_TYPE_STREAM, B_ERR_NOT_SUPPORTED, s_reserve, stream,
|
||||
len);
|
||||
@@ -650,6 +742,10 @@ enum b_status b_stream_reserve(b_stream *stream, size_t len)
|
||||
enum b_status b_stream_seek(
|
||||
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_stream, B_TYPE_STREAM, B_ERR_NOT_SUPPORTED, s_seek, stream,
|
||||
offset, origin);
|
||||
@@ -657,37 +753,65 @@ enum b_status b_stream_seek(
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
enum b_status b_stream_read_bytes(
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
if (IS_BSTR(dest)) {
|
||||
return stream_read_line_to_bstr(&src_p, (b_bstr *)dest);
|
||||
}
|
||||
|
||||
status = stream_get_data(dest, &dest_p);
|
||||
if (!B_OK(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(
|
||||
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);
|
||||
}
|
||||
|
||||
enum b_status b_stream_read_all_bytes_s(
|
||||
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;
|
||||
struct stream_data src_p, dest_p;
|
||||
struct b_stream_buffer_p *buffer_p;
|
||||
@@ -722,39 +858,66 @@ enum b_status b_stream_read_all_bytes_s(
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
enum b_status b_stream_write_string(
|
||||
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);
|
||||
}
|
||||
|
||||
enum b_status b_stream_write_bytes(
|
||||
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);
|
||||
}
|
||||
|
||||
enum b_status b_stream_write_fmt(
|
||||
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;
|
||||
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(
|
||||
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;
|
||||
enum b_status status = stream_get_data(stream, &p);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user