Compare commits

...

2 Commits

Author SHA1 Message Date
41476ed89b Merge branch 'master' of github.com:utkumaden/libmx 2024-03-23 23:50:16 +03:00
c36b0ac253 Stream IO library. 2024-01-19 16:20:16 +03:00
2 changed files with 270 additions and 0 deletions

53
include/mx/io/stream.h Normal file

@ -0,0 +1,53 @@
#ifndef _MX_IO_STREAM_H_
#define _MX_IO_STREAM_H_
#include "mx/base.h"
typedef enum mx_open_flags
{
MX_OPEN_READ = 1 << 0,
MX_OPEN_WRITE = 1 << 1,
MX_OPEN_APPEND = 1 << 2,
MX_OPEN_NEW = 1 << 3,
} mx_open_flags;
typedef enum mx_seek_origin
{
MX_SEEK_START,
MX_SEEK_CURRENT,
MX_SEEK_END,
} mx_seek_origin;
typedef enum mx_stream_flags
{
MX_STREAM_OPEN = 1 << 0,
MX_STREAM_EOF = 1 << 1,
} mx_stream_flags;
typedef struct mx_stream_base_t
{
mx_stream_flags (*flags)(mx_stream_t str);
mx_len_t (*read)(mx_stream_t str, void *dst, mx_len_t size);
mx_len_t (*write)(mx_stream_t str, const void *src, mx_len_t size);
mx_len_t (*seek)(mx_stream_t str, mx_len_t offset, mx_seek_origin origin);
void (*close)(mx_stream_t str);
} mx_stream_base_t;
typedef const mx_stream_base_t* mx_stream_t;
MX_API mx_stream_t mx_get_stdin(void);
MX_API mx_stream_t mx_get_stdout(void);
MX_API mx_stream_t mx_get_stderr(void);
MX_API mx_stream_t mx_open(const char *file, mx_open_flags flags);
MX_API mx_stream_flags mx_sflags(mx_stream_t str);
MX_API mx_len_t mx_read(mx_stream_t str, void *dst, mx_len_t size);
MX_API mx_len_t mx_write(mx_stream_t str, const void *dst, mx_len_t size);
MX_API mx_len_t mx_seek(mx_stream_t str, mx_len_t offset, mx_seek_origin origin);
MX_API void mx_close(mx_stream_t str);
#define mx_stdin (mx_get_stdin())
#define mx_stdout (mx_get_stdout())
#define mx_stderr (mx_get_stderr())
#endif

217
src/stream.stdc.c Normal file

