// 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" #include "../mapmanip" #ifdef TOOLS_MEM #include "../mem" #endif #include #include #include #include // in TOOLS_STL, we don't have (yet) a performant map. #ifdef tools_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 #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 { typedef rbuf parent; 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 obj_map; #endif #ifdef TOOLS_RROOT_OBJ_MAP_OMAP typedef omap obj_map; #endif #ifdef TOOLS_RROOT_OBJ_MAP_HASH_TABLE class obj_map : public hash_table { typedef hash_table parent; protected: virtual unsigned int hash(const uint32& a_key) { return 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) :parent(a_out,a_byte_swap,a_buffer+a_size,m_pos) ,m_byte_swap(a_byte_swap) ,m_verbose(a_verbose) //,m_verbose(true) ,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) ,m_map_objs(true) { #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) :parent(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: bool byte_swap() const {return m_byte_swap;} bool verbose() const {return m_verbose;} 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;} bool map_objs() const {return m_map_objs;} 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(!parent::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(!parent::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 = std::move(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: void remove_in_map(iro* a_obj) { #ifdef TOOLS_RROOT_OBJ_MAP_HASH_TABLE m_out << "tools::rroot::remove_in_map : dummy." << std::endl; #else find_and_remove_value(m_objs,a_obj); #endif } bool read_object(ifac& a_fac,const ifac::args& a_args,iro*& a_obj,bool& a_created){ a_obj = 0; a_created = false; //m_out << "debug : tools::rroot::buffer::read_object : begin :" << std::endl; // 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) << ", is_ref " << is_ref << ", bcnt " << bcnt << std::endl; } if(is_ref) { //bcnt is the position toward an object or an object ref. //m_out << "debug : 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) { m_out << "tools::rroot::buffer::read_object : warning :" << " class " << sout(sclass) << ", is_ref but map objs is not enabled on this buffer." << std::endl; } if(m_map_objs) { #ifdef TOOLS_RROOT_OBJ_MAP_HASH_TABLE if(m_objs.find(obj_offset,a_obj)) { //m_out << "debug : tools::rroot::buffer::read_object : found object in map." << bcnt << std::endl; 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(!parent::read(first_int)) { m_out << "tools::rroot::buffer::read_object : parent::read(first_int) failed." << std::endl; return false; } if(first_int & kByteCountMask()) { std::string scls; if(!read_class_tag(scls)) { m_out << "tools::rroot::buffer::read_object : read_class_tag() failed." << std::endl; 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) { m_out << "tools::rroot::buffer::read_object : is_ref : creation of object" << " of class " << sout(sclass) << " failed." << std::endl; 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); delete obj; return false; } //m_out << "tools::rroot::buffer::read_object :" // << " is_ref : streamed ok for class " << sout(scls) // << std::endl; a_obj = obj; a_created = true; } 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 << "debug : tools::rroot::buffer::read_object : found empty class name." << std::endl; m_pos = m_buffer+startpos+bcnt+sizeof(unsigned int); } else { //m_out << "debug : tools::rroot::buffer::read_object : not a ref, create object." << std::endl; // 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) { m_out << "tools::rroot::buffer::read_object : creation of object" << " of class " << sout(sclass) << " failed." << std::endl; return false; } //m_out << "debug : tools::rroot::buffer::read_object : object created." << std::endl; 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)) { m_out << "tools::rroot::buffer::read_object : object.stream() failed" << " for object of class " << sout(sclass) << "." << std::endl; //restore a good buffer position : //m_pos = m_buffer+startpos+bcnt+sizeof(unsigned int); delete 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; delete obj; return false; } a_obj = obj; a_created = true; } } 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(!parent::read(version)) return false; // if this is a byte count, then skip next short and read version if(version & kByteCountVMask()) { if(!parent::read(version)) return false; if(!parent::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(!parent::read(v.vers[1])) return false; if(!parent::read(v.vers[0])) return false; } else { if(!parent::read(v.vers[0])) return false; if(!parent::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(!parent::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(difflen) { 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(!parent::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; }; inline bool dummy_TXxx_pointer_stream(buffer& a_buffer,ifac& a_fac) { ifac::args args; iro* obj; bool created; bool status = a_buffer.read_object(a_fac,args,obj,created); if(obj) { if(created) { if(a_buffer.map_objs()) a_buffer.remove_in_map(obj); delete obj; } } return status; } template inline bool pointer_stream(buffer& a_buffer, ifac& a_fac,ifac::args& a_args, const std::string& a_T_class, T*& a_obj,bool& a_created){ iro* obj; if(!a_buffer.read_object(a_fac,a_args,obj,a_created)) { a_buffer.out() << "tools::rroot::pointer_stream : read_object failed." << std::endl; a_obj = 0; a_created = false; return false; } if(!obj) { a_obj = 0; a_created = false; } else { a_obj = (T*)obj->cast(a_T_class); if(!a_obj) { a_buffer.out() << "tools::rroot::pointer_stream : " << " tools::cast to " << a_T_class << " failed." << ". Object is a " << obj->s_cls() << "." << std::endl; if(a_created) delete obj; a_created = false; return false; } } return true; } template inline bool pointer_stream(buffer& a_buffer, ifac& a_fac,ifac::args& a_args, cid a_T_class, T*& a_obj,bool& a_created){ iro* obj; if(!a_buffer.read_object(a_fac,a_args,obj,a_created)) { a_buffer.out() << "tools::rroot::pointer_stream : read_object failed." << std::endl; a_obj = 0; a_created = false; return false; } if(!obj) { a_obj = 0; a_created = false; } else { a_obj = (T*)obj->cast(a_T_class); if(!a_obj) { a_buffer.out() << "tools::rroot::pointer_stream : " << " tools::cast to " << a_T_class << " failed." << ". Object is a " << obj->s_cls() << "." << std::endl; if(a_created) delete obj; a_created = false; return false; } } return true; } template inline bool pointer_stream(buffer& a_buffer,ifac& a_fac,ifac::args& a_args,T*& a_obj,bool& a_created){ //return pointer_stream(a_buffer,a_fac,a_args,T::s_class(),a_obj,a_created); return pointer_stream(a_buffer,a_fac,a_args,T::id_class(),a_obj,a_created); } template inline bool fixed_array_stream(buffer& a_buffer,int a_n,T*& a_v){ delete [] a_v; a_v = 0; char is_array; if(!a_buffer.read(is_array)) return false; if(!is_array) return true; if(!a_n) return true; a_v = new T[a_n]; if(!a_buffer.read_fast_array(a_v,a_n)) { delete [] a_v; a_v = 0; return false; } return true; } }} #endif