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:
304
src/Buffer.c
Normal file
304
src/Buffer.c
Normal 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
217
src/Bytes.c
Normal 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
273
src/IO.c
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user