Files
bluelib/core/stream.c

612 lines
11 KiB
C
Raw Normal View History

#include "printf.h"
#include <blue/core/stream.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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_ptr;
enum b_status status = B_SUCCESS;
size_t count = fread(out, 1, max, fp);
if (ferror(fp)) {
status = B_ERR_IO_FAILURE;
}
*nr_read = count;
return status;
}
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_ptr;
enum b_status status = B_SUCCESS;
size_t w = fwrite(data, 1, count, fp);
if (ferror(fp)) {
status = B_ERR_IO_FAILURE;
}
*nr_written = w;
return status;
}
static enum b_status stdio_seek(
struct b_stream *stream, long long offset, b_stream_seek_origin origin)
{
FILE *fp = stream->s_ptr;
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(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_ptr;
long pos = ftell(fp);
if (pos == -1L) {
return B_ERR_NOT_SUPPORTED;
}
*cursor = (size_t)pos;
return B_SUCCESS;
}
static struct b_stream stdio[] = {
[IDX_STDIN] = {
.s_mode = B_STREAM_READ,
.s_read = stdio_read,
.s_seek = stdio_seek,
.s_tell = stdio_tell,
.s_ptr = NULL, /* set to stdin (stdio.h) at runtime */
},
[IDX_STDOUT] = {
.s_mode = B_STREAM_WRITE,
.s_write = stdio_write,
.s_seek = stdio_seek,
.s_tell = stdio_tell,
.s_ptr = NULL, /* set to stdout (stdio.h) at runtime */
},
[IDX_STDERR] = {
.s_mode = B_STREAM_WRITE,
.s_write = stdio_write,
.s_seek = stdio_seek,
.s_tell = stdio_tell,
.s_ptr = NULL, /* set to stderr (stdio.h) at runtime */
},
};
struct b_stream *z__b_stream_get_stdin(void)
{
stdio[IDX_STDIN].s_ptr = stdin;
return &stdio[IDX_STDIN];
}
struct b_stream *z__b_stream_get_stdout(void)
{
stdio[IDX_STDOUT].s_ptr = stdout;
return &stdio[IDX_STDOUT];
}
struct b_stream *z__b_stream_get_stderr(void)
{
stdio[IDX_STDERR].s_ptr = 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_ptr = 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) {
return 0;
}
size_t cursor = 0;
enum b_status status = stream->s_tell(stream, &cursor);
if (!B_OK(status)) {
return 0;
}
return cursor;
}
enum b_status b_stream_push_indent(b_stream *stream, int indent)
{
if (!(stream->s_mode & B_STREAM_WRITE)) {
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;
}
enum b_status b_stream_pop_indent(b_stream *stream)
{
if (!(stream->s_mode & B_STREAM_WRITE)) {
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;
}
enum b_status b_stream_read_char(struct b_stream *stream, int *c)
{
if (!(stream->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) {
size_t r;
unsigned char v = 0;
status = stream->s_read(stream, &v, 1, &r);
*c = v;
if (status == B_SUCCESS && r < 1) {
status = B_ERR_NO_DATA;
}
}
return status;
}
enum b_status b_stream_read_bytes(
struct b_stream *stream, void *buf, size_t count, size_t *nr_read)
{
if (!(stream->s_mode & B_STREAM_READ)) {
return B_ERR_NOT_SUPPORTED;
}
enum b_status status = B_ERR_NOT_SUPPORTED;
if (!stream->s_read) {
return status;
}
return stream->s_read(stream, buf, count, nr_read);
}
enum b_status b_stream_read_line(struct b_stream *stream, char *s, size_t max)
{
if (!(stream->s_mode & B_STREAM_READ)) {
return B_ERR_NOT_SUPPORTED;
}
enum b_status status = B_SUCCESS;
size_t i = 0;
int c = 0;
while (1) {
if (i >= max) {
break;
}
status = b_stream_read_char(stream, &c);
if (status != B_SUCCESS) {
break;
}
if (c == '\n') {
break;
}
s[i++] = c;
s[i] = '\0';
}
return B_SUCCESS;
}
enum b_status b_stream_read_line_s(struct b_stream *src, b_stream *dest)
{
if (!(src->s_mode & B_STREAM_READ)) {
return B_ERR_NOT_SUPPORTED;
}
if (!(dest->s_mode & B_STREAM_WRITE)) {
return B_ERR_NOT_SUPPORTED;
}
enum b_status status = B_SUCCESS;
size_t i = 0;
int c = 0;
while (1) {
status = b_stream_read_char(src, &c);
if (status != B_SUCCESS) {
break;
}
if (c == '\n') {
break;
}
b_stream_write_char(dest, c);
}
return B_SUCCESS;
}
enum b_status b_stream_read_all_bytes(
struct b_stream *stream, void *p, size_t max, size_t *out_nr_read)
{
if (!(stream->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 = b_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;
}
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)
{
if (!(src->s_mode & B_STREAM_READ)) {
return B_ERR_NOT_SUPPORTED;
}
if (!(dest->s_mode & B_STREAM_WRITE)) {
return B_ERR_NOT_SUPPORTED;
}
if (!pipeline) {
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);
b_stream_reserve(dest, length);
}
enum b_status status = B_SUCCESS;
size_t nr_read = 0;
while (1) {
size_t r = 0, w = 0;
status = b_stream_read_bytes(
src, pipeline->p_buf, pipeline->p_buf_len, &r);
if (status != B_SUCCESS) {
break;
}
status = b_stream_write_bytes(dest, pipeline->p_buf, r, &w);
nr_read += w;
if (status != B_SUCCESS || w != pipeline->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 __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)
{
size_t i;
enum b_status status = B_SUCCESS;
for (i = 0; s[i]; i++) {
status = b_stream_write_char(stream, s[i]);
if (!B_OK(status)) {
break;
}
}
if (nr_written) {
*nr_written = i;
}
return status;
}
enum b_status b_stream_write_bytes(
struct b_stream *stream, const void *buf, size_t count, size_t *nr_written)
{
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 status;
}
return stream->s_write(stream, buf, count, nr_written);
}
static void fctprintf_callback(char c, void *p)
{
struct b_stream *stream = p;
b_stream_write_char(stream, c);
}
enum b_status b_stream_write_fmt(
b_stream *stream, size_t *nr_written, const char *format, ...)
{
va_list arg;
va_start(arg, format);
int w = z__b_fctprintf(fctprintf_callback, stream, 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)
{
int w = z__b_fctprintf(fctprintf_callback, stream, format, arg);
if (nr_written) {
*nr_written = w;
}
return B_SUCCESS;
}