#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 <stdlib.h> #include <sys/stat.h> #include <time.h> #include <string.h> #include <fstream.h> #include <iostream.h> #include <slist> #include <typeinfo> #include <set> #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 T> 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<streampos, size_t> POS_SIZE_PAIR; typedef pair<OBJ_ID, POS_SIZE_PAIR> REF_PAIR; typedef map<long, REF_PAIR> 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<T>::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<T> _factory; REF_TBL _refmap; DskMBTree _bt; objAvl _avl; }; template<class T> POBbase<T>::POBbase():_sysoi(1) { init_oi(); } template<class T> POBbase<T>::POBbase(const char* name):_sysoi(1) { try{ openfile(name); } catch(POBException* e) { cout << *e << endl; exit(-1); } init_oi(); } template<class T> void POBbase<T>::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<class T> bool POBbase<T>::eof() { if(_markp <= _iof.tellg() || _iof.eof()) return true; else return false; } template<class T> bool POBbase<T>::findObj(streampos& loc, OBJ_ID oi) { loc = -1; BTnode root; slist<DskMBTree::PATHNODE> 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<class T> POBbase<T>::OBJ_ID POBbase<T>::insertObj(const T& t) throw (POBException*) { int pos; POBbase<T>::OBJ_ID oi = new_oi(); streampos loc; slist<DskMBTree::PATHNODE> path; T* pt = const_cast<T*>(&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<T>::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<class T> POBbase<T>::OBJ_ID POBbase<T>::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<T>::OBJ_ID) + sizeof(size_t) + type_size; if(size > ref.second.second) { int pos; slist<DskMBTree::PATHNODE> 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<T>::OBJ_ID id = 0; _iof.write((char*)&id, sizeof(POBbase<T>::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<class T> bool POBbase<T>::removeObj(OBJ_ID oi, T*& t) throw (POBException*) { int pos; long data; slist<DskMBTree::PATHNODE> 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<T>::OBJ_ID id = 0; _iof.write((char*)&id, sizeof(POBbase<T>::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<class T> int POBbase<T>::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<T>::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<T>::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<class T> POBbase<T>::OBJ_ID POBbase<T>::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<T>::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<T>::OBJ_ID*)p); if(oi == _sysoi) //* sys object; avl obj if(access == SEQUENCE) { loc = access; t = NULL; return 0; } p += sizeof(POBbase<T>::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<class T> bool POBbase<T>::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<class T> bool POBbase<T>::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<class T> void POBbase<T>::register_create_function(const char* clsid, GFactory<T>::PFUNC pfc) { _factory.add_function(clsid, pfc); } template<class T> void POBbase<T>::init_oi() { _sequence = time(NULL); } template<class T> POBbase<T>::OBJ_ID POBbase<T>::new_oi() { return ++_sequence; } template<class T> void POBbase<T>::load_avl() { POBbase<T>::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<DskMBTree::PATHNODE> 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<class T> void POBbase<T>::update_avl(POBbase<T>::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<T>::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<T>::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<T>::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<T>::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<DskMBTree::PATHNODE> 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<class T> void POBbase<T>::maxmemory() { _avl.spaceRecollection(); } #endif
Hosted by www.Geocities.ws

1