mirror of
https://github.com/utkumaden/libmx.git
synced 2025-01-23 05:56:33 +01:00
New trait library.
This commit is contained in:
parent
41476ed89b
commit
1bb9a9cff7
@ -2,6 +2,62 @@
|
||||
#define _MX_IO_STREAM_H_
|
||||
|
||||
#include "mx/base.h"
|
||||
#include "mx/trait.h"
|
||||
|
||||
typedef enum IStream_SeekOrigin
|
||||
{
|
||||
ISTREAM_SEEK_START,
|
||||
ISTERAM_SEEK_CURRENT,
|
||||
ISTREAM_SEEK_END,
|
||||
} IStream_SeekOrigin;
|
||||
|
||||
typedef enum mx_stream_flags
|
||||
{
|
||||
MX_STREAM_OPEN = 1 << 0,
|
||||
MX_STREAM_EOF = 1 << 1,
|
||||
} mx_stream_flags;
|
||||
|
||||
typedef struct IStream
|
||||
{
|
||||
IObject Object;
|
||||
|
||||
mx_stream_flags (*get_flags)(void *self);
|
||||
mx_len_t (*read)(void *self, char *buffer, mx_len_t max);
|
||||
mx_len_t (*seek)(void *self, mx_len_t offset, IStream_SeekOrigin origin);
|
||||
mx_len_t (*write)(void* self, const void *src, mx_len_t size);
|
||||
void (*close)(void *self);
|
||||
} IStream;
|
||||
fatptr_define(IStream);
|
||||
|
||||
MX_INLINE fatptr_t(IObject) IStream_AsIObject(fatptr_t(IStream) str)
|
||||
{
|
||||
return fat_new(str.ptr, str.traits->Object, IObject);
|
||||
}
|
||||
|
||||
MX_INLINE mx_stream_flags IStream_flags(fatptr_t(IStream) str)
|
||||
{
|
||||
return fatptr_vcall(str, get_flags);
|
||||
}
|
||||
|
||||
MX_INLINE mx_len_t IStream_read(fatptr_t(IStream) str, char *buffer, mx_len_t max)
|
||||
{
|
||||
return fatptr_vcall(str, read, buffer, max);
|
||||
}
|
||||
|
||||
MX_INLINE mx_len_t IStream_seek(fatptr_t(IStream) str, mx_len_t offset, IStream_SeekOrigin origin)
|
||||
{
|
||||
return fatptr_vcall(str, seek, offset, origin);
|
||||
}
|
||||
|
||||
MX_INLINE mx_len_t IStream_write(fatptr_t(IStream) str, const void *buffer, mx_len_t size)
|
||||
{
|
||||
return fatptr_vcall(str, write, buffer, size);
|
||||
}
|
||||
|
||||
MX_INLINE void IStream_close(fatptr_t(IStream) str)
|
||||
{
|
||||
fatptr_vcall(str, close);
|
||||
}
|
||||
|
||||
typedef enum mx_open_flags
|
||||
{
|
||||
@ -11,43 +67,13 @@ typedef enum mx_open_flags
|
||||
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);
|
||||
MX_API fatptr_t(IStream) mx_get_stdin(void);
|
||||
MX_API fatptr_t(IStream) mx_get_stdout(void);
|
||||
MX_API fatptr_t(IStream) mx_get_stderr(void);
|
||||
MX_API fatptr_t(IStream) mx_open(const char *file, mx_open_flags flags);
|
||||
|
||||
#define mx_stdin (mx_get_stdin())
|
||||
#define mx_stdout (mx_get_stdout())
|
||||
#define mx_stderr (mx_get_stderr())
|
||||
|
||||
|
||||
#endif
|
219
include/mx/trait.h
Normal file
219
include/mx/trait.h
Normal file
@ -0,0 +1,219 @@
|
||||
#ifndef _MX_TRAIT_H_
|
||||
#define _MX_TRAIT_H_
|
||||
|
||||
/**
|
||||
* @file trait.h Interface Traits and Fat Pointers
|
||||
*
|
||||
* This is the MX interface library. Interfaces define common traits C and C++
|
||||
* objects can have. This functionality is exposed through a fat pointer, which
|
||||
* is just two pointers: the object reference and its virtual table (vtable).
|
||||
*
|
||||
* @section new_interface Creating an Interface
|
||||
* The convention in the library is to prefix an interface with an I, and use
|
||||
* Pascal case naming conventions for the interface structure.
|
||||
*
|
||||
* Define the interface like any other C structure, except, only include function
|
||||
* pointers and other interfaces in it. Then use `fatptr_define` to create a fat
|
||||
* pointer for the given interface.
|
||||
*
|
||||
* @code
|
||||
* typedef struct IFoo {
|
||||
* IObject Object; // Base interface.
|
||||
* void (*bar)(void *self); // Virtual method objects can implement.
|
||||
* } IFoo;
|
||||
*
|
||||
* fatptr_define(IFoo);
|
||||
* @endcode
|
||||
*
|
||||
* Now, in order to create a fat pointer with this interface, you can use the
|
||||
* generic type fatptr_t(T). Notice the parenthesis, because this is a C
|
||||
* library.
|
||||
*
|
||||
* A suggestion is to create inline static functions right after this interface
|
||||
* declaration to aid calling the virtual methods of the trait defined.
|
||||
*
|
||||
* @code
|
||||
* MX_INLINE void IFoo_bar(fatptr_t(IFoo) ptr) {
|
||||
* fatptr_vcall(ptr, bar);
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* In order to implement these traits, create a source code file, and implement
|
||||
* the trait functions as you like. These functions can be static. Make sure
|
||||
* the virtual methods and the implementations have the same function signature.
|
||||
*
|
||||
* Then, create a vtable for the object using the fat_vtable(type, trait), and
|
||||
* refrerence the implemented functions.
|
||||
*
|
||||
* @code
|
||||
* typedef struct MyFoo
|
||||
* {
|
||||
* // Private implementation data.
|
||||
* } MyFoo;
|
||||
*
|
||||
* static void MyFoo_IFoo_bar(void *self)
|
||||
* {
|
||||
* printf("Hello World!\n");
|
||||
* }
|
||||
*
|
||||
* IFoo fat_vtable(MyFoo, IFoo) = { MyFoo_IFoo_bar };
|
||||
* @endcode
|
||||
*
|
||||
* Now you have sucessfully implemented this trait. Now let's see an example
|
||||
* of how this is used.
|
||||
*
|
||||
* @code
|
||||
* void FooConsumer(fatptr_t(IFoo) foo)
|
||||
* {
|
||||
* fat_vcall(foo, bar);
|
||||
* }
|
||||
*
|
||||
* int main(void)
|
||||
* {
|
||||
* MyFoo foo = {};
|
||||
* fatptr_t(IFoo) foo_trait = fat_cast(foo, MyFoo, IFoo);
|
||||
* FooConsumer(foo_trait);
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* If everything went right, this should print <code>Hello World!</code> and exit.
|
||||
*
|
||||
* @section fat_functions "fat" Functions
|
||||
* The structure fatptr_t(T) serves as the main type for this library, where
|
||||
* T is the interface that defines the object traits. Ideally all objects should
|
||||
* implement the IObject trait, but this isn't enforced at all.
|
||||
*
|
||||
* You can use fat_new(ptr, vtable, trait) in your code to create a fat pointer
|
||||
* to the object "ptr", with a "vtable" (the implementation of the traits
|
||||
* defined by the interface) for the interface "trait".
|
||||
*
|
||||
* You can use fat_cast(object, type, trait) to cast the value "object" of
|
||||
* "type" to a fat pointer with traits "trait". This will look up the object
|
||||
* vtable and create a fat pointer automatically.
|
||||
*
|
||||
* fat_reinterpret(value, trait) will reinterpret the fatptr "value" a
|
||||
* fatptr_t(trait). This type of cast is only valid as long as the first member
|
||||
* of the source fat pointer interface is also an interface.
|
||||
*
|
||||
* fat_vnill(fatptr, vmethod) checks if a virtual method "vmethod" of the fat
|
||||
* pointer is nill. This is mostly useful for optional traits.
|
||||
*
|
||||
* fat_vcall(fatptr, vmethod, ...) will invoke the "vmethod" of "fatptr". This
|
||||
* is a GNU-C macro so your mileage may vary using it on niche compilers.
|
||||
*/
|
||||
|
||||
#include "mx/base.h"
|
||||
|
||||
/**
|
||||
* Fat pointer to any object.
|
||||
*/
|
||||
#define fatptr_t(trait) struct __mx_fatptr_##trait##_t
|
||||
|
||||
/**
|
||||
* @brief Define the fat pointer structure.
|
||||
* @param base Base pointer type
|
||||
*/
|
||||
#define fatptr_define(trait) fatptr_t(trait) { void *ptr; trait *traits; }
|
||||
|
||||
/**
|
||||
* Fat pointer vtable for object #type for traits #trait
|
||||
*/
|
||||
#define fat_vtable(type, trait) __mx_fat_vtable_##type##_##trait
|
||||
|
||||
/**
|
||||
* Create a fat pointer to #ptr, with #vtable of #trait.
|
||||
*/
|
||||
#define fat_new(ptr, vtable, trait) ((fatptr_t(trait)){ (ptr), &(vtable)})
|
||||
|
||||
/**
|
||||
* Cast a value #object of type #type into fat pointer of #trait.
|
||||
*/
|
||||
#define fat_cast(object, type, trait) fat_new(&(object), fat_vtable(type, trait), trait)
|
||||
|
||||
/**
|
||||
* Reinterpret a fatptr into another fatptr.
|
||||
*/
|
||||
#define fat_reinterpret(value, trait) (*((fatptr_t(trait)*)&(value)))
|
||||
|
||||
/**
|
||||
* True if vmethod is null.
|
||||
*/
|
||||
#define fat_vnil(fatptr, vmethod) ((fatptr)->traits.vmethod == NULL)
|
||||
|
||||
/**
|
||||
* Base traits of all MX objects.
|
||||
*/
|
||||
typedef struct IObject
|
||||
{
|
||||
/**
|
||||
* Get the size of this object in bytes.
|
||||
* @param self Instance object.
|
||||
* @return The size of the object in bytes.
|
||||
*/
|
||||
size_t (*get_size)(void *self);
|
||||
/**
|
||||
* Get the type object for this.
|
||||
* @param self Instance object.
|
||||
*/
|
||||
void * (*get_type)(void *self);
|
||||
/**
|
||||
* Convert this object to a string.
|
||||
* @param self Instance object.
|
||||
* @param[out] buffer The buffer to write the string into.
|
||||
* @param[in] max The maximum number of bytes to write.
|
||||
* @return The size of the written string.
|
||||
*/
|
||||
size_t (*to_string)(void *self, char *buffer, size_t max);
|
||||
/**
|
||||
* Destructor function for this object.
|
||||
* @param self Instance object.
|
||||
*/
|
||||
void (*destruct)(void* self);
|
||||
} IObject;
|
||||
fatptr_define(IObject);
|
||||
|
||||
// This is GNU C :/
|
||||
|
||||
#define fatptr_vcall(fatptr, vmethod, ...) ((fatptr).traits->vmethod(fatptr.ptr, ## __VA_ARGS__))
|
||||
|
||||
/**
|
||||
* Get the size of this object in bytes.
|
||||
* @param self Instance object.
|
||||
* @return The size of the object in bytes.
|
||||
*/
|
||||
MX_INLINE size_t IObject_get_size(fatptr_t(IObject) obj)
|
||||
{
|
||||
return fatptr_vcall(obj, get_size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type object for this.
|
||||
* @param self Instance object.
|
||||
*/
|
||||
MX_INLINE void* IObject_get_type(fatptr_t(IObject) obj)
|
||||
{
|
||||
return fatptr_vcall(obj, get_type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this object to a string.
|
||||
* @param self Instance object.
|
||||
* @param[out] buffer The buffer to write the string into.
|
||||
* @param[in] max The maximum number of bytes to write.
|
||||
* @return The size of the written string.
|
||||
*/
|
||||
MX_INLINE size_t IObject_to_string(fatptr_t(IObject) obj, char *buffer, size_t max)
|
||||
{
|
||||
return fatptr_vcall(obj, to_string, buffer, max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor function for this object.
|
||||
* @param self Instance object.
|
||||
*/
|
||||
MX_INLINE void IObject_destruct(fatptr_t(IObject) obj)
|
||||
{
|
||||
return fatptr_vcall(obj, destruct);
|
||||
}
|
||||
|
||||
#endif
|
@ -4,214 +4,172 @@
|
||||
#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);
|
||||
}
|
||||
typedef struct mx_file_t {
|
||||
FILE *f;
|
||||
} mx_file_t;
|
||||
|
||||
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);
|
||||
}
|
||||
static size_t mx_file_IObject_get_size(mx_file_t *self);
|
||||
static size_t mx_file_IObject_to_string(mx_file_t *self, char *buffer, size_t max);
|
||||
static void mx_file_IObject_destruct(mx_file_t *self);
|
||||
|
||||
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
|
||||
static mx_stream_flags mx_file_IStream_get_flags(mx_file_t *self);
|
||||
static mx_len_t mx_file_IStream_read(mx_file_t *self, char *buffer, mx_len_t max);
|
||||
static mx_len_t mx_file_IStream_seek(mx_file_t *self, mx_len_t offset, IStream_SeekOrigin origin);
|
||||
static mx_len_t mx_file_IStream_write(mx_file_t *self, const char *buffer, mx_len_t max);
|
||||
static void mx_file_IStream_close(mx_file_t *self);
|
||||
|
||||
const IObject fat_vtable(mx_file_t, IObject) = {
|
||||
.get_size = mx_file_IObject_get_size,
|
||||
.get_type = NULL,
|
||||
.to_string = mx_file_IObject_to_string,
|
||||
.destruct = mx_file_IObject_destruct
|
||||
};
|
||||
|
||||
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
|
||||
const IStream fat_vtable(mx_file_t, IStream) = {
|
||||
.Object = {
|
||||
.get_size = mx_file_IObject_get_size,
|
||||
.get_type = NULL,
|
||||
.to_string = mx_file_IObject_to_string,
|
||||
.destruct = mx_file_IObject_destruct
|
||||
},
|
||||
.get_flags = mx_file_IStream_get_flags,
|
||||
.read = mx_file_IStream_read,
|
||||
.seek = mx_file_IStream_seek,
|
||||
.write = mx_file_IStream_write,
|
||||
.close = mx_file_IStream_close,
|
||||
};
|
||||
|
||||
return &str;
|
||||
static size_t mx_file_IObject_get_size(mx_file_t *self)
|
||||
{
|
||||
return sizeof(self);
|
||||
}
|
||||
|
||||
MX_IMPL mx_stream_t mx_get_stderr(void)
|
||||
static size_t mx_file_IObject_to_string(mx_file_t *self, char *buffer, size_t max)
|
||||
{
|
||||
const static mx_stream_base_t str = {
|
||||
.flags = mx_stdxxx_flags,
|
||||
.write = mx_stderr_write,
|
||||
.read = NULL,
|
||||
.seek = NULL,
|
||||
.close = NULL
|
||||
};
|
||||
const char in[] = "stdin";
|
||||
const char out[] = "stdout";
|
||||
const char err[] = "stderr";
|
||||
|
||||
return &str;
|
||||
if (self->f == stdin)
|
||||
{
|
||||
strncpy(buffer, in, max);
|
||||
return max > sizeof(in) ? sizeof(in) : max;
|
||||
}
|
||||
else if (self->f == stdout)
|
||||
{
|
||||
strncpy(buffer, out, max);
|
||||
return max > sizeof(out) ? sizeof(out) : max;
|
||||
}
|
||||
else if (self->f == stderr)
|
||||
{
|
||||
strncpy(buffer, err, max);
|
||||
return max > sizeof(err) ? sizeof(err) : max;
|
||||
}
|
||||
else
|
||||
{
|
||||
return snprintf(buffer, max, "FILE* %p", self->f);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct mx_stdc_stream_t
|
||||
static void mx_file_IObject_destruct(mx_file_t *self)
|
||||
{
|
||||
mx_stream_base_t base;
|
||||
FILE *file;
|
||||
} mx_stdc_stream_t;
|
||||
fatptr_vcall(fat_new(self, fat_vtable(mx_file_t, IStream), IStream), close);
|
||||
free(self);
|
||||
}
|
||||
|
||||
#define THIS_FILE(x) (((mx_stdc_stream_t*)(str))->file)
|
||||
|
||||
static mx_stream_flags mx_stdc_flags(mx_stream_t str)
|
||||
static mx_stream_flags mx_file_IStream_get_flags(mx_file_t *self)
|
||||
{
|
||||
mx_stream_flags flags = 0;
|
||||
FILE *f = THIS_FILE(str);
|
||||
if (self->f == NULL)
|
||||
return MX_STREAM_EOF;
|
||||
|
||||
if (fileno(f) != -1)
|
||||
flags |= MX_STREAM_OPEN;
|
||||
if (feoff(f))
|
||||
flags |= MX_STREAM_EOF;
|
||||
mx_stream_flags flags = MX_STREAM_OPEN;
|
||||
|
||||
flags |= !!feof(self->f) * MX_STREAM_EOF;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
static mx_len_t mx_stdc_read(mx_stream_t str, void *dst, mx_len_t size)
|
||||
static mx_len_t mx_file_IStream_read(mx_file_t *self, char *buffer, mx_len_t max)
|
||||
{
|
||||
FILE *f = THIS_FILE(str);
|
||||
return (mx_len_t)fread(dst, 1, (size_t)size, f);
|
||||
return fread(buffer, 1, max, self->f);
|
||||
}
|
||||
|
||||
static mx_len_t mx_stdc_write(mx_stream_t str, const void *src, mx_len_t size)
|
||||
static mx_len_t mx_file_IStream_seek(mx_file_t *self, mx_len_t offset, IStream_SeekOrigin origin)
|
||||
{
|
||||
FILE *f = THIS_FILE(str);
|
||||
return (mx_len_t)fwrite(src, 1, (size_t)size, f);
|
||||
return fseek(self->f, offset, origin);
|
||||
}
|
||||
|
||||
static mx_len_t mx_stdc_seek(mx_stream_t str, mx_len_t offset, mx_seek_origin origin)
|
||||
static mx_len_t mx_file_IStream_write(mx_file_t *self, const char *buffer, mx_len_t max)
|
||||
{
|
||||
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;
|
||||
return fwrite(buffer, 1, max, self->f);
|
||||
}
|
||||
|
||||
fseek(f, offset, stdc_origin);
|
||||
return (mx_len_t)ftell(f);
|
||||
static void mx_file_IStream_close(mx_file_t *self)
|
||||
{
|
||||
if (self->f == NULL)
|
||||
return;
|
||||
else if (self->f != stdin || self->f != stdout || self->f != stderr)
|
||||
fclose(self->f);
|
||||
|
||||
self->f = NULL;
|
||||
}
|
||||
|
||||
static void mx_stdc_close(mx_stream_t str)
|
||||
MX_API fatptr_t(IStream) mx_get_stdin(void)
|
||||
{
|
||||
FILE *f = THIS_FILE(str);
|
||||
static bool init = false;
|
||||
static mx_file_t fd;
|
||||
|
||||
fclose(f);
|
||||
free((void*)str);
|
||||
if (!init)
|
||||
{
|
||||
fd.f = stdin;
|
||||
init = true;
|
||||
}
|
||||
|
||||
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");
|
||||
return fat_cast(fd, mx_file_t, IStream);
|
||||
}
|
||||
|
||||
if (flags & MX_OPEN_WRITE)
|
||||
MX_API fatptr_t(IStream) mx_get_stdout(void)
|
||||
{
|
||||
strcat(mode, "w");
|
||||
}
|
||||
static bool init = false;
|
||||
static mx_file_t fd;
|
||||
|
||||
if (flags & MX_OPEN_APPEND)
|
||||
if (!init)
|
||||
{
|
||||
strcat(mode, "a");
|
||||
fd.f = stdout;
|
||||
init = true;
|
||||
}
|
||||
|
||||
FILE *f = fopen(file, mode);
|
||||
return fat_cast(fd, mx_file_t, IStream);
|
||||
}
|
||||
|
||||
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)))
|
||||
MX_API fatptr_t(IStream) mx_get_stderr(void)
|
||||
{
|
||||
str->base.write = NULL;
|
||||
static bool init = false;
|
||||
static mx_file_t fd;
|
||||
|
||||
if (!init)
|
||||
{
|
||||
fd.f = stderr;
|
||||
init = true;
|
||||
}
|
||||
|
||||
return str;
|
||||
return fat_cast(fd, mx_file_t, IStream);
|
||||
}
|
||||
|
||||
MX_IMPL mx_len_t mx_read(mx_stream_t str, void *dst, mx_len_t size)
|
||||
MX_API fatptr_t(IStream) mx_open(const char *file, mx_open_flags flags)
|
||||
{
|
||||
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);
|
||||
char options[6] = "b";
|
||||
if (flags & MX_OPEN_READ) strcat(options, "r");
|
||||
if (flags & MX_OPEN_WRITE) strcat(options, "w");
|
||||
if (flags & MX_OPEN_APPEND) strcat(options, "a");
|
||||
if (flags & MX_OPEN_NEW) strcat(options, "s");
|
||||
|
||||
mx_file_t *self = malloc(sizeof(mx_file_t));
|
||||
if (!self)
|
||||
{
|
||||
return fat_new(NULL, *NULL, IStream);
|
||||
}
|
||||
|
||||
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);
|
||||
self->f = fopen(file, options);
|
||||
return fat_new(self, fat_vtable(mx_file_t, IStream), IStream);
|
||||
}
|
Loading…
Reference in New Issue
Block a user