@ -0,0 +1,217 @@
#include "mx/io/stream.h"
#include "mx/assert.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static mx_stream_flags mx_stdin_flags(mx_stream_t str)
{
return MX_STREAM_OPEN | (feof(stdin) ? MX_STREAM_EOF : 0);
}
static mx_len_t mx_stdin_read(mx_stream_t str, void *dst, mx_len_t size)
{
return (mx_len_t)fread(dst, 1, (size_t)size, stdin);
}
MX_IMPL mx_stream_t mx_get_stdin(void)
{
const static mx_stream_base_t str = {
.flags = mx_stdin_flags,
.read = mx_stdin_read,
.write = NULL,
.seek = NULL,
.close = NULL
};
return &str;
}
static mx_stream_flags mx_stdxxx_flags(mx_stream_t str)
{
return MX_STREAM_OPEN;
}
static mx_len_t mx_stdout_write(mx_stream_t str, const void *src, mx_len_t size)
{
return (mx_len_t)fwrite(src, 1, (size_t)size, stdout);
}
static mx_len_t mx_stderr_write(mx_stream_t str, const void *src, mx_len_t size)
{
return (mx_len_t)fwrite(src, 1, (size_t)size, stderr);
}
MX_IMPL mx_stream_t mx_get_stdout(void)
{
const static mx_stream_base_t str = {
.flags = mx_stdxxx_flags,
.write = mx_stdout_write,
.read = NULL,
.seek = NULL,
.close = NULL
};
return &str;
}
MX_IMPL mx_stream_t mx_get_stderr(void)
{
const static mx_stream_base_t str = {
.flags = mx_stdxxx_flags,
.write = mx_stderr_write,
.read = NULL,
.seek = NULL,
.close = NULL
};
return &str;
}
typedef struct mx_stdc_stream_t
{
mx_stream_base_t base;
FILE *file;
} mx_stdc_stream_t;
#define THIS_FILE(x) (((mx_stdc_stream_t*)(str))->file)
static mx_stream_flags mx_stdc_flags(mx_stream_t str)
{
mx_stream_flags flags = 0;
FILE *f = THIS_FILE(str);
if (fileno(f) != -1)
flags |= MX_STREAM_OPEN;
if (feoff(f))
flags |= MX_STREAM_EOF;
return flags;
}
static mx_len_t mx_stdc_read(mx_stream_t str, void *dst, mx_len_t size)
{
FILE *f = THIS_FILE(str);
return (mx_len_t)fread(dst, 1, (size_t)size, f);
}
static mx_len_t mx_stdc_write(mx_stream_t str, const void *src, mx_len_t size)
{
FILE *f = THIS_FILE(str);
return (mx_len_t)fwrite(src, 1, (size_t)size, f);
}
static mx_len_t mx_stdc_seek(mx_stream_t str, mx_len_t offset, mx_seek_origin origin)
{
FILE *f = THIS_FILE(str);
int stdc_origin;
switch (origin)
{
case MX_SEEK_START: stdc_origin = SEEK_SET; break;
case MX_SEEK_CURRENT: stdc_origin = SEEK_CUR; break;
case MX_SEEK_END: stdc_origin = SEEK_END; break;
}
fseek(f, offset, stdc_origin);
return (mx_len_t)ftell(f);
}
static void mx_stdc_close(mx_stream_t str)
{
FILE *f = THIS_FILE(str);
fclose(f);
free((void*)str);
}
const static mx_stream_base_t mx_stdc_stream_base =
{
.flags = mx_stdc_flags,
.read = mx_stdc_read,
.write = mx_stdc_write,
.seek = mx_stdc_seek,
.close = mx_stdc_close
};
MX_API mx_stream_t mx_open(const char *file, mx_open_flags flags)
{
MX_ASSERT_PTR(file, "File path cannot be null.");
char mode[16] = "b";
if (flags & MX_OPEN_READ)
{
strcat(mode, "r");
}
if (flags & MX_OPEN_WRITE)
{
strcat(mode, "w");
}
if (flags & MX_OPEN_APPEND)
{
strcat(mode, "a");
}
FILE *f = fopen(file, mode);
if (f == NULL)
return NULL;
mx_stdc_stream_t *str = malloc(sizeof(*str));
memcpy(&str->base, &mx_stdc_stream_base, sizeof mx_stdc_stream_base);
if (!(flags & (MX_OPEN_APPEND|MX_OPEN_WRITE)))
{
str->base.write = NULL;
}
return str;
}
MX_IMPL mx_len_t mx_read(mx_stream_t str, void *dst, mx_len_t size)
{
MX_ASSERT_SELFPTR(str);
MX_ASSERT_PTR(dst, "Destination cannot be null");
MX_ASSERT(size >= 0, "Must be non-negative.");
MX_ASSERT(str->read, "Stream has no reader.");
return str->read(str, dst, size);
}
MX_IMPL mx_len_t mx_write(mx_stream_t str, void *dst, mx_len_t size)
{
MX_ASSERT_SELFPTR(str);
MX_ASSERT_PTR(dst, "Source cannot be null");
MX_ASSERT(size >= 0, "Must be non-negative.");
MX_ASSERT(str->write, "Stream has no writer.");
return str->write(str, dst, size);
}
MX_IMPL mx_len_t mx_seek(mx_stream_t str, mx_len_t offset, mx_seek_origin origin)
{
MX_ASSERT_SELFPTR(str);
MX_ASSERT(str->seek, "Stream has no seeker.");
return str->seek(str, offset, origin);
}
MX_IMPL mx_stream_flags mx_sflags(mx_stream_t str)
{
MX_ASSERT_SELFPTR(str);
if (str->flags)
{
return str->flags(str);
}
else
{
return 0;
}
}
MX_IMPL void mx_close(mx_stream_t str)
{
if (!str) return;
if (!str->close) return;
str->close(str);
}