/*#io
Store ioDoc(
docCopyright("Steve Dekorte", 2002)
docLicense("BSD revised")
docObject("Store")
docDescription("This object can be used to store the Io state in a way similar to a Smalltalk system image. However, objects are stored as individual indexed key/value records, so it opens the possibility of lazy loading and using the Io namespace itself as a high performance, transactional database.")
*/
#include "IoStore.h"
#include "IoObject_persistence.h"
#include "IoState.h"
#include "IoNumber.h"
#include "IoList.h"
//#include "IoPObject.h"
#include "ByteArray.h"
#define DATA(self) ((IoStoreData *)IoObject_dataPointer(self))
IoTag *IoStore_tag(void *state)
{
IoTag *tag = IoTag_newWithName_("Store");
tag->state = state;
tag->cloneFunc = (TagCloneFunc *)IoStore_rawClone;
tag->freeFunc = (TagFreeFunc *)IoStore_free;
tag->markFunc = (TagMarkFunc *)IoStore_mark;
return tag;
}
IoStore *IoStore_proto(void *state)
{
IoObject *self = IoObject_new(state);
self->tag = IoStore_tag(state);
IoObject_setDataPointer_(self, calloc(1, sizeof(IoStoreData)));
DATA(self)->path = IoState_symbolWithCString_((IoState *)state, "DefaultStore");
DATA(self)->pidToObject = Hash_new();
DATA(self)->objectsToSave = List_new();
DATA(self)->tmpKeyStream = BStream_new();
DATA(self)->tmpValueStream = BStream_new();
DATA(self)->debug = 0;
IoState_registerProtoWithFunc_((IoState *)state, self, IoStore_proto);
IoStore_addMethods(self);
return self;
}
IoStore *IoStore_rawClone(IoStore *self)
{
// singleton
return self;
}
void IoStore_addMethods(IoStore *self)
{
IoMethodTable methodTable[] = {
{"path", IoStore_path},
{"setPath", IoStore_setPath},
{"store", IoStore_store},
{"load", IoStore_load},
{NULL, NULL},
};
IoObject_addMethodTable_(self, methodTable);
}
// -----------------------------------------------------------
IoStore *IoStore_new(void *state)
{
IoObject *proto = IoState_protoWithInitFunction_((IoState *)state, IoStore_proto);
return IOCLONE(proto);
}
void IoStore_free(IoStore *self)
{
IoStoreData *data = DATA(self);
Hash_free(data->pidToObject);
List_free(data->objectsToSave);
BStream_free(data->tmpKeyStream);
BStream_free(data->tmpValueStream);
free(data);
}
void IoStore_mark(IoStore *self)
{
IoObject_shouldMark(DATA(self)->path);
}
int IoStore_debugIsOn(IoStore *self)
{
return DATA(self)->debug;
}
BStream *IoStore_valueStream(IoStore *self)
{
return DATA(self)->tmpValueStream;
}
IoObject *IoStore_path(IoStore *self, IoObject *locals, IoMessage *m)
{
return DATA(self)->path;
}
IoObject *IoStore_setPath(IoStore *self, IoObject *locals, IoMessage *m)
{
IoSymbol *p = IoMessage_locals_seqArgAt_(m, locals, 0);
DATA(self)->path = IOREF(p);
return self;
}
// store exec ----------------------------------------------------
SkipDBM *IoStore_sdbm(IoStore *self)
{
return IOSTATE->sdbm;
}
UDB *IoStore_udb(IoStore *self)
{
return SkipDBM_udb(IOSTATE->sdbm);
}
void IoStore_beginTransaction(IoStore *self)
{
SkipDBM_beginTransaction(IoStore_sdbm(self));
}
void IoStore_commitTransaction(IoStore *self)
{
SkipDBM_commitTransaction(IoStore_sdbm(self));
}
void IoStore_openIfNeeded(IoStore *self)
{
SkipDBM *sdbm = IoStore_sdbm(self);
if (!SkipDBM_isOpen(sdbm))
{
SkipDBM_setPath_(sdbm, CSTRING(DATA(self)->path));
SkipDBM_open(sdbm);
/*
{
IoState_error_(IOSTATE, m, "unable to open Store");
return self;
}
*/
}
}
IoObject *IoStore_store(IoStore *self, IoObject *locals, IoMessage *m)
{
//IoObject *obj = IoMessage_locals_valueArgAt_(m, locals, 0);
IoObject *obj = IOSTATE->lobby;
UDB *udb = IoStore_udb(self);
DATA(self)->context.self = self;
DATA(self)->context.locals = locals;
DATA(self)->context.message = m;
IoStore_openIfNeeded(self);
IoStore_beginTransaction(self);
// create a place holder for the lobby record, if needed
if (UDB_at_(udb, 2).size == 0)
{
PID_TYPE pid = UDB_put_(udb, Datum_Empty());
if (pid != 2)
{
printf("wrong pid - expected 2 but got %" PID_FORMAT "\n", pid);
}
}
obj->isDirty = 1;
{
PID_TYPE pid;
IoObject_setPersistentId_(obj, 2);
pid = IoStore_pidForObject_(self, obj);
if (pid != 2)
{
printf("wrong pid for Lobby - expected 2 but got %" PID_FORMAT "\n", pid);
exit(1);
}
}
IoStore_finishSave(self);
IoStore_commitTransaction(self);
return self;
}
IoObject *IoStore_load(IoStore *self, IoObject *locals, IoMessage *m)
{
IoObject *root = IONIL(self);
DATA(self)->context.self = self;
DATA(self)->context.locals = locals;
DATA(self)->context.locals = m;
IoState_pushCollectorPause(IOSTATE);
IoStore_openIfNeeded(self);
root = IoStore_objectWithPid_(self, 2);
IoState_setLobby_(IOSTATE, root);
IoState_popCollectorPause(IOSTATE);
return root;
}
/*
void IoStore_setValueStreamByteData_length_(IoStore *self, unsigned char *data, size_t size)
{
BStream *vs = DATA(self)->tmpValueStream;
ByteArray_setData_size_(BStream_byteArray(vs), data, size);
}
*/
// db ops ----------------------------------
void IoStore_atPid_put_(IoStore *self, PID_TYPE pid, ByteArray *vb)
{
UDB *udb = IoStore_udb(self);
UDB_at_put_(udb, pid, Datum_FromByteArray_(vb));
}
void IoStore_atPid_intoStream_(IoStore *self, PID_TYPE pid, BStream *stream)
{
UDB *udb = IoStore_udb(self);
Datum v = UDB_at_(udb, pid);
BStream_setData_length_(stream, v.data, v.size);
}
BStream *IoStore_atPid_(IoStore *self, PID_TYPE pid)
{
UDB *udb = IoStore_udb(self);
BStream *stream = DATA(self)->tmpValueStream;
Datum v = UDB_at_(udb, pid);
BStream_setData_length_(stream, v.data, v.size);
return stream;
}
void IoStore_removeAtPid_(IoStore *self, PID_TYPE pid)
{
UDB *udb = IoStore_udb(self);
UDB_removeAt_(udb, pid);
}
// cleanup ----------------------------------------------
void IoStore_willFreePersistentObject_(IoStore *self, IoObject *obj)
{
PID_TYPE pid = IoObject_persistentId(obj);
Hash_removeKey_(DATA(self)->pidToObject, (void *)pid);
}
// save ----------------------------------------------
PID_TYPE IoStore_pidForObject_(IoStore *self, IoObject *obj)
{
UDB *udb = IoStore_udb(self);
PID_TYPE pid = IoObject_persistentId(obj);
if (!pid)
{
pid = UDB_put_(udb, Datum_Empty());
IoObject_setPersistentId_(obj, pid);
obj->isDirty = 1;
if (!obj->isDirty)
{
printf("error, no pid on %s and not dirty\n", IoObject_name(obj));
exit(1);
}
Hash_at_put_(DATA(self)->pidToObject, (void *)pid, (void *)obj);
}
if (obj->isDirty)
{
List_append_(DATA(self)->objectsToSave, obj);
obj->isDirty = 0;
}
return pid;
}
void IoStore_saveObject_(IoStore *self, IoObject *obj)
{
BStream *vs = DATA(self)->tmpValueStream;
PID_TYPE pid = IoStore_pidForObject_(self, obj);
const char *name = IoObject_name(obj);
char isProto = (obj == IoState_protoWithName_(IOSTATE, name)) ? 'p' : 'i';
/*
if (pid == 2)
{
printf("saving pid 2\n");
}
*/
if (!name || !strlen(name))
{
printf("no proto for object %p\n", (void *)obj);
exit(1);
}
BStream_empty(vs);
BStream_writeTaggedUint8_(vs, isProto);
BStream_writeTaggedCString_(vs, name);
IoObject_writeToStore_stream_(obj, self, vs);
IoStore_atPid_put_(self, pid, BStream_byteArray(vs));
}
void IoStore_finishSave(IoStore *self)
{
List *objectsToSave = DATA(self)->objectsToSave;
IoObject *obj;
while ((obj = List_pop(objectsToSave)))
{
if (DATA(self)->debug)
{
printf("writing %" PID_FORMAT "\n", IoObject_persistentId(obj));
}
IoStore_saveObject_(self, obj);
}
}
// load ----------------------------------------------
// [pid] : [isPrimitiveProto][primitiveTypeName][data]
IoObject *IoStore_objectWithPid_(IoStore *self, PID_TYPE pid)
{
IoObject *obj = Hash_at_(DATA(self)->pidToObject, (void *)pid);
if (obj)
{
return obj;
}
return IoStore_loadObjectWithPid_(self, pid);
}
IoObject *IoStore_loadObjectWithPid_(IoStore *self, PID_TYPE pid)
{
IoObject *instance = IONIL(self);
BStream *stream = BStream_new();
IoStore_atPid_intoStream_(self, pid, stream);
if (!BStream_isEmpty(stream))
{
char isProto = BStream_readTaggedUint8(stream);
char *protoName = (char *)ByteArray_asCString(BStream_readTaggedByteArray(stream));
IoObject *proto = IoState_protoWithName_(IOSTATE, protoName);
//printf("%i %c %s\n", pid, isProto, protoName);
if (!proto)
{
printf("no proto named '%s'\n", protoName);
IoState_protoWithName_(IOSTATE, protoName);
exit(1);
}
if (isProto == 'p')
{
instance = proto;
/*printf("%c %s\n", isProto, protoName);*/
IoObject_setPersistentId_(instance, pid);
Hash_at_put_(DATA(self)->pidToObject, (void *)pid, instance);
}
else if (proto->tag->allocFromStoreOnStreamFunc)
{
instance = IoObject_allocFromStore_stream_(proto, self, stream);
IoObject_setPersistentId_(instance, pid);
Hash_at_put_(DATA(self)->pidToObject, (void *)pid, instance);
}
else
{
TagCloneFunc *func = proto->tag->cloneFunc;
/*
if (strcmp(protoName, "Socket") == 0)
{
printf("reading socket\n");
BStream_show(stream);
}
*/
instance = (func)(proto);
IoObject_rawRemoveAllProtos(instance);
IoObject_setPersistentId_(instance, pid);
Hash_at_put_(DATA(self)->pidToObject, (void *)pid, instance);
IoObject_readFromStore_stream_(instance, self, stream);
}
if (DATA(self)->debug)
{
printf("reading %s%" PID_FORMAT "\n", protoName, pid);
}
}
BStream_free(stream);
return instance;
}
// PObjects ------------------------------------------------
/*
IoObject *IoStore_objectWithPid_(IoStore *self, long pid)
{
IoObject *obj = Hash_at_(DATA(self)->pidToObject, (void *)pid);
if (obj)
{
return obj;
}
return IoPObject_newWithStore_pid_(IOSTATE, self, pid);
}
IoObject *IoStore_loadPObject_(IoStore *self, IoObject *pObject)
{
PID_TYPE pid = IoObject_persistentId(pObject);
BStream *stream = IoStore_atPid_(self, pid);
char isProto = BStream_readByte(stream);
IoObject *proto = IoState_protoWithName_(IOSTATE, (char *)BStream_readCString(stream));
IoObject *instance = (isProto == 'p') ? proto : IOCLONE(proto);
IoObject_setPersistentId_(instance, pid);
instance = IoObject_readFromStore_stream_(instance, self);
Hash_at_put_(DATA(self)->pidToObject, (void *)pid, instance);
return instance;
}
*/
syntax highlighted by Code2HTML, v. 0.9.1