Refactor IO, add buffered IO

I wasn't happy with existing implementation of the IO, so I decided
to change it - as a result there is no longer BH_IOOpen and BH_IOClose
and many IO operations are now optional (behind BH_IOCtl).

Finnally implemented buffered IO and fixed size memory buffer IO.
This commit is contained in:
2025-04-26 07:50:13 +03:00
parent 48ddd91dd4
commit 1b6c858a1b
16 changed files with 1473 additions and 1122 deletions

304
src/Buffer.c Normal file
View File

@@ -0,0 +1,304 @@
#include <BH/IO.h>
#include <stdlib.h>
#include <string.h>
typedef struct BH_BufferData
{
char *data;
size_t size;
size_t capacity;
size_t offset;
} BH_BufferData;
typedef struct BH_Buffer
{
BH_IO parent;
BH_IO *device;
BH_BufferData in;
BH_BufferData out;
int flags;
} BH_Buffer;
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
static int bufferInit(BH_Buffer *buffer,
BH_IO *device,
size_t size)
{
if (!size)
size = 4096;
if (!device)
return BH_ERROR;
buffer->device = device;
/* Allocate input buffer */
buffer->in.data = malloc(size);
buffer->in.capacity = size;
buffer->in.size = buffer->in.offset = 0;
if (!buffer->in.data)
return BH_OOM;
/* Allocate output buffer */
buffer->out.data = malloc(size);
buffer->out.capacity = size;
buffer->out.size = buffer->out.offset = 0;
if (!buffer->out.data)
{
free(buffer->out.data);
return BH_OOM;
}
return BH_OK;
}
static int bufferDestroy(BH_Buffer *buffer)
{
/* Free buffers on destruction */
free(buffer->in.data);
if (buffer->in.data != buffer->out.data)
free(buffer->out.data);
free(buffer);
return BH_OK;
}
static int bufferRefill(BH_Buffer *buffer)
{
size_t size;
int result;
/* Shift existing data towards begining */
memmove(buffer->in.data, buffer->in.data + buffer->in.offset, buffer->in.size);
buffer->in.offset = 0;
/* Read data from the device */
size = buffer->in.capacity - buffer->in.size;
if ((result = BH_IORead(buffer->device, buffer->in.data + buffer->in.size, size, &size)))
goto done;
buffer->in.size += size;
done:
return result;
}
static int bufferFlush(BH_Buffer *buffer)
{
size_t size;
int result;
/* Flush data to underlying input/output device */
result = BH_IOWrite(buffer->device, buffer->out.data, buffer->out.size, &size);
if (!result)
{
/* Move remaining data */
buffer->out.offset += size;
buffer->out.size -= size;
memmove(buffer->out.data, buffer->out.data + buffer->out.offset, buffer->out.size);
buffer->out.offset = 0;
if (buffer->out.size)
return BH_SHORT;
}
return result;
}
static int bufferRead(BH_Buffer *buffer,
BH_IOReadInfo *info)
{
size_t readed, size;
int result;
result = BH_OK;
for (readed = 0; readed < info->size; )
{
/* Refill input buffer if needed */
if (!buffer->in.size && ((result = bufferRefill(buffer)) || !buffer->in.size))
break;
/* Copy data from buffer into user buffer */
size = MIN(info->size - readed, buffer->in.size);
memcpy(info->data + readed, buffer->in.data + buffer->in.offset, size);
buffer->in.offset += size;
buffer->in.size -= size;
readed += size;
}
/* Report readed size */
if (info->actual)
*info->actual = readed;
return result;
}
static int bufferWrite(BH_Buffer *buffer,
BH_IOWriteInfo *info)
{
size_t written, size;
int result;
result = BH_OK;
for (written = 0; written < info->size; )
{
/* Write data into output buffer */
size = MIN(info->size - written, buffer->out.capacity - buffer->out.size);
memcpy(buffer->out.data + buffer->out.size, info->data + written, size);
buffer->out.size += size;
written += size;
/* Flush output buffer if needed */
if (buffer->out.size == buffer->out.capacity && (result = bufferFlush(buffer)))
break;
}
/* Report written size */
if (info->actual)
*info->actual = written;
return result;
}
int bufferPeek(BH_Buffer *buffer,
BH_IOReadInfo *info)
{
size_t size;
int result;
/* Requested size can't be bigger then buffer capacity */
if (info->size > buffer->in.capacity)
return BH_FULL;
/* Try to refill the buffer if we dont have enough data */
if (info->size > buffer->in.size && (result = bufferRefill(buffer)))
return result;
/* Copy data */
size = MIN(info->size, buffer->in.size);
memcpy(info->data, buffer->in.data + buffer->in.offset, size);
if (info->actual)
*info->actual = size;
return BH_OK;
}
static int bufferGetIO(BH_Buffer *buffer,
BH_IO **io)
{
*io = buffer->device;
return BH_OK;
}
static int bufferSetIO(BH_Buffer *buffer,
BH_IO *io)
{
/* Reset buffer states and change input/output deivce */
buffer->in.offset = 0;
buffer->in.size = 0;
buffer->out.offset = 0;
buffer->out.size = 0;
buffer->device = io;
return BH_OK;
}
static int bufferCap(BH_Buffer *buffer,
int *op)
{
BH_UNUSED(buffer);
/* Return operations supported by the buffer input/output device */
switch (*op)
{
case BH_IO_CTL_FLUSH:
case BH_IO_CTL_PEEK:
case BH_IO_CTL_GET_IO:
case BH_IO_CTL_SET_IO:
return BH_OK;
default:
return BH_NOIMPL;
}
}
static int bufferCtl(BH_Buffer *buffer,
BH_IOCtlInfo *info)
{
/* Handle supported operations */
switch (info->op)
{
case BH_IO_CTL_FLUSH:
return bufferFlush(buffer);
case BH_IO_CTL_PEEK:
return bufferPeek(buffer, (BH_IOReadInfo *)info->arg);
case BH_IO_CTL_GET_IO:
return bufferGetIO(buffer, (BH_IO **)info->arg);
case BH_IO_CTL_SET_IO:
return bufferSetIO(buffer, (BH_IO *)info->arg);
default:
return BH_NOIMPL;
}
}
static int bufferCallback(BH_Buffer *buffer,
int type,
void *arg)
{
/* Handle basic input/output operations */
switch (type)
{
case BH_IO_OP_DESTROY: return bufferDestroy(buffer);
case BH_IO_OP_READ: return bufferRead(buffer, (BH_IOReadInfo *)arg);
case BH_IO_OP_WRITE: return bufferWrite(buffer, (BH_IOWriteInfo *)arg);
case BH_IO_OP_CTL: return bufferCtl(buffer, (BH_IOCtlInfo *)arg);
case BH_IO_OP_CAP: return bufferCap(buffer, (int*)arg);
default: return BH_NOIMPL;
}
}
BH_IO *BH_BufferNew(BH_IO *device,
size_t size,
int *result)
{
BH_Buffer *buffer;
int code;
code = BH_OOM;
/* Allocate new buffer object and initialize it */
if ((buffer = malloc(sizeof(*buffer))))
{
buffer->parent.callback = (BH_IOCallback)bufferCallback;
if ((code = bufferInit(buffer, device, size)))
{
free(buffer);
buffer = NULL;
}
}
/* Report error code */
if (result)
*result = code;
return (BH_IO*)buffer;
}

