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.
1135 lines
24 KiB
C
1135 lines
24 KiB
C
#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, ...) \
|
|
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
|
|
|
|
B_DECLARE_TYPE(b_stdio_stream);
|
|
|
|
B_TYPE_CLASS_DECLARATION_BEGIN(b_stdio_stream)
|
|
B_TYPE_CLASS_DECLARATION_END(b_stdio_stream)
|
|
|
|
/*** PRIVATE DATA *************************************************************/
|
|
|
|
typedef enum b_stream_buffer_flags {
|
|
B_STREAM_BUFFER_F_NONE = 0x00u,
|
|
B_STREAM_BUFFER_F_DYNAMIC = 0x01u,
|
|
} b_stream_buffer_flags;
|
|
|
|
struct b_stream_p {
|
|
int *s_istack;
|
|
int s_add_indent;
|
|
size_t s_istack_ptr, s_istack_size;
|
|
void *s_ptr0, *s_ptr1;
|
|
};
|
|
|
|
struct b_stdio_stream_p {
|
|
FILE *s_fp;
|
|
};
|
|
|
|
struct b_stream_buffer_p {
|
|
b_stream_buffer_flags p_flags;
|
|
void *p_buf;
|
|
size_t p_buf_len;
|
|
};
|
|
|
|
struct stream_data {
|
|
b_stream *s_obj;
|
|
struct b_stream_p *s_private;
|
|
struct b_stream_cfg *s_cfg;
|
|
b_stream_class *s_ops;
|
|
};
|
|
|
|
static b_stream *stdio[] = {
|
|
[IDX_STDIN] = NULL,
|
|
[IDX_STDOUT] = NULL,
|
|
[IDX_STDERR] = NULL,
|
|
};
|
|
|
|
/*** PRIVATE FUNCTIONS ********************************************************/
|
|
|
|
b_type b_stdio_stream_get_type(void);
|
|
static enum b_status stdio_read(b_stream *, void *, size_t, size_t *);
|
|
static enum b_status stdio_write(b_stream *, const void *, 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)
|
|
{
|
|
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);
|
|
}
|
|
|
|
static int current_indent(struct stream_data *stream)
|
|
{
|
|
if (!stream->s_private->s_istack || !stream->s_private->s_istack_size) {
|
|
return 0;
|
|
}
|
|
|
|
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 B_NPOS;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
static enum b_status stream_seek(
|
|
struct stream_data *stream, long long offset, b_stream_seek_origin origin)
|
|
{
|
|
if (!stream || !stream->s_ops || !stream->s_ops->s_seek) {
|
|
return B_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
return stream->s_ops->s_seek(stream->s_obj, offset, origin);
|
|
}
|
|
|
|
static enum b_status stream_reserve(struct stream_data *stream, size_t len)
|
|
{
|
|
if (!stream || !stream->s_ops || !stream->s_ops->s_reserve) {
|
|
return B_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
return stream->s_ops->s_reserve(stream->s_obj, len);
|
|
}
|
|
|
|
static enum b_status read_char_binary(struct stream_data *stream, b_wchar *c)
|
|
{
|
|
size_t r;
|
|
unsigned char v = 0;
|
|
enum b_status status = stream->s_ops->s_read(stream->s_obj, &v, 1, &r);
|
|
|
|
*c = v;
|
|
|
|
if (status == B_SUCCESS && r < 1) {
|
|
status = B_ERR_NO_DATA;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static enum b_status read_char_utf8(struct stream_data *stream, b_wchar *c)
|
|
{
|
|
size_t r;
|
|
unsigned char s[4];
|
|
unsigned int len = 0;
|
|
enum b_status status = stream->s_ops->s_read(stream->s_obj, s, 1, &r);
|
|
|
|
if (!B_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (r < 1) {
|
|
return B_ERR_NO_DATA;
|
|
}
|
|
|
|
len = b_wchar_utf8_header_decode(s[0]);
|
|
if (len <= 0 || len > 4) {
|
|
return B_ERR_BAD_FORMAT;
|
|
}
|
|
|
|
if (len == 1) {
|
|
*c = s[0];
|
|
return B_SUCCESS;
|
|
}
|
|
|
|
status = stream->s_ops->s_read(stream->s_obj, s + 1, len - 1, &r);
|
|
|
|
if (!B_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (r != len - 1) {
|
|
return B_ERR_BAD_FORMAT;
|
|
}
|
|
|
|
b_wchar result = b_wchar_utf8_codepoint_decode((char *)s);
|
|
|
|
if (result == B_WCHAR_INVALID) {
|
|
return B_ERR_BAD_FORMAT;
|
|
}
|
|
|
|
*c = result;
|
|
return B_SUCCESS;
|
|
}
|
|
|
|
static enum b_status stream_read_char(struct stream_data *stream, b_wchar *c)
|
|
{
|
|
if (!(stream->s_cfg->s_mode & B_STREAM_READ)) {
|
|
return B_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
enum b_status status = B_ERR_NOT_SUPPORTED;
|
|
|
|
#if 0
|
|
if (stream->s_ops->s_getc) {
|
|
return stream->s_ops->s_getc(stream->s_obj, c);
|
|
}
|
|
#endif
|
|
|
|
if (!stream->s_ops->s_read) {
|
|
return B_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (stream->s_cfg->s_mode & B_STREAM_BINARY) {
|
|
return read_char_binary(stream, c);
|
|
}
|
|
|
|
return read_char_utf8(stream, c);
|
|
}
|
|
|
|
static enum b_status __write_char(struct stream_data *stream, b_wchar c)
|
|
{
|
|
size_t w, len;
|
|
enum b_status status;
|
|
char s[4];
|
|
|
|
if (stream->s_cfg->s_mode & B_STREAM_BINARY) {
|
|
s[0] = c & 0xFF;
|
|
len = 1;
|
|
} else {
|
|
len = b_wchar_utf8_codepoint_encode(c, s);
|
|
if (len == 0 || len > 4) {
|
|
return B_ERR_INVALID_ARGUMENT;
|
|
}
|
|
}
|
|
|
|
status = stream->s_ops->s_write(stream->s_obj, (unsigned char *)s, len, &w);
|
|
|
|
if (!B_OK(status)) {
|
|
return status;
|
|
}
|
|
|
|
if (status == B_SUCCESS && w < len) {
|
|
status = B_ERR_IO_FAILURE;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static enum b_status stream_write_char(struct stream_data *stream, b_wchar 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_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_ops->s_read(stream->s_obj, buf, count, nr_read);
|
|
}
|
|
|
|
static enum b_status stream_read_line(struct stream_data *stream, char *s, size_t max)
|
|
{
|
|
if (!(stream->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) {
|
|
if (i >= max) {
|
|
break;
|
|
}
|
|
|
|
status = stream_read_char(stream, &c);
|
|
if (status != B_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
if (c == '\n') {
|
|
break;
|
|
}
|
|
|
|
s[i++] = c;
|
|
s[i] = '\0';
|
|
}
|
|
|
|
return B_SUCCESS;
|
|
}
|
|
|
|
static enum b_status stream_read_line_s(
|
|
struct stream_data *src, struct stream_data *dest)
|
|
{
|
|
if (!(src->s_cfg->s_mode & B_STREAM_READ)) {
|
|
return B_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!(dest->s_cfg->s_mode & B_STREAM_WRITE)) {
|
|
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;
|
|
}
|
|
|
|
stream_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_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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
enum b_status status = B_SUCCESS;
|
|
size_t nr_read = 0;
|
|
unsigned char *s = p;
|
|
|
|
while (nr_read < max) {
|
|
int c;
|
|
status = stream_read_char(stream, &c);
|
|
if (status != B_SUCCESS) {
|
|
break;
|
|
}
|
|
|
|
s[nr_read++] = c;
|
|
}
|
|
|
|
if (status == B_ERR_NO_DATA && nr_read > 0) {
|
|
status = B_SUCCESS;
|
|
}
|
|
|
|
*out_nr_read = nr_read;
|
|
return status;
|
|
}
|
|
|
|
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_cfg->s_mode & B_STREAM_READ)) {
|
|
return B_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!(dest->s_cfg->s_mode & B_STREAM_WRITE)) {
|
|
return B_ERR_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (!buffer) {
|
|
return B_ERR_INVALID_ARGUMENT;
|
|
}
|
|
|
|
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);
|
|
|
|
stream_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 = stream_write_bytes(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_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)
|
|
{
|
|
size_t i;
|
|
enum b_status status = B_SUCCESS;
|
|
|
|
for (i = 0; s[i]; i++) {
|
|
status = stream_write_char(stream, s[i]);
|
|
|
|
if (!B_OK(status)) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (nr_written) {
|
|
*nr_written = i;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static b_stream *init_stdio_stream(FILE *fp, b_stream_mode mode)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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 = 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 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)
|
|
{
|
|
return init_stdio_stream(fp, B_STREAM_READ | B_STREAM_WRITE);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
|
|
status = stream_get_data(src, &src_p);
|
|
if (!B_OK(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);
|
|
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)
|
|
{
|
|
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;
|
|
|
|
status = stream_get_data(src, &src_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);
|
|
|
|
va_list arg;
|
|
va_start(arg, format);
|
|
int w = z__b_fctprintf(fctprintf_callback, &p, format, arg);
|
|
va_end(arg);
|
|
|
|
if (nr_written) {
|
|
*nr_written = w;
|
|
}
|
|
|
|
return B_SUCCESS;
|
|
}
|
|
|
|
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);
|
|
|
|
int w = z__b_fctprintf(fctprintf_callback, &p, format, arg);
|
|
|
|
if (nr_written) {
|
|
*nr_written = w;
|
|
}
|
|
|
|
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, void *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 void *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)
|