// 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 #include #include #include 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. :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: 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 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 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(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,(::strlen(a_s)+1)*sizeof(char)); } template 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(a_a,a_n); } template bool write_fast_array(const std::vector& a_v) { if(a_v.empty()) return true; uint32 l = a_v.size() * sizeof(T); if((m_pos+l)>m_max) { if(!expand2(m_size+l)) return false; } return m_wb.write(a_v); } template 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 bool write_array(const std::vector a_v) { if(!write((uint32)a_v.size())) return false; return write_fast_array(a_v); } template bool write_array2(const std::vector< std::vector > a_v) { if(!write((uint32)a_v.size())) return false; for(unsigned int index=0;indexkMaxVersion()) { 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::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(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(2*m_size,a_new_size));} //CERN-ROOT logic. bool expand(uint32 a_new_size) { unsigned long len = m_pos-m_buffer; if(!realloc(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; } unsigned int 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 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; } 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::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(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; } static std::string sout(const std::string& a_string) { return std::string("\"")+a_string+"\""; } 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 m_objs; std::vector< std::pair > m_obj_mapped; std::map m_clss; std::vector< std::pair > m_cls_mapped; }; }} #endif