00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "GaudiKernel/MsgStream.h"
00014 #include "GaudiKernel/IRegistry.h"
00015 #include "GaudiKernel/IUpdateable.h"
00016 #include "GaudiKernel/IIncidentSvc.h"
00017 #include "GaudiKernel/IDataManagerSvc.h"
00018 #include "GaudiKernel/IDataProviderSvc.h"
00019 #include "GaudiKernel/KeyedContainer.h"
00020 #include "GaudiKernel/DataIncident.h"
00021 #include "GaudiKernel/LinkManager.h"
00022 #include "GaudiKernel/Incident.h"
00023 #include "GaudiKernel/System.h"
00024 #include "GaudiUtils/IIODataManager.h"
00025 #include "RootCnv/RootRefs.h"
00026 #include "RootCnv/RootCnvSvc.h"
00027 #include "RootCnv/RootAddress.h"
00028 #include "RootCnv/RootConverter.h"
00029 #include "RootCnv/RootDatabaseCnv.h"
00030 #include "RootCnv/RootDirectoryCnv.h"
00031 #include "RootCnv/RootNTupleCnv.h"
00032 #include "RootCnv/RootDataConnection.h"
00033 #include "RootUtils.h"
00034
00035
00036 #include "TROOT.h"
00037 #include "TClass.h"
00038 #include "TTree.h"
00039 #include "TBranch.h"
00040
00041 using namespace std;
00042 using namespace Gaudi;
00043 typedef const string& CSTR;
00044
00045 #define S_OK StatusCode::SUCCESS
00046 #define S_FAIL StatusCode::FAILURE
00047 namespace GaudiRoot { bool patchStreamers(MsgStream& log); }
00048
00049 namespace {
00050 static map<string, TClass*> s_classesNames;
00051 static map<CLID, TClass*> s_classesClids;
00052 }
00053 #define MBYTE 1024*1024
00054
00055
00056 RootCnvSvc::RootCnvSvc(CSTR nam, ISvcLocator* svc)
00057 : ConversionSvc( nam, svc, ROOT_StorageType),
00058 m_ioMgr(0), m_incidentSvc(0), m_current(0), m_setup(0)
00059 {
00060 m_classRefs = m_classDO = 0;
00061 m_setup = new RootConnectionSetup();
00062 m_setup->cacheBranches.push_back("*");
00063 declareProperty("IOPerfStats", m_ioPerfStats);
00064 declareProperty("ShareFiles", m_shareFiles = "NO");
00065 declareProperty("EnableIncident", m_incidentEnabled = true);
00066 declareProperty("RecordsName", m_recordName = "/FileRecords");
00067
00068 declareProperty("BasketSize", m_setup->basketSize = 40*MBYTE);
00069 declareProperty("CacheSize", m_setup->cacheSize = 10*MBYTE);
00070 declareProperty("AutoFlush", m_setup->autoFlush = 100);
00071 declareProperty("LearnEntries", m_setup->learnEntries = 10);
00072 declareProperty("LoadSection", m_setup->loadSection = "Event");
00073 declareProperty("CacheBranches", m_setup->cacheBranches);
00074 declareProperty("VetoBranches", m_setup->vetoBranches);
00075 declareProperty("GlobalCompression",m_compression);
00076 }
00077
00078
00079 RootCnvSvc::~RootCnvSvc() {
00080 if (m_setup) m_setup->release();
00081 }
00082
00083
00084 StatusCode RootCnvSvc::error(CSTR msg) {
00085 if ( m_log ) {
00086 log() << MSG::ERROR << "Error: " << msg << endmsg;
00087 return S_FAIL;
00088 }
00089 MsgStream m(msgSvc(),name());
00090 m << MSG::ERROR << "Error: " << msg << endmsg;
00091 return S_FAIL;
00092 }
00093
00094
00095 StatusCode RootCnvSvc::initialize() {
00096 string cname;
00097 StatusCode status = ConversionSvc::initialize();
00098 if ( !status.isSuccess() )
00099 return error("Failed to initialize ConversionSvc base class.");
00100 m_log = new MsgStream(msgSvc(),name());
00101 if ( !m_compression.empty() ) {
00102 log() << MSG::INFO << "Setting global ROOT compression to:" << m_compression << endmsg;
00103 if ( !(status=RootConnectionSetup::setCompression(m_compression)).isSuccess() ) {
00104 return error("Unable to interprete ROOT compression encoding:"+m_compression);
00105 }
00106 }
00107 if( !(status=service("IODataManager", m_ioMgr)).isSuccess() )
00108 return error("Unable to localize interface from service:IODataManager");
00109 if( !(status=service("IncidentSvc", m_incidentSvc)).isSuccess() )
00110 return error("Unable to localize interface from service:IncidentSvc");
00111 m_setup->setMessageSvc(new MsgStream(msgSvc(),name()));
00112 GaudiRoot::patchStreamers(log());
00113 cname = System::typeinfoName(typeid(DataObject));
00114 m_classDO = gROOT->GetClass(cname.c_str());
00115 if ( 0 == m_classDO )
00116 return error("Unable to load class description for DataObject");
00117 cname = System::typeinfoName(typeid(RootObjectRefs));
00118 m_classRefs = gROOT->GetClass(cname.c_str());
00119 if ( 0 == m_classRefs )
00120 return error("Unable to load class description for ObjectRefs");
00121 return S_OK;
00122 }
00123
00124
00125 StatusCode RootCnvSvc::finalize() {
00126 log() << MSG::INFO;
00127 if ( m_ioMgr ) {
00128 IIODataManager::Connections cons = m_ioMgr->connections(0);
00129 for(IIODataManager::Connections::iterator i=cons.begin(); i != cons.end(); ++i) {
00130 RootDataConnection* pc = dynamic_cast<RootDataConnection*>(*i);
00131 if ( pc ) {
00132 if ( pc->owner() == this && !m_ioPerfStats.empty() ) {
00133 pc->saveStatistics(m_ioPerfStats);
00134 }
00135 if ( pc->lookupClient(this) ) {
00136 size_t num_clients = pc->removeClient(this);
00137 if ( num_clients == 0 ) {
00138 if ( m_ioMgr->disconnect(pc).isSuccess() ) {
00139 log() << "Disconnected data IO:" << pc->fid()
00140 << " [" << pc->pfn() << "]" << endmsg;
00141 delete pc;
00142 }
00143 }
00144 }
00145 }
00146 }
00147 releasePtr(m_ioMgr);
00148 }
00149 deletePtr(m_log);
00150 releasePtr(m_incidentSvc);
00151 return ConversionSvc::finalize();
00152 }
00153
00154
00155 IConverter* RootCnvSvc::createConverter(long typ,const CLID& wanted,const ICnvFactory*) {
00156 if ( wanted == CLID_StatisticsFile )
00157 return new RootDatabaseCnv(typ,wanted,serviceLocator().get(),this);
00158 else if ( wanted == CLID_StatisticsDirectory )
00159 return new RootDirectoryCnv(typ,wanted,serviceLocator().get(),this);
00160 else if ( wanted == CLID_RowWiseTuple )
00161 return new RootNTupleCnv(typ,wanted,serviceLocator().get(),this);
00162 else if ( wanted == CLID_ColumnWiseTuple )
00163 return new RootNTupleCnv(typ,wanted,serviceLocator().get(),this);
00164 else
00165 return new RootConverter(typ,wanted,serviceLocator().get(),this);
00166 }
00167
00168
00169 void RootCnvSvc::loadConverter(DataObject* pObject) {
00170 if (pObject) {
00171 string cname = System::typeinfoName(typeid(*pObject));
00172 log() << MSG::DEBUG << "Trying to 'Autoload' dictionary for class " << cname << endmsg;
00173 TClass* cl = s_classesNames[cname];
00174 if ( 0 == cl ) {
00175 cl = gROOT->GetClass(cname.c_str());
00176 if ( cl ) {
00177 s_classesNames[cname] = cl;
00178 s_classesClids[pObject->clID()] = cl;
00179 }
00180 }
00181 }
00182 }
00183
00184
00185 TClass* RootCnvSvc::getClass(DataObject* pObject) {
00186 map<CLID, TClass*>::iterator i=s_classesClids.find(pObject->clID());
00187 if ( i != s_classesClids.end() ) return (*i).second;
00188 loadConverter(pObject);
00189 i=s_classesClids.find(pObject->clID());
00190 if ( i != s_classesClids.end() ) return (*i).second;
00191
00192 string cname = System::typeinfoName(typeid(*pObject));
00193 throw runtime_error("Unknown ROOT class for object:"+cname);
00194 return 0;
00195 }
00196
00197
00198 StatusCode RootCnvSvc::connectOutput(CSTR dsn, CSTR openMode) {
00199 StatusCode sc = S_FAIL;
00200 m_current = 0;
00201 m_currSection = "";
00202 if ( ::strncasecmp(openMode.c_str(),"RECREATE",3)==0 )
00203 sc = connectDatabase(dsn, IDataConnection::RECREATE, &m_current);
00204 else if ( ::strncasecmp(openMode.c_str(),"NEW",1)==0 )
00205 sc = connectDatabase(dsn, IDataConnection::CREATE, &m_current);
00206 else if ( ::strncasecmp(openMode.c_str(),"CREATE",1)==0 )
00207 sc = connectDatabase(dsn, IDataConnection::CREATE, &m_current);
00208 else if ( ::strncasecmp(openMode.c_str(),"UPDATE",1)==0 )
00209 sc = connectDatabase(dsn, IDataConnection::UPDATE, &m_current);
00210 if ( sc.isSuccess() && m_current && m_current->isConnected() ) {
00211 return S_OK;
00212 }
00213 m_incidentSvc->fireIncident(Incident(dsn,IncidentType::FailOutputFile));
00214 log() << MSG::ERROR << "The dataset " << dsn << " cannot be opened in mode "
00215 << openMode << ". [Invalid mode]" << endmsg;
00216 return sc;
00217 }
00218
00219
00220 StatusCode
00221 RootCnvSvc::connectDatabase(CSTR dataset, int mode, RootDataConnection** con) {
00222 try {
00223 IDataConnection* c = m_ioMgr->connection(dataset);
00224 bool fire_incident = false;
00225 *con = 0;
00226 if ( !c ) {
00227 auto_ptr<IDataConnection> connection(new RootDataConnection(this,dataset,m_setup));
00228 StatusCode sc = (mode != IDataConnection::READ)
00229 ? m_ioMgr->connectWrite(connection.get(),IDataConnection::IoType(mode),"ROOT")
00230 : m_ioMgr->connectRead(false,connection.get());
00231 c = sc.isSuccess() ? m_ioMgr->connection(dataset) : 0;
00232 if ( c ) {
00233 fire_incident = m_incidentEnabled && (0 != (mode&(IDataConnection::UPDATE|IDataConnection::READ)));
00234 if ( 0 != (mode&IDataConnection::READ) ) {
00235 if ( !m_ioPerfStats.empty() ) {
00236 RootDataConnection* pc = dynamic_cast<RootDataConnection*>(c);
00237 pc->enableStatistics(m_setup->loadSection);
00238 }
00239 }
00240 connection.release();
00241 }
00242 else {
00243 m_incidentSvc->fireIncident(Incident(dataset,mode == IDataConnection::READ
00244 ? IncidentType::FailInputFile
00245 : IncidentType::FailOutputFile));
00246
00247 return StatusCode::FAILURE;
00248 }
00249 }
00250 RootDataConnection* pc = dynamic_cast<RootDataConnection*>(c);
00251 if ( pc ) {
00252 if ( !pc->isConnected() ) pc->connectRead();
00253 *con = pc;
00254 pc->resetAge();
00255 pc->addClient(this);
00256 }
00257 if ( *con ) {
00258 if ( fire_incident ) {
00259 IOpaqueAddress* pAddr = 0;
00260 string fid = pc->fid();
00261 string section = m_recordName[0] == '/' ? m_recordName.substr(1) : m_recordName;
00262 TBranch* b = pc->getBranch(section,m_recordName);
00263 log() << MSG::VERBOSE;
00264 if ( b ) {
00265 const string par[2] = { fid, m_recordName };
00266 unsigned long ipar[2] = { (unsigned long)(*con), (unsigned long)b->GetEntries()-1 };
00267 for(int i=0; i<b->GetEntries(); ++i) {
00268 ipar[1] = i;
00269 if ( !pc->mergeFIDs().empty() )
00270 fid = pc->mergeFIDs()[i];
00271 if ( !createAddress(repSvcType(),CLID_DataObject,par,ipar,pAddr).isSuccess() ) {
00272 log() << "Failed to create address for " << m_recordName << " in:" << fid
00273 << " [" << pc->fid() << "][" << i << "]" << endmsg;
00274 continue;
00275 }
00276 log() << "Prepare " << m_recordName << " " << fid << " [" << par[0] << "][" << i << "]" << endmsg;
00277 m_incidentSvc->fireIncident(ContextIncident<IOpaqueAddress*>(fid,"FILE_OPEN_READ",pAddr));
00278 }
00279 }
00280 else {
00281 log() << "No valid Records " << m_recordName << " present in:" << pc->fid() << endmsg;
00282 }
00283 }
00284
00285 IIODataManager::Connections cons = m_ioMgr->connections(this);
00286 for(IIODataManager::Connections::iterator i=cons.begin(); i != cons.end(); ++i) {
00287 if ( (*i) != *con && !(*i)->isConnected() ) {
00288 RootDataConnection* pc = dynamic_cast<RootDataConnection*>(*i);
00289 if ( pc && pc->lookupClient(this) ) {
00290 size_t num_client = pc->removeClient(this);
00291 if ( num_client == 0 ) {
00292 if ( m_ioMgr->disconnect(pc).isSuccess() ) {
00293 log() << MSG::INFO << "Removed disconnected IO stream:" << pc->fid()
00294 << " [" << pc->pfn() << "]" << endmsg;
00295 delete pc;
00296 }
00297 }
00298 }
00299 }
00300 }
00301 return S_OK;
00302 }
00303 m_incidentSvc->fireIncident(Incident(dataset,IncidentType::FailOutputFile));
00304 return S_FAIL;
00305 }
00306 catch (exception& e) {
00307 m_incidentSvc->fireIncident(Incident(dataset,IncidentType::FailOutputFile));
00308 return error(string("connectDatabase> Caught exception:")+e.what());
00309 }
00310 catch (...) {
00311 m_incidentSvc->fireIncident(Incident(dataset,IncidentType::FailOutputFile));
00312 return error("connectDatabase> Unknown Fatal Exception for "+dataset);
00313 }
00314 }
00315
00316
00317 StatusCode RootCnvSvc::connectOutput(CSTR db_name) {
00318 return connectOutput(db_name, "NEW");
00319 }
00320
00321
00322 StatusCode RootCnvSvc::commitOutput(CSTR dsn, bool ) {
00323 if ( m_current ) {
00324 size_t len = m_currSection.find('/',1);
00325 string section = m_currSection.substr(1,len==string::npos ? string::npos : len-1);
00326 TBranch* b = m_current->getBranch(section, m_currSection);
00327 if ( b ) {
00328 Long64_t evt = b->GetEntries();
00329 TTree* t = b->GetTree();
00330 TObjArray* a = t->GetListOfBranches();
00331 Int_t nb = a->GetEntriesFast();
00332 log() << MSG::DEBUG;
00334 for(Int_t i=0; i<nb; ++i) {
00335 TBranch* br_ptr = (TBranch*)a->UncheckedAt(i);
00336 Long64_t br_evt = br_ptr->GetEntries();
00337 if ( br_evt < evt ) {
00338 Long64_t num = evt-br_evt;
00339 br_ptr->SetAddress(0);
00340 while(num>0) { br_ptr->Fill(); --num; }
00341 log() << "commit: Added " << long(evt-br_evt)
00342 << " Section: " << evt << " Branch: " << br_ptr->GetEntries()
00343 << " RefNo: " << br_ptr->GetEntries()-1
00344 << " NULL entries to:" << br_ptr->GetName() << endmsg;
00345 }
00346 }
00347
00348 b->GetTree()->SetEntries(evt);
00349 if ( evt == 1 ) {
00350 b->GetTree()->OptimizeBaskets(m_setup->basketSize,1.1,"");
00351 }
00352 if ( evt > 0 && (evt%m_setup->autoFlush)==0 ) {
00353 if ( evt == m_setup->autoFlush ) {
00354 b->GetTree()->SetAutoFlush(m_setup->autoFlush);
00355 b->GetTree()->OptimizeBaskets(m_setup->basketSize,1.,"");
00356 }
00357 else {
00358 b->GetTree()->FlushBaskets();
00359 }
00360 }
00361 log() << MSG::DEBUG << "Set section entries of " << m_currSection
00362 << " to " << long(evt) << " entries." << endmsg;
00363 }
00364 else {
00365 return error("commitOutput> Failed to update entry numbers on "+dsn);
00366 }
00367 }
00368 return S_OK;
00369 }
00370
00371
00372 StatusCode RootCnvSvc::disconnect(CSTR dataset) {
00373 IDataConnection* c = m_ioMgr->connection(dataset);
00374 return c ? m_ioMgr->disconnect(c) : S_FAIL;
00375 }
00376
00377
00378 StatusCode RootCnvSvc::createAddress(long typ,
00379 const CLID& clid,
00380 const string* par,
00381 const unsigned long* ip,
00382 IOpaqueAddress*& refpAddress)
00383 {
00384 refpAddress = new RootAddress(typ,clid,par[0],par[1],ip[0],ip[1]);
00385 return S_OK;
00386 }
00387
00388
00389 StatusCode RootCnvSvc::createNullRep(const std::string& path) {
00390 size_t len = path.find('/',1);
00391 string section = path.substr(1,len==string::npos ? string::npos : len-1);
00392 m_current->saveObj(section,path,0,0);
00393 return S_OK;
00394 }
00395
00396
00397 StatusCode RootCnvSvc::createNullRef(const std::string& path) {
00398 RootObjectRefs* refs = 0;
00399 size_t len = path.find('/',1);
00400 string section = path.substr(1,len==string::npos ? string::npos : len-1);
00401 pair<int,unsigned long> ret = m_current->save(section,path+"#R",0,refs);
00402 log() << MSG::VERBOSE << "Writing object:" << path << " "
00403 << ret.first << " " << hex << ret.second << dec << " [NULL]" << endmsg;
00404 return S_OK;
00405 }
00406
00407
00408 StatusCode RootCnvSvc::i__createRep(DataObject* pObj, IOpaqueAddress*& refpAddr) {
00409 refpAddr = 0;
00410 if ( pObj ) {
00411 CLID clid = pObj->clID();
00412 IRegistry* pR = pObj->registry();
00413 string p[2] = {m_current->fid(), pR->identifier()};
00414 TClass* cl = (clid == CLID_DataObject) ? m_classDO : getClass(pObj);
00415 size_t len = p[1].find('/',1);
00416 string sect = p[1].substr(1,len==string::npos ? string::npos : len-1);
00417 pair<int,unsigned long> ret = m_current->saveObj(sect,p[1],cl,pObj,true);
00418 if ( ret.first > 1 || (clid == CLID_DataObject && ret.first==1) ) {
00419 unsigned long ip[2] = {0,ret.second};
00420 if ( m_currSection.empty() ) m_currSection = p[1];
00421 return createAddress(repSvcType(),clid,p,ip,refpAddr);
00422 }
00423 return error("Failed to write object data for:"+p[1]);
00424 }
00425 return error("createRep> Current Database is invalid!");
00426 }
00427
00428
00429 StatusCode RootCnvSvc::i__fillRepRefs(IOpaqueAddress* , DataObject* pObj) {
00430 if ( pObj ) {
00431 typedef vector<IRegistry*> Leaves;
00432 Leaves leaves;
00433 RootObjectRefs refs;
00434 IRegistry* pR = pObj->registry();
00435 SmartIF<IDataManagerSvc> dataMgr(pR->dataSvc());
00436 if ( dataMgr ) {
00437 StatusCode status = dataMgr->objectLeaves(pObj, leaves);
00438 if ( status.isSuccess() ) {
00439 RootRef ref;
00440 const string& id = pR->identifier();
00441 size_t len = id.find('/',1);
00442 string sect = id.substr(1,len==string::npos ? string::npos : len-1);
00443 LinkManager* pLinks = pObj->linkMgr();
00444 for(Leaves::iterator i=leaves.begin(), iend=leaves.end(); i != iend; ++i) {
00445 if ( (*i)->address() ) {
00446 m_current->makeRef(*i,ref);
00447 ref.entry = (*i)->address()->ipar()[1];
00448 refs.refs.push_back(ref);
00449 }
00450 }
00451 for(int i = 0, n=pLinks->size(); i < n; ++i) {
00452 LinkManager::Link* lnk = pLinks->link(i);
00453 int link_id = m_current->makeLink(lnk->path());
00454 refs.links.push_back(link_id);
00455 }
00456 pair<int,unsigned long> ret = m_current->save(sect,id+"#R",m_classRefs,&refs,true);
00457 if ( ret.first > 1 ) {
00458 log() << MSG::DEBUG << "Writing object:" << id << " "
00459 << ret.first << " " << hex << ret.second << dec << endmsg;
00460 return S_OK;
00461 }
00462 }
00463 }
00464 }
00465 return S_FAIL;
00466 }
00467
00468
00469 StatusCode RootCnvSvc::i__createObj(IOpaqueAddress* pA, DataObject*& refpObj) {
00470 refpObj = 0;
00471 if ( pA ) {
00472 RootDataConnection* con = 0;
00473 const string* par = pA->par();
00474 unsigned long* ipar = const_cast<unsigned long*>(pA->ipar());
00475 StatusCode sc = connectDatabase(par[0],IDataConnection::READ,&con);
00476 if ( sc.isSuccess() ) {
00477 ipar[0] = (unsigned long)con;
00478 DataObject* pObj = 0;
00479 size_t len = par[1].find('/',1);
00480 string section = par[1].substr(1,len==string::npos ? string::npos : len-1);
00481
00482 int nb = con->loadObj(section,par[1],ipar[1],pObj);
00483 if ( nb > 1 || (nb == 1 && pObj->clID() == CLID_DataObject) ) {
00484 refpObj = pObj;
00485 return S_OK;
00486 }
00487 delete pObj;
00488 }
00489 string tag = par[0]+":"+par[1];
00490 if ( m_badFiles.find(tag) == m_badFiles.end() ) {
00491 m_badFiles.insert(tag);
00492 return error("createObj> Cannot access the object:"+tag);
00493 }
00494 return S_FAIL;
00495 }
00496 return S_FAIL;
00497 }
00498
00499
00500 StatusCode RootCnvSvc::i__fillObjRefs(IOpaqueAddress* pA, DataObject* pObj) {
00501 if ( pA && pObj ) {
00502 const unsigned long* ipar = pA->ipar();
00503 RootDataConnection* con = (RootDataConnection*)ipar[0];
00504 if ( con ) {
00505 RootObjectRefs refs;
00506 const string* par = pA->par();
00507 size_t len = par[1].find('/',1);
00508 string section = par[1].substr(1,len==string::npos ? string::npos : len-1);
00509 int nb = con->loadRefs(section,par[1],ipar[1],refs);
00510 log() << MSG::VERBOSE;
00511 if ( nb >= 1 ) {
00512 string npar[3];
00513 unsigned long nipar[2];
00514 IOpaqueAddress* nPA;
00515 IRegistry* pR = pObj->registry();
00516 SmartIF<IService> isvc(pR->dataSvc());
00517 SmartIF<IDataManagerSvc> dataMgr(pR->dataSvc());
00518 LinkManager* mgr = pObj->linkMgr();
00519 bool active = log().isActive();
00520 for(vector<int>::const_iterator i=refs.links.begin(); i!=refs.links.end();++i) {
00521 mgr->addLink(con->getLink(*i),0);
00522 }
00523 for(size_t j=0, n=refs.refs.size(); j<n; ++j) {
00524 const RootRef& r = refs.refs[j];
00525 npar[0] = con->getDb(r.dbase);
00526 npar[1] = con->getCont(r.container);
00527 npar[2] = con->getLink(r.link);
00528 nipar[0] = 0;
00529 nipar[1] = r.entry;
00530 StatusCode sc = addressCreator()->createAddress(r.svc,r.clid,npar,nipar,nPA);
00531 if ( sc.isSuccess() ) {
00532 if ( active ) {
00533 log() << isvc->name() << " -> Register:" << pA->registry()->identifier()
00534 << "#" << npar[2] << "[" << r.entry << "]" << endmsg;
00535 }
00536 sc = dataMgr->registerAddress(pA->registry(),npar[2],nPA);
00537 if ( sc.isSuccess() ) {
00538 continue;
00539 }
00540 }
00541 log() << MSG::ERROR << con->fid() << ": Failed to create address!!!!" << endmsg;
00542 return S_FAIL;
00543 }
00544 return pObj->update();
00545 }
00546 }
00547 return S_FAIL;
00548 }
00549 return error("read> Cannot read object -- no valid object address present ");
00550 }