#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "ktx.h"
#include "ktxint.h"
#include "memstream.h"
#define KTX_MEM_DEFAULT_ALLOCATED_SIZE 256
struct ktxMem
{
const ktx_uint8_t* robytes;
ktx_uint8_t* bytes;
ktx_size_t alloc_size;
ktx_size_t used_size;
ktx_off_t pos;
};
static KTX_error_code ktxMem_expand(ktxMem* pMem, const ktx_size_t size);
static KTX_error_code
ktxMem_construct(ktxMem* pMem)
{
pMem->pos = 0;
pMem->alloc_size = 0;
pMem->robytes = 0;
pMem->bytes = 0;
pMem->used_size = 0;
return ktxMem_expand(pMem, KTX_MEM_DEFAULT_ALLOCATED_SIZE);
}
static KTX_error_code
ktxMem_create(ktxMem** ppMem)
{
ktxMem* pNewMem = (ktxMem*)malloc(sizeof(ktxMem));
if (pNewMem) {
KTX_error_code result = ktxMem_construct(pNewMem);
if (result == KTX_SUCCESS)
*ppMem = pNewMem;
return result;
}
else {
return KTX_OUT_OF_MEMORY;
}
}
static void
ktxMem_construct_ro(ktxMem* pMem, const void* bytes, ktx_size_t numBytes)
{
pMem->pos = 0;
pMem->robytes = bytes;
pMem->bytes = 0;
pMem->used_size = numBytes;
pMem->alloc_size = numBytes;
}
static KTX_error_code
ktxMem_create_ro(ktxMem** ppMem, const void* bytes, ktx_size_t numBytes)
{
ktxMem* pNewMem = (ktxMem*)malloc(sizeof(ktxMem));
if (pNewMem) {
ktxMem_construct_ro(pNewMem, bytes, numBytes);
*ppMem = pNewMem;
return KTX_SUCCESS;
}
else {
return KTX_OUT_OF_MEMORY;
}
}
static void
ktxMem_destroy(ktxMem* pMem, ktx_bool_t freeData)
{
assert(pMem != NULL);
if (freeData) {
free(pMem->bytes);
}
free(pMem);
}
#ifdef KTXMEM_CLEAR_USED
static void
ktxMem_clear(ktxMem* pMem)
{
assert(pMem != NULL);
memset(pMem, 0, sizeof(ktxMem));
}
#endif
static KTX_error_code
ktxMem_expand(ktxMem *pMem, const ktx_size_t newsize)
{
ktx_size_t new_alloc_size;
assert(pMem != NULL && newsize != 0);
new_alloc_size = pMem->alloc_size == 0 ?
KTX_MEM_DEFAULT_ALLOCATED_SIZE : pMem->alloc_size;
while (new_alloc_size < newsize) {
ktx_size_t alloc_size = new_alloc_size;
new_alloc_size <<= 1;
if (new_alloc_size < alloc_size) {
new_alloc_size = (ktx_size_t)-1L;
}
}
if (new_alloc_size == pMem->alloc_size)
return KTX_SUCCESS;
if (!pMem->bytes)
pMem->bytes = (ktx_uint8_t*)malloc(new_alloc_size);
else
pMem->bytes = (ktx_uint8_t*)realloc(pMem->bytes, new_alloc_size);
if (!pMem->bytes)
{
pMem->alloc_size = 0;
pMem->used_size = 0;
return KTX_OUT_OF_MEMORY;
}
pMem->alloc_size = new_alloc_size;
return KTX_SUCCESS;
}
static
KTX_error_code ktxMemStream_read(ktxStream* str, void* dst, const ktx_size_t count)
{
ktxMem* mem;
ktx_off_t newpos;
const ktx_uint8_t* bytes;
if (!str || (mem = str->data.mem)== 0)
return KTX_INVALID_VALUE;
newpos = mem->pos + count;
if (newpos < mem->pos || (ktx_uint32_t)newpos > mem->used_size)
return KTX_FILE_UNEXPECTED_EOF;
bytes = mem->robytes ? mem->robytes : mem->bytes;
memcpy(dst, bytes + mem->pos, count);
mem->pos = newpos;
return KTX_SUCCESS;
}
static
KTX_error_code ktxMemStream_skip(ktxStream* str, const ktx_size_t count)
{
ktxMem* mem;
ktx_off_t newpos;
if (!str || (mem = str->data.mem) == 0)
return KTX_INVALID_VALUE;
newpos = mem->pos + count;
if (newpos < mem->pos || (ktx_uint32_t)newpos > mem->used_size)
return KTX_FILE_UNEXPECTED_EOF;
mem->pos = newpos;
return KTX_SUCCESS;
}
static
KTX_error_code ktxMemStream_write(ktxStream* str, const void* src,
const ktx_size_t size, const ktx_size_t count)
{
ktxMem* mem;
KTX_error_code rc = KTX_SUCCESS;
ktx_size_t new_size;
if (!str || (mem = str->data.mem) == 0)
return KTX_INVALID_VALUE;
if (mem->robytes)
return KTX_INVALID_OPERATION;
new_size = mem->pos + (size*count);
if ((ktx_off_t)new_size < mem->pos)
return KTX_FILE_OVERFLOW;
if (mem->alloc_size < new_size) {
rc = ktxMem_expand(mem, new_size);
if (rc != KTX_SUCCESS)
return rc;
}
memcpy(mem->bytes + mem->pos, src, size*count);
mem->pos += size*count;
if (mem->pos > (ktx_off_t)mem->used_size)
mem->used_size = mem->pos;
return KTX_SUCCESS;
}
static
KTX_error_code ktxMemStream_getpos(ktxStream* str, ktx_off_t* const pos)
{
if (!str || !pos)
return KTX_INVALID_VALUE;
assert(str->type == eStreamTypeMemory);
*pos = str->data.mem->pos;
return KTX_SUCCESS;
}
static
KTX_error_code ktxMemStream_setpos(ktxStream* str, ktx_off_t pos)
{
if (!str)
return KTX_INVALID_VALUE;
assert(str->type == eStreamTypeMemory);
if (pos > (ktx_off_t)str->data.mem->alloc_size)
return KTX_INVALID_OPERATION;
str->data.mem->pos = pos;
return KTX_SUCCESS;
}
KTX_error_code ktxMemStream_getdata(ktxStream* str, ktx_uint8_t** ppBytes)
{
if (!str || !ppBytes)
return KTX_INVALID_VALUE;
assert(str->type == eStreamTypeMemory);
*ppBytes = str->data.mem->bytes;
return KTX_SUCCESS;
}
static
KTX_error_code ktxMemStream_getsize(ktxStream* str, ktx_size_t* pSize)
{
if (!str || !pSize)
return KTX_INVALID_VALUE;
assert(str->type == eStreamTypeMemory);
*pSize = str->data.mem->used_size;
return KTX_SUCCESS;
}
void
ktxMemStream_setup(ktxStream* str)
{
str->type = eStreamTypeMemory;
str->read = ktxMemStream_read;
str->skip = ktxMemStream_skip;
str->write = ktxMemStream_write;
str->getpos = ktxMemStream_getpos;
str->setpos = ktxMemStream_setpos;
str->getsize = ktxMemStream_getsize;
str->destruct = ktxMemStream_destruct;
}
KTX_error_code ktxMemStream_construct(ktxStream* str,
ktx_bool_t freeOnDestruct)
{
ktxMem* mem;
KTX_error_code result = KTX_SUCCESS;
if (!str)
return KTX_INVALID_VALUE;
result = ktxMem_create(&mem);
if (KTX_SUCCESS == result) {
str->data.mem = mem;
ktxMemStream_setup(str);
str->closeOnDestruct = freeOnDestruct;
}
return result;
}
KTX_error_code ktxMemStream_construct_ro(ktxStream* str,
const ktx_uint8_t* bytes,
const ktx_size_t numBytes)
{
ktxMem* mem;
KTX_error_code result = KTX_SUCCESS;
if (!str || !bytes || numBytes == 0)
return KTX_INVALID_VALUE;
result = ktxMem_create_ro(&mem, bytes, numBytes);
if (KTX_SUCCESS == result) {
str->data.mem = mem;
ktxMemStream_setup(str);
str->closeOnDestruct = KTX_FALSE;
}
return result;
}
void
ktxMemStream_destruct(ktxStream* str)
{
assert(str && str->type == eStreamTypeMemory);
ktxMem_destroy(str->data.mem, str->closeOnDestruct);
str->data.mem = NULL;
}