217
src/Bytes.c Normal file
View File

@@ -0,0 +1,217 @@
#include <BH/IO.h>
#include <string.h>
#include <stdlib.h>
#define MIN(a,b) ((a)<(b)?(a):(b))
#define MAX(a,b) ((a)>(b)?(a):(b))
typedef struct BH_Bytes
{
BH_IO parent;
char *data;
size_t size;
size_t position;
} BH_Bytes;
static int bytesInit(BH_Bytes *bytes,
char *data,
size_t size)
{
if (!data || !size)
return BH_ERROR;
bytes->data = data;
bytes->size = size;
bytes->position = 0;
return BH_OK;
}
static int bytesDestroy(BH_Bytes *bytes)
{
BH_UNUSED(bytes);
return BH_OK;
}
static int bytesRead(BH_Bytes *bytes,
BH_IOReadInfo *info)
{
size_t size;
size = MIN(info->size, bytes->size - bytes->position);
memmove(info->data, bytes->data + bytes->position, size);
bytes->position += size;
if (info->actual)
*info->actual = size;
return BH_OK;
}
static int bytesWrite(BH_Bytes *bytes,
BH_IOWriteInfo *info)
{
size_t size;
size = MIN(info->size, bytes->size - bytes->position);
memmove(bytes->data + bytes->position, info->data, size);
bytes->position += size;
if (info->actual)
*info->actual = size;
return BH_OK;
}
static int bytesSeek(BH_Bytes *bytes,
BH_IOSeekInfo *info)
{
int64_t offset;
switch (info->whence)
{
case BH_IO_SEEK_SET:
offset = MAX(info->offset, 0);
offset = MIN(offset, (int64_t)bytes->size);
break;
case BH_IO_SEEK_CUR:
offset = MAX((int64_t)bytes->position + info->offset, 0);
offset = MIN(offset, (int64_t)bytes->size);
break;
case BH_IO_SEEK_END:
offset = MAX((int64_t)bytes->size + info->offset, 0);
offset = MIN(offset, (int64_t)bytes->size);
break;
}
bytes->position = offset;
return BH_OK;
}
static int bytesTell(BH_Bytes *bytes,
int64_t *pos)
{
*pos = bytes->position;
return BH_OK;
}
static int bytesSize(BH_Bytes *bytes,
int64_t *size)
{
*size = bytes->size;
return BH_OK;
}
static int bytesFlags(BH_Bytes *bytes,
int *flags)
{
if (bytes->position == bytes->size)
*flags = BH_IO_FLAG_EOF;
else
*flags = 0;
return BH_OK;
}
static int bytesCap(BH_Bytes *bytes,
int *op)
{
BH_UNUSED(bytes);
/* Return operations supported by the buffer input/output device */
switch (*op)
{
case BH_IO_CTL_FLAGS:
case BH_IO_CTL_SIZE:
case BH_IO_CTL_TELL:
case BH_IO_CTL_SEEK:
return BH_OK;
default:
return BH_NOIMPL;
}
}
static int bytesCtl(BH_Bytes *bytes,
BH_IOCtlInfo *info)
{
/* Handle supported operations */
switch (info->op)
{
case BH_IO_CTL_FLAGS:
return bytesFlags(bytes, (int *)info->arg);
case BH_IO_CTL_SIZE:
return bytesSize(bytes, (int64_t *)(info->arg));
case BH_IO_CTL_TELL:
return bytesTell(bytes, (int64_t *)(info->arg));
case BH_IO_CTL_SEEK:
return bytesSeek(bytes, (BH_IOSeekInfo *)(info->arg));
default:
return BH_NOIMPL;
}
}
static int bytesCallback(BH_Bytes *bytes,
int type,
void *arg)
{
/* Handle basic input/output operations */
switch (type)
{
case BH_IO_OP_DESTROY: return bytesDestroy(bytes);
case BH_IO_OP_READ: return bytesRead(bytes, (BH_IOReadInfo *)arg);
case BH_IO_OP_WRITE: return bytesWrite(bytes, (BH_IOWriteInfo *)arg);
case BH_IO_OP_CTL: return bytesCtl(bytes, (BH_IOCtlInfo *)arg);
case BH_IO_OP_CAP: return bytesCap(bytes, (int*)arg);
default: return BH_NOIMPL;
}
}
BH_IO *BH_BytesNew(char *data,
size_t size,
int *result)
{
BH_Bytes *bytes;
int code;
code = BH_OOM;
/* Allocate new buffer object and initialize it */
if ((bytes = malloc(sizeof(*bytes))))
{
bytes->parent.callback = (BH_IOCallback)bytesCallback;
if ((code = bytesInit(bytes, data, size)))
{
free(bytes);
bytes = NULL;
}
}
/* Report error code */
if (result)
*result = code;
return (BH_IO*)bytes;
}

273
src/IO.c
View File

