From 785070798c661de51e7485a6d72bec472ed480c2 Mon Sep 17 00:00:00 2001 From: Mikhail Romanko Date: Mon, 8 Jul 2024 22:25:02 +0300 Subject: Add peek buffer to io, hide file/buffer private functions Implements peek buffer on top of the raw io read (with small storage optimization). Hides implementation details of the file/buffer. --- include/bh/buffer.h | 32 +--- include/bh/file.h | 35 +--- include/bh/internal/buffer.h | 34 +++- include/bh/internal/file.h | 34 +++- include/bh/internal/io.h | 6 - include/bh/io.h | 26 ++- src/buffer.c | 14 +- src/file_null.c | 15 +- src/file_posix.c | 15 +- src/file_win.c | 16 +- src/io.c | 425 +++++++++++++++++++++++++++++++++++++++++-- 11 files changed, 524 insertions(+), 128 deletions(-) diff --git a/include/bh/buffer.h b/include/bh/buffer.h index b3a891f..da758e2 100644 --- a/include/bh/buffer.h +++ b/include/bh/buffer.h @@ -8,8 +8,6 @@ typedef struct bh_buffer_s bh_buffer_t; bh_buffer_t *bh_buffer_new(void); -void bh_buffer_free(bh_buffer_t *buffer); - const char *bh_buffer_data(bh_buffer_t *buffer); void bh_buffer_set_data(bh_buffer_t *buffer, @@ -21,34 +19,8 @@ size_t bh_buffer_capacity(bh_buffer_t *buffer); int bh_buffer_reserve(bh_buffer_t *buffer, size_t size); -int bh_buffer_open_base(bh_buffer_t *buffer, - int mode); - -void bh_buffer_close_base(bh_buffer_t *buffer); - -int bh_buffer_is_open_base(bh_buffer_t *buffer); - -size_t bh_buffer_read_base(bh_buffer_t *buffer, - char *data, - size_t size); - -size_t bh_buffer_write_base(bh_buffer_t *buffer, - const char *data, - size_t size); - -void bh_buffer_flush_base(bh_buffer_t *buffer); - -int bh_buffer_seek_base(bh_buffer_t *buffer, - bh_off_t pos, - int dir); - -bh_off_t bh_buffer_size_base(bh_buffer_t *buffer); - -bh_off_t bh_buffer_tell_base(bh_buffer_t *buffer); - -bh_off_t bh_buffer_available_base(bh_buffer_t *buffer); - -void bh_buffer_clear_base(bh_buffer_t *buffer); +#define bh_buffer_free(buffer) \ + bh_io_free((bh_io_t *)(buffer)) #define bh_buffer_open(buffer, mode) \ bh_io_open((bh_io_t *)(buffer), (mode)) diff --git a/include/bh/file.h b/include/bh/file.h index 8a0d3b1..304eb55 100644 --- a/include/bh/file.h +++ b/include/bh/file.h @@ -8,36 +8,8 @@ typedef struct bh_file_s bh_file_t; bh_file_t *bh_file_new(const char *path); -void bh_file_free(bh_file_t *file); - -int bh_file_open_base(bh_file_t *file, - int mode); - -void bh_file_close_base(bh_file_t *file); - -int bh_file_is_open_base(bh_file_t *file); - -size_t bh_file_read_base(bh_file_t *file, - char *data, - size_t size); - -size_t bh_file_write_base(bh_file_t *file, - const char *data, - size_t size); - -void bh_file_flush_base(bh_file_t *file); - -int bh_file_seek_base(bh_file_t *file, - bh_off_t pos, - int dir); - -bh_off_t bh_file_size_base(bh_file_t *file); - -bh_off_t bh_file_tell_base(bh_file_t *file); - -bh_off_t bh_file_available_base(bh_file_t *file); - -void bh_file_clear_base(bh_file_t *file); +#define bh_file_free(file) \ + bh_io_free((bh_io_t *)(file)) #define bh_file_open(file, mode) \ bh_io_open((bh_io_t *)(file), (mode)) @@ -51,6 +23,9 @@ void bh_file_clear_base(bh_file_t *file); #define bh_file_read(file, data, size) \ bh_io_read((bh_io_t *)(file), (data), (size)) +#define bh_file_peek(file, data, size) \ + bh_io_peek((bh_io_t *)(file), (data), (size)) + #define bh_file_write(file, data, size) \ bh_io_write((bh_io_t *)(file), (data), (size)) diff --git a/include/bh/internal/buffer.h b/include/bh/internal/buffer.h index 4e232d8..96b295e 100644 --- a/include/bh/internal/buffer.h +++ b/include/bh/internal/buffer.h @@ -16,6 +16,38 @@ struct bh_buffer_s int bh_buffer_init(bh_buffer_t *buffer); -void bh_buffer_destroy(bh_buffer_t *buffer); +#define bh_buffer_destroy(buffer) \ + bh_io_destroy((bh_io_t *)(buffer)) + +void bh_buffer_destroy_base(bh_buffer_t *buffer); + +int bh_buffer_open_base(bh_buffer_t *buffer, + int mode); + +void bh_buffer_close_base(bh_buffer_t *buffer); + +int bh_buffer_is_open_base(bh_buffer_t *buffer); + +size_t bh_buffer_read_base(bh_buffer_t *buffer, + char *data, + size_t size); + +size_t bh_buffer_write_base(bh_buffer_t *buffer, + const char *data, + size_t size); + +void bh_buffer_flush_base(bh_buffer_t *buffer); + +int bh_buffer_seek_base(bh_buffer_t *buffer, + bh_off_t pos, + int dir); + +bh_off_t bh_buffer_size_base(bh_buffer_t *buffer); + +bh_off_t bh_buffer_tell_base(bh_buffer_t *buffer); + +bh_off_t bh_buffer_available_base(bh_buffer_t *buffer); + +void bh_buffer_clear_base(bh_buffer_t *buffer); #endif /* BH_INTERNAL_BUFFER_H */ diff --git a/include/bh/internal/file.h b/include/bh/internal/file.h index 1e9fed0..20c6c31 100644 --- a/include/bh/internal/file.h +++ b/include/bh/internal/file.h @@ -16,6 +16,38 @@ int bh_file_init(bh_file_t *file, const char *path); -void bh_file_destroy(bh_file_t *file); +#define bh_file_destroy(file) \ + bh_io_destroy((bh_io_t *)(file)) + +void bh_file_destroy_base(bh_file_t *file); + +int bh_file_open_base(bh_file_t *file, + int mode); + +void bh_file_close_base(bh_file_t *file); + +int bh_file_is_open_base(bh_file_t *file); + +size_t bh_file_read_base(bh_file_t *file, + char *data, + size_t size); + +size_t bh_file_write_base(bh_file_t *file, + const char *data, + size_t size); + +void bh_file_flush_base(bh_file_t *file); + +int bh_file_seek_base(bh_file_t *file, + bh_off_t pos, + int dir); + +bh_off_t bh_file_size_base(bh_file_t *file); + +bh_off_t bh_file_tell_base(bh_file_t *file); + +bh_off_t bh_file_available_base(bh_file_t *file); + +void bh_file_clear_base(bh_file_t *file); #endif /* BH_INTERNAL_FILE_H */ diff --git a/include/bh/internal/io.h b/include/bh/internal/io.h index d0be93b..c840703 100644 --- a/include/bh/internal/io.h +++ b/include/bh/internal/io.h @@ -4,10 +4,4 @@ #include "bh.h" #include -void bh_io_init(bh_io_t *io, - const bh_io_table_t *table); - -void bh_io_destroy(bh_io_t *io); - #endif /* BH_INTERNAL_IO_H */ - diff --git a/include/bh/io.h b/include/bh/io.h index cee0834..f44fce7 100644 --- a/include/bh/io.h +++ b/include/bh/io.h @@ -5,6 +5,7 @@ #define BH_IO_ERROR 0x0001 #define BH_IO_EOF 0x0002 +#define BH_IO_UNIFIED 0x0004 #define BH_IO_NONE 0x0000 #define BH_IO_READ 0x0001 @@ -63,13 +64,30 @@ typedef struct bh_io_s { const bh_io_table_t *table; int flags; + struct + { + char *data; + size_t head; + size_t tail; + size_t size; + size_t capacity; + } buffer; } bh_io_t; -bh_io_t *bh_io_new(bh_io_table_t *table, - size_t size); +void bh_io_destroy_base(bh_io_t *io); + +bh_io_t *bh_io_new(const bh_io_table_t *table, + size_t size, + int unified); void bh_io_free(bh_io_t *io); +void bh_io_init(bh_io_t *io, + const bh_io_table_t *table, + int unified); + +void bh_io_destroy(bh_io_t *io); + int bh_io_open(bh_io_t *io, int mode); @@ -81,6 +99,9 @@ size_t bh_io_read(bh_io_t *io, char *data, size_t size); +size_t bh_io_peek(bh_io_t *io, + char *data, + size_t size); size_t bh_io_write(bh_io_t *io, const char* data, @@ -97,7 +118,6 @@ bh_off_t bh_io_size(bh_io_t *io); bh_off_t bh_io_tell(bh_io_t *io); - bh_off_t bh_io_available(bh_io_t *io); int bh_io_error(bh_io_t *io); diff --git a/src/buffer.c b/src/buffer.c index 3e5c062..58a1e82 100644 --- a/src/buffer.c +++ b/src/buffer.c @@ -15,7 +15,7 @@ static const bh_io_table_t bh_buffer_table = { (bh_off_t (*)(struct bh_io_s *)) bh_buffer_tell_base, (bh_off_t (*)(struct bh_io_s *)) bh_buffer_available_base, (void (*)(struct bh_io_s *)) bh_buffer_clear_base, - (void (*)(struct bh_io_s *)) bh_buffer_destroy + (void (*)(struct bh_io_s *)) bh_buffer_destroy_base }; bh_buffer_t *bh_buffer_new(void) @@ -34,17 +34,10 @@ bh_buffer_t *bh_buffer_new(void) return result; } -void bh_buffer_free(bh_buffer_t *buffer) -{ - /* Destroy buffer and free the memory */ - bh_buffer_destroy(buffer); - free(buffer); -} - int bh_buffer_init(bh_buffer_t *buffer) { /* Initialize underlying io object */ - bh_io_init(&buffer->base, &bh_buffer_table); + bh_io_init(&buffer->base, &bh_buffer_table, 1); /* Setup internal information */ buffer->data = NULL; @@ -56,12 +49,13 @@ int bh_buffer_init(bh_buffer_t *buffer) return BH_OK; } -void bh_buffer_destroy(bh_buffer_t *buffer) +void bh_buffer_destroy_base(bh_buffer_t *buffer) { /* Close the buffer and free allocated memory */ bh_buffer_close(buffer); if (buffer->data) free(buffer->data); + bh_io_destroy_base(&buffer->base); } const char *bh_buffer_data(bh_buffer_t *buffer) diff --git a/src/file_null.c b/src/file_null.c index 75684d8..ac2cfe4 100644 --- a/src/file_null.c +++ b/src/file_null.c @@ -21,19 +21,6 @@ bh_file_t *bh_file_new(const char *path) return NULL; } -/** - * Frees the \a file object. - * - * Before freeing the file object, this function ensures that underlying file - * was closed. - * - * \param file Pointer to the file object - */ -void bh_file_free(bh_file_t *file) -{ - (void)file; -} - /** * Initializes the \a file object with specified \a path to file. * @@ -59,7 +46,7 @@ int bh_file_init(bh_file_t *file, * * \param file Pointer to the file object */ -void bh_file_destroy(bh_file_t *file) +void bh_file_destroy_base(bh_file_t *file) { (void)file; } diff --git a/src/file_posix.c b/src/file_posix.c index e6915a5..9b61c01 100644 --- a/src/file_posix.c +++ b/src/file_posix.c @@ -19,7 +19,7 @@ static const bh_io_table_t bh_file_table = { (bh_off_t (*)(struct bh_io_s *)) bh_file_tell_base, (bh_off_t (*)(struct bh_io_s *)) bh_file_available_base, (void (*)(struct bh_io_s *)) bh_file_clear_base, - (void (*)(struct bh_io_s *)) bh_file_destroy + (void (*)(struct bh_io_s *)) bh_file_destroy_base }; bh_file_t *bh_file_new(const char *path) @@ -38,12 +38,6 @@ bh_file_t *bh_file_new(const char *path) return result; } -void bh_file_free(bh_file_t *file) -{ - bh_file_destroy(file); - free(file); -} - int bh_file_init(bh_file_t *file, const char *path) { @@ -51,8 +45,8 @@ int bh_file_init(bh_file_t *file, if (!path) return BH_INVALID; - /* Initialize base io object */ - bh_io_init(&file->base, &bh_file_table); + /* Initialize underlying io object */ + bh_io_init(&file->base, &bh_file_table, 1); /* Initialize file io object */ file->handle = -1; @@ -62,11 +56,12 @@ int bh_file_init(bh_file_t *file, return BH_OK; } -void bh_file_destroy(bh_file_t *file) +void bh_file_destroy_base(bh_file_t *file) { /* Close the file */ bh_file_close(file); free(file->path); + bh_io_destroy_base(&file->base); } int bh_file_open_base(bh_file_t *file, diff --git a/src/file_win.c b/src/file_win.c index 15d2a61..b4c4730 100644 --- a/src/file_win.c +++ b/src/file_win.c @@ -12,7 +12,7 @@ static const bh_io_table_t bh_file_table = { (bh_off_t (*)(struct bh_io_s *)) bh_file_tell_base, (bh_off_t (*)(struct bh_io_s *)) bh_file_available_base, (void (*)(struct bh_io_s *)) bh_file_clear_base, - (void (*)(struct bh_io_s *)) bh_file_destroy + (void (*)(struct bh_io_s *)) bh_file_destroy_base }; bh_file_t *bh_file_new(const char *path) @@ -31,13 +31,6 @@ bh_file_t *bh_file_new(const char *path) return result; } -void bh_file_free(bh_file_t *file) -{ - /* Destroy file object and free allocated memory */ - bh_file_destroy(file); - free(file); -} - int bh_file_init(bh_file_t *file, const char *path) { @@ -45,20 +38,23 @@ int bh_file_init(bh_file_t *file, if (!path) return BH_INVALID; + /* Initialize underlying io object */ + bh_io_init(&file->base, &bh_file_table, 1); + /* Fill file structure information */ file->handle = INVALID_HANDLE_VALUE; file->mode = BH_IO_NONE; file->path = strdup(path); - file->base.table = &bh_file_table; return BH_OK; } -void bh_file_destroy(bh_file_t *file) +void bh_file_destroy_base(bh_file_t *file) { /* Close the file and free allocated memory */ bh_file_close(file); free(file->path); + bh_io_destroy_base(&file->base); } int bh_file_open_base(bh_file_t *file, diff --git a/src/io.c b/src/io.c index 5e94f35..3e18e4a 100644 --- a/src/io.c +++ b/src/io.c @@ -1,5 +1,8 @@ #include #include +#include + +#define BH_IO_BUFFER_STEP 256 /** * \defgroup io Input/Output @@ -8,11 +11,339 @@ * \{ */ +/** + * \internal + * Returns pointer to the \a io peek buffer. + * + * \param io Pointer to the io object + * + * \return Pointer to the peek buffer. + * + * \warning Always use this function to get data of the buffer! + */ +static char *bh_io_buffer_data(bh_io_t *io) +{ + if (io->buffer.capacity) + return io->buffer.data; + + return (char *)&io->buffer.data; +} + +/** + * \internal + * Returns peek buffer capacity of the /a io object. + * + * \param io Pointer to the io object + * + * \return Peek buffer capacity. + * + * \warning Always use this function to get capacity of the buffer! + */ +static size_t bh_io_buffer_capacity(bh_io_t *io) +{ + if (io->buffer.capacity) + return io->buffer.capacity; + return sizeof(char *); +} + +/** + * \internal + * Reduces the capacity of the \a io object peek buffer. + * + * \param io Pointer to the io object. + * + * \note This function does not guarantee that the capacity of the buffer will + * actually be reduced. + */ +static void bh_io_buffer_shrink(bh_io_t *io) +{ + size_t capacity; + char *data; + + /* Check if buffer is allocated */ + if (!io->buffer.capacity) + return; + + /* Calculate new capacity and check against old capacity */ + capacity = (io->buffer.size + BH_IO_BUFFER_STEP - 1) / BH_IO_BUFFER_STEP; + capacity *= BH_IO_BUFFER_STEP; + if (io->buffer.capacity >= capacity) + return; + + /* Allocate new buffer space */ + data = malloc(capacity); + if (!data) + return; + + /* Copy data */ + if (io->buffer.size) + { + size_t block; + + /* Depending on the tail-head positions copy data in two or one go */ + if (io->buffer.tail <= io->buffer.head) + { + block = io->buffer.capacity - io->buffer.head; + memmove(data, io->buffer.data + io->buffer.head, block); + memmove(data + block, io->buffer.data, io->buffer.size - block); + } + else + { + block = io->buffer.size; + memmove(data, io->buffer.data + io->buffer.head, block); + } + } + + /* Update fields */ + free(io->buffer.data); + io->buffer.data = data; + io->buffer.head = 0; + io->buffer.tail = io->buffer.size; + io->buffer.capacity = capacity; + if (io->buffer.tail >= capacity) + io->buffer.tail = 0; +} + +/** + * \internal + * Reserves the capacity of the \a io object peek buffer. + * + * \param io Pointer to the io object + * \param size New peek buffer capacity + * + * \return On success, returns zero value. + * \return On failure, returns error code. + */ +static int bh_io_buffer_reserve(bh_io_t *io, + size_t size) +{ + size_t capacity; + char *data; + + /* Check if entire buffer can fit into a pointer */ + if (size <= sizeof(char *)) + return BH_OK; + + /* Check that size can fit into a buffer */ + if (io->buffer.capacity >= size) + return BH_OK; + + /* Calculate new buffer capacity */ + capacity = (size + BH_IO_BUFFER_STEP - 1) / BH_IO_BUFFER_STEP; + capacity *= BH_IO_BUFFER_STEP; + + /* Allocate new buffer space */ + data = malloc(capacity); + if (!data) + return BH_OOM; + + /* Copy data */ + if (io->buffer.size) + { + size_t block; + + /* Depending on the tail-head positions copy data in two or one go */ + if (io->buffer.tail <= io->buffer.head) + { + block = io->buffer.capacity - io->buffer.head; + memmove(data, bh_io_buffer_data(io) + io->buffer.head, block); + memmove(data + block, bh_io_buffer_data(io), io->buffer.size - block); + } + else + { + block = io->buffer.size; + memmove(data, bh_io_buffer_data(io) + io->buffer.head, block); + } + } + + /* Update fields */ + if (io->buffer.capacity) + free(io->buffer.data); + io->buffer.data = data; + io->buffer.head = 0; + io->buffer.tail = io->buffer.size; + io->buffer.capacity = capacity; + if (io->buffer.tail >= capacity) + io->buffer.tail = 0; + + return BH_OK; +} + +/** + * \internal + * Reads data from the \a io peek buffer. + * + * \param io Pointer to the io object + * \param data Pointer to the memory buffer + * \param size Maximum number of bytes to be read + * \param peek Reading or peeking flag + * + * \return On success, returns number of bytes successfuly read. + * \return On failure, returns zero. + */ +static size_t bh_io_buffer_read(bh_io_t *io, + char *data, + size_t size, + int peek) +{ + size_t block; + + /* Check if buffer is empty */ + if (!io->buffer.size) + return 0; + + /* Reading size can't be bigger then buffer size */ + if (size > io->buffer.size) + size = io->buffer.size; + + /* Copy data */ + if (data) + { + /* Depending on the tail-head positions copy data in two or one go */ + if (io->buffer.tail <= io->buffer.head) + { + block = bh_io_buffer_capacity(io) - io->buffer.head; + if (block > size) + block = size; + memmove(data, bh_io_buffer_data(io) + io->buffer.head, block); + memmove(data + block, bh_io_buffer_data(io), size - block); + } + else + { + block = size; + memmove(data, bh_io_buffer_data(io) + io->buffer.head, block); + } + } + + /* Adjust head position and size if we are not peeking */ + if (!peek) + { + io->buffer.head += size; + io->buffer.size -= size; + + if (io->buffer.head >= bh_io_buffer_capacity(io)) + io->buffer.head -= bh_io_buffer_capacity(io); + + /* Shrink buffer if neccesary*/ + bh_io_buffer_shrink(io); + } + + /* Return readed size */ + return size; +} + +/** + * \internal + * Writes data to the \a io peek buffer. + * + * \param io Pointer to the io object + * \param data Pointer to the memory buffer + * \param size Maximum number of bytes to be read + * + * \return On success, returns number of bytes successfuly written. + * \return On failure, returns zero. + */ +static size_t bh_io_buffer_write(bh_io_t *io, + const char *data, + size_t size) +{ + size_t block; + + /* Reserve capacity in the buffer */ + if (bh_io_buffer_reserve(io, io->buffer.size + size)) + return 0; + + /* Depending on the tail-head positions copy data in two or one go */ + if (io->buffer.tail + size > bh_io_buffer_capacity(io)) + { + block = io->buffer.capacity - io->buffer.tail; + memmove(bh_io_buffer_data(io) + io->buffer.tail, data, block); + memmove(bh_io_buffer_data(io), data + block, size - block); + } + else + { + block = size; + memmove(bh_io_buffer_data(io) + io->buffer.tail, data, block); + } + + /* Adjust tail and size */ + io->buffer.tail += size; + io->buffer.size += size; + + if (io->buffer.tail >= bh_io_buffer_capacity(io)) + io->buffer.tail -= bh_io_buffer_capacity(io); + + return size; +} + +/** + * \internal + * Adjusts \a io object peek buffer according to the specified seek direction + * \a dir and \a offset. + * + * \param io Pointer to the io object + * \param offset Number of bytes to seek in specified direciton + * \param dir Seeking direction + * + * \return Returns new offset for the io seek function. + */ +static bh_off_t bh_io_buffer_seek(bh_io_t *io, + bh_off_t offset, + int dir) +{ + /* Check if seek is relative to current position */ + if (dir != BH_IO_CURRENT) + { + /* It's not - reset peek buffer and return offset as is */ + io->buffer.size = 0; + io->buffer.head = 0; + io->buffer.tail = 0; + return offset; + } + + /* If offset is negative or past peek buffer size */ + if (offset < 0 || offset >= (bh_off_t)io->buffer.size) + { + /* It's not - reset peek buffer and adjust offset position */ + offset -= io->buffer.size; + io->buffer.size = 0; + io->buffer.head = 0; + io->buffer.tail = 0; + return offset; + } + + /* Skip offset amount of bytes */ + bh_io_buffer_read(io, NULL, offset, 0); + return 0; +} + +/** + * Destroys base io object. + * + * \param io Pointer to the base io object + * + * \warning This function should be used in context of implementing child + * io objects (files, sockets, streaming compression, etc). + */ +void bh_io_destroy_base(bh_io_t *io) +{ + if (io->buffer.capacity) + free(io->buffer.data); +} + /** * Creates the new io object with specified \a table and \a size. * - * \param table Pointer to the io table - * \param size Size of the io object + * The \a unified flag specifies the behaivor of the internal peek buffer. + * If the \a unified flag is set - the io object will assume that read and + * write operations are dependant on each other (ex. writing to the file + * affects next read operation). + * If the \a unified flag is not set - the io object will asusme that read and + * write operations are independant. + * + * \param table Pointer to the io table + * \param size Size of the io object + * \param unified Unified read/write stream flag * * \return On success, returns new semi-initialized io object. * \return On failure, returns null pointer. @@ -20,14 +351,15 @@ * \warning This function should be used in context of implementing child * io objects (files, sockets, streaming compression, etc). */ -bh_io_t *bh_io_new(bh_io_table_t *table, - size_t size) +bh_io_t *bh_io_new(const bh_io_table_t *table, + size_t size, + int unified) { bh_io_t *result; result = malloc(size); if (result) - bh_io_init(result, table); + bh_io_init(result, table, unified); return result; } @@ -46,14 +378,27 @@ void bh_io_free(bh_io_t *io) /** * Initializes the \a io object with specified \a table. * - * \param io Pointer to the io object to be initialized - * \param table Pointer to the io table + * The \a unified flag specifies the behaivor of the internal peek buffer. + * If the \a unified flag is set - the io object will assume that read and + * write operations are dependant on each other (ex. writing to the file + * affects next read operation). + * If the \a unified flag is not set - the io object will asusme that read and + * write operations are independant. + * + * \param io Pointer to the io object to be initialized + * \param table Pointer to the io table + * \param unified Unified read/write stream flag */ void bh_io_init(bh_io_t *io, - const bh_io_table_t *table) + const bh_io_table_t *table, + int unified) { io->table = table; - io->flags = 0; + io->flags = (unified) ? (BH_IO_UNIFIED) : (0); + io->buffer.capacity = 0; + io->buffer.head = 0; + io->buffer.tail = 0; + io->buffer.size = 0; } /** @@ -121,7 +466,50 @@ size_t bh_io_read(bh_io_t *io, char *data, size_t size) { - return io->table->read(io, data, size); + + size_t result; + + /* Read as much data from peek buffer as possible */ + result = bh_io_buffer_read(io, data, size, 0); + + /* If that wasn't enough read from the device */ + if (result != size) + result += io->table->read(io, data + result, size - result); + + /* Return amount of bytes read */ + return result; +} + +/** + * Peeks up to \a size amount of bytes from the \a io object into memory buffer + * pointed by \a data pointer. + * + * \param io Pointer to the io object + * \param data Pointer to the memory buffer + * \param size Maximum number of bytes to be read + * + * \return On success, returns number of bytes successfuly read. + * \return On failure, returns zero. + */ +size_t bh_io_peek(bh_io_t *io, + char *data, + size_t size) +{ + /* Fill peek buffer if it's size is less then requested */ + if (io->buffer.size < size) + { + size_t read; + + read = io->table->read(io, data, size - io->buffer.size); + bh_io_buffer_write(io, data, read); + } + + /* Clear EOF flag if peek buffer is not empty */ + if (io->buffer.size > 0) + io->flags &= ~BH_IO_EOF; + + /* Read data from the peek buffer */ + return bh_io_buffer_read(io, data, size, 1); } /** @@ -138,9 +526,19 @@ size_t bh_io_read(bh_io_t *io, * \note To check for error see bh_io_error. */ size_t bh_io_write(bh_io_t *io, - const char* data, + const char *data, size_t size) { + /* Clear peek buffer and adjust the position if IO is unified */ + if (io->buffer.size && (io->flags & BH_IO_UNIFIED)) + { + io->table->seek(io, -(bh_off_t)io->buffer.size, BH_IO_CURRENT); + io->buffer.head = 0; + io->buffer.tail = 0; + io->buffer.size = 0; + } + + /* Write data */ return io->table->write(io, data, size); } @@ -172,6 +570,7 @@ int bh_io_seek(bh_io_t *io, bh_off_t offset, int dir) { + offset = bh_io_buffer_seek(io, offset, dir); return io->table->seek(io, offset, dir); } @@ -198,7 +597,7 @@ bh_off_t bh_io_size(bh_io_t *io) */ bh_off_t bh_io_tell(bh_io_t *io) { - return io->table->tell(io); + return io->table->tell(io) - io->buffer.size; } /** @@ -211,7 +610,7 @@ bh_off_t bh_io_tell(bh_io_t *io) */ bh_off_t bh_io_available(bh_io_t *io) { - return io->table->available(io); + return io->table->available(io) + io->buffer.size; } /** -- cgit v1.2.3