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_
|
#define _MX_IO_STREAM_H_
|
||||||
|
|
||||||
#include "mx/base.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
|
typedef enum mx_open_flags
|
||||||
{
|
{
|
||||||
@ -11,43 +67,13 @@ typedef enum mx_open_flags
|
|||||||
MX_OPEN_NEW = 1 << 3,
|
MX_OPEN_NEW = 1 << 3,
|
||||||
} mx_open_flags;
|
} mx_open_flags;
|
||||||
|
|
||||||
typedef enum mx_seek_origin
|
MX_API fatptr_t(IStream) mx_get_stdin(void);
|
||||||
{
|
MX_API fatptr_t(IStream) mx_get_stdout(void);
|
||||||
MX_SEEK_START,
|
MX_API fatptr_t(IStream) mx_get_stderr(void);
|
||||||
MX_SEEK_CURRENT,
|
MX_API fatptr_t(IStream) mx_open(const char *file, mx_open_flags flags);
|
||||||
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_stdin (mx_get_stdin())
|
||||||
#define mx_stdout (mx_get_stdout())
|
#define mx_stdout (mx_get_stdout())
|
||||||
#define mx_stderr (mx_get_stderr())
|
#define mx_stderr (mx_get_stderr())
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#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 <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
static mx_stream_flags mx_stdin_flags(mx_stream_t str)
|
typedef struct mx_file_t {
|
||||||
|
FILE *f;
|
||||||
|
} mx_file_t;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t mx_file_IObject_get_size(mx_file_t *self)
|
||||||
{
|
{
|
||||||
return MX_STREAM_OPEN | (feof(stdin) ? MX_STREAM_EOF : 0);
|
return sizeof(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
static mx_len_t mx_stdin_read(mx_stream_t str, void *dst, mx_len_t size)
|
static size_t mx_file_IObject_to_string(mx_file_t *self, char *buffer, size_t max)
|
||||||
{
|
{
|
||||||
return (mx_len_t)fread(dst, 1, (size_t)size, stdin);
|
const char in[] = "stdin";
|
||||||
|
const char out[] = "stdout";
|
||||||
|
const char err[] = "stderr";
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MX_IMPL mx_stream_t mx_get_stdin(void)
|
static void mx_file_IObject_destruct(mx_file_t *self)
|
||||||
{
|
{
|
||||||
const static mx_stream_base_t str = {
|
fatptr_vcall(fat_new(self, fat_vtable(mx_file_t, IStream), IStream), close);
|
||||||
.flags = mx_stdin_flags,
|
free(self);
|
||||||
.read = mx_stdin_read,
|
|
||||||
.write = NULL,
|
|
||||||
.seek = NULL,
|
|
||||||
.close = NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
return &str;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static mx_stream_flags mx_stdxxx_flags(mx_stream_t str)
|
static mx_stream_flags mx_file_IStream_get_flags(mx_file_t *self)
|
||||||
{
|
{
|
||||||
return MX_STREAM_OPEN;
|
if (self->f == NULL)
|
||||||
}
|
return MX_STREAM_EOF;
|
||||||
|
|
||||||
static mx_len_t mx_stdout_write(mx_stream_t str, const void *src, mx_len_t size)
|
mx_stream_flags flags = MX_STREAM_OPEN;
|
||||||
{
|
|
||||||
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)
|
flags |= !!feof(self->f) * MX_STREAM_EOF;
|
||||||
{
|
|
||||||
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;
|
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 fread(buffer, 1, max, self->f);
|
||||||
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)
|
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 fseek(self->f, offset, origin);
|
||||||
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)
|
static mx_len_t mx_file_IStream_write(mx_file_t *self, const char *buffer, mx_len_t max)
|
||||||
{
|
{
|
||||||
FILE *f = THIS_FILE(str);
|
return fwrite(buffer, 1, max, self->f);
|
||||||
int stdc_origin;
|
}
|
||||||
|
|
||||||
switch (origin)
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
MX_API fatptr_t(IStream) mx_get_stdin(void)
|
||||||
|
{
|
||||||
|
static bool init = false;
|
||||||
|
static mx_file_t fd;
|
||||||
|
|
||||||
|
if (!init)
|
||||||
{
|
{
|
||||||
case MX_SEEK_START: stdc_origin = SEEK_SET; break;
|
fd.f = stdin;
|
||||||
case MX_SEEK_CURRENT: stdc_origin = SEEK_CUR; break;
|
init = true;
|
||||||
case MX_SEEK_END: stdc_origin = SEEK_END; break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fseek(f, offset, stdc_origin);
|
return fat_cast(fd, mx_file_t, IStream);
|
||||||
return (mx_len_t)ftell(f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mx_stdc_close(mx_stream_t str)
|
MX_API fatptr_t(IStream) mx_get_stdout(void)
|
||||||
{
|
{
|
||||||
FILE *f = THIS_FILE(str);
|
static bool init = false;
|
||||||
|
static mx_file_t fd;
|
||||||
|
|
||||||
fclose(f);
|
if (!init)
|
||||||
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");
|
fd.f = stdout;
|
||||||
|
init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & MX_OPEN_WRITE)
|
return fat_cast(fd, mx_file_t, IStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
MX_API fatptr_t(IStream) mx_get_stderr(void)
|
||||||
|
{
|
||||||
|
static bool init = false;
|
||||||
|
static mx_file_t fd;
|
||||||
|
|
||||||
|
if (!init)
|
||||||
{
|
{
|
||||||
strcat(mode, "w");
|
fd.f = stderr;
|
||||||
|
init = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flags & MX_OPEN_APPEND)
|
return fat_cast(fd, mx_file_t, IStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
MX_API fatptr_t(IStream) mx_open(const char *file, mx_open_flags flags)
|
||||||
|
{
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
strcat(mode, "a");
|
return fat_new(NULL, *NULL, IStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE *f = fopen(file, mode);
|
self->f = fopen(file, options);
|
||||||
|
return fat_new(self, fat_vtable(mx_file_t, IStream), 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)))
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user