// Copyright (C) 2010, Guy Barrand. All rights reserved. // See the file tools.license for terms. #ifndef tools_wroot_basket #define tools_wroot_basket #include "ibo" #include "key" #include "buffer" #include "../cmemT" namespace tools { namespace wroot { class basket : public virtual ibo, public key { typedef key parent; public: static const std::string& s_class() { static const std::string s_v("tools::wroot::basket"); return s_v; } public: //ibo virtual const std::string& store_cls() const { static const std::string s_v("TBasket"); return s_v; } virtual bool stream(buffer& a_buffer) const { // in principle we pass here only for the last basket // of a branch when it is streamed from branch::stream(). // We pass also here in the case of filling "tree in a tree" (MEMPHYS sim). // some consitency checks : //G.Barrand : the below test is "too much". Someone // may have to write a tree (and then a basket) // which had been never filled. // Moreover with the today branch code, a // basket.write_on_file() // happens only in a branch.fill() that deletes // the basket just after the call. if(m_seek_key) { m_out << "tools::wroot::basket::stream :" << " m_seek_key is not null (" << m_seek_key << ")." << std::endl; return false; } if(m_last) { m_out << "tools::wroot::basket::stream :" << " m_last is not null." << std::endl; return false; } if(!m_entry_offset) { m_out << "tools::wroot::basket::stream :" << " m_entry_offset is null." << std::endl; return false; } {uint32 _last = m_data.length()+m_key_length; if(_last>m_last) { const_cast(*this).m_last = _last; }} if(m_last>m_buf_size) { const_cast(*this).m_buf_size = m_last; } char flag = 11; if(m_displacement) flag += 40; if(!_stream_header(a_buffer,m_verbose,flag)) return false; if(m_entry_offset && m_nev) { if(!a_buffer.write_array(m_entry_offset,m_nev)) return false; if(m_displacement) { if(!a_buffer.write_array(m_displacement,m_nev)) return false; } } if(m_data.to_displace()) { if(!const_cast(*this).m_data.displace_mapped(m_key_length)) { m_out << "tools::wroot::basket::stream :" << " m_data.displace_mapped() failed." << std::endl; return false; } } buffer bref(m_out,a_buffer.byte_swap(),256); if(!_stream_header(bref,m_verbose)) return false; //then header stored twice ! if(!bref.write_fast_array(m_data.buf(),m_data.length())) return false; if(!a_buffer.write_fast_array(bref.buf(),bref.length())) return false; return true; } public: basket(std::ostream& a_out, bool a_byte_swap, seek a_seek_directory, const std::string& a_object_name, const std::string& a_object_title, const std::string& a_object_class, uint32 a_basket_size,bool a_verbose) :parent(a_out,a_seek_directory,a_object_name,a_object_title,a_object_class) ,m_verbose(a_verbose) ,m_data(a_out,a_byte_swap,a_basket_size) ,m_nev_buf_size(1000) ,m_nev(0) ,m_last(0) ,m_entry_offset(0) ,m_displacement(0) { if(m_version>big_file_version_tag()) { } else { // G.Barrand : April 2016 : WARNING : this is a change in the ROOT/IO format compared to CERN-ROOT/4.x : // We follow the logic found on CERN-ROOT/5.x that enforces "+1000" on all baskets. m_version += big_file_version_tag(); } m_key_length = header_record_size(m_version); initialize_zero(); if(m_nev_buf_size) { m_entry_offset = new int[m_nev_buf_size]; {for(uint32 i=0;i=m_nev_buf_size) { // for the +1, we follow CERN-ROOT/TBasket logic that wants to store // nev+1 elements for m_entry_offset in the write_on_file() method. uint32 newsize = max_of(10,2*m_nev_buf_size); if(!cmem_realloc(m_entry_offset,newsize,m_nev_buf_size,true)){ m_out << "tools::wroot::basket::update : realloc failed." << std::endl; return false; } if(m_displacement) { if(!cmem_realloc(m_displacement,newsize,m_nev_buf_size,true)){ m_out << "tools::wroot::basket::update : realloc failed." << std::endl; return false; } } m_nev_buf_size = newsize; } m_entry_offset[m_nev] = (int)a_offset; } m_nev++; return true; } bool write_on_file(ifile& a_file,uint16 a_cycle,uint32& a_nbytes) { // write m_data buffer into file. //NOTE : m_data does not contain the key at its head. // At this point m_seek_key should be 0. a_nbytes = 0; if(m_seek_key) { m_out << "tools::wroot::basket::write_on_file :" << " m_seek_key should be 0 (" << m_seek_key << ")." << std::endl; return false; } if(m_version>big_file_version_tag()) { } else { m_out << "tools::wroot::basket::write_on_file : " << " we should not pass here (1)." << std::endl; return false; } // Transfer m_entry_offset table at the end of fBuffer. Offsets to fBuffer // are transformed in entry length to optimize compression algorithm. m_last = m_key_length+m_data.length(); if(m_entry_offset) { if(!m_data.write_array(m_entry_offset,m_nev+1)) { // for the +1 we follow CERN-ROOT/TBasket logic. delete [] m_entry_offset; m_entry_offset = 0; return false; } delete [] m_entry_offset; m_entry_offset = 0; if(m_displacement) { if(!m_data.write_array(m_displacement,m_nev+1)) { delete [] m_displacement; m_displacement = 0; return false; } delete [] m_displacement; m_displacement = 0; } } m_object_size = m_data.length(); //uncompressed size. m_cycle = a_cycle; if(!m_data.displace_mapped(m_key_length)) return false; char* kbuf = 0; uint32 klen = 0; bool kdelete = false; a_file.compress_buffer(m_data,kbuf,klen,kdelete); if(!initialize(a_file,klen)) { //it will do a m_seek_key = a_file.END() and then a_file.set_END(...) m_out << "tools::wroot::basket::write_on_file :" << " initialize() failed." << std::endl; if(kdelete) delete [] kbuf; return false; } //write header of the key : {buffer bref(m_out,a_file.byte_swap(),256); if(!_stream_header(bref,a_file.verbose())) return false; if(bref.length()!=m_key_length) { m_out << "tools::wroot::basket::write_on_file :" << " key len anomaly " << bref.length() << " m_key_length " << m_key_length << std::endl; if(kdelete) delete [] kbuf; return false; } ::memcpy(m_buffer,bref.buf(),m_key_length);} ::memcpy(m_buffer+m_key_length,kbuf,klen); if(kdelete) delete [] kbuf; uint32 nbytes; if(!parent::write_file(a_file,nbytes)) return false; m_data.pos() = m_data.buf(); //empty m_data. a_nbytes = m_key_length + klen; return true; } protected: uint32 header_record_size(uint32 a_version) const { // header only. uint32 nbytes = parent::record_size(a_version); nbytes += sizeof(short); //version nbytes += sizeof(uint32); //m_buf_size nbytes += sizeof(uint32); //m_nev_buf_size nbytes += sizeof(uint32); //m_nev nbytes += sizeof(uint32); //m_last nbytes += sizeof(char); //flag return nbytes; } bool _stream_header(buffer& a_buffer,bool a_verbose,char a_flag = 0) const { {uint32 l = parent::record_size(m_version); if((a_buffer.length()+l)>a_buffer.size()) { if(!a_buffer.expand(a_buffer.size()+l)) return false; } wbuf wb(m_out,a_buffer.byte_swap(),a_buffer.max_pos(),a_buffer.pos()); if(!parent::to_buffer(wb,a_verbose)) return false;} if(!a_buffer.write_version(2)) return false; if(!a_buffer.write(m_buf_size)) return false; if(!a_buffer.write(m_nev_buf_size)) return false; if(!a_buffer.write(m_nev)) return false; if(!a_buffer.write(m_last)) return false; if(!a_buffer.write(a_flag)) return false; return true; } protected: bool m_verbose; buffer m_data; protected: uint32 m_nev_buf_size; //Length in Int_t of m_entry_offset uint32 m_nev; //Number of entries in basket uint32 m_last; //Pointer to last used byte in basket int* m_entry_offset; //[m_nev] Offset of entries in fBuffer(TKey) int* m_displacement; //![m_nev] Displacement of entries in fBuffer(TKey) }; }} #endif