Files
geant4/source/analysis/g4tools/include/tools/rroot/file
T
2017-12-08 12:52:30 +01:00

503 lines
15 KiB
Plaintext

// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.
#ifndef tools_rroot_file
#define tools_rroot_file
#include "ifile"
#include "directory"
#include "../platform"
#include <string>
#include <fcntl.h>
#include <errno.h>
#ifdef _MSC_VER
#include <io.h>
#include <sys/stat.h>
#else
#include <unistd.h>
#endif
namespace tools {
namespace rroot {
class file : public virtual ifile {
file& get_me() {return *this;} //_MSC_VER : to avoid warning about the usage of "this" in the constructor.
static int not_open() {return -1;}
public:
static const std::string& s_class() {
static const std::string s_v("tools::rroot::file");
return s_v;
}
virtual const std::string& s_cls() const {return s_class();}
public: //ifile
virtual const std::string& path() const {return m_path;}
virtual bool verbose() const {return m_verbose;}
virtual std::ostream& out() const {return m_out;}
virtual bool byte_swap() const {return is_little_endian();}
virtual bool set_pos(seek a_offset = 0,from a_from = begin){
int whence = 0;
switch(a_from) {
case begin:
whence = SEEK_SET;
break;
case current:
whence = SEEK_CUR;
break;
case end:
whence = SEEK_END;
break;
}
#if defined(__linux__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 2)
if (::lseek64(m_file, a_offset, whence) < 0) {
#elif defined(_MSC_VER)
if (::_lseeki64(m_file, a_offset, whence) < 0) {
#else
if (::lseek(m_file, a_offset, whence) < 0) {
#endif
m_out << "tools::rroot::file::set_pos :"
<< " cannot set position " << a_offset
<< " in file " << sout(m_path) << "."
<< std::endl;
return false;
}
return true;
}
virtual bool read_buffer(char* a_buffer,uint32 a_length) {
// Read a buffer from the file.
// This is the basic low level read operation.
#ifdef _MSC_VER
typedef int ssize_t;
#endif
ssize_t siz;
while ((siz = ::read(m_file,a_buffer,a_length)) < 0 &&
error_number() == EINTR) reset_error_number();
if (siz < 0) {
m_out << "tools::rroot::file::read_buffer :"
<< " error reading from file " << sout(m_path) << "."
<< std::endl;
return false;
}
if (siz != ssize_t(a_length)) {
m_out << "tools::rroot::file::read_buffer :"
<< " error reading all requested bytes from file "
<< sout(m_path) << ", got " << long_out(siz)
<< " of " << a_length
<< std::endl;
return false;
}
m_bytes_read += siz;
return true;
}
virtual bool unziper(char a_key,decompress_func& a_func) const {
std::map<char,decompress_func>::const_iterator it = m_unzipers.find(a_key);
if(it==m_unzipers.end()) {
a_func = 0;
return false;
}
a_func = (*it).second;
return true;
}
virtual key& sinfos_key() {return m_streamer_info_key;}
public:
file(std::ostream& a_out,const std::string& a_path,bool a_verbose = false)
:m_out(a_out)
,m_path(a_path)
,m_verbose(a_verbose)
,m_file(not_open())
,m_bytes_read(0)
,m_root_directory(get_me())
,m_streamer_info_key(a_out)
// begin of record :
,m_version(0)
,m_BEGIN(0)
,m_END(0)
,m_seek_free(0)
,m_seek_info(0)
,m_nbytes_free(0)
,m_nbytes_info(0)
,m_nbytes_name(0)
{
#ifdef TOOLS_MEM
mem::increment(s_class().c_str());
#endif
m_file = _open(a_path.c_str(),
#ifdef _MSC_VER
O_RDONLY | O_BINARY,S_IREAD | S_IWRITE
#else
O_RDONLY,0644
#endif
);
if(m_file==not_open()) {
m_out << "tools::rroot::file::file :"
<< " can't open " << sout(a_path) << "."
<< std::endl;
return;
}
initialize();
}
virtual ~file() {
close();
#ifdef TOOLS_MEM
mem::decrement(s_class().c_str());
#endif
}
protected:
file(const file& a_from)
:ifile(a_from)
,m_out(a_from.m_out)
,m_root_directory(get_me())
,m_streamer_info_key(a_from.m_out)
{
#ifdef TOOLS_MEM
mem::increment(s_class().c_str());
#endif
}
file& operator=(const file&){return *this;}
public:
uint32 version() const {return m_version;}
bool is_open() const {
return (m_file==not_open()?false:true);
}
void close() {
if(m_file!=not_open()) ::close(m_file);
m_file = not_open();
m_root_directory.clear_keys();
}
directory& dir() {return m_root_directory;}
const directory& dir() const {return m_root_directory;}
bool add_unziper(char a_key,decompress_func a_func){
std::map<char,decompress_func>::const_iterator it = m_unzipers.find(a_key);
if(it!=m_unzipers.end()) {
//(*it).second = a_func; //override ?
return false;
} else {
m_unzipers[a_key] = a_func;
return true;
}
}
protected:
static int _open(const char* a_name,int a_flags,uint32 a_mode) {
#if defined(__linux__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 2)
return ::open64(a_name,a_flags,a_mode);
#else
return ::open(a_name,a_flags,a_mode);
#endif
}
static std::string sout(const std::string& a_string) {return "\""+a_string+"\"";}
bool initialize() {
if(!read_header()) {
m_out << "tools::rroot::file::initialize :"
<< " can't read header."
<< std::endl;
return false;
}
/*
fRootDirectory->setSeekDirectory(fBEGIN);
// Read Free segments structure if file is writable :
if (fWritable) {
if (fSeekFree > fBEGIN) {
if(!readFreeSegments()) {
m_out << "tools::rroot::file::initialize : Cannot read free segments."
<< std::endl;
return false;
}
} else {
m_out << "tools::rroot::file::initialize : file \"" << fName
<< "\" probably not closed, cannot read free segments" << std::endl;
}
}
*/
// Read Directory info :
uint32 nbytes = m_nbytes_name + m_root_directory.record_size(m_version);
char* header = new char[nbytes];
char* buffer = header;
if(!set_pos(m_BEGIN)) {
m_out << "tools::rroot::file::initialize :"
<< " can't set position."
<< std::endl;
delete [] header;
return false;
}
if(!read_buffer(buffer,nbytes)) {
m_out << "tools::rroot::file::initialize :"
<< " can't read buffer."
<< std::endl;
delete [] header;
return false;
}
buffer = header+m_nbytes_name;
const char* eob = header+nbytes;
if(!m_root_directory.from_buffer(eob,buffer)) {
m_out << "tools::rroot::file::initialize :"
<< " can't read buffer (2)."
<< std::endl;
delete [] header;
return false;
}
uint32 nk = //size of Key
sizeof(int) + //Key::fNumberOfBytes
sizeof(short) + //Key::fVersion
2*sizeof(int) + //Key::fObjectSize, Date
2*sizeof(short) + //Key::fKeyLength,fCycle
2*sizeof(seek32); //Key::fSeekKey,fSeekParentDirectory
//WARNING : the upper is seek32 since at begin of file.
buffer = header+nk;
std::string cname;
rbuf rb(m_out,byte_swap(),eob,buffer);
// Should be "TFile".
if(!rb.read(cname)) {
m_out << "tools::rroot::file::initialize :"
<< " can't read buffer (3)."
<< std::endl;
delete [] header;
return false;
}
if(cname!="TFile") {
m_out << "tools::rroot::file::initialize : TFile expected." << std::endl;
delete [] header;
return false;
}
if(m_verbose) {
m_out << "tools::rroot::file::initialize :"
<< " " << sout("TFile") << " found."
<< std::endl;
}
if(!rb.read(cname)) {
m_out << "tools::rroot::file::initialize :"
<< " can't read buffer (4)."
<< std::endl;
delete [] header;
return false;
}
if(m_verbose) {
m_out << "tools::rroot::file::initialize :"
<< " found file name " << sout(cname)
<< std::endl;
}
if(!rb.read(m_title)) {
m_out << "tools::rroot::file::initialize :"
<< " can't read buffer (5)."
<< std::endl;
delete [] header;
return false;
}
delete [] header;
if(m_verbose) {
m_out << "tools::rroot::file::initialize :"
<< " found title " << sout(m_title)
<< std::endl;
}
uint32 dirNbytesName = m_root_directory.nbytes_name();
if (dirNbytesName < 10 || dirNbytesName > 1000) {
m_out << "tools::rroot::file::initialize :"
<< " can't read directory info."
<< std::endl;
return false;
}
// Read keys of the top directory :
if(m_root_directory.seek_keys() > m_BEGIN) {
uint32 n;
if(!m_root_directory.read_keys(n)) {
m_out << "tools::rroot::file::initialize :"
<< " can't read keys."
<< std::endl;
return false;
}
} else {
m_out << "tools::rroot::file::initialize :"
<< " file " << sout(m_path)
<< " probably not closed."
<< std::endl;
return false;
}
// Create StreamerInfo index
if(m_seek_info > m_BEGIN) {
if(!read_streamer_infos()) {
m_out << "tools::rroot::file::initialize :"
<< " read_streamer_infos() failed."
<< std::endl;
return false;
}
} else {
m_out << "tools::rroot::file::initialize :"
<< " file " << sout(m_path)
<< " probably not closed."
<< std::endl;
return false;
}
return true;
}
bool read_header() {
static const uint32 kBegin = 64;
char header[kBegin];
if(!set_pos()) return false;
if(!read_buffer(header,kBegin)) return false;
// make sure this is a root file
if(::strncmp(header, "root", 4)) {
m_out << "tools::rroot::file::read_header :"
<< " " << sout(m_path) << " not a file at the CERN-ROOT format."
<< std::endl;
return false;
}
if(m_verbose) {
m_out << "tools::rroot::file::read_header :"
<< " file signature is " << sout("root")
<< std::endl;
}
char* buffer = header + 4; // skip the "root" file identifier
const char* eob = header + kBegin;
rbuf rb(m_out,byte_swap(),eob,buffer);
{int v;
if(!rb.read(v)) return false;
m_version = v;}
{seek32 i;
if(!rb.read(i)) return false;
m_BEGIN = i;}
if(m_version>1000000) {
if(!rb.read(m_END)) return false;
if(!rb.read(m_seek_free)) return false;
} else {
{seek32 i;
if(!rb.read(i)) return false;
m_END = i;}
{seek32 i;
if(!rb.read(i)) return false;
m_seek_free = i;}
}
if(m_verbose) {
m_out << "tools::rroot::file::read_header :"
<< " begin " << m_BEGIN
<< " end " << m_END
<< std::endl;
}
{int v;
if(!rb.read(v)) return false;
m_nbytes_free = v;}
int nfree = 0;
if(!rb.read(nfree)) return false;
{int v;
if(!rb.read(v)) return false;
m_nbytes_name = v;}
//m_out << "debug : 1002 " << m_nbytes_name << std::endl;
{char fUnits;
if(!rb.read(fUnits)) return false;}
{int fCompress;
if(!rb.read(fCompress)) return false;}
if(m_version>1000000) {
if(!rb.read(m_seek_info)) return false;
} else {
{seek32 i;
if(!rb.read(i)) return false;
m_seek_info = i;}
}
if(!rb.read(m_nbytes_info)) return false;
//m_out << "debug : seek_info " << m_seek_info << " nbytes_info " << m_nbytes_info << std::endl;
return true;
}
bool read_streamer_infos() {
// Read the list of StreamerInfo from this file
// The key with name holding the list of TStreamerInfo objects is read.
// The corresponding TClass objects are updated.
if(m_seek_info<=0) return false;
if(m_seek_info>=m_END) return false;
if(!set_pos(m_seek_info)) return false;
char* buffer = new char[m_nbytes_info+1];
if(!read_buffer(buffer,m_nbytes_info)) {delete [] buffer;return false;}
char* buf = buffer;
if(!m_streamer_info_key.from_buffer(byte_swap(),buffer+m_nbytes_info,buf,m_verbose)) {
delete [] buffer;
return false;
}
delete [] buffer;
return true;
}
#if defined(__sun) && !defined(__linux__) && (__SUNPRO_CC > 0x420)
int error_number() {return ::errno;}
void reset_error_number() {::errno = 0;}
#else
int error_number() {return errno;}
void reset_error_number() {errno = 0;}
#endif
protected:
std::ostream& m_out;
std::string m_path;
bool m_verbose;
int m_file;
uint64 m_bytes_read; //Number of bytes read from this file
directory m_root_directory;
key m_streamer_info_key;
std::map<char,decompress_func> m_unzipers;
std::string m_title;
// begin of record :
uint32 m_version; //File format version
seek m_BEGIN; //First used byte in file
seek m_END; //Last used byte in file
seek m_seek_free; //Location on disk of free segments structure
seek m_seek_info; //Location on disk of StreamerInfo record
uint32 m_nbytes_free; //Number of bytes for free segments structure
uint32 m_nbytes_info; //Number of bytes for StreamerInfo record
//int nfree
uint32 m_nbytes_name; //Number of bytes in TNamed at creation time
};
}}
#endif
//doc
//
// A ROOT file is a suite of consecutive data records with the following
// format (see also the TKey class);
// TKey ---------------------
// byte 1->4 Nbytes = Length of compressed object (in bytes)
// 5->6 Version = TKey version identifier
// 7->10 ObjLen = Length of uncompressed object
// 11->14 Datime = Date and time when object was written to file
// 15->16 KeyLen = Length of the key structure (in bytes)
// 17->18 Cycle = Cycle of key
// 19->22 SeekKey = Pointer to record itself (consistency check)
// 23->26 SeekPdir = Pointer to directory header
// 27->27 lname = Number of bytes in the class name
// 28->.. ClassName = Object Class Name
// ..->.. lname = Number of bytes in the object name
// ..->.. Name = lName bytes with the name of the object
// ..->.. lTitle = Number of bytes in the object title
// ..->.. Title = Title of the object
// -----> DATA = Data bytes associated to the object
//
// The first data record starts at byte fBEGIN (currently set to kBegin)
// Bytes 1->kBegin contain the file description:
// byte 1->4 "root" = Root file identifier
// 5->8 fVersion = File format version
// 9->12 fBEGIN = Pointer to first data record
// 13->16 fEND = Pointer to first free word at the EOF
// 17->20 fSeekFree = Pointer to FREE data record
// 21->24 fNbytesFree = Number of bytes in FREE data record
// 25->28 nfree = Number of free data records
// 29->32 fNbytesName = Number of bytes in TNamed at creation time
// 33->33 fUnits = Number of bytes for file pointers
// 34->37 fCompress = Zip compression level
//