Files
geant4/source/analysis/g4tools/include/tools/rroot/buffer
T
2016-06-10 14:11:04 +02:00

596 lines
16 KiB
Plaintext

// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.
#ifndef tools_rroot_buffer
#define tools_rroot_buffer
#include "rbuf"
#include "ifac"
#include "iro"
#include "../sout"
#ifdef TOOLS_MEM
#include "../mem"
#endif
#include <string>
#include <vector>
#include <ostream>
// in TOOLS_STL, we don't have (yet) a performant map.
#ifdef inlib_stl_vector
#define TOOLS_RROOT_OBJ_MAP_HASH_TABLE
//#define TOOLS_RROOT_OBJ_MAP_OMAP
#else
#define TOOLS_RROOT_OBJ_MAP_STD_MAP
#endif
#ifdef TOOLS_RROOT_OBJ_MAP_STD_MAP
#include <map>
#endif
#ifdef TOOLS_RROOT_OBJ_MAP_OMAP
#include "../omap"
#endif
#ifdef TOOLS_RROOT_OBJ_MAP_HASH_TABLE
#include "../hash_table"
#include "../hash"
#endif
namespace tools {
namespace rroot {
class buffer : public rbuf {
static const std::string& s_class() {
static const std::string s_v("tools::rroot::buffer");
return s_v;
}
#ifdef TOOLS_RROOT_OBJ_MAP_STD_MAP
typedef std::map<uint32,iro*> obj_map;
#endif
#ifdef TOOLS_RROOT_OBJ_MAP_OMAP
typedef inlib::omap<uint32,iro*> obj_map;
#endif
#ifdef TOOLS_RROOT_OBJ_MAP_HASH_TABLE
class obj_map : public inlib::hash_table<uint32,iro*> {
typedef inlib::hash_table<uint32,iro*> parent;
protected:
virtual unsigned int hash(const uint32& a_key) {
return inlib::hash(&a_key,sizeof(uint32));
}
virtual bool cmp(const uint32& a_key1,const uint32& a_key2) {
return a_key1==a_key2?true:false;
}
public:
obj_map():parent(){}
virtual ~obj_map(){}
private:
obj_map(const obj_map& a_from):parent(a_from){}
obj_map& operator=(const obj_map& a_from){
parent::operator=(a_from);
return *this;
}
};
#endif
public:
buffer(std::ostream& a_out,bool a_byte_swap,
uint32 a_size,char* a_buffer,uint32 a_klen,bool a_verbose)
: rbuf(a_out,a_byte_swap,a_buffer+a_size,m_pos)
,m_byte_swap(a_byte_swap)
,m_verbose(a_verbose)
,m_size(a_size) //expect a not zero size.
,m_buffer(a_buffer) //don't get ownership.
,m_pos(a_buffer)
,m_klen(a_klen) //to compute refs (used in read_class, read_object)
,m_map_objs(false)
{
#ifdef TOOLS_MEM
mem::increment(s_class().c_str());
#endif
}
virtual ~buffer(){
m_objs.clear();
#ifdef TOOLS_MEM
mem::decrement(s_class().c_str());
#endif
}
protected:
buffer(const buffer& a_from)
: rbuf(a_from.m_out,a_from.m_byte_swap,0,m_pos)
,m_byte_swap(a_from.m_byte_swap)
,m_map_objs(a_from.m_map_objs)
{
#ifdef TOOLS_MEM
mem::increment(s_class().c_str());
#endif
}
buffer& operator=(const buffer&){return *this;}
public:
void set_offset(unsigned int a_off) {m_pos = m_buffer+a_off;}
//char* buffer() const {return m_buffer;}
uint32 length() const {return uint32(m_pos-m_buffer);}
uint32 size() const {return m_size;}
void set_map_objs(bool a_value) {m_map_objs = a_value;}
protected:
// on first_int :
static uint32 kNullTag() {return 0;}
static uint32 kByteCountMask() {return 0x40000000;}
// on tag :
static uint32 kNewClassTag() {return 0xFFFFFFFF;}
static uint32 kClassMask() {return 0x80000000; }
static uint32 kMapOffset() {return 2;}
static short kByteCountVMask() {return 0x4000;}
bool read_class_tag(std::string& a_class) {
a_class.clear();
uint32 tag;
if(!rbuf::read(tag)) return false;
if(tag==kNewClassTag()) {
char s[80];
if(!read_string(s, 80)) {
m_out << "tools::rroot::read_class_tag :"
<< " read string." << std::endl;
return false;
}
//m_out << "tools::rroot::read_class_tag :"
// << " tag kNewClassTag"
// << " class " << s
// << std::endl;
a_class = s;
return true;
} else if(tag & kClassMask()) {
//m_out << "tools::rroot::read_class_tag :"
// << " tag & kClassMask"
// << " ref " << (uint32)(tag & ~kClassMask)
// << " recurse..."
// << std::endl;
unsigned int cl_offset = (tag & ~kClassMask());
cl_offset -= kMapOffset();
cl_offset -= m_klen;
char* old_pos = m_pos;
m_pos = m_buffer + cl_offset;
//std::string scls;
//uint32 ref;
if(!read_class_tag(a_class)) return false;
m_pos = old_pos;
return true;
} else {
std::ios::fmtflags old_flags = m_out.flags();
m_out << "tools::rroot::read_class_tag :"
<< " tag unknown case ! "
<< tag << " hex " << std::hex << tag
<< std::endl;
m_out.flags(old_flags);
return false;
}
}
bool read_class(std::string& a_class,uint32& a_bcnt,bool& a_is_ref){
//m_verbose = true;
a_class.clear();
a_bcnt = 0;
a_is_ref = false;
//uint32 fVersion = 0; //Buffer format version
// read byte count and/or tag (older files don't have byte count)
uint32 first_int = 0;
if(!rbuf::read(first_int)) return false;
if(m_verbose) {
std::ios::fmtflags old_flags = m_out.flags();
m_out << "tools::rroot::read_class :"
<< " first_int " << std::hex << first_int
<< std::endl;
m_out.flags(old_flags);
}
if(first_int==kNullTag()) { //GB
if(m_verbose) {
m_out << "tools::rroot::read_class :"
<< " first_int is kNullTag."
<< std::endl;
}
a_bcnt = 0;
return true;
//} else if(first_int==kNewClassTag()) { // class desc follows.
} else if(first_int & kByteCountMask()) {
// at write :
// skip int to store bcnt.
// + write class
// if(!write((clIdx | Rio_kClassMask))) return false;
// or
// if(!write(Rio_kNewClassTag)) return false;
// + write object
// + set byte cnt (cnt|kByteCountMask()) at skipped int.
if(m_verbose) {
m_out << "tools::rroot::read_class :"
<< " first_int & kByteCountMask."
<< std::endl;
}
uint32 bef_tag = uint32(m_pos-m_buffer);
//fVersion = 1;
std::string scls;
if(!read_class_tag(scls)) return false;
if(scls.empty()) {
m_out << "tools::rroot::buffer::read_class :"
<< " read_class_tag did not find a class name."
<< std::endl;
return false;
}
a_class = scls;
a_bcnt = (first_int & ~kByteCountMask());
if(m_verbose) {
m_out << "tools::rroot::read_class :"
<< " kNewClassTag : read class name " << sout(a_class)
<< " a_bcnt " << a_bcnt
<< " bef_tag " << bef_tag
<< "." << std::endl;
}
return true;
} else {
if(m_verbose) {
std::ios::fmtflags old_flags = m_out.flags();
m_out << "tools::rroot::read_class :"
<< " first_int " << std::hex << first_int
<< ". first_int is position toward object."
<< std::endl;
m_out.flags(old_flags);
}
a_bcnt = first_int; //pos toward object.
a_is_ref = true;
a_class.clear();
return true;
}
return false;
}
public:
bool read_object(ifac& a_fac,const ifac::args& a_args,iro*& a_obj){
a_obj = 0;
// before reading object save start position
uint32 startpos = (uint32)(m_pos-m_buffer);
uint32 bcnt;
std::string sclass;
bool is_ref;
if(!read_class(sclass,bcnt,is_ref)) {
m_out << "tools::rroot::buffer::read_object :"
<< " can't read class." << std::endl;
return false;
}
//::printf("debug : %d\n",length()-startpos);
if(m_verbose) {
m_out << "tools::rroot::buffer::read_object :"
<< " class " << sout(sclass)
<< " bcnt " << bcnt
<< std::endl;
}
if(is_ref) {
//bcnt is the position toward an object or an object ref.
//m_out << "tools::rroot::buffer::read_object :"
// << " is_ref : bcnt " << bcnt
// << std::endl;
unsigned int obj_offset = bcnt;
obj_offset -= kMapOffset();
obj_offset -= m_klen;
if(m_map_objs) {
#ifdef TOOLS_RROOT_OBJ_MAP_HASH_TABLE
if(m_objs.find(obj_offset,a_obj)) return true;
#else
obj_map::const_iterator it = m_objs.find(obj_offset);
if(it!=m_objs.end()) {
a_obj = (*it).second;
//::printf("debug : find : %d %lu\n",obj_offset,a_obj);
//stay at current m_pos ?
return true;
}
#endif
}
{char* old_pos = m_pos;
m_pos = m_buffer + obj_offset;
uint32 first_int;
if(!rbuf::read(first_int)) return false;
if(first_int & kByteCountMask()) {
std::string scls;
if(!read_class_tag(scls)) return false;
if(scls.empty()) {
m_out << "tools::rroot::buffer::read_object :"
<< " read_class_tag did not find a class name."
<< std::endl;
return false;
}
//m_out << "tools::rroot::buffer::read_object :"
// << " is_ref : class " << sout(scls)
// << std::endl;
iro* obj = a_fac.create(scls,a_args);
if(!obj) return false;
if(m_map_objs) {
//must be done before stream()
#ifdef TOOLS_RROOT_OBJ_MAP_HASH_TABLE
if(!m_objs.insert(obj_offset,obj)) {
m_out << "tools::rroot::buffer::read_object :"
<< " map insert failed."
<< std::endl;
}
#else
m_objs[obj_offset] = obj;
#endif
}
if(!obj->stream(*this)) {
m_out << "tools::rroot::buffer::read_object :"
<< " is_ref : streamed failed for class " << sout(scls)
<< std::endl;
//restore a good buffer position :
//m_pos = m_buffer+startpos+sizeof(unsigned int);
a_fac.destroy(obj);
return false;
}
//m_out << "tools::rroot::buffer::read_object :"
// << " is_ref : streamed ok for class " << sout(scls)
// << std::endl;
a_obj = obj;
} else {
m_out << "tools::rroot::buffer::read_object :"
<< " is_ref : zzz"
<< std::endl;
}
m_pos = old_pos;}
// in principle at this point m_pos should be
// m_buffer+startpos+sizeof(unsigned int)
// but enforce it anyway :
m_pos = m_buffer+startpos+sizeof(unsigned int);
//check_byte_count(startpos,0,sclass) would always be ok.
//a_obj = ???
} else {
if(sclass.empty()) {
//m_out << "tools::rroot::buffer::read_object :"
// << " found empty class name."
// << std::endl;
m_pos = m_buffer+startpos+bcnt+sizeof(unsigned int);
} else {
// Being not a ref, it must NOT be in the map.
// We gain a lot by not doing the below find.
/*
if(m_map_objs) {
#ifdef TOOLS_RROOT_OBJ_MAP_HASH_TABLE
if(m_objs.find(startpos,a_obj)) return true;
#else
obj_map::const_iterator it = m_objs.find(startpos);
if(it!=m_objs.end()) {
a_obj = (*it).second;
::printf("debug : find xxx : %d %lu\n",startpos,a_obj);
//stay at current m_pos ?
return true;
}
#endif
}
*/
iro* obj = a_fac.create(sclass,a_args);
if(!obj) return false;
if(m_map_objs) {
//must be done before stream()
#ifdef TOOLS_RROOT_OBJ_MAP_HASH_TABLE
if(!m_objs.insert(startpos,obj)) {
m_out << "tools::rroot::buffer::read_object :"
<< " map insert failed."
<< std::endl;
}
#else
m_objs[startpos] = obj;
#endif
}
//::printf("debug : map : %d %lu\n",startpos,obj);
//NOTE : if class ref, "up there"-startpos = 8.
if(!obj->stream(*this)) {
//restore a good buffer position :
//m_pos = m_buffer+startpos+bcnt+sizeof(unsigned int);
a_fac.destroy(obj);
return false;
}
//NOTE : if obj stream ok, the below line is not needed.
//m_pos = m_buffer+startpos+bcnt+sizeof(unsigned int);
if(!check_byte_count(startpos,bcnt,sclass)) {
m_out << "tools::rroot::buffer::read_object :"
<< " check_byte_count failed "
<< "for object of class "
<< sout(sclass) << "." << std::endl;
a_fac.destroy(obj);
return false;
}
a_obj = obj;
}
}
if(m_verbose) {
m_out << "tools::rroot::buffer::read_object :"
<< " end : "
<< std::endl;
}
return true;
}
public:
bool read_version(short& a_version){
// Read class version from I/O buffer.
// Is read a short or three shorts.
a_version = 0;
short version = 0;
// not interested in byte count
if(!rbuf::read(version)) return false;
// if this is a byte count, then skip next short and read version
if(version & kByteCountVMask()) {
if(!rbuf::read(version)) return false;
if(!rbuf::read(version)) return false;
}
a_version = version;
return true;
}
bool read_version(short& a_version,uint32& a_start_pos,uint32& a_byte_count){
// Read class version from I/O buffer.
// Is read one or three shorts.
a_version = 0;
a_start_pos = 0;
a_byte_count = 0;
short version = 0;
// before reading object save start position
uint32 startpos = (uint32)(m_pos-m_buffer);
// read byte count (older files don't have byte count)
// byte count is packed in two individual shorts, this to be
// backward compatible with old files that have at this location
// only a single short (i.e. the version)
union {
unsigned int cnt;
short vers[2];
} v;
if(m_byte_swap) {
if(!rbuf::read(v.vers[1])) return false;
if(!rbuf::read(v.vers[0])) return false;
} else {
if(!rbuf::read(v.vers[0])) return false;
if(!rbuf::read(v.vers[1])) return false;
}
// no bytecount, backup and read version
uint32 bcnt = 0;
if(v.cnt & kByteCountMask()) {
bcnt = (v.cnt & ~kByteCountMask());
} else {
m_pos -= sizeof(unsigned int);
}
if(!rbuf::read(version)) return false;
//printf("Reading version=%d at pos=%d, bytecount=%d\n",
//version,*startpos,*bcnt);
a_version = version;
a_start_pos = startpos;
a_byte_count = bcnt;
return true;
}
bool check_byte_count(uint32 a_start_pos,
uint32 a_byte_count,
const std::string& a_store_cls){
if(!a_byte_count) return true;
size_t len = a_start_pos + a_byte_count + sizeof(unsigned int);
size_t diff = size_t(m_pos-m_buffer);
if(diff==len) return true;
if(diff<len) {
m_out << "tools::rroot::buffer::check_byte_count :"
<< " object of class " << sout(a_store_cls)
<< " read too few bytes ("
<< long_out(long(len-diff)) << " missing)."
<< std::endl;
}
if(diff>len) {
m_out << "tools::rroot::buffer::check_byte_count :"
<< " object of class " << sout(a_store_cls)
<< " read too many bytes ("
<< long_out(long(diff-len)) << " in excess)." << std::endl;
}
m_out << "tools::rroot::buffer::check_byte_count :"
<< " " << sout(a_store_cls)
<< " streamer not in sync with data on file, fix streamer."
<< std::endl;
m_pos = m_buffer+len;
return false;
}
protected:
bool read_string(char* a_string,uint32 a_max) {
// Read string from I/O buffer. String is read till 0 character is
// found or till max-1 characters are read (i.e. string s has max
// bytes allocated).
int nr = 0;
while (nr < int(a_max-1)) {
char ch;
if(!rbuf::read(ch)) return false;
// stop when 0 read
if(ch == 0) break;
a_string[nr++] = ch;
}
a_string[nr] = 0;
return true;
}
protected:
// buffer objects cannot be copied or assigned
//void checkCount(unsigned int);
//unsigned int checkObject(unsigned int,const IClass*,bool = false);
protected:
bool m_byte_swap;
bool m_verbose;
uint32 m_size;
char* m_buffer;
char* m_pos;
uint32 m_klen; //GB
bool m_map_objs;
obj_map m_objs;
};
}}
#endif