Author: mesyeti <mesyeti@mesyeti.uk>
add stream library
README.md | 11 ++ nbplib/stream.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++++ nbplib/stream.h | 86 +++++++++++++++++
diff --git a/README.md b/README.md index b08f18ae34af6bead4aa4296174edb0f182b2710..98e8899d5446cd299419eef1324898a7a6670ef3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,12 @@ # plib -A collection (soon) of libraries to help with portability \ No newline at end of file +A collection (soon) of libraries to help with portability + +## How to use +Add the `.c` file for the library/s you want to use to your build system, or `#include` the +`.c` file in your project's source file. Use the header files the same way you would +typically use a library. + +## Libraries +- stream + - An extendable stream implementation diff --git a/nbplib/stream.c b/nbplib/stream.c new file mode 100644 index 0000000000000000000000000000000000000000..01264065db5a2d71721b118a77cd818b676d4c2c --- /dev/null +++ b/nbplib/stream.c @@ -0,0 +1,255 @@ +#ifndef NBP_NO_STDIO + #include <stdio.h> +#endif + +#include <string.h> +#include <stdlib.h> +#include "stream.h" + +void NBP_Stream_Close(NBP_Stream* stream) { + if (stream->close) { + stream->close(stream); + } + + if (stream->data) { + free(stream->data); + } +} + +size_t NBP_Stream_Write(NBP_Stream* stream, size_t size, void* data) { + if (!stream->write) return 0; + + return stream->write(stream, size, data); +} + +size_t NBP_Stream_Read(NBP_Stream* stream, size_t size, void* dest) { + return stream->read(stream, size, dest); +} + +size_t NBP_Stream_Peek(NBP_Stream* stream) { + return stream->peek(stream); +} + +bool NBP_Stream_Seek(NBP_Stream* stream, size_t where) { + return stream->seek(stream, where); +} + +size_t NBP_Stream_Size(NBP_Stream* stream) { + return stream->size(stream); +} + +bool NBP_Stream_Advance(NBP_Stream* stream, size_t bytes) { + return NBP_Stream_Seek(stream, NBP_Stream_Peek(stream) + bytes); +} + +#ifndef NBP_NO_STDIO + typedef struct { + FILE* file; + } FileStream; + + static void FileClose(NBP_Stream* stream) { + FileStream* data = (FileStream*) stream->data; + fclose(data->file); + } + + static size_t FileWrite(NBP_Stream* p_stream, size_t size, void* data) { + FileStream* stream = (FileStream*) p_stream->data; + + return fwrite(data, 1, size, stream->file); + } + + static size_t FileRead(NBP_Stream* stream, size_t size, void* dest) { + FileStream* data = (FileStream*) stream->data; + + return fread(dest, 1, size, data->file); + } + + static size_t FilePeek(NBP_Stream* stream) { + FileStream* data = (FileStream*) stream->data; + + return (size_t) ftell(data->file); + } + + static bool FileSeek(NBP_Stream* stream, size_t where) { + FileStream* data = (FileStream*) stream->data; + + return fseek(data->file, (long) where, SEEK_SET) == 0; + } + + static size_t FileSize(NBP_Stream* stream) { + FileStream* data = (FileStream*) stream->data; + + long oldPos = ftell(data->file); + fseek(data->file, 0, SEEK_END); + + size_t ret = (size_t) ftell(data->file); + fseek(data->file, oldPos, SEEK_SET); + + return ret; + } + + bool NBP_Stream_File(NBP_Stream* dest, FILE* file, bool close) { + FileStream* data = malloc(sizeof(FileStream)); + + if (!data) return false; + + data->file = file; + + *dest = (NBP_Stream) { + .data = data, + .close = close? &FileClose : NULL, + .write = &FileWrite, + .read = &FileRead, + .peek = &FilePeek, + .seek = &FileSeek, + .size = &FileSize + }; + return true; + } +#endif + +typedef struct { + uint8_t* data; + size_t offset; + size_t size; +} MemStream; + +static void MemClose(NBP_Stream* stream) { + MemStream* data = (MemStream*) stream->data; + + free(data->data); +} + +static size_t MemRead(NBP_Stream* stream, size_t size, void* dest) { + MemStream* data = (MemStream*) stream->data; + + if (data->offset + size > data->size) { + size = data->size - data->offset; + } + if (data->offset >= data->size) { + return 0; + } + + memcpy(dest, &data->data[data->offset], size); + + data->offset += size; + + return size; +} + +static size_t MemPeek(NBP_Stream* stream) { + MemStream* data = (MemStream*) stream->data; + + return data->offset; +} + +static bool MemSeek(NBP_Stream* stream, size_t where) { + MemStream* data = (MemStream*) stream->data; + + if (where > data->size) { + return false; + } + + data->offset = where; + return true; +} + +static size_t MemSize(NBP_Stream* stream) { + MemStream* data = (MemStream*) stream->data; + + return data->size; +} + +bool NBP_Stream_Memory(NBP_Stream* dest, void* addr, size_t size, bool free) { + MemStream* data = malloc(sizeof(MemStream)); + + if (!data) return false; + + data->data = addr; + data->offset = 0; + data->size = size; + + *dest = (NBP_Stream) { + .data = data, + .close = free? &MemClose : NULL, + .write = NULL, + .read = &MemRead, + .peek = &MemPeek, + .seek = &MemSeek, + .size = &MemSize + }; +} + +typedef struct { + NBP_Stream* stream; + size_t offset; + size_t start; + size_t size; +} SubStream; + +static size_t SubRead(NBP_Stream* stream, size_t size, void* dest) { + SubStream* data = (SubStream*) stream->data; + + if (data->offset + size > data->size) { + size = data->size - data->offset; + } + if (data->offset >= data->size) { + return 0; + } + + NBP_Stream_Seek(data->stream, data->start + data->offset); + size_t ret = NBP_Stream_Read(data->stream, size, dest); + + data->offset += size; + + return ret; +} + +static size_t SubPeek(NBP_Stream* stream) { + SubStream* data = (SubStream*) stream->data; + return data->offset; +} + +static bool SubSeek(NBP_Stream* stream, size_t where) { + SubStream* data = (SubStream*) stream->data; + + if (where > data->size) { + return false; + } + + data->offset = where; + return true; +} + +static size_t SubSize(NBP_Stream* stream) { + SubStream* data = (SubStream*) stream->data; + + return data->size; +} + +bool NBP_Stream_SubStream(NBP_Stream* dest, NBP_Stream* stream, size_t start, size_t size) { + SubStream* data = malloc(sizeof(SubStream)); + + if (!data) return false; + + data->stream = stream; + data->offset = 0; + data->start = start; + data->size = size; + + *dest = (NBP_Stream) { + .data = data, + .close = NULL, + .write = NULL, + .read = &SubRead, + .peek = &SubPeek, + .seek = &SubSeek, + .size = &SubSize + }; + return true; +} + +bool NBP_Stream_Blank(NBP_Stream* dest) { + *dest = (NBP_Stream) {0}; + return true; +} diff --git a/nbplib/stream.h b/nbplib/stream.h new file mode 100644 index 0000000000000000000000000000000000000000..70abb7ae3c6155640215f5cfa697be991964975f --- /dev/null +++ b/nbplib/stream.h @@ -0,0 +1,86 @@ +#ifndef NBP_STREAM_H +#define NBP_STREAM_H + +#include <stdint.h> + +// Options +// define `NBP_NO_STDIO` to remove operations requiring stdio + +// Template for creating Stream implementations: +/* +static void Close(Stream* stream) { + TYPE* data = (TYPE*) stream->data; +} + +static size_t Read(Stream* stream, size_t size, void* dest) { + TYPE* data = (TYPE*) stream->data; +} + +static size_t Peek(Stream* stream) { + TYPE* data = (TYPE*) stream->data; +} + +static bool Seek(Stream* stream, size_t where) { + TYPE* data = (TYPE*) stream->data; +} + +static size_t Size(Stream* stream) { + TYPE* data = (TYPE*) stream->data; +} +*/ + +typedef struct NBP_Stream NBP_Stream; + +struct NBP_Stream { + void* data; + + void (*close)(NBP_Stream* stream); + size_t (*write)(NBP_Stream* stream, size_t size, void* data); + size_t (*read)(NBP_Stream* stream, size_t size, void* dest); + size_t (*peek)(NBP_Stream* stream); + bool (*seek)(NBP_Stream* stream, size_t where); + size_t (*size)(NBP_Stream* stream); +}; + +// FUNCTIONS + +// Closes a stream +void NBP_Stream_Close(NBP_Stream* stream); + +// Writes `size` bytes from `data` into `stream` +size_t NBP_Stream_Write(NBP_Stream* stream, size_t size, void* data); + +// Reads `size` bytes from `stream` into `dest` +size_t NBP_Stream_Read(NBP_Stream* stream, size_t size, void* dest); + +// Returns the offset where `stream` is seeked +size_t NBP_Stream_Peek(NBP_Stream* stream); + +// Seeks `stream` to `where` bytes from the start of the stream +bool NBP_Stream_Seek(NBP_Stream* stream, size_t where); + +// Returns the length of the stream +size_t NBP_Stream_Size(NBP_Stream* stream); + +// Seeks `stream` forward by `bytes` bytes +bool NBP_Stream_Advance(NBP_Stream* stream, size_t bytes); + +// Creates a file stream +// - `close` - If true, closing the stream will close `file` +// - Returns true on success +bool NBP_Stream_File(NBP_Stream* dest, FILE* file, bool close); + +// Creates a memory stream +// - `free` - If true, closing the stream will free `addr` +// - Returns true on success +bool NBP_Stream_Memory(NBP_Stream* dest, void* addr, size_t size, bool free); + +// Creates a sub-stream that reads data from a certain point in `stream` +// - Returns true on success +bool NBP_Stream_SubStream(NBP_Stream* dest, NBP_Stream* stream, size_t start, size_t size); + +// Blank stream that can't do anything +// - Returns true on success +bool NBP_Stream_Blank(NBP_Stream* dest); + +#endif