#ifndef POBBASE_H
#define POBBASE_H
/*
Copyright (c) 1999, Gary Yihsiang Hsiao. All Rights Reserved.
bugs report to: ghsiao@rbcds.com or ghsiao@netzero.net
Permission to use, copy, modify, and distribute this software
for NON-COMMERCIAL purposes and without fee
is hereby granted provided that this copyright notice
appears in all copies. This software is distributed on an 'as is'
basis without warranty.
Release: 1.0 15-May-1999
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "DskMBTree.h"
#include "GFactory.h"
#include "objAvl.h"
#include "POBException.h"
// base class template to implement persistent objects
// use POBroker class to execute functions
template
class POBbase
{
public:
enum ACCESS {
RANDOM, SEQUENCE
};
// OBJ_ID -> unsigned long
// POS_SIZE_PAIR -> POS+SIZE
// REF_PAIR -> OBJ_ID + POS_SIZE_PAIR
// REF_TBL -> number + REF_PAIR
typedef unsigned long OBJ_ID;
typedef pair POS_SIZE_PAIR;
typedef pair REF_PAIR;
typedef map REF_TBL;
POBbase();
~POBbase() {};
POBbase(const char*);
void openfile(const char*) throw (POBException*);
bool eof();
bool findObj(streampos&, OBJ_ID);
OBJ_ID insertObj(const T&) throw (POBException*);
bool add2map(long, OBJ_ID, streampos, int);
bool deleteRef(T*);
OBJ_ID changeObj(T*) throw (POBException*);
bool removeObj(OBJ_ID, T*&) throw (POBException*);
void maxmemory();
OBJ_ID read(ACCESS, int&, streampos&, T*&) throw (POBException*);
int write(OBJ_ID, streampos&, T*) throw (POBException*);
void register_create_function(const char*, GFactory::PFUNC);
private:
void init_oi();
OBJ_ID new_oi();
void load_avl();
void update_avl(OBJ_ID);
private:
const OBJ_ID _sysoi;
OBJ_ID _sequence;
streampos _markp, _markg;
fstream _iof;
GFactory _factory;
REF_TBL _refmap;
DskMBTree _bt;
objAvl _avl;
};
template POBbase::POBbase():_sysoi(1)
{
init_oi();
}
template POBbase::POBbase(const char* name):_sysoi(1)
{
try{
openfile(name);
} catch(POBException* e) {
cout << *e << endl;
exit(-1);
}
init_oi();
}
template void POBbase::openfile(const char* fname)
throw (POBException*)
{
_iof.open(fname, ios::in|ios::out|ios::binary);
if(!_iof)
throw new POBException(POBException::FILE_OPEN_ERROR, "POBbase::openfile");
_iof.seekp(0L, ios::end);
_markp = _iof.tellp();
_markg = _iof.tellg();
string btname;
if(!strchr(fname, '.')) {
btname = fname; btname += ".bt";
}
else {
string str = fname;
int i = str.find('.');
str.erase(i);
btname = str + ".bt";
}
register_create_function(typeid(objAvl).name(), objAvl::create);
_bt.open_root(btname.data());
load_avl();
_iof.seekg(0L, ios::beg);
_iof.seekp(0L, ios::beg);
}
template bool POBbase::eof()
{
if(_markp <= _iof.tellg() || _iof.eof())
return true;
else return false;
}
template bool POBbase::findObj(streampos& loc, OBJ_ID oi)
{
loc = -1;
BTnode root;
slist path;
int pos;
if(!_bt.find(pos, path, oi)) return false;
else {
DskMBTree::PATHNODE& pnode = path.front();
BTnode nd = pnode.first;
loc = nd.data().data[pos];
return true;
}
}
template POBbase::OBJ_ID POBbase::insertObj(const T& t)
throw (POBException*)
{
int pos;
POBbase::OBJ_ID oi = new_oi();
streampos loc;
slist path;
T* pt = const_cast(&t);
string str = pt->operator const string& ();
int cls_size = str.size();
int type_size = strlen(typeid(*pt).name());
int size = cls_size + sizeof(POBbase::OBJ_ID) + sizeof(size_t) + type_size;
if(!_avl.getAvl(size+sizeof(size_t), loc)) {
loc = -1;
write(oi, loc, pt);
}
else {
write(oi, loc, pt);
update_avl(oi);
}
//*
//* oi redundant,
//* previous obj already used it, insert fail!
//*
if(_bt.find(pos, path, oi)) return 0;
if(pos == -1) {
_bt.makeroot(oi, loc);
}
else {
_bt.insert(path, oi, loc);
}
return oi;
}
template POBbase::OBJ_ID POBbase::changeObj(T* t)
throw (POBException*)
{
REF_PAIR ref;
if((_refmap.find((long)t)) != _refmap.end()) {
ref = _refmap[(long)t];
t->clear();
string str = t->operator const string& ();
int cls_size = str.size();
int type_size = strlen(typeid(*t).name());
int size = cls_size + sizeof(POBbase::OBJ_ID) + sizeof(size_t) + type_size;
if(size > ref.second.second) {
int pos;
slist path;
if(!_bt.find(pos, path, ref.first)) {
cout << "changeObj:Invalid OI" << ref.first << endl;
throw new POBException(POBException::NODE_NOT_FOUND, "POBbase::changeObj -- BTree find() fails");
}
_avl.addAvl(ref.second.second+sizeof(size_t), ref.second.first);
streampos nloc;
if(_avl.getAvl(size+sizeof(size_t), nloc)) {
//* insert
write(ref.first, nloc, t);
}
else {
//* append
nloc = -1;
write(ref.first, nloc, t);
}
if(!_iof)
throw new POBException(POBException::FILE_DATA_WRITE_ERROR, "POBbase::changeObj -- add an object(size>) to file fails");
//* update B-tree node
DskMBTree::PATHNODE pnode = path.front();
BTnode nd = pnode.first;
nd.data().data[pos] = nloc;
_bt.updatenode(pnode.second.first, nd);
//* delete old object
_iof.seekp(ref.second.first+sizeof(size_t), ios::beg);
POBbase::OBJ_ID id = 0;
_iof.write((char*)&id, sizeof(POBbase::OBJ_ID));
if(!_iof)
throw new POBException(POBException::FILE_DATA_WRITE_ERROR, "POBbase::changeObj -- delete object id fails");
REF_PAIR& rref = _refmap[(long)t];
rref.second.first = nloc;
rref.second.second = size;
update_avl(ref.first);
}
else if(size < ref.second.second) {
streampos nloc = ref.second.first;
write(ref.first, nloc, t);
if(!_iof)
throw new POBException(POBException::FILE_DATA_WRITE_ERROR, "POBbase::changeObj -- overwrite(size<) object fails");
_avl.addAvl(ref.second.second - size, ref.second.first + size + sizeof(size_t));
REF_PAIR& rref = _refmap[(long)t];
rref.second.first = nloc;
rref.second.second = size;
update_avl(ref.first);
}
else {
streampos nloc = ref.second.first;
write(ref.first, nloc, t);
if(!_iof)
throw new POBException(POBException::FILE_DATA_WRITE_ERROR, "POBbase::changeObj -- overwrite(size=) object fails");
}
}
else {
cout << "No object addr in map ..." << endl;
throw new POBException(POBException::NO_OBJ_FOR_CHANGE, "POBbase::changeObj -- no such object in memory");
}
return ref.first;
}
template bool POBbase::removeObj(OBJ_ID oi, T*& t)
throw (POBException*)
{
int pos;
long data;
slist path;
if(!_bt.find(pos, path, oi)) return false;
else {
int ob_sz = 0;
DskMBTree::PATHNODE pnode = path.front();
BTnode nd = pnode.first;
streampos obloc = nd.data().data[pos];
if(read(RANDOM, ob_sz, obloc, t) != oi)
{
cout << "OI not match -" << oi << endl;
throw new POBException(POBException::OBJ_ID_NOT_MATCH, "POBbase::removeObj");
}
//*
//* delete object, marked oi to 0
//*
_iof.seekp(obloc+sizeof(size_t), ios::beg);
POBbase::OBJ_ID id = 0;
_iof.write((char*)&id, sizeof(POBbase::OBJ_ID));
if(!_iof) throw new POBException(POBException::FILE_SIZE_WRITE_ERROR, "POBbase::removeObj");
_bt.deleten(oi, path);
//*
//* add avl space
//*
_avl.addAvl(ob_sz+sizeof(size_t), obloc); //* class data + data size
update_avl(oi);
return true;
}
}
template int POBbase::write(OBJ_ID oi, streampos& pos, T* t)
throw (POBException*)
{
bool append;
if(pos == -1) {
//* append new record
_iof.seekp(_markp, ios::beg);
pos = _markp;
append = true;
}
else {
//* insert new record
_iof.seekp(pos, ios::beg);
append = false;
}
t->clear();
string str = t->operator const string& ();
size_t cls_size = str.size();
size_t type_size = strlen(typeid(*t).name());
size_t size = cls_size + sizeof(POBbase::OBJ_ID) + sizeof(size_t) + type_size;
_iof.write((char*)&size, sizeof(size_t));
if(!_iof) throw new POBException(POBException::FILE_SIZE_WRITE_ERROR, "POBbase::write");
_iof.write((char*)&oi, sizeof(POBbase::OBJ_ID));
if(!_iof) throw new POBException(POBException::FILE_OI_WRITE_ERROR, "POBbase::write");
_iof.write((char*)&type_size, sizeof(size_t));
_iof.write(typeid(*t).name(), type_size);
if(!_iof) throw new POBException(POBException::FILE_TYPE_WRITE_ERROR, "POBbase::write");
_iof.write(str.data(), cls_size);
if(!_iof) throw new POBException(POBException::FILE_OBJ_WRITE_ERROR, "POBbase::write");
if(append) _markp = _iof.tellp();
return size;
}
template
POBbase::OBJ_ID POBbase::read(ACCESS access, int& size, streampos& loc, T*& t)
throw (POBException*)
{
if(access == RANDOM) //* random access:use assigned location
_iof.seekg(loc, ios::beg);
else { //* sequential access:from current location
size_t rsize;;
loc = _iof.tellp();
if(_avl.isEmptySlot(loc, rsize)) {
_iof.seekg(loc+rsize, ios::beg);
t = NULL;
return 0;
}
}
POBbase::OBJ_ID oi;
size_t cls_size;
_iof.read((char*)&size, sizeof(size_t));
if(!_iof) throw new POBException(POBException::FILE_SIZE_READ_ERROR, "POBbase::read");
int const bf_sz = size+1;
char datbf[bf_sz];
_iof.read(datbf, size);
if(!_iof)
throw new POBException(POBException::FILE_DATA_READ_ERROR, "POBbase::read");
datbf[size] = 0;
char* p = datbf;
oi = *((POBbase::OBJ_ID*)p);
if(oi == _sysoi) //* sys object; avl obj
if(access == SEQUENCE) {
loc = access;
t = NULL;
return 0;
}
p += sizeof(POBbase::OBJ_ID);
size_t type_size = *((size_t*)p);
p += sizeof(size_t);
char cc = *(p + type_size);
*(p + type_size) = '\0';
string type = p;
*(p + type_size) = cc;
if(!_factory.findClassTypeId(type.c_str()))
throw new POBException(POBException::NO_CLASS_TYPE_ID_ERROR, type);
//*
//* create class
//*
t = _factory.create(type.c_str());
if(t == NULL)
throw new POBException(POBException::NO_CLASS_CREATION_FUNC_ERROR, type);
string data;
p += type_size;
cls_size = size - (p - datbf);
data.assign(p, cls_size);
t->operator=(data);
return oi;
}
template
bool POBbase::add2map(long addr, OBJ_ID oi, streampos loc, int sz)
{
POS_SIZE_PAIR obj(loc, sz);
REF_PAIR ref(oi, obj);
_refmap[addr] = ref;
}
template
bool POBbase::deleteRef(T* t)
{
REF_TBL::iterator itr;
if((itr = _refmap.find((long)t)) != _refmap.end()) {
delete t;
_refmap.erase(itr);
return true;
}
delete t;
return false;
}
template
void POBbase::register_create_function(const char* clsid, GFactory::PFUNC pfc)
{
_factory.add_function(clsid, pfc);
}
template void POBbase::init_oi()
{
_sequence = time(NULL);
}
template POBbase::OBJ_ID POBbase::new_oi()
{
return ++_sequence;
}
template void POBbase::load_avl()
{
POBbase::OBJ_ID oi;
streampos loc;
if(findObj(loc, _sysoi)) {
int size;
POBObj* pobj;
oi = read(RANDOM, size, loc, pobj);
if(oi != _sysoi) {
cout << "sys obj oi not matched " << oi << endl;
exit(-1);
}
objAvl* pAvl = (objAvl*)(pobj);
_avl = *pAvl;
add2map((long)&_avl, _sysoi, loc, size);
delete pAvl;
} else {
int pos;
loc = -1;
slist path;
int size = write(_sysoi, loc, &_avl);
add2map((long)&_avl, _sysoi, loc, size);
_avl.removepad();
_bt.find(pos, path, _sysoi);
if(pos == -1) {
_bt.makeroot(_sysoi, loc);
} else {
_bt.insert(path, _sysoi, loc);
}
}
}
template void POBbase::update_avl(POBbase::OBJ_ID oi)
{
string str = _avl.operator const string& ();
int cls_size = str.size();
int type_size = strlen(typeid(_avl).name());
int size = cls_size + sizeof(POBbase::OBJ_ID) + sizeof(size_t) + type_size;
REF_PAIR& ref = _refmap[(long)(&_avl)];
streampos nloc = ref.second.first;
if(size > ref.second.second) {
_avl.addAvl(ref.second.second+sizeof(size_t), ref.second.first);
str = _avl.operator const string& ();
cls_size = str.size();
size = cls_size + sizeof(POBbase::OBJ_ID) + sizeof(size_t) + type_size;
if(_avl.getAvl(size+sizeof(size_t), nloc)) {
//* insert
str = _avl.operator const string& ();
cls_size = str.size();
size = cls_size + sizeof(POBbase::OBJ_ID) + sizeof(size_t) + type_size;
} else {
//* append
nloc = -1;
}
} else if(size < ref.second.second) {
nloc = ref.second.first;
_avl.addAvl(ref.second.second - size, ref.second.first + size + sizeof(size_t));
str = _avl.operator const string& ();
cls_size = str.size();
size = cls_size + sizeof(POBbase::OBJ_ID) + sizeof(size_t) + type_size;
}
write(ref.first, nloc, &_avl);
if(!_iof)
throw new POBException(POBException::FILE_DATA_WRITE_ERROR, "POBbase::changeObj -- add an object(size>) to file fails");
ref.second.first = nloc;
ref.second.second = size;
int pos;
slist path;
if(!_bt.find(pos, path, ref.first)) {
cout << "update_avl:Invalid system OI" << ref.first << endl;
throw new POBException(POBException::NODE_NOT_FOUND, "POBbase::changeObj -- BTree find() fails");
}
DskMBTree::PATHNODE pnode = path.front();
BTnode nd = pnode.first;
nd.data().data[pos] = nloc;
_bt.updatenode(pnode.second.first, nd);
}
template
void POBbase::maxmemory()
{
_avl.spaceRecollection();
}
#endif