Files
geant4/source/externals/g4tools/include/tools/wroot/buffer
T
2021-06-25 16:12:29 +02:00

408 lines
11 KiB
Plaintext

// Copyright (C) 2010, Guy Barrand. All rights reserved.
// See the file tools.license for terms.
#ifndef tools_wroot_buffer
#define tools_wroot_buffer
// class used for serializing objects.
#include "wbuf"
#include "ibo"
#include "../realloc"
#include "../mnmx"
#include "../forit"
#ifdef TOOLS_MEM
#include "../mem"
#endif
#include <string>
#include <vector>
#include <ostream>
#include <map>
namespace tools {
namespace wroot {
class buffer {
static const std::string& s_class() {
static const std::string s_v("tools::wroot::buffer");
return s_v;
}
public:
buffer(std::ostream& a_out,bool a_byte_swap,uint32 a_size) // we expect a not zero value for a_size.
:m_out(a_out)
,m_byte_swap(a_byte_swap)
,m_size(0)
,m_buffer(0)
,m_max(0)
,m_pos(0)
,m_wb(a_out,a_byte_swap,0,m_pos) //it holds a ref on m_pos.
{
#ifdef TOOLS_MEM
mem::increment(s_class().c_str());
#endif
m_size = a_size;
m_buffer = new char[m_size];
//if(!m_buffer) {}
m_max = m_buffer+m_size;
m_pos = m_buffer;
m_wb.set_eob(m_max);
}
virtual ~buffer(){
m_objs.clear();
m_obj_mapped.clear();
m_clss.clear();
m_cls_mapped.clear();
delete [] m_buffer;
#ifdef TOOLS_MEM
mem::decrement(s_class().c_str());
#endif
}
protected:
buffer(const buffer& a_from)
:m_out(a_from.m_out)
,m_byte_swap(a_from.m_byte_swap)
,m_size(0)
,m_buffer(0)
,m_max(0)
,m_pos(0)
,m_wb(a_from.m_out,a_from.m_byte_swap,0,m_pos)
{
#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;}
std::ostream& out() const {return m_out;}
//void set_offset(unsigned int a_off) {m_pos = m_buffer+a_off;}
char* buf() {return m_buffer;}
const char* buf() const {return m_buffer;}
uint32 length() const {return uint32(m_pos-m_buffer);}
uint32 size() const {return m_size;}
char*& pos() {return m_pos;} //used in basket.
char* max_pos() const {return m_max;} //used in basket.
public:
template <class T>
bool write(T x){
if(m_pos+sizeof(T)>m_max) {
if(!expand2(m_size+sizeof(T))) return false;
}
return m_wb.write(x);
}
bool write(bool x) {return write<unsigned char>(x?1:0);}
bool write(const std::string& x) {
uint32 sz = (uint32)(x.size() + sizeof(int) + 1);
if((m_pos+sz)>m_max) {
if(!expand2(m_size+sz)) return false;
}
return m_wb.write(x);
}
bool write_fast_array(const char* a_a,uint32 a_n) {
if(!a_n) return true;
uint32 l = a_n * sizeof(char);
if((m_pos+l)>m_max) {
if(!expand2(m_size+l)) return false;
}
::memcpy(m_pos,a_a,l);
m_pos += l;
return true;
}
bool write_cstring(const char* a_s) {return write_fast_array(a_s,(uint32(::strlen(a_s))+1)*sizeof(char));}
template <class T>
bool write_fast_array(const T* a_a,uint32 a_n) {
if(!a_n) return true;
uint32 l = a_n * sizeof(T);
if((m_pos+l)>m_max) {
if(!expand2(m_size+l)) return false;
}
return m_wb.write<T>(a_a,a_n);
}
template <class T>
bool write_fast_array(const std::vector<T>& a_v) {
if(a_v.empty()) return true;
uint32 l = uint32(a_v.size() * sizeof(T));
if((m_pos+l)>m_max) {
if(!expand2(m_size+l)) return false;
}
return m_wb.write<T>(a_v);
}
template <class T>
bool write_array(const T* a_a,uint32 a_n) {
if(!write(a_n)) return false;
return write_fast_array(a_a,a_n);
}
template <class T>
bool write_array(const std::vector<T> a_v) {
if(!write((uint32)a_v.size())) return false;
return write_fast_array(a_v);
}
template <class T>
bool write_array2(const std::vector< std::vector<T> > a_v) {
if(!write((uint32)a_v.size())) return false;
for(unsigned int index=0;index<a_v.size();index++) {
if(!write_array(a_v[index])) return false;
}
return true;
}
public:
bool write_version(short a_version){
if(a_version>kMaxVersion()) {
m_out << "tools::wroot::buffer::write_version :"
<< " version number " << a_version
<< " cannot be larger than " << kMaxVersion() << "."
<< std::endl;
return false;
}
return write(a_version);
}
bool write_version(short a_version,uint32& a_pos){
// reserve space for leading byte count
a_pos = (uint32)(m_pos-m_buffer);
//NOTE : the below test is lacking in CERN-ROOT !
if((m_pos+sizeof(unsigned int))>m_max) {
if(!expand2(m_size+sizeof(unsigned int))) return false;
}
m_pos += sizeof(unsigned int);
if(a_version>kMaxVersion()) {
m_out << "tools::wroot::buffer::write_version :"
<< " version number " << a_version
<< " cannot be larger than " << kMaxVersion() << "."
<< std::endl;
return false;
}
return write(a_version);
}
bool set_byte_count(uint32 a_pos){
uint32 cnt = (uint32)(m_pos-m_buffer) - a_pos - sizeof(unsigned int);
if(cnt>=kMaxMapCount()) {
m_out << "tools::wroot::buffer::set_byte_count :"
<< " bytecount too large (more than "
<< kMaxMapCount() << ")."
<< std::endl;
return false;
}
union {
uint32 cnt;
short vers[2];
} v;
v.cnt = cnt;
char* opos = m_pos;
m_pos = (char*)(m_buffer+a_pos);
if(m_byte_swap) {
if(!m_wb.write(short(v.vers[1]|kByteCountVMask())))
{m_pos = opos;return false;}
if(!m_wb.write(v.vers[0])) {m_pos = opos;return false;}
} else {
if(!m_wb.write(short(v.vers[0]|kByteCountVMask())))
{m_pos = opos;return false;}
if(!m_wb.write(v.vers[1])) {m_pos = opos;return false;}
}
m_pos = opos;
return true;
}
bool write_object(const ibo& a_obj){
//GB : if adding a write map logic, think to have a displace_mapped()
// in basket::write_on_file().
std::map<ibo*,uint32>::const_iterator it = m_objs.find((ibo*)&a_obj);
if(it!=m_objs.end()) {
uint32 objIdx = (*it).second;
unsigned int offset = (unsigned int)(m_pos-m_buffer);
// save index of already stored object
if(!write(objIdx)) return false;
m_obj_mapped.push_back(std::pair<uint32,uint32>(offset,objIdx));
} else {
// reserve space for leading byte count
uint32 cntpos = (unsigned int)(m_pos-m_buffer);
//NOTE : the below test is lacking in CERN-ROOT !
if((m_pos+sizeof(unsigned int))>m_max) {
if(!expand2(m_size+sizeof(unsigned int))) return false;
}
m_pos += sizeof(unsigned int);
// write class of object first
if(!write_class(a_obj.store_cls())) return false;
// add to map before writing rest of object (to handle self reference)
// (+kMapOffset so it's != kNullTag)
m_objs[(ibo*)&a_obj] = cntpos + kMapOffset();
// let the object write itself :
if(!a_obj.stream(*this)) return false;
// write byte count
if(!set_byte_count_obj(cntpos)) return false;
}
return true;
}
bool expand2(uint32 a_new_size) {return expand(mx<uint32>(2*m_size,a_new_size));} //CERN-ROOT logic.
bool expand(uint32 a_new_size) {
diff_pointer_t len = m_pos-m_buffer;
if(!realloc<char>(m_buffer,a_new_size,m_size)) {
m_out << "tools::wroot::buffer::expand :"
<< " can't realloc " << a_new_size << " bytes."
<< std::endl;
m_size = 0;
m_max = 0;
m_pos = 0;
m_wb.set_eob(m_max);
return false;
}
m_size = a_new_size;
m_max = m_buffer + m_size;
m_pos = m_buffer + len;
m_wb.set_eob(m_max);
return true;
}
size_t to_displace() const {return m_cls_mapped.size()+m_obj_mapped.size();}
bool displace_mapped(unsigned int a_num){
char* opos = m_pos;
//m_out << "tools::wroot::buffer::displace_mapped :"
// << " cls num " << m_cls_mapped.size()
// << std::endl;
typedef std::pair<uint32,uint32> offset_id;
{tools_vforcit(offset_id,m_cls_mapped,it) {
unsigned int offset = (*it).first;
unsigned int id = (*it).second;
//m_out << "displace " << offset << " " << id << std::endl;
m_pos = m_buffer+offset;
unsigned int clIdx = id+a_num;
if(!write(uint32(clIdx|kClassMask()))) {m_pos = opos;return false;}
}}
//m_out << "tools::wroot::buffer::displace_mapped :"
// << " obj num " << m_obj_mapped.size()
// << std::endl;
{tools_vforcit(offset_id,m_obj_mapped,it) {
uint32 offset = (*it).first;
uint32 id = (*it).second;
//m_out << "displace at " << offset
// << " the obj pos " << id
// << " by " << a_num
// << std::endl;
m_pos = m_buffer+offset;
unsigned int objIdx = id+a_num;
if(!write(objIdx)) {m_pos = opos;return false;}
}}
m_pos = opos;
return true;
}
void reset_objs_map() {
m_objs.clear();
//m_clss.clear();
}
protected:
static short kMaxVersion() {return 0x3FFF;}
static uint32 kMaxMapCount() {return 0x3FFFFFFE;}
static short kByteCountVMask() {return 0x4000;}
static uint32 kNewClassTag() {return 0xFFFFFFFF;}
static int kMapOffset() {return 2;}
static unsigned int kClassMask() {return 0x80000000;}
static uint32 kByteCountMask() {return 0x40000000;}
bool write_class(const std::string& a_cls){
std::map<std::string,uint32>::const_iterator it = m_clss.find(a_cls);
if(it!=m_clss.end()) {
uint32 clIdx = (*it).second;
unsigned int offset = (unsigned int)(m_pos-m_buffer);
// save index of already stored class
if(!write(uint32(clIdx|kClassMask()))) return false;
m_cls_mapped.push_back(std::pair<uint32,uint32>(offset,clIdx));
} else {
unsigned int offset = (unsigned int)(m_pos-m_buffer);
if(!write(kNewClassTag())) return false;
if(!write_cstring(a_cls.c_str())) return false;
m_clss[a_cls] = offset + kMapOffset();
}
return true;
}
bool set_byte_count_obj(uint32 a_pos){
uint32 cnt = (uint32)(m_pos-m_buffer) - a_pos - sizeof(unsigned int);
if(cnt>=kMaxMapCount()) {
m_out << "tools::wroot::buffer::set_byte_count_obj :"
<< " bytecount too large (more than "
<< kMaxMapCount() << ")."
<< std::endl;
return false;
}
char* opos = m_pos;
m_pos = (char*)(m_buffer+a_pos);
if(!m_wb.write(uint32(cnt|kByteCountMask()))) {m_pos = opos;return false;}
m_pos = opos;
return true;
}
protected:
std::ostream& m_out;
bool m_byte_swap;
uint32 m_size;
char* m_buffer;
char* m_max;
char* m_pos;
wbuf m_wb;
std::map<ibo*,uint32> m_objs;
std::vector< std::pair<uint32,uint32> > m_obj_mapped;
std::map<std::string,uint32> m_clss;
std::vector< std::pair<uint32,uint32> > m_cls_mapped;
};
}}
#endif