Files
2025-12-05 08:54:02 +01:00

582 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"
#include "../mapmanip"
#include <string>
#include <vector>
#include <ostream>
#include <utility>
#include <map>
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;
}
typedef std::map<uint32,iro*> obj_map;
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_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(true)
{
}
virtual ~buffer(){
m_objs.clear();
}
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)
{
}
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;}
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;
}
a_class = _s;
return true;
} else if(tag & kClassMask()) {
unsigned int cl_offset = (tag & ~kClassMask());
cl_offset -= kMapOffset();
cl_offset -= m_klen;
char* old_pos = m_pos;
m_pos = m_buffer + cl_offset;
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){
a_class.clear();
a_bcnt = 0;
a_is_ref = false;
// 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 & 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) {
find_and_remove_value(m_objs,a_obj);
}
bool read_object(ifac& a_fac,const ifac::args& a_args,iro*& a_obj,bool& a_created){
a_obj = 0;
a_created = false;
// 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;
}
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.
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) {
obj_map::const_iterator it = m_objs.find(obj_offset);
if(it!=m_objs.end()) {
a_obj = (*it).second;
return true;
}
}
{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;
}
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()
m_objs[obj_offset] = obj;
}
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;
}
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.
} else {
if(sclass.empty()) {
m_pos = m_buffer+startpos+bcnt+sizeof(unsigned int);
} else {
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;
}
if(m_map_objs) {
//must be done before stream()
m_objs[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;
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(!parent::read(ch)) return false;
// stop when 0 read
if(ch == 0) break;
a_string[nr++] = ch;
}
a_string[nr] = 0;
return true;
}
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 <class T>
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 <class T>
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 <class T>
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 <class T>
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<T>(a_v,a_n)) {
delete [] a_v;
a_v = 0;
return false;
}
return true;
}
}}
#endif