blob: 9310007d76246405d6747df37cdeac5fb95a86d9 [file] [log] [blame]
#include "THMemoryFile.h"
#include "THFilePrivate.h"
typedef struct THMemoryFile__
{
THFile file;
THCharStorage *storage;
long size;
long position;
} THMemoryFile;
static int THMemoryFile_isOpened(THFile *self)
{
THMemoryFile *mfself = (THMemoryFile*)self;
return (mfself->storage != NULL);
}
static char *THMemoryFile_strnextspace(char *str_, char *c_)
{
char c;
while( (c = *str_) )
{
if( (c != ' ') && (c != '\n') && (c != ':') && (c != ';') )
break;
str_++;
}
while( (c = *str_) )
{
if( (c == ' ') || (c == '\n') || (c == ':') || (c == ';') )
{
*c_ = c;
*str_ = '\0';
return(str_);
}
str_++;
}
return NULL;
}
static void THMemoryFile_grow(THMemoryFile *self, long size)
{
long missingSpace;
if(size <= self->size)
return;
else
{
if(size < self->storage->size) /* note the "<" and not "<=" */
{
self->size = size;
self->storage->data[self->size] = '\0';
return;
}
}
missingSpace = size-self->storage->size+1; /* +1 for the '\0' */
THCharStorage_resize(self->storage, (self->storage->size/2 > missingSpace ?
self->storage->size + (self->storage->size/2)
: self->storage->size + missingSpace));
}
static int THMemoryFile_mode(const char *mode, int *isReadable, int *isWritable)
{
*isReadable = 0;
*isWritable = 0;
if(strlen(mode) == 1)
{
if(*mode == 'r')
{
*isReadable = 1;
return 1;
}
else if(*mode == 'w')
{
*isWritable = 1;
return 1;
}
}
else if(strlen(mode) == 2)
{
if(mode[0] == 'r' && mode[1] == 'w')
{
*isReadable = 1;
*isWritable = 1;
return 1;
}
}
return 0;
}
/********************************************************/
#define READ_WRITE_METHODS(TYPE, TYPEC, ASCII_READ_ELEM, ASCII_WRITE_ELEM, INSIDE_SPACING) \
static long THMemoryFile_read##TYPEC(THFile *self, TYPE *data, long n) \
{ \
THMemoryFile *mfself = (THMemoryFile*)self; \
long nread = 0L; \
\
THArgCheck(mfself->storage != NULL, 1, "attempt to use a closed file"); \
THArgCheck(mfself->file.isReadable, 1, "attempt to read in a write-only file"); \
\
if(mfself->file.isBinary) \
{ \
long nByte = sizeof(TYPE)*n; \
long nByteRemaining = (mfself->position + nByte <= mfself->size ? nByte : mfself->size-mfself->position); \
nread = nByteRemaining/sizeof(TYPE); \
memmove(data, mfself->storage->data+mfself->position, nread*sizeof(TYPE)); \
mfself->position += nread*sizeof(TYPE); \
} \
else \
{ \
long i; \
for(i = 0; i < n; i++) \
{ \
long nByteRead = 0; \
char spaceChar = 0; \
char *spacePtr = THMemoryFile_strnextspace(mfself->storage->data+mfself->position, &spaceChar); \
ASCII_READ_ELEM; \
if(ret == EOF) \
{ \
while(mfself->storage->data[mfself->position]) \
mfself->position++; \
} \
else \
mfself->position += nByteRead; \
if(spacePtr) \
*spacePtr = spaceChar; \
} \
if(mfself->file.isAutoSpacing && (n > 0)) \
{ \
if( (mfself->position < mfself->size) && (mfself->storage->data[mfself->position] == '\n') ) \
mfself->position++; \
} \
} \
\
if(nread != n) \
{ \
mfself->file.hasError = 1; /* shouldn't we put hasError to 0 all the time ? */ \
if(!mfself->file.isQuiet) \
THError("read error: read %d blocks instead of %d", nread, n); \
} \
\
return nread; \
} \
\
static long THMemoryFile_write##TYPEC(THFile *self, TYPE *data, long n) \
{ \
THMemoryFile *mfself = (THMemoryFile*)self; \
\
THArgCheck(mfself->storage != NULL, 1, "attempt to use a closed file"); \
THArgCheck(mfself->file.isWritable, 1, "attempt to write in a read-only file"); \
\
if(mfself->file.isBinary) \
{ \
long nByte = sizeof(TYPE)*n; \
THMemoryFile_grow(mfself, mfself->position+nByte); \
memmove(mfself->storage->data+mfself->position, data, nByte); \
mfself->position += nByte; \
if(mfself->position > mfself->size) \
{ \
mfself->size = mfself->position; \
mfself->storage->data[mfself->size] = '\0'; \
} \
} \
else \
{ \
long i; \
for(i = 0; i < n; i++) \
{ \
long nByteWritten; \
while (1) \
{ \
ASCII_WRITE_ELEM; \
if( (nByteWritten > -1) && (nByteWritten < mfself->storage->size-mfself->position) ) \
{ \
mfself->position += nByteWritten; \
break; \
} \
THMemoryFile_grow(mfself, mfself->storage->size + (mfself->storage->size/2) + 2); \
} \
if(mfself->file.isAutoSpacing) \
{ \
if(i < n-1) \
{ \
THMemoryFile_grow(mfself, mfself->position+1); \
sprintf(mfself->storage->data+mfself->position, " "); \
mfself->position++; \
} \
if(i == n-1) \
{ \
THMemoryFile_grow(mfself, mfself->position+1); \
sprintf(mfself->storage->data+mfself->position, "\n"); \
mfself->position++; \
} \
} \
} \
if(mfself->position > mfself->size) \
{ \
mfself->size = mfself->position; \
mfself->storage->data[mfself->size] = '\0'; \
} \
} \
\
return n; \
}
THCharStorage *THMemoryFile_storage(THFile *self)
{
THMemoryFile *mfself = (THMemoryFile*)self;
THArgCheck(mfself->storage != NULL, 1, "attempt to use a closed file");
THCharStorage_resize(mfself->storage, mfself->size+1);
return mfself->storage;
}
static void THMemoryFile_synchronize(THFile *self)
{
THMemoryFile *mfself = (THMemoryFile*)self;
THArgCheck(mfself->storage != NULL, 1, "attempt to use a closed file");
}
static void THMemoryFile_seek(THFile *self, long position)
{
THMemoryFile *mfself = (THMemoryFile*)self;
THArgCheck(mfself->storage != NULL, 1, "attempt to use a closed file");
THArgCheck(position >= 0, 2, "position must be positive");
if(position <= mfself->size)
mfself->position = position;
else
{
mfself->file.hasError = 1;
if(!mfself->file.isQuiet)
THError("unable to seek at position %d", position);
}
}
static void THMemoryFile_seekEnd(THFile *self)
{
THMemoryFile *mfself = (THMemoryFile*)self;
THArgCheck(mfself->storage != NULL, 1, "attempt to use a closed file");
mfself->position = mfself->size;
}
static long THMemoryFile_position(THFile *self)
{
THMemoryFile *mfself = (THMemoryFile*)self;
THArgCheck(mfself->storage != NULL, 1, "attempt to use a closed file");
return mfself->position;
}
static void THMemoryFile_close(THFile *self)
{
THMemoryFile *mfself = (THMemoryFile*)self;
THArgCheck(mfself->storage != NULL, 1, "attempt to use a closed file");
THCharStorage_free(mfself->storage);
mfself->storage = NULL;
}
static void THMemoryFile_free(THFile *self)
{
THMemoryFile *mfself = (THMemoryFile*)self;
if(mfself->storage)
THCharStorage_free(mfself->storage);
THFree(mfself);
}
/* READ_WRITE_METHODS(bool, Bool, */
/* int value = 0; int ret = sscanf(mfself->storage->data+mfself->position, "%d%n", &value, &nByteRead); data[i] = (value ? 1 : 0), */
/* int value = (data[i] ? 1 : 0); nByteWritten = snprintf(mfself->storage->data+mfself->position, mfself->storage->size-mfself->position, "%d", value), */
/* 1) */
READ_WRITE_METHODS(unsigned char, Byte,
long ret = (mfself->position + n <= mfself->size ? n : mfself->size-mfself->position); \
if(spacePtr) *spacePtr = spaceChar; \
nByteRead = ret; \
nread = ret; \
i = n-1; \
memmove(data, mfself->storage->data+mfself->position, nByteRead),
nByteWritten = (n < mfself->storage->size-mfself->position ? n : -1); \
i = n-1; \
if(nByteWritten > -1)
memmove(mfself->storage->data+mfself->position, data, nByteWritten),
0)
/* DEBUG: we should check if %n is count or not as a element (so ret might need to be ret-- on some systems) */
/* Note that we do a trick for char */
READ_WRITE_METHODS(char, Char,
long ret = (mfself->position + n <= mfself->size ? n : mfself->size-mfself->position); \
if(spacePtr) *spacePtr = spaceChar; \
nByteRead = ret; \
nread = ret; \
i = n-1; \
memmove(data, mfself->storage->data+mfself->position, nByteRead),
nByteWritten = (n < mfself->storage->size-mfself->position ? n : -1); \
i = n-1; \
if(nByteWritten > -1)
memmove(mfself->storage->data+mfself->position, data, nByteWritten),
0)
READ_WRITE_METHODS(short, Short,
int nByteRead_; int ret = sscanf(mfself->storage->data+mfself->position, "%hd%n", &data[i], &nByteRead_); nByteRead = nByteRead_; if(ret <= 0) break; else nread++,
nByteWritten = snprintf(mfself->storage->data+mfself->position, mfself->storage->size-mfself->position, "%hd", data[i]),
1)
READ_WRITE_METHODS(int, Int,
int nByteRead_; int ret = sscanf(mfself->storage->data+mfself->position, "%d%n", &data[i], &nByteRead_); nByteRead = nByteRead_; if(ret <= 0) break; else nread++,
nByteWritten = snprintf(mfself->storage->data+mfself->position, mfself->storage->size-mfself->position, "%d", data[i]),
1)
READ_WRITE_METHODS(long, Long,
int nByteRead_; int ret = sscanf(mfself->storage->data+mfself->position, "%ld%n", &data[i], &nByteRead_); nByteRead = nByteRead_; if(ret <= 0) break; else nread++,
nByteWritten = snprintf(mfself->storage->data+mfself->position, mfself->storage->size-mfself->position, "%ld", data[i]),
1)
READ_WRITE_METHODS(float, Float,
int nByteRead_; int ret = sscanf(mfself->storage->data+mfself->position, "%g%n", &data[i], &nByteRead_); nByteRead = nByteRead_; if(ret <= 0) break; else nread++,
nByteWritten = snprintf(mfself->storage->data+mfself->position, mfself->storage->size-mfself->position, "%g", data[i]),
1)
READ_WRITE_METHODS(double, Double,
int nByteRead_; int ret = sscanf(mfself->storage->data+mfself->position, "%lg%n", &data[i], &nByteRead_); nByteRead = nByteRead_; if(ret <= 0) break; else nread++,
nByteWritten = snprintf(mfself->storage->data+mfself->position, mfself->storage->size-mfself->position, "%lg", data[i]),
1)
static char* THMemoryFile_cloneString(const char *str, long size)
{
char *cstr = THAlloc(size);
memcpy(cstr, str, size);
return cstr;
}
static long THMemoryFile_readString(THFile *self, const char *format, char **str_)
{
THMemoryFile *mfself = (THMemoryFile*)self;
THArgCheck(mfself->storage != NULL, 1, "attempt to use a closed file");
THArgCheck(mfself->file.isReadable, 1, "attempt to read in a write-only file");
THArgCheck((strlen(format) >= 2 ? (format[0] == '*') && (format[1] == 'a' || format[1] == 'l') : 0), 2, "format must be '*a' or '*l'");
if(mfself->position == mfself->size) /* eof ? */
{
mfself->file.hasError = 1;
if(!mfself->file.isQuiet)
THError("read error: read 0 blocks instead of 1");
*str_ = NULL;
return 0;
}
if(format[1] == 'a')
{
long str_size = mfself->size-mfself->position;
*str_ = THMemoryFile_cloneString(mfself->storage->data+mfself->position, str_size);
mfself->position = mfself->size;
return str_size;
}
else
{
char *p = mfself->storage->data+mfself->position;
long posEol = -1;
long i;
for(i = 0L; i < mfself->size-mfself->position; i++)
{
if(p[i] == '\n')
{
posEol = i;
break;
}
}
if(posEol >= 0)
{
*str_ = THMemoryFile_cloneString(mfself->storage->data+mfself->position, posEol);
mfself->position += posEol+1;
return posEol;
}
else /* well, we read all! */
{
long str_size = mfself->size-mfself->position;
*str_ = THMemoryFile_cloneString(mfself->storage->data+mfself->position, str_size);
mfself->position = mfself->size;
return str_size;
}
}
*str_ = NULL;
return 0;
}
static long THMemoryFile_writeString(THFile *self, const char *str, long size)
{
THMemoryFile *mfself = (THMemoryFile*)self;
THArgCheck(mfself->storage != NULL, 1, "attempt to use a closed file");
THArgCheck(mfself->file.isWritable, 1, "attempt to write in a read-only file");
THMemoryFile_grow(mfself, mfself->position+size);
memmove(mfself->storage->data+mfself->position, str, size);
mfself->position += size;
if(mfself->position > mfself->size)
{
mfself->size = mfself->position;
mfself->storage->data[mfself->size] = '\0';
}
return size;
}
THFile *THMemoryFile_newWithStorage(THCharStorage *storage, const char *mode)
{
static struct THFileVTable vtable = {
THMemoryFile_isOpened,
THMemoryFile_readByte,
THMemoryFile_readChar,
THMemoryFile_readShort,
THMemoryFile_readInt,
THMemoryFile_readLong,
THMemoryFile_readFloat,
THMemoryFile_readDouble,
THMemoryFile_readString,
THMemoryFile_writeByte,
THMemoryFile_writeChar,
THMemoryFile_writeShort,
THMemoryFile_writeInt,
THMemoryFile_writeLong,
THMemoryFile_writeFloat,
THMemoryFile_writeDouble,
THMemoryFile_writeString,
THMemoryFile_synchronize,
THMemoryFile_seek,
THMemoryFile_seekEnd,
THMemoryFile_position,
THMemoryFile_close,
THMemoryFile_free
};
THMemoryFile *mfself;
int isReadable;
int isWritable;
if(storage)
{
THArgCheck(storage->data[storage->size-1] == '\0', 1, "provided CharStorage must be terminated by 0");
THArgCheck(THMemoryFile_mode(mode, &isReadable, &isWritable), 2, "file mode should be 'r','w' or 'rw'");
THCharStorage_retain(storage);
}
else
{
THArgCheck(THMemoryFile_mode(mode, &isReadable, &isWritable), 2, "file mode should be 'r','w' or 'rw'");
storage = THCharStorage_newWithSize(1);
storage->data[0] = '\0';
}
mfself = THAlloc(sizeof(THMemoryFile));
mfself->storage = storage;
mfself->size = (storage ? storage->size-1 : 0);
mfself->position = 0;
mfself->file.vtable = &vtable;
mfself->file.isQuiet = 0;
mfself->file.isReadable = isReadable;
mfself->file.isWritable = isWritable;
mfself->file.isBinary = 0;
mfself->file.isAutoSpacing = 1;
mfself->file.hasError = 0;
return (THFile*)mfself;
}
THFile *THMemoryFile_new(const char *mode)
{
return THMemoryFile_newWithStorage(NULL, mode);
}