/*
* io_dp.c
*
* Implements the dynamic pointer interface.
*
* Based on GD.pm code by Lincoln Stein for interfacing to libgd.
* Added support for reading as well as support for 'tell' and 'seek'.
*
* As will all I/O modules, most functions are for local use only (called
* via function pointers in the I/O context).
*
* gdDPExtractData is the exception to this: it will return the pointer to
* the internal data, and reset the internal storage.
*
* Written/Modified 1999, Philip Warner.
*
*/
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include "gd.h"
#define TRUE 1
#define FALSE 0
/* this is used for creating images in main memory*/
typedef struct dpStruct {
void* data;
int logicalSize;
int realSize;
int dataGood;
int pos;
} dynamicPtr;
typedef struct dpIOCtx {
gdIOCtx ctx;
dynamicPtr *dp;
} dpIOCtx;
typedef struct dpIOCtx *dpIOCtxPtr;
/* these functions operate on in-memory dynamic pointers */
static int allocDynamic (dynamicPtr* dp,int initialSize, void *data);
static int appendDynamic (dynamicPtr* dp, const void* src, int size);
static int reallocDynamic (dynamicPtr* dp, int required);
static int trimDynamic (dynamicPtr* dp);
static void freeDynamicCtx(struct gdIOCtx* ctx);
static dynamicPtr* newDynamic(int initialSize, void *data);
static int dynamicPutbuf( struct gdIOCtx*, const void *, int );
static void dynamicPutchar( struct gdIOCtx*, int a );
static int dynamicGetbuf( gdIOCtxPtr ctx, void *buf, int len);
static int dynamicGetchar( gdIOCtxPtr ctx );
static int dynamicSeek(struct gdIOCtx*, const int);
static long dynamicTell(struct gdIOCtx*);
/* return data as a dynamic pointer */
gdIOCtx* gdNewDynamicCtx (int initialSize, void *data) {
dpIOCtx *ctx;
dynamicPtr* dp;
ctx = (dpIOCtx*) malloc(sizeof(dpIOCtx));
if (ctx == NULL) {
return NULL;
}
dp = newDynamic(initialSize, data);
if (!dp) {
free(ctx);
return NULL;
};
ctx->dp = dp;
ctx->ctx.getC = dynamicGetchar;
ctx->ctx.putC = dynamicPutchar;
ctx->ctx.getBuf = dynamicGetbuf;
ctx->ctx.putBuf = dynamicPutbuf;
ctx->ctx.seek = dynamicSeek;
ctx->ctx.tell = dynamicTell;
ctx->ctx.free = freeDynamicCtx;
return (gdIOCtx*)ctx;
}
void* gdDPExtractData(struct gdIOCtx* ctx, int *size)
{
dynamicPtr *dp;
dpIOCtx *dctx;
void *data;
dctx = (dpIOCtx*) ctx;
dp = dctx->dp;
/* clean up the data block and return it */
if (dp->dataGood) {
trimDynamic(dp);
*size = dp->logicalSize;
data = dp->data;
} else {
*size = 0;
data = NULL;
if (dp->data != NULL) {
free(dp->data);
}
}
dp->data = NULL;
dp->realSize=0;
dp->logicalSize=0;
return data;
}
static
void freeDynamicCtx(struct gdIOCtx* ctx)
{
dynamicPtr *dp;
dpIOCtx *dctx;
dctx = (dpIOCtx*) ctx;
dp = dctx->dp;
free(ctx);
/* clean up the data block and return it */
if (dp->data != NULL) {
free(dp->data);
dp->data = NULL;
}
dp->realSize=0;
dp->logicalSize=0;
free(dp);
}
static long dynamicTell(struct gdIOCtx* ctx)
{
dpIOCtx *dctx;
dctx = (dpIOCtx*) ctx;
return (dctx->dp->pos);
}
static int dynamicSeek(struct gdIOCtx* ctx, const int pos)
{
int bytesNeeded;
dynamicPtr *dp;
dpIOCtx *dctx;
dctx = (dpIOCtx*) ctx;
dp = dctx->dp;
if (!dp->dataGood) return FALSE;
bytesNeeded = pos;
if (bytesNeeded > dp->realSize) {
if (!reallocDynamic(dp,dp->realSize*2)) {
dp->dataGood = FALSE;
return FALSE;
}
}
/* if we get here, we can be sure that we have enough bytes
to copy safely */
/* Extend the logical size if we seek beyond EOF. */
if (pos > dp->logicalSize) {
dp->logicalSize = pos;
};
dp->pos = pos;
return TRUE;
}
/* return data as a dynamic pointer */
static dynamicPtr* newDynamic (int initialSize, void *data) {
dynamicPtr* dp;
dp = (dynamicPtr*) malloc(sizeof(dynamicPtr));
if (dp == NULL) {
return NULL;
}
if (!allocDynamic(dp,initialSize, data))
return NULL;
dp->pos = 0;
return dp;
}
static int
dynamicPutbuf( struct gdIOCtx* ctx, const void *buf, int size )
{
dpIOCtx *dctx;
dctx = (dpIOCtx*) ctx;
appendDynamic(dctx->dp,buf,size);
if (dctx->dp->dataGood) {
return size;
} else {
return -1;
};
}
static void
dynamicPutchar( struct gdIOCtx* ctx, int a )
{
unsigned char b;
dpIOCtxPtr dctx;
b = a;
dctx = (dpIOCtxPtr) ctx;
appendDynamic(dctx->dp,&b,1);
}
static int
dynamicGetbuf( gdIOCtxPtr ctx, void *buf, int len)
{
int rlen, remain;
dpIOCtxPtr dctx;
dynamicPtr* dp;
dctx = (dpIOCtxPtr) ctx;
dp = dctx->dp;
remain = dp->logicalSize - dp->pos;
if (remain >= len) {
rlen = len;
} else {
if (remain == 0) {
return EOF;
}
rlen = remain;
}
memcpy(buf, (void*)((char*)dp->data + dp->pos), rlen);
dp->pos += rlen;
return rlen;
}
static int
dynamicGetchar( gdIOCtxPtr ctx )
{
unsigned char b;
int rv;
rv = dynamicGetbuf(ctx, &b, 1);
if (rv != 1) {
return EOF;
} else {
return b ;/* (b & 0xff); */
}
}
/* *********************************************************************
*
* InitDynamic - Return a dynamically resizable void*
*
* *********************************************************************
*/
static int
allocDynamic (dynamicPtr* dp,int initialSize, void *data) {
if (data == NULL) {
dp->logicalSize = 0;
dp->dataGood = FALSE;
dp->data = malloc(initialSize);
} else {
dp->logicalSize = initialSize;
dp->dataGood = TRUE;
dp->data = data;
}
if (dp->data !=NULL) {
dp->realSize = initialSize;
dp->dataGood = TRUE;
dp->pos = 0;
return TRUE;
} else {
dp->realSize = 0;
return FALSE;
}
}
/* append bytes to the end of a dynamic pointer */
static int
appendDynamic (dynamicPtr* dp, const void* src, int size) {
int bytesNeeded;
char* tmp;
if (!dp->dataGood) return FALSE;
/* bytesNeeded = dp->logicalSize + size; */
bytesNeeded = dp->pos + size;
if (bytesNeeded > dp->realSize) {
if (!reallocDynamic(dp,bytesNeeded*2)) {
dp->dataGood = FALSE;
return FALSE;
}
}
/* if we get here, we can be sure that we have enough bytes
to copy safely */
/*printf("Mem OK Size: %d, Pos: %d\n", dp->realSize, dp->pos); */
tmp = (char*)dp->data;
memcpy((void*)(tmp+(dp->pos)),src,size);
dp->pos += size;
if (dp->pos > dp->logicalSize) {
dp->logicalSize = dp->pos;
};
return TRUE;
}
/* grow (or shrink) dynamic pointer */
static int
reallocDynamic (dynamicPtr* dp, int required) {
void* newPtr;
/* First try realloc(). If that doesn't work, make a new
memory block and copy. */
if ( (newPtr = realloc(dp->data,required)) ) {
dp->realSize = required;
dp->data = newPtr;
return TRUE;
}
/* create a new pointer */
newPtr = malloc(required);
if (!newPtr) {
dp->dataGood = FALSE;
return FALSE;
}
/* copy the old data into it */
memcpy(newPtr,dp->data,dp->logicalSize);
free(dp->data);
dp->data = newPtr;
dp->realSize = required;
return TRUE;
}
/* trim pointer so that its real and logical sizes match */
static int
trimDynamic (dynamicPtr* dp) {
return reallocDynamic(dp,dp->logicalSize);
}