Geant4 Cross Reference |
1 // Copyright (C) 2010, Guy Barrand. All rights reserved. 2 // See the file tools.license for terms. 3 4 #ifndef tools_rroot_file 5 #define tools_rroot_file 6 7 #include "ifile" 8 9 #include "directory" 10 11 #include "../platform" 12 13 #include "obj_list" 14 #include "info" 15 #include "streamer_fac" 16 17 #include <string> 18 #include <fcntl.h> 19 #include <errno.h> 20 21 #if defined(_MSC_VER) || defined(__MINGW32__) 22 #include <io.h> 23 #include <sys/stat.h> 24 #else 25 #include <unistd.h> 26 #endif 27 28 namespace tools { 29 namespace rroot { 30 31 class file : public virtual ifile { 32 file& get_me() {return *this;} //_MSC_VER : to avoid warning about the usage of "this" in the constructor. 33 static int not_open() {return -1;} 34 public: 35 static const std::string& s_class() { 36 static const std::string s_v("tools::rroot::file"); 37 return s_v; 38 } 39 virtual const std::string& s_cls() const {return s_class();} 40 public: //ifile 41 virtual const std::string& path() const {return m_path;} 42 43 virtual bool verbose() const {return m_verbose;} 44 virtual std::ostream& out() const {return m_out;} 45 46 virtual bool byte_swap() const {return is_little_endian();} 47 virtual bool set_pos(seek a_offset = 0,from a_from = begin){ 48 int whence = 0; 49 switch(a_from) { 50 case begin: 51 whence = SEEK_SET; 52 break; 53 case current: 54 whence = SEEK_CUR; 55 break; 56 case end: 57 whence = SEEK_END; 58 break; 59 } 60 61 #if defined(__linux__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 2) 62 if (::lseek64(m_file, a_offset, whence) < 0) { 63 #elif defined(_MSC_VER) || defined(__MINGW32__) 64 if (::_lseeki64(m_file, a_offset, whence) < 0) { 65 #else 66 if (::lseek(m_file, a_offset, whence) < 0) { 67 #endif 68 m_out << "tools::rroot::file::set_pos :" 69 << " cannot set position " << a_offset 70 << " in file " << sout(m_path) << "." 71 << std::endl; 72 return false; 73 } 74 return true; 75 } 76 virtual bool read_buffer(char* a_buffer,uint32 a_length) { 77 // Read a buffer from the file. 78 // This is the basic low level read operation. 79 #ifdef _MSC_VER 80 typedef int ssize_t; 81 #endif 82 ssize_t siz; 83 while ((siz = ::read(m_file,a_buffer,a_length)) < 0 && 84 error_number() == EINTR) reset_error_number(); 85 if (siz < 0) { 86 m_out << "tools::rroot::file::read_buffer :" 87 << " error reading from file " << sout(m_path) << "." 88 << std::endl; 89 return false; 90 } 91 if (siz != ssize_t(a_length)) { 92 m_out << "tools::rroot::file::read_buffer :" 93 << " error reading all requested bytes from file " 94 << sout(m_path) << ", got " << long_out(siz) 95 << " of " << a_length 96 << std::endl; 97 return false; 98 } 99 m_bytes_read += siz; 100 return true; 101 } 102 virtual bool unziper(char a_key,decompress_func& a_func) const { 103 std::map<char,decompress_func>::const_iterator it = m_unzipers.find(a_key); 104 if(it==m_unzipers.end()) { 105 a_func = 0; 106 return false; 107 } 108 a_func = (*it).second; 109 return true; 110 } 111 112 virtual key& sinfos_key() {return m_streamer_infos_key;} 113 114 public: 115 file(std::ostream& a_out,const std::string& a_path,bool a_verbose = false) 116 :m_out(a_out) 117 ,m_path(a_path) 118 ,m_verbose(a_verbose) 119 ,m_file(not_open()) 120 ,m_bytes_read(0) 121 ,m_root_directory(get_me()) 122 ,m_streamer_infos_key(a_out) 123 ,m_streamer_fac(a_out) 124 ,m_streamer_infos(m_streamer_fac) 125 // begin of record : 126 ,m_version(0) 127 ,m_BEGIN(0) 128 ,m_END(0) 129 ,m_seek_free(0) 130 ,m_seek_info(0) 131 ,m_nbytes_free(0) 132 ,m_nbytes_info(0) 133 ,m_nbytes_name(0) 134 { 135 #ifdef TOOLS_MEM 136 mem::increment(s_class().c_str()); 137 #endif 138 139 m_file = _open(a_path.c_str(), 140 #if defined(_MSC_VER) || defined(__MINGW32__) 141 O_RDONLY | O_BINARY,S_IREAD | S_IWRITE 142 #else 143 O_RDONLY,0644 144 #endif 145 ); 146 if(m_file==not_open()) { 147 m_out << "tools::rroot::file::file :" 148 << " can't open " << sout(a_path) << "." 149 << std::endl; 150 return; 151 } 152 initialize(); 153 } 154 virtual ~file() { 155 close(); 156 #ifdef TOOLS_MEM 157 mem::decrement(s_class().c_str()); 158 #endif 159 } 160 protected: 161 file(const file& a_from) 162 :ifile(a_from) 163 ,m_out(a_from.m_out) 164 ,m_root_directory(get_me()) 165 ,m_streamer_infos_key(a_from.m_out) 166 ,m_streamer_fac(a_from.m_out) 167 ,m_streamer_infos(m_streamer_fac) 168 { 169 #ifdef TOOLS_MEM 170 mem::increment(s_class().c_str()); 171 #endif 172 } 173 file& operator=(const file&){return *this;} 174 public: 175 uint32 version() const {return m_version;} 176 177 bool is_open() const { 178 return (m_file==not_open()?false:true); 179 } 180 181 void close() { 182 if(m_file!=not_open()) ::close(m_file); 183 m_file = not_open(); 184 m_root_directory.clear_keys(); 185 } 186 187 directory& dir() {return m_root_directory;} 188 const directory& dir() const {return m_root_directory;} 189 190 bool add_unziper(char a_key,decompress_func a_func){ 191 std::map<char,decompress_func>::const_iterator it = m_unzipers.find(a_key); 192 if(it!=m_unzipers.end()) { 193 //(*it).second = a_func; //override ? 194 return false; 195 } else { 196 m_unzipers[a_key] = a_func; 197 return true; 198 } 199 } 200 201 bool dump_streamer_infos() { 202 // read_streamer_infos_data() done here (and not in initialize) since it may need to have unziper declared. 203 if(m_streamer_infos.empty()) {if(!read_streamer_infos_data()) return false;} 204 tools_vforcit(iro*,m_streamer_infos,it) { 205 streamer_info* info = safe_cast<iro,streamer_info>(*(*it)); 206 if(!info) return false; 207 info->out(m_out); 208 } 209 return true; 210 } 211 streamer_info* find_streamer_info(const std::string& a_class) { 212 // read_streamer_infos_data() done here (and not in initialize) since it may need to have unziper declared. 213 if(m_streamer_infos.empty()) {if(!read_streamer_infos_data()) return 0;} 214 tools_vforcit(iro*,m_streamer_infos,it) { 215 streamer_info* info = safe_cast<iro,streamer_info>(*(*it)); 216 if(info) { 217 if(info->name()==a_class) return info; 218 } 219 } 220 return 0; 221 } 222 223 protected: 224 static int _open(const char* a_name,int a_flags,uint32 a_mode) { 225 #if defined(__linux__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 2) 226 return ::open64(a_name,a_flags,a_mode); 227 #else 228 return ::open(a_name,a_flags,a_mode); 229 #endif 230 } 231 static std::string sout(const std::string& a_string) {return "\""+a_string+"\"";} 232 bool initialize() { 233 if(!read_header()) { 234 m_out << "tools::rroot::file::initialize :" 235 << " can't read header." 236 << std::endl; 237 return false; 238 } 239 /* 240 fRootDirectory->setSeekDirectory(fBEGIN); 241 // Read Free segments structure if file is writable : 242 if (fWritable) { 243 if (fSeekFree > fBEGIN) { 244 if(!readFreeSegments()) { 245 m_out << "tools::rroot::file::initialize : Cannot read free segments." 246 << std::endl; 247 return false; 248 } 249 } else { 250 m_out << "tools::rroot::file::initialize : file \"" << fName 251 << "\" probably not closed, cannot read free segments" << std::endl; 252 } 253 } 254 */ 255 // Read Directory info : 256 uint32 nbytes = m_nbytes_name + m_root_directory.record_size(m_version); 257 char* header = new char[nbytes]; 258 char* buffer = header; 259 if(!set_pos(m_BEGIN)) { 260 m_out << "tools::rroot::file::initialize :" 261 << " can't set position." 262 << std::endl; 263 delete [] header; 264 return false; 265 } 266 if(!read_buffer(buffer,nbytes)) { 267 m_out << "tools::rroot::file::initialize :" 268 << " can't read buffer." 269 << std::endl; 270 delete [] header; 271 return false; 272 } 273 buffer = header+m_nbytes_name; 274 const char* eob = header+nbytes; 275 if(!m_root_directory.from_buffer(eob,buffer)) { 276 m_out << "tools::rroot::file::initialize :" 277 << " can't read buffer (2)." 278 << std::endl; 279 delete [] header; 280 return false; 281 } 282 uint32 nk = //size of Key 283 sizeof(int) + //Key::fNumberOfBytes 284 sizeof(short) + //Key::fVersion 285 2*sizeof(int) + //Key::fObjectSize, Date 286 2*sizeof(short) + //Key::fKeyLength,fCycle 287 2*sizeof(seek32); //Key::fSeekKey,fSeekParentDirectory 288 //WARNING : the upper is seek32 since at begin of file. 289 buffer = header+nk; 290 std::string cname; 291 rbuf rb(m_out,byte_swap(),eob,buffer); 292 // Should be "TFile". 293 if(!rb.read(cname)) { 294 m_out << "tools::rroot::file::initialize :" 295 << " can't read buffer (3)." 296 << std::endl; 297 delete [] header; 298 return false; 299 } 300 if(cname!="TFile") { 301 m_out << "tools::rroot::file::initialize : TFile expected." << std::endl; 302 delete [] header; 303 return false; 304 } 305 if(m_verbose) { 306 m_out << "tools::rroot::file::initialize :" 307 << " " << sout("TFile") << " found." 308 << std::endl; 309 } 310 if(!rb.read(cname)) { 311 m_out << "tools::rroot::file::initialize :" 312 << " can't read buffer (4)." 313 << std::endl; 314 delete [] header; 315 return false; 316 } 317 if(m_verbose) { 318 m_out << "tools::rroot::file::initialize :" 319 << " found file name " << sout(cname) 320 << std::endl; 321 } 322 if(!rb.read(m_title)) { 323 m_out << "tools::rroot::file::initialize :" 324 << " can't read buffer (5)." 325 << std::endl; 326 delete [] header; 327 return false; 328 } 329 delete [] header; 330 if(m_verbose) { 331 m_out << "tools::rroot::file::initialize :" 332 << " found title " << sout(m_title) 333 << std::endl; 334 } 335 uint32 dirNbytesName = m_root_directory.nbytes_name(); 336 if (dirNbytesName < 10 || dirNbytesName > 1000) { 337 m_out << "tools::rroot::file::initialize :" 338 << " can't read directory info." 339 << std::endl; 340 return false; 341 } 342 // Read keys of the top directory : 343 if(m_root_directory.seek_keys() > m_BEGIN) { 344 uint32 n; 345 if(!m_root_directory.read_keys(n)) { 346 m_out << "tools::rroot::file::initialize :" 347 << " can't read keys." 348 << std::endl; 349 return false; 350 } 351 } else { 352 m_out << "tools::rroot::file::initialize :" 353 << " file " << sout(m_path) 354 << " probably not closed." 355 << std::endl; 356 return false; 357 } 358 359 // Create StreamerInfo index 360 if(m_seek_info > m_BEGIN) { 361 if(!read_streamer_infos_key()) { 362 m_out << "tools::rroot::file::initialize :" 363 << " read_streamer_infos_key() failed." 364 << std::endl; 365 return false; 366 } 367 } else { 368 m_out << "tools::rroot::file::initialize :" 369 << " file " << sout(m_path) 370 << " probably not closed." 371 << std::endl; 372 return false; 373 } 374 375 return true; 376 } 377 bool read_header() { 378 static const uint32 kBegin = 64; 379 char header[kBegin]; 380 if(!set_pos()) return false; 381 if(!read_buffer(header,kBegin)) return false; 382 // make sure this is a root file 383 if(::strncmp(header, "root", 4)) { 384 m_out << "tools::rroot::file::read_header :" 385 << " " << sout(m_path) << " not a file at the CERN-ROOT format." 386 << std::endl; 387 return false; 388 } 389 if(m_verbose) { 390 m_out << "tools::rroot::file::read_header :" 391 << " file signature is " << sout("root") 392 << std::endl; 393 } 394 char* buffer = header + 4; // skip the "root" file identifier 395 const char* eob = header + kBegin; 396 rbuf rb(m_out,byte_swap(),eob,buffer); 397 {int v; 398 if(!rb.read(v)) return false; 399 m_version = v;} 400 {seek32 i; 401 if(!rb.read(i)) return false; 402 m_BEGIN = i;} 403 if(m_version>1000000) { 404 if(!rb.read(m_END)) return false; 405 if(!rb.read(m_seek_free)) return false; 406 } else { 407 {seek32 i; 408 if(!rb.read(i)) return false; 409 m_END = i;} 410 {seek32 i; 411 if(!rb.read(i)) return false; 412 m_seek_free = i;} 413 } 414 if(m_verbose) { 415 m_out << "tools::rroot::file::read_header :" 416 << " begin " << m_BEGIN 417 << " end " << m_END 418 << std::endl; 419 } 420 {int v; 421 if(!rb.read(v)) return false; 422 m_nbytes_free = v;} 423 int nfree = 0; 424 if(!rb.read(nfree)) return false; 425 {int v; 426 if(!rb.read(v)) return false; 427 m_nbytes_name = v;} 428 //m_out << "debug : 1002 " << m_nbytes_name << std::endl; 429 {char fUnits; 430 if(!rb.read(fUnits)) return false;} 431 {int fCompress; 432 if(!rb.read(fCompress)) return false;} 433 if(m_version>1000000) { 434 if(!rb.read(m_seek_info)) return false; 435 } else { 436 {seek32 i; 437 if(!rb.read(i)) return false; 438 m_seek_info = i;} 439 } 440 if(!rb.read(m_nbytes_info)) return false; 441 //m_out << "debug : seek_info " << m_seek_info << " nbytes_info " << m_nbytes_info << std::endl; 442 return true; 443 } 444 445 bool read_streamer_infos_key() { 446 // Read the list of StreamerInfo from this file 447 // The key with name holding the list of TStreamerInfo objects is read. 448 // The corresponding TClass objects are updated. 449 if(m_seek_info<=0) return false; 450 if(m_seek_info>=m_END) return false; 451 if(!set_pos(m_seek_info)) return false; 452 char* buffer = new char[m_nbytes_info+1]; 453 if(!read_buffer(buffer,m_nbytes_info)) {delete [] buffer;return false;} 454 char* buf = buffer; 455 if(!m_streamer_infos_key.from_buffer(byte_swap(),buffer+m_nbytes_info,buf,m_verbose)) { 456 delete [] buffer; 457 return false; 458 } 459 delete [] buffer; 460 return true; 461 } 462 463 bool read_streamer_infos_data() { 464 key& k = m_streamer_infos_key; 465 if(k.object_class()!="TList") { 466 m_out << "tools::rroot::file::read_streamer_infos_data : key not a TList." << std::endl; 467 return false; 468 } 469 unsigned int sz; 470 char* buf = k.get_object_buffer(*this,sz); //we don't have ownership of buf. 471 if(!buf) { 472 m_out << "tools::rroot::file::read_streamer_infos :" 473 << " can't get data buffer of " << k.object_name() << "." 474 << std::endl; 475 return false; 476 } 477 buffer b(m_out,byte_swap(),sz,buf,k.key_length(),false); 478 return m_streamer_infos.stream(b); 479 } 480 481 #if defined(__sun) && !defined(__linux__) && (__SUNPRO_CC > 0x420) 482 int error_number() {return ::errno;} 483 void reset_error_number() {::errno = 0;} 484 #else 485 int error_number() {return errno;} 486 void reset_error_number() {errno = 0;} 487 #endif 488 489 protected: 490 std::ostream& m_out; 491 std::string m_path; 492 bool m_verbose; 493 int m_file; 494 uint64 m_bytes_read; //Number of bytes read from this file 495 directory m_root_directory; 496 key m_streamer_infos_key; 497 streamer_fac m_streamer_fac; 498 obj_list m_streamer_infos; 499 std::map<char,decompress_func> m_unzipers; 500 std::string m_title; 501 // begin of record : 502 uint32 m_version; //File format version 503 seek m_BEGIN; //First used byte in file 504 seek m_END; //Last used byte in file 505 seek m_seek_free; //Location on disk of free segments structure 506 seek m_seek_info; //Location on disk of StreamerInfo record 507 uint32 m_nbytes_free; //Number of bytes for free segments structure 508 uint32 m_nbytes_info; //Number of bytes for StreamerInfo record 509 //int nfree 510 uint32 m_nbytes_name; //Number of bytes in TNamed at creation time 511 }; 512 513 514 }} 515 516 #endif 517 518 //doc 519 // 520 // A ROOT file is a suite of consecutive data records with the following 521 // format (see also the TKey class); 522 // TKey --------------------- 523 // byte 1->4 Nbytes = Length of compressed object (in bytes) 524 // 5->6 Version = TKey version identifier 525 // 7->10 ObjLen = Length of uncompressed object 526 // 11->14 Datime = Date and time when object was written to file 527 // 15->16 KeyLen = Length of the key structure (in bytes) 528 // 17->18 Cycle = Cycle of key 529 // 19->22 SeekKey = Pointer to record itself (consistency check) 530 // 23->26 SeekPdir = Pointer to directory header 531 // 27->27 lname = Number of bytes in the class name 532 // 28->.. ClassName = Object Class Name 533 // ..->.. lname = Number of bytes in the object name 534 // ..->.. Name = lName bytes with the name of the object 535 // ..->.. lTitle = Number of bytes in the object title 536 // ..->.. Title = Title of the object 537 // -----> DATA = Data bytes associated to the object 538 // 539 // The first data record starts at byte fBEGIN (currently set to kBegin) 540 // Bytes 1->kBegin contain the file description: 541 // byte 1->4 "root" = Root file identifier 542 // 5->8 fVersion = File format version 543 // 9->12 fBEGIN = Pointer to first data record 544 // 13->16 fEND = Pointer to first free word at the EOF 545 // 17->20 fSeekFree = Pointer to FREE data record 546 // 21->24 fNbytesFree = Number of bytes in FREE data record 547 // 25->28 nfree = Number of free data records 548 // 29->32 fNbytesName = Number of bytes in TNamed at creation time 549 // 33->33 fUnits = Number of bytes for file pointers 550 // 34->37 fCompress = Zip compression level 551 //