@@ -5,231 +5,176 @@
#define BUFFER_SIZE (sizeof(char *))
struct BH_IO
{
BH_IOCallback cb;
};
BH_IO *BH_IONew(BH_IOCallback cb,
void *data)
{
size_t requested;
BH_IO *io;
/* Get information about IO device size */
if (cb(NULL, BH_IO_INFO_CB, &requested, NULL))
return NULL;
/* Allocate space for the IO device */
io = malloc(sizeof(*io) + requested);
if (!io)
return NULL;
/* Initialize IO device */
io->cb = cb;
if (cb(io + 1, BH_IO_INIT_CB, data, NULL))
{
free(io);
return NULL;
}
return io;
}
void BH_IOFree(BH_IO *io)
void BH_IOFree(BH_IO *device)
{
/* Prevent working with NULL io */
if (!io)
if (!device)
return;
/* Call the IO device destruction handler */
io->cb(io + 1, BH_IO_DESTROY_CB, NULL, NULL);
/* Deallocate object */
free(io);
/* Call the IO device destruction handler and deallocate */
device->callback(device, BH_IO_OP_DESTROY, NULL);
}
const char *BH_IOClassname(BH_IO *io)
{
const char *name;
if (!io)
goto error;
if (io->cb(io + 1, BH_IO_INFO_CB, NULL, &name) != BH_OK)
goto error;
return name;
error:
return NULL;
}
int BH_IOOpen(BH_IO *io,
int mode)
{
/* Prevent working with NULL io */
if (!io)
return BH_ERROR;
/* Call the IO device open handler with specified mode */
return io->cb(io + 1, BH_IO_OPEN_CB, &mode, NULL);
}
int BH_IOClose(BH_IO *io)
{
/* Prevent working with NULL io */
if (!io)
return BH_ERROR;
/* Call the IO device close handler */
return io->cb(io + 1, BH_IO_CLOSE_CB, NULL, NULL);
}
int BH_IORead(BH_IO *io,
int BH_IORead(BH_IO *device,
char *buffer,
size_t size,
size_t *actual)
{
int code;
BH_IOReadInfo info;
/* Prevent working with NULL io */
if (!io)
if (!device)
return BH_ERROR;
/* Call the IO device read handler */
code = io->cb(io + 1, BH_IO_READ_CB, buffer, &size);
/* If caller wants to know actual read size - report it back */
if (actual)
*actual = size;
return code;
info.data = buffer;
info.size = size;
info.actual = actual;
return device->callback(device, BH_IO_OP_READ, &info);
}
int BH_IOWrite(BH_IO *io,
int BH_IOWrite(BH_IO *device,
const char *buffer,
size_t size,
size_t *actual)
{
int code;
BH_IOWriteInfo info;
/* Prevent working with NULL io */
if (!io)
if (!device)
return BH_ERROR;
/* Call the IO device write handler */
code = io->cb(io + 1, BH_IO_WRITE_CB, (void *)buffer, &size);
info.data = buffer;
info.size = size;
info.actual = actual;
/* If caller wants to know actual written size - report it back */
if (actual)
*actual = size;
return code;
return device->callback(device, BH_IO_OP_WRITE, &info);
}
int BH_IOPeek(BH_IO *io,
int BH_IOCtl(BH_IO *device,
int op,
void *arg)
{
BH_IOCtlInfo info;
if (!device)
return BH_ERROR;
info.op = op;
info.arg = arg;
return device->callback(device, BH_IO_OP_CTL, &info);
}
int BH_IOCap(BH_IO *device,
int op)
{
if (!device)
return BH_ERROR;
return device->callback(device, BH_IO_OP_CAP, &op);
}
int BH_IOPeek(BH_IO *device,
char *buffer,
size_t size,
size_t *actual)
{
int code;
BH_IOReadInfo readInfo;
BH_IOSeekInfo posInfo;
size_t readed;
/* Prevent working with NULL io */
if (!io)
return BH_ERROR;
readInfo.data = buffer;
readInfo.size = size;
readInfo.actual = actual;
/* Call the IO device peek handler */
code = io->cb(io + 1, BH_IO_PEEK_CB, (void *)buffer, &size);
if (BH_IOCap(device, BH_IO_CTL_PEEK))
{
if (BH_IOCap(device, BH_IO_CTL_SEEK))
return BH_NOIMPL;
/* If caller wants to know actual written size - report it back */
if (actual)
*actual = size;
if (BH_IORead(device, buffer, size, &readed))
return BH_ERROR;
return code;
posInfo.whence = BH_IO_SEEK_CUR;
posInfo.offset = -(int64_t)readed;
if (BH_IOCtl(device, BH_IO_CTL_SEEK, &posInfo))
return BH_ERROR;
if (actual)
*actual = readed;
return BH_OK;
}
return BH_IOCtl(device, BH_IO_CTL_PEEK, &readInfo);
}
int BH_IOTell(BH_IO *io,
int64_t *position)
int BH_IOTell(BH_IO *device,
int64_t *offset)
{
/* Prevent working with NULL io */
if (!io)
return BH_ERROR;
/* Call the IO device tell handler */
return io->cb(io + 1, BH_IO_TELL_CB, position, NULL);
return BH_IOCtl(device, BH_IO_CTL_TELL, offset);
}
int BH_IOSeek(BH_IO *io,
int64_t position,
int direction)
int BH_IOSeek(BH_IO *device,
int64_t offset,
int whence)
{
/* Prevent working with NULL io */
if (!io)
return BH_ERROR;
BH_IOSeekInfo info;
/* Call the IO device seek handler */
return io->cb(io + 1, BH_IO_SEEK_CB, &position, &direction);
info.offset = offset;
info.whence = whence;
return BH_IOCtl(device, BH_IO_CTL_SEEK, &info);
}
int BH_IOFlush(BH_IO *io)
int BH_IOFlush(BH_IO *device)
{
/* Prevent working with NULL io */
if (!io)
return BH_ERROR;
/* Call the IO device flush handler */
return io->cb(io + 1, BH_IO_FLUSH_CB, NULL, NULL);
return BH_IOCtl(device, BH_IO_CTL_FLUSH, NULL);
}
int BH_IOSize(BH_IO *io,
int BH_IOSize(BH_IO *device,
int64_t *size)
{
/* Prevent working with NULL io */
if (!io)
return BH_IOCtl(device, BH_IO_CTL_SIZE, size);
}
int BH_IOFlags(BH_IO *device, int *flags)
{
return BH_IOCtl(device, BH_IO_CTL_FLAGS, flags);
}
int BH_IOClear(BH_IO *device)
{
return BH_IOCtl(device, BH_IO_CTL_CLEAR, NULL);
}
int BH_IOError(BH_IO *device)
{
int flags;
if (BH_IOFlags(device, &flags))
return BH_ERROR;
/* Call the IO device size handler */
return io->cb(io + 1, BH_IO_SIZE_CB, size, NULL);
return flags & BH_IO_FLAG_ERROR;
}
int BH_IOFlags(BH_IO *io)
int BH_IOEndOfFile(BH_IO *device)
{
/* Prevent working with NULL io */
if (!io)
return BH_IO_FLAG_ERROR;
int flags;
/* Call the IO device flags handler */
return io->cb(io + 1, BH_IO_FLAGS_CB, NULL, NULL);
}
int BH_IOClear(BH_IO *io)
{
/* Prevent working with NULL io */
if (!io)
return BH_OK;
/* Call the IO device clear error handler */
return io->cb(io + 1, BH_IO_CLEAR_CB, NULL, NULL);
}
BH_IO *BH_BufferNew(BH_IO *io)
{
(void)io;
return NULL;
if (BH_IOFlags(device, &flags))
return BH_ERROR;
return flags & BH_IO_FLAG_EOF;
}

View File

@@ -1,200 +1,15 @@
#include <BH/IO.h>
typedef struct BH_File
BH_IO *BH_FileNew(const char *path,
int mode,
int *result)
{
int implement;
int me;
} BH_File;
BH_UNUSED(path);
BH_UNUSED(mode);
if (result)
*result = BH_NOIMPL;
static int BH_FileInfo(BH_File *file,
size_t *size,
const char **name);
static int BH_FileInit(BH_File *file,
const char *path);
static int BH_FileDestroy(BH_File *file);
static int BH_FileOpen(BH_File *file,
int *mode);
static int BH_FileClose(BH_File *file);
static int BH_FileRead(BH_File *file,
char *data,
size_t *size);
static int BH_FileWrite(BH_File *file,
const char *data,
size_t *size);
static int BH_FilePeek(BH_File* file,
char *data,
size_t *size);
static int BH_FileFlush(BH_File *file);
static int BH_FileSeek(BH_File *file,
int64_t *pos,
int *dir);
static int BH_FileTell(BH_File *file,
int64_t *pos);
static int BH_FileSize(BH_File *file,
int64_t *size);
static int BH_FileFlags(BH_File *file);
static int BH_FileClear(BH_File *file);
static int BH_FileInfo(BH_File *file,
size_t *size,
const char **name)
{
static const char classname[] = BH_FILE_CLASSNAME;
if (size)
*size = sizeof(*file);
if (name)
*name = classname;
return BH_NOIMPL;
}
static int BH_FileInit(BH_File *file,
const char *path)
{
return BH_NOIMPL;
}
static int BH_FileDestroy(BH_File *file)
{
return BH_NOIMPL;
}
static int BH_FileOpen(BH_File *file,
int *mode)
{
return BH_NOIMPL;
}
static int BH_FileClose(BH_File *file)
{
return BH_NOIMPL;
}
static int BH_FileRead(BH_File *file,
char *data,
size_t *size)
{
return BH_NOIMPL;
}
static int BH_FileWrite(BH_File *file,
const char *data,
size_t *size)
{
return BH_NOIMPL;
}
static int BH_FilePeek(BH_File *file,
char *data,
size_t *size)
{
return BH_NOIMPL;
}
static int BH_FileFlush(BH_File *file)
{
return BH_NOIMPL;
}
static int BH_FileSeek(BH_File *file,
int64_t *pos,
int *dir)
{
return BH_NOIMPL;
}
static int BH_FileTell(BH_File *file,
int64_t *pos)
{
return BH_NOIMPL;
}
static int BH_FileSize(BH_File *file,
int64_t *size)
{
return BH_NOIMPL;
}
static int BH_FileFlags(BH_File *file)
{
return BH_IO_FLAG_ERROR;
}
static int BH_FileClear(BH_File *file)
{
return BH_NOIMPL;
}
static int BH_FileCallback(BH_File *file,
int type,
void *arg1,
void *arg2)
{
switch (type)
{
case BH_IO_INFO_CB: return BH_FileInfo(file, (size_t *)arg1, (const char **)arg2);
case BH_IO_INIT_CB: return BH_FileInit(file, (const char *)arg1);
case BH_IO_DESTROY_CB: return BH_FileDestroy(file);
case BH_IO_OPEN_CB: return BH_FileOpen(file, (int *)arg1);
case BH_IO_CLOSE_CB: return BH_FileClose(file);
case BH_IO_READ_CB: return BH_FileRead(file, (char *)arg1, (size_t *)arg2);
case BH_IO_WRITE_CB: return BH_FileWrite(file, (const char *)arg1, (size_t *)arg2);
case BH_IO_PEEK_CB: return BH_FilePeek(file, (char *)arg1, (size_t *)arg2);
case BH_IO_FLUSH_CB: return BH_FileFlush(file);
case BH_IO_SEEK_CB: return BH_FileSeek(file, (int64_t *)arg1, (int *)arg2);
case BH_IO_TELL_CB: return BH_FileTell(file, (int64_t *)arg1);
case BH_IO_SIZE_CB: return BH_FileSize(file, (int64_t *)arg1);
case BH_IO_FLAGS_CB: return BH_FileFlags(file);
case BH_IO_CLEAR_CB: return BH_FileClear(file);
default: return BH_NOIMPL;
}
}
BH_IO *BH_FileNew(const char *path)
{
return BH_IONew((BH_IOCallback)BH_FileCallback, (void *)path);
return NULL;
}

View File

@@ -10,166 +10,67 @@
typedef struct BH_File
{
char *path;
int mode;
BH_IO parent;
int flags;
int handle;
} BH_File;
static int BH_FileInfo(BH_File *file,
size_t *size,
const char **name);
static int BH_FileInit(BH_File *file,
const char *path);
static int BH_FileDestroy(BH_File *file);
static int BH_FileOpen(BH_File *file,
int *mode);
static int BH_FileClose(BH_File *file);
static int BH_FileRead(BH_File *file,
char *data,
size_t *size);
static int BH_FileWrite(BH_File *file,
const char *data,
size_t *size);
static int BH_FilePeek(BH_File* file,
char *data,
size_t *size);
static int BH_FileFlush(BH_File *file);
static int BH_FileSeek(BH_File *file,
int64_t *pos,
int *dir);
static int BH_FileTell(BH_File *file,
int64_t *pos);
static int BH_FileSize(BH_File *file,
int64_t *size);
static int BH_FileFlags(BH_File *file);
static int BH_FileClear(BH_File *file);
static int BH_FileInfo(BH_File *file,
size_t *size,
const char **name)
{
static const char classname[] = BH_FILE_CLASSNAME;
if (size)
*size = sizeof(*file);
if (name)
*name = classname;
return BH_OK;
}
static int BH_FileInit(BH_File *file,
const char *path)
{
/* Check if path is valid */
if (!path)
return BH_ERROR;
/* Duplicate path string and initialize the file struct */
file->path = strdup(path);
file->mode = 0;
file->handle = -1;
file->flags = 0;
return BH_OK;
}
static int BH_FileDestroy(BH_File *file)
{
/* Close the file handle on destruction */
if (file->handle != -1)
BH_FileClose(file);
/* Free path string */
free(file->path);
return BH_OK;
}
static int BH_FileOpenFlags(int mode)
static int fileOpenFlags(int mode)
{
int flags = 0;
/* Determine read/write flags */
if ((mode & BH_IO_READWRITE) == BH_IO_READWRITE)
if ((mode & BH_FILE_READWRITE) == BH_FILE_READWRITE)
flags |= O_RDWR;
else if (mode & BH_IO_WRITE)
else if (mode & BH_FILE_WRITE)
flags |= O_WRONLY;
else if (mode & BH_IO_READ)
else if (mode & BH_FILE_READ)
flags |= O_RDONLY;
else
return -1;
/* Check if existing file should be opened */
if (!(mode & BH_IO_EXIST))
if (!(mode & BH_FILE_EXIST))
{
flags |= O_CREAT;
/* Check if file should be created */
if (mode & BH_IO_CREATE)
if (mode & BH_FILE_CREATE)
flags |= O_EXCL;
}
/* Check if file should be opened in append mode */
if (mode & BH_IO_APPEND)
if (mode & BH_FILE_APPEND)
flags |= O_APPEND;
/* Check if file should be truncated */
if (mode & BH_IO_TRUNCATE)
if (mode & BH_FILE_TRUNCATE)
flags |= O_TRUNC;
return flags;
}
static int BH_FileOpen(BH_File *file,
int *mode)
static int fileInit(BH_File *file,
const char *path,
int mode)
{
static const mode_t open_mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
int flags;
/* If file is already opened - report error */
if (file->handle != -1)
/* Check if path is valid */
if (!path)
return BH_ERROR;
/* Determine file open flags */
flags = BH_FileOpenFlags(*mode);
flags = fileOpenFlags(mode);
if (flags == -1)
return BH_ERROR;
/* Open the file */
file->handle = open(file->path, flags, open_mode);
file->flags = 0;
file->handle = open(path, flags, open_mode);
if (file->handle == -1)
return BH_ERROR;
@@ -177,32 +78,22 @@ static int BH_FileOpen(BH_File *file,
}
static int BH_FileClose(BH_File *file)
static int fileDestroy(BH_File *file)
{
/* If file is closed - report error */
if (file->handle == -1)
return BH_ERROR;
/* Close and reset the file handle */
close(file->handle);
file->handle = -1;
free(file);
return BH_OK;
}
static int BH_FileRead(BH_File *file,
char *data,
size_t *size)
static int fileRead(BH_File *file,
BH_IOReadInfo *info)
{
ssize_t readed;
/* Check if file is open */
if (file->handle == -1)
goto error;
/* Read data from the file */
readed = read(file->handle, data, *size);
readed = read(file->handle, info->data, info->size);
if (readed < 0)
goto error;
@@ -212,7 +103,8 @@ static int BH_FileRead(BH_File *file,
else
file->flags |= BH_IO_FLAG_EOF;
*size = readed;
if (info->actual)
*info->actual = readed;
return BH_OK;
error:
@@ -221,18 +113,13 @@ error:
}
static int BH_FileWrite(BH_File *file,
const char *data,
size_t *size)
static int fileWrite(BH_File *file,
BH_IOWriteInfo *info)
{
ssize_t written;
/* Check if file is open */
if (file->handle == -1)
goto error;
/* Write data to the file */
written = write(file->handle, data, *size);
written = write(file->handle, info->data, info->size);
if (written < 0)
goto error;
@@ -242,7 +129,8 @@ static int BH_FileWrite(BH_File *file,
else
file->flags &= ~BH_IO_FLAG_EOF;
*size = written;
if (info->actual)
*info->actual = written;
return BH_OK;
error:
@@ -251,60 +139,19 @@ error:
}
static int BH_FilePeek(BH_File *file,
char *data,
size_t *size)
static int fileFlush(BH_File *file)
{
int64_t position;
int direction;
/* Check if file is open */
if (file->handle == -1)
goto error;
/* Read data from the file */
if (BH_FileRead(file, data, size))
goto error;
/* Backtrack by the read amount */
position = -((int64_t)*size);
direction = BH_IO_SEEK_CUR;
if (BH_FileSeek(file, &position, &direction))
goto error;
return BH_OK;
error:
file->flags |= BH_IO_FLAG_ERROR;
return BH_ERROR;
}
static int BH_FileFlush(BH_File *file)
{
/* Check if file is open */
if (file->handle == -1)
{
file->flags |= BH_IO_FLAG_ERROR;
return BH_ERROR;
}
/* Flush the buffers */
fsync(file->handle);
return BH_OK;
}
static int BH_FileSeek(BH_File *file,
int64_t *pos,
int *dir)
static int fileSeek(BH_File *file,
BH_IOSeekInfo *info)
{
/* Check if file is open */
if (file->handle == -1)
goto error;
/* Seek to the specified position */
if (lseek(file->handle, *pos, *dir) == -1)
if (lseek(file->handle, info->offset, info->whence) == -1)
goto error;
return BH_OK;
@@ -315,13 +162,9 @@ error:
}
static int BH_FileTell(BH_File *file,
int64_t *pos)
static int fileTell(BH_File *file,
int64_t *pos)
{
/* Check if file is open */
if (file->handle == -1)
goto error;
/* Get current offset in the file */
if ((*pos = lseek(file->handle, 0, SEEK_CUR)) == -1)
goto error;
@@ -334,15 +177,11 @@ error:
}
static int BH_FileSize(BH_File *file,
int64_t *size)
static int fileSize(BH_File *file,
int64_t *size)
{
struct stat sb;
/* Check if file is open */
if (file->handle == -1)
goto error;
/* Get file size from the OS */
if (fstat(file->handle, &sb))
goto error;
@@ -356,51 +195,114 @@ error:
}
static int BH_FileFlags(BH_File *file)
static int fileFlags(BH_File *file,
int *flags)
{
/* If file handle is valid - append IO_OPEN flag */
if (file->handle != -1)
return file->flags | BH_IO_FLAG_OPEN;
return file->flags;
*flags = file->flags;
return BH_OK;
}
static int BH_FileClear(BH_File *file)
static int fileClear(BH_File *file)
{
/* Clear IO_ERROR flag */
/* Clear BH_IO_FLAG_ERROR flag */
file->flags &= ~BH_IO_FLAG_ERROR;
return BH_OK;
}
static int BH_FileCallback(BH_File *file,
int type,
void *arg1,
void *arg2)
static int fileCap(BH_File *file,
int *op)
{
BH_UNUSED(file);
/* Return operations supported by the file input/output device */
switch (*op)
{
case BH_IO_CTL_FLAGS:
case BH_IO_CTL_CLEAR:
case BH_IO_CTL_FLUSH:
case BH_IO_CTL_SIZE:
case BH_IO_CTL_TELL:
case BH_IO_CTL_SEEK:
return BH_OK;
default:
return BH_NOIMPL;
}
}
static int fileCtl(BH_File *file,
BH_IOCtlInfo *info)
{
/* Handle supported operations */
switch (info->op)
{
case BH_IO_CTL_FLAGS:
return fileFlags(file, (int *)info->arg);
case BH_IO_CTL_CLEAR:
return fileClear(file);
case BH_IO_CTL_FLUSH:
return fileFlush(file);
case BH_IO_CTL_SIZE:
return fileSize(file, (int64_t *)(info->arg));
case BH_IO_CTL_TELL:
return fileTell(file, (int64_t *)(info->arg));
case BH_IO_CTL_SEEK:
return fileSeek(file, (BH_IOSeekInfo *)(info->arg));
default:
return BH_NOIMPL;
}
}
static int fileCallback(BH_File *file,
int type,
void *arg)
{
/* Handle basic input/output operations */
switch (type)
{
case BH_IO_INFO_CB: return BH_FileInfo(file, (size_t *)arg1, (const char **)arg2);
case BH_IO_INIT_CB: return BH_FileInit(file, (const char *)arg1);
case BH_IO_DESTROY_CB: return BH_FileDestroy(file);
case BH_IO_OPEN_CB: return BH_FileOpen(file, (int *)arg1);
case BH_IO_CLOSE_CB: return BH_FileClose(file);
case BH_IO_READ_CB: return BH_FileRead(file, (char *)arg1, (size_t *)arg2);
case BH_IO_WRITE_CB: return BH_FileWrite(file, (const char *)arg1, (size_t *)arg2);
case BH_IO_PEEK_CB: return BH_FilePeek(file, (char *)arg1, (size_t *)arg2);
case BH_IO_FLUSH_CB: return BH_FileFlush(file);
case BH_IO_SEEK_CB: return BH_FileSeek(file, (int64_t *)arg1, (int *)arg2);
case BH_IO_TELL_CB: return BH_FileTell(file, (int64_t *)arg1);
case BH_IO_SIZE_CB: return BH_FileSize(file, (int64_t *)arg1);
case BH_IO_FLAGS_CB: return BH_FileFlags(file);
case BH_IO_CLEAR_CB: return BH_FileClear(file);
case BH_IO_OP_DESTROY: return fileDestroy(file);
case BH_IO_OP_READ: return fileRead(file, (BH_IOReadInfo *)arg);
case BH_IO_OP_WRITE: return fileWrite(file, (BH_IOWriteInfo *)arg);
case BH_IO_OP_CTL: return fileCtl(file, (BH_IOCtlInfo *)arg);
case BH_IO_OP_CAP: return fileCap(file, (int*)arg);
default: return BH_NOIMPL;
}
}
BH_IO *BH_FileNew(const char *path)
BH_IO *BH_FileNew(const char *path,
int mode,
int *result)
{
return BH_IONew((BH_IOCallback)BH_FileCallback, (void *)path);
BH_File *file;
int code;
code = BH_OOM;
/* Allocate new file object and initialize it */
if ((file = malloc(sizeof(*file))))
{
file->parent.callback = (BH_IOCallback)fileCallback;
if ((code = fileInit(file, path, mode)))
{
free(file);
file = NULL;
}
}
/* Report error code */
if (result)
*result = code;
return (BH_IO*)file;
}

View File

@@ -6,195 +6,87 @@
typedef struct BH_File
{
char *path;
int mode;
BH_IO parent;
int flags;
int mode;
HANDLE handle;
} BH_File;
static int BH_FileInfo(BH_File *file,
size_t *size,
const char **name);
static int BH_FileInit(BH_File *file,
const char *path);
static int BH_FileDestroy(BH_File *file);
static int BH_FileOpen(BH_File *file,
int *mode);
static int BH_FileClose(BH_File *file);
static int BH_FileRead(BH_File *file,
char *data,
size_t *size);
static int BH_FileWrite(BH_File *file,
const char *data,
size_t *size);
static int BH_FilePeek(BH_File* file,
char *data,
size_t *size);
static int BH_FileFlush(BH_File *file);
static int BH_FileSeek(BH_File *file,
int64_t *pos,
int *dir);
static int BH_FileTell(BH_File *file,
int64_t *pos);
static int BH_FileSize(BH_File *file,
int64_t *size);
static int BH_FileFlags(BH_File *file);
static int BH_FileClear(BH_File *file);
static int BH_FileInfo(BH_File *file,
size_t *size,
const char **name)
static int fileInit(BH_File *file,
const char *path,
int mode)
{
static const char classname[] = BH_FILE_CLASSNAME;
DWORD access = 0, how = 0;
if (size)
*size = sizeof(*file);
if (name)
*name = classname;
return BH_OK;
}
static int BH_FileInit(BH_File *file,
const char *path)
{
/* Check if path is valid */
if (!path)
return BH_ERROR;
/* Duplicate path string and initialize the file struct */
file->path = strdup(path);
file->mode = 0;
file->handle = INVALID_HANDLE_VALUE;
file->flags = 0;
return BH_OK;
}
static int BH_FileDestroy(BH_File *file)
{
/* Close the file handle on destruction */
if (file->handle != INVALID_HANDLE_VALUE)
BH_FileClose(file);
/* Free path string */
free(file->path);
return BH_OK;
}
static int BH_FileOpen(BH_File *file,
int *mode)
{
DWORD access = 0, how = 0;
/* Check if file is already openned */
if (file->handle != INVALID_HANDLE_VALUE)
return BH_ERROR;
/* Determine read/write access flags */
if (*mode & BH_IO_READ)
if (mode & BH_FILE_READ)
access |= GENERIC_READ;
if (*mode & BH_IO_WRITE)
if (mode & BH_FILE_WRITE)
access |= GENERIC_WRITE;
if (!access)
return BH_ERROR;
/* Determine open mode flags */
if (*mode & BH_IO_TRUNCATE)
if (mode & BH_FILE_TRUNCATE)
{
switch (*mode & (BH_IO_CREATE | BH_IO_EXIST))
switch (mode & (BH_FILE_CREATE | BH_FILE_EXIST))
{
case 0: how = CREATE_ALWAYS; break;
case BH_IO_CREATE: how = CREATE_NEW; break;
case BH_IO_EXIST: how = TRUNCATE_EXISTING; break;
default: return BH_ERROR;
case 0: how = CREATE_ALWAYS; break;
case BH_FILE_CREATE: how = CREATE_NEW; break;
case BH_FILE_EXIST: how = TRUNCATE_EXISTING; break;
default: return BH_ERROR;
}
}
else
{
switch (*mode & (BH_IO_CREATE | BH_IO_EXIST))
switch (mode & (BH_FILE_CREATE | BH_FILE_EXIST))
{
case 0: how = OPEN_ALWAYS; break;
case BH_IO_CREATE: how = CREATE_NEW; break;
case BH_IO_EXIST: how = OPEN_EXISTING; break;
default: return BH_ERROR;
case 0: how = OPEN_ALWAYS; break;
case BH_FILE_CREATE: how = CREATE_NEW; break;
case BH_FILE_EXIST: how = OPEN_EXISTING; break;
default: return BH_ERROR;
}
}
/* Save mode that we are in and open file */
file->mode = *mode;
file->handle = CreateFileA(file->path, access, FILE_SHARE_READ, NULL, how, FILE_ATTRIBUTE_NORMAL, NULL);
file->flags = 0;
file->mode = mode;
file->handle = CreateFileA(path, access, FILE_SHARE_READ, NULL, how, FILE_ATTRIBUTE_NORMAL, NULL);
if (file->handle == INVALID_HANDLE_VALUE)
return BH_ERROR;
/* Truncate file if needed */
if (*mode & BH_IO_TRUNCATE)
if (mode & BH_FILE_TRUNCATE)
SetEndOfFile(file->handle);
return BH_OK;
}
static int BH_FileClose(BH_File *file)
static int fileDestroy(BH_File *file)
{
/* If file is opened - close it */
if (file->handle == INVALID_HANDLE_VALUE)
return BH_ERROR;
/* Reset handle and mode values */
/* Close the file handle on destruction */
CloseHandle(file->handle);
file->handle = INVALID_HANDLE_VALUE;
file->mode = 0;
free(file);
return BH_OK;
}
static int BH_FileRead(BH_File *file,
char *data,
size_t *size)
static int fileRead(BH_File *file,
BH_IOReadInfo *info)
{
DWORD readed;
/* Check if file is opened */
if (file->handle == INVALID_HANDLE_VALUE)
goto error;
/* Read data from the file */
if (!ReadFile(file->handle, data, (DWORD)*size, &readed, NULL))
if (!ReadFile(file->handle, info->data, (DWORD)info->size, &readed, NULL))
goto error;
/* Check if we reached end of file */
@@ -203,7 +95,9 @@ static int BH_FileRead(BH_File *file,
else
file->flags &= ~BH_IO_FLAG_EOF;
*size = readed;
if (info->actual)
*info->actual = readed;
return BH_OK;
error:
@@ -212,28 +106,22 @@ error:
}
static int BH_FileWrite(BH_File *file,
const char *data,
size_t *size)
static int fileWrite(BH_File *file,
BH_IOWriteInfo *info)
{
DWORD written;
/* Check if file is opened */
if (file->handle == INVALID_HANDLE_VALUE)
goto error;
LARGE_INTEGER position;
/* Adjust current position in the file to the end */
if (file->mode & BH_IO_APPEND)
if (file->mode & BH_FILE_APPEND)
{
LARGE_INTEGER position;
position.QuadPart = 0;
if (!SetFilePointerEx(file->handle, position, NULL, FILE_END))
goto error;
}
/* Write data to the file */
if (!WriteFile(file->handle, data, (DWORD)*size, &written, NULL))
if (!WriteFile(file->handle, info->data, (DWORD)info->size, &written, NULL))
goto error;
/* Check for end of file */
@@ -242,7 +130,9 @@ static int BH_FileWrite(BH_File *file,
else
file->flags &= ~BH_IO_FLAG_EOF;
*size = written;
if (info->actual)
*info->actual = written;
return BH_OK;
error:
@@ -251,56 +141,22 @@ error:
}
static int BH_FilePeek(BH_File *file,
char *data,
size_t *size)
static int fileFlush(BH_File *file)
{
int64_t position;
int direction;
/* Read data from the file */
if (BH_FileRead(file, data, size))
return BH_ERROR;
/* Backtrack by the read amount */
position = -((int64_t)*size);
direction = BH_IO_SEEK_CUR;
if (BH_FileSeek(file, &position, &direction))
return BH_ERROR;
return BH_OK;
}
static int BH_FileFlush(BH_File *file)
{
/* Check if file is opened */
if (file->handle == INVALID_HANDLE_VALUE)
goto error;
/* Flush OS buffers */
FlushFileBuffers(file->handle);
return BH_OK;
error:
file->flags |= BH_IO_FLAG_ERROR;
return BH_ERROR;
}
static int BH_FileSeek(BH_File *file,
int64_t *pos,
int *dir)
static int fileSeek(BH_File *file,
BH_IOSeekInfo *info)
{
LARGE_INTEGER position;
/* Check if file is opened */
if (file->handle == INVALID_HANDLE_VALUE)
goto error;
/* Set read/write position in the file */
position.QuadPart = *pos;
if (!SetFilePointerEx(file->handle, position, NULL, *dir))
position.QuadPart = info->offset;
if (!SetFilePointerEx(file->handle, position, NULL, info->whence))
goto error;
return BH_OK;
@@ -311,15 +167,11 @@ error:
}
static int BH_FileTell(BH_File *file,
int64_t *pos)
static int fileTell(BH_File *file,
int64_t *pos)
{
LARGE_INTEGER dummy, position;
/* Check if file is opened */
if (file->handle == INVALID_HANDLE_VALUE)
goto error;
/* Readback current position in the file */
dummy.QuadPart = 0;
if (!SetFilePointerEx(file->handle, dummy, &position, BH_IO_SEEK_CUR))
@@ -334,15 +186,11 @@ error:
}
static int BH_FileSize(BH_File *file,
int64_t *size)
static int fileSize(BH_File *file,
int64_t *size)
{
LARGE_INTEGER dummy;
/* Check if file is opened */
if (file->handle == INVALID_HANDLE_VALUE)
goto error;
/* Get current file size */
if (!GetFileSizeEx(file->handle, &dummy))
goto error;
@@ -356,50 +204,114 @@ error:
}
static int BH_FileFlags(BH_File *file)
static int fileFlags(BH_File *file,
int *flags)
{
/* If file handle is valid - append IO_OPEN flag */
if (file->handle != INVALID_HANDLE_VALUE)
return file->flags | BH_IO_FLAG_OPEN;
return file->flags;
*flags = file->flags;
return BH_OK;
}
static int BH_FileClear(BH_File *file)
static int fileClear(BH_File *file)
{
/* Clear IO_ERROR flag */
/* Clear BH_IO_FLAG_ERROR flag */
file->flags &= ~BH_IO_FLAG_ERROR;
return BH_OK;
}
static int BH_FileCallback(BH_File *file,
int type,
void *arg1,
void *arg2)
static int fileCap(BH_File *file,
int *op)
{
BH_UNUSED(file);
/* Return operations supported by the file input/output device */
switch (*op)
{
case BH_IO_CTL_FLAGS:
case BH_IO_CTL_CLEAR:
case BH_IO_CTL_FLUSH:
case BH_IO_CTL_SIZE:
case BH_IO_CTL_TELL:
case BH_IO_CTL_SEEK:
return BH_OK;
default:
return BH_NOIMPL;
}
}
static int fileCtl(BH_File *file,
BH_IOCtlInfo *info)
{
/* Handle supported operations */
switch (info->op)
{
case BH_IO_CTL_FLAGS:
return fileFlags(file, (int *)(info->arg));
case BH_IO_CTL_CLEAR:
return fileClear(file);
case BH_IO_CTL_FLUSH:
return fileFlush(file);
case BH_IO_CTL_SIZE:
return fileSize(file, (int64_t *)(info->arg));
case BH_IO_CTL_TELL:
return fileTell(file, (int64_t *)(info->arg));
case BH_IO_CTL_SEEK:
return fileSeek(file, (BH_IOSeekInfo *)(info->arg));
default:
return BH_NOIMPL;
}
}
static int fileCallback(BH_File *file,
int type,
void *arg)
{
/* Handle basic input/output operations */
switch (type)
{
case BH_IO_INFO_CB: return BH_FileInfo(file, (size_t *)arg1, (const char **)arg2);
case BH_IO_INIT_CB: return BH_FileInit(file, (const char *)arg1);
case BH_IO_DESTROY_CB: return BH_FileDestroy(file);
case BH_IO_OPEN_CB: return BH_FileOpen(file, (int *)arg1);
case BH_IO_CLOSE_CB: return BH_FileClose(file);
case BH_IO_READ_CB: return BH_FileRead(file, (char *)arg1, (size_t *)arg2);
case BH_IO_WRITE_CB: return BH_FileWrite(file, (const char *)arg1, (size_t *)arg2);
case BH_IO_PEEK_CB: return BH_FilePeek(file, (char *)arg1, (size_t *)arg2);
case BH_IO_FLUSH_CB: return BH_FileFlush(file);
case BH_IO_SEEK_CB: return BH_FileSeek(file, (int64_t *)arg1, (int *)arg2);
case BH_IO_TELL_CB: return BH_FileTell(file, (int64_t *)arg1);
case BH_IO_SIZE_CB: return BH_FileSize(file, (int64_t *)arg1);
case BH_IO_FLAGS_CB: return BH_FileFlags(file);
case BH_IO_CLEAR_CB: return BH_FileClear(file);
case BH_IO_OP_DESTROY: return fileDestroy(file);
case BH_IO_OP_READ: return fileRead(file, (BH_IOReadInfo *)arg);
case BH_IO_OP_WRITE: return fileWrite(file, (BH_IOWriteInfo *)arg);
case BH_IO_OP_CTL: return fileCtl(file, (BH_IOCtlInfo *)arg);
case BH_IO_OP_CAP: return fileCap(file, (int*)arg);
default: return BH_NOIMPL;
}
}
BH_IO *BH_FileNew(const char *path)
BH_IO *BH_FileNew(const char *path,
int mode,
int *result)
{
return BH_IONew((BH_IOCallback)BH_FileCallback, (void *)path);
BH_File *file;
int code;
code = BH_OOM;
/* Allocate new file object and initialize it */
if ((file = malloc(sizeof(*file))))
{
file->parent.callback = (BH_IOCallback)fileCallback;
if ((code = fileInit(file, path, mode)))
{
free(file);
file = NULL;
}
}
/* Report error code */
if (result)
*result = code;
return (BH_IO*)file;
}