565 lines
16 KiB
Plaintext
565 lines
16 KiB
Plaintext
// Copyright (C) 2010, Guy Barrand. All rights reserved.
|
|
// See the file tools.license for terms.
|
|
|
|
#ifndef tools_wroot_directory
|
|
#define tools_wroot_directory
|
|
|
|
#include "idir"
|
|
|
|
#include "date"
|
|
#include "key"
|
|
#include "ifile"
|
|
#include "date"
|
|
#include "buffer"
|
|
#include "iobject"
|
|
|
|
#include "../strip"
|
|
#include "../vmanip"
|
|
#include "../forit"
|
|
|
|
#include <vector>
|
|
#include <list>
|
|
|
|
namespace tools {
|
|
namespace wroot {
|
|
|
|
class directory : public virtual idir {
|
|
static uint32 class_version() {return 1;}
|
|
public:
|
|
static const std::string& s_class() {
|
|
static const std::string s_v("tools::wroot::directory");
|
|
return s_v;
|
|
}
|
|
public: //idir
|
|
virtual ifile& file() {return m_file;}
|
|
virtual seek seek_directory() const {return m_seek_directory;}
|
|
virtual void append_object(iobject* a_object) {m_objs.push_back(a_object);} //take ownership of a_object
|
|
public:
|
|
directory(ifile& a_file)
|
|
:m_file(a_file)
|
|
,m_parent(0)
|
|
,m_is_valid(false)
|
|
,m_date_C(0)
|
|
,m_date_M(0)
|
|
,m_nbytes_keys(0)
|
|
,m_nbytes_name(0)
|
|
,m_seek_directory(0)
|
|
,m_seek_parent(0)
|
|
,m_seek_keys(0)
|
|
{
|
|
#ifdef TOOLS_MEM
|
|
mem::increment(s_class().c_str());
|
|
#endif
|
|
}
|
|
directory(ifile& a_file,const std::string& a_name,const std::string& a_title)
|
|
:m_file(a_file)
|
|
,m_parent(0)
|
|
,m_is_valid(false)
|
|
,m_name(a_name)
|
|
,m_title(a_title)
|
|
,m_nbytes_keys(0)
|
|
,m_nbytes_name(0)
|
|
,m_seek_directory(0)
|
|
,m_seek_parent(0)
|
|
,m_seek_keys(0)
|
|
{
|
|
#ifdef TOOLS_MEM
|
|
mem::increment(s_class().c_str());
|
|
#endif
|
|
m_date_C = get_date();
|
|
m_date_M = get_date();
|
|
|
|
if(m_name.empty()) {
|
|
m_file.out() << "tools::wroot::directory::directory :"
|
|
<< " directory name cannot be \"\"."
|
|
<< std::endl;
|
|
return; //FIXME : throw
|
|
}
|
|
if(m_name.find('/')!=std::string::npos) {
|
|
m_file.out() << "tools::wroot::directory::directory :"
|
|
<< " directory name " << sout(m_name)
|
|
<< " cannot contain a slash."
|
|
<< std::endl;
|
|
return; //FIXME : throw
|
|
}
|
|
if(m_title.empty()) m_title = m_name;
|
|
m_is_valid = true;
|
|
}
|
|
directory(ifile& a_file,
|
|
directory* a_parent, //assume a_parent not nul.
|
|
const std::string& a_name,
|
|
const std::string& a_title)
|
|
:m_file(a_file)
|
|
,m_parent(a_parent)
|
|
,m_is_valid(false)
|
|
,m_name(a_name)
|
|
,m_title(a_title)
|
|
,m_nbytes_keys(0)
|
|
,m_nbytes_name(0)
|
|
,m_seek_directory(0)
|
|
,m_seek_parent(0)
|
|
,m_seek_keys(0)
|
|
{
|
|
#ifdef TOOLS_MEM
|
|
mem::increment(s_class().c_str());
|
|
#endif
|
|
m_date_C = get_date();
|
|
m_date_M = get_date();
|
|
|
|
if(m_name.empty()) {
|
|
m_file.out() << "tools::wroot::directory::directory :"
|
|
<< " directory name cannot be \"\"."
|
|
<< std::endl;
|
|
return; //FIXME : throw
|
|
}
|
|
if(m_name.find('/')!=std::string::npos) {
|
|
m_file.out() << "tools::wroot::directory::directory :"
|
|
<< " directory name " << sout(m_name)
|
|
<< " cannot contain a slash."
|
|
<< std::endl;
|
|
return; //FIXME : throw
|
|
}
|
|
|
|
if(m_title.empty()) m_title = m_name;
|
|
|
|
if(m_parent->find_key(m_name)) {
|
|
m_file.out() << "tools::wroot::directory::directory :"
|
|
<< " directory " << sout(m_name) << " exists already."
|
|
<< std::endl;
|
|
return; //FIXME : throw
|
|
}
|
|
|
|
m_seek_parent = m_parent->seek_directory();
|
|
uint32 nbytes = record_size();
|
|
|
|
wroot::key* key = new wroot::key(m_file.out(),m_file,m_parent->seek_directory(),
|
|
m_name,m_title,"TDirectory",nbytes); // It does a m_file.set_END().
|
|
m_nbytes_name = key->key_length();
|
|
m_seek_directory = key->seek_key(); //at EOF
|
|
if(!m_seek_directory) {
|
|
m_file.out() << "tools::wroot::directory::directory :"
|
|
<< " bad key."
|
|
<< std::endl;
|
|
delete key;
|
|
return; //FIXME : throw
|
|
}
|
|
{char* buffer = key->data_buffer();
|
|
wbuf wb(m_file.out(),m_file.byte_swap(),key->eob(),buffer);
|
|
if(!to_buffer(wb)) {
|
|
m_file.out() << "tools::wroot::directory::directory :"
|
|
<< " directory name " << sout(m_name)
|
|
<< " cannot fill buffer."
|
|
<< std::endl;
|
|
delete key;
|
|
return; //FIXME : throw
|
|
}}
|
|
uint16 cycle = m_parent->append_key(key);
|
|
key->set_cycle(cycle);
|
|
if(!key->write_self(m_file)) {
|
|
m_file.out() << "tools::wroot::directory::directory :"
|
|
<< " key.write_self() failed."
|
|
<< std::endl;
|
|
return; //FIXME : throw
|
|
}
|
|
uint32 n;
|
|
if(!key->write_file(m_file,n)) {
|
|
m_file.out() << "tools::wroot::directory::directory :"
|
|
<< " directory name " << sout(m_name)
|
|
<< " cannot write key to file."
|
|
<< std::endl;
|
|
return; //FIXME : throw
|
|
}
|
|
|
|
m_is_valid = true;
|
|
}
|
|
virtual ~directory(){
|
|
clear_dirs();
|
|
clear_objs();
|
|
clear_keys();
|
|
#ifdef TOOLS_MEM
|
|
mem::decrement(s_class().c_str());
|
|
#endif
|
|
}
|
|
protected:
|
|
directory(const directory& a_from)
|
|
:idir(a_from)
|
|
,m_file(a_from.m_file)
|
|
,m_parent(0)
|
|
,m_is_valid(false){
|
|
#ifdef TOOLS_MEM
|
|
mem::increment(s_class().c_str());
|
|
#endif
|
|
}
|
|
directory& operator=(const directory &){
|
|
m_is_valid = false;
|
|
return *this;
|
|
}
|
|
public:
|
|
bool is_valid() const {return m_is_valid;}
|
|
void set_seek_directory(seek a_seek) {m_seek_directory = a_seek;}
|
|
|
|
directory* mkdir(const std::string& a_name,const std::string& a_title = ""){
|
|
// Create a sub-directory and return a pointer to the created directory.
|
|
// Note that the directory name cannot contain slashes.
|
|
if(a_name.empty()) {
|
|
m_file.out() << "tools::wroot::directory::mkdir :"
|
|
<< " directory name cannot be \"\"."
|
|
<< std::endl;
|
|
return 0;
|
|
}
|
|
if(a_name.find('/')!=std::string::npos) {
|
|
m_file.out() << "tools::wroot::directory::mkdir :"
|
|
<< " " << sout(a_name)
|
|
<< " cannot contain a slash."
|
|
<< std::endl;
|
|
return 0;
|
|
}
|
|
directory* dir = new directory(m_file,this,a_name,a_title.empty()?a_name:a_title);
|
|
if(!dir->is_valid()) {
|
|
m_file.out() << "tools::wroot::directory::mkdir :"
|
|
<< " directory badly created."
|
|
<< std::endl;
|
|
delete dir;
|
|
return 0;
|
|
}
|
|
m_dirs.push_back(dir);
|
|
return dir;
|
|
}
|
|
|
|
//uint32 nbytes_name() const {return m_nbytes_name;}
|
|
void set_nbytes_name(uint32 a_n) {m_nbytes_name = a_n;}
|
|
|
|
uint32 record_size() const {
|
|
uint32 nbytes = sizeof(short);
|
|
nbytes += sizeof(date); //m_date_C.record_size();
|
|
nbytes += sizeof(date); //m_date_M.record_size();
|
|
nbytes += sizeof(m_nbytes_keys);
|
|
nbytes += sizeof(m_nbytes_name);
|
|
//ROOT version >= 40000:
|
|
nbytes += sizeof(seek);
|
|
nbytes += sizeof(seek);
|
|
nbytes += sizeof(seek);
|
|
return nbytes;
|
|
}
|
|
|
|
bool close() {
|
|
if(!save()) return false;
|
|
clear_dirs();
|
|
clear_objs();
|
|
clear_keys();
|
|
return true;
|
|
}
|
|
|
|
bool to_buffer(wbuf& a_wb){
|
|
// Decode input buffer.
|
|
// (Name, title) are stored in the (name, title) of the associated key.
|
|
short version = class_version();
|
|
version += big_file_version_tag(); //GB : enforce writing on seek (and not seek32).
|
|
if(!a_wb.write(version)) return false;
|
|
if(!a_wb.write(m_date_C)) return false;
|
|
if(!a_wb.write(m_date_M)) return false;
|
|
if(!a_wb.write(m_nbytes_keys)) return false;
|
|
if(!a_wb.write(m_nbytes_name)) return false;
|
|
|
|
if(!a_wb.write(m_seek_directory)) return false;
|
|
if(!a_wb.write(m_seek_parent)) return false;
|
|
if(!a_wb.write(m_seek_keys)) return false;
|
|
|
|
if(m_file.verbose()) {
|
|
m_file.out() << "tools::wroot::key::to_buffer :"
|
|
<< " nbytes keys : " << m_nbytes_keys
|
|
<< ", pos keys : " << m_seek_keys
|
|
<< std::endl;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool write(uint32& a_nbytes){
|
|
// Write all objects in memory to disk.
|
|
// Loop on all objects in memory (including subdirectories).
|
|
// A new key is created in the m_keys linked list for each object.
|
|
// For allowed options see TObject::Write().
|
|
// The directory header info is rewritten on the directory header record
|
|
a_nbytes = 0;
|
|
if(m_file.verbose()) {
|
|
m_file.out() << "tools::wroot::directory::write :"
|
|
<< " " << sout(m_name)
|
|
<< " : " << m_dirs.size()
|
|
<< " : " << m_objs.size()
|
|
<< " objects."
|
|
<< std::endl;
|
|
}
|
|
|
|
uint32 nbytes = 0;
|
|
|
|
{tools_vforcit(directory*,m_dirs,it) {
|
|
uint32 n;
|
|
if(!(*it)->write(n)) return false;
|
|
nbytes += n;
|
|
}}
|
|
|
|
{tools_vforit(iobject*,m_objs,it) {
|
|
uint32 n;
|
|
if(!write_object(*(*it),n)) {
|
|
m_file.out() << "tools::wroot::directory::write :"
|
|
<< " for directory " << sout(m_name)
|
|
<< ", write_object " << sout((*it)->name())
|
|
<< " failed."
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
nbytes += n;
|
|
}}
|
|
|
|
if(!save_self()) {
|
|
m_file.out() << "tools::wroot::directory::write :"
|
|
<< " for directory " << sout(m_name)
|
|
<< ", save_self failed."
|
|
<< std::endl;
|
|
return false; //it will write keys of objects.
|
|
}
|
|
|
|
a_nbytes = nbytes;
|
|
return true;
|
|
}
|
|
|
|
void clear_dirs() {safe_clear<directory>(m_dirs);}
|
|
void clear_objs() {safe_clear<iobject>(m_objs);}
|
|
|
|
const std::list<key*>& keys() const {return m_keys;}
|
|
//std::list<key*>& keys() {return m_keys;}
|
|
|
|
protected:
|
|
void clear_keys() {
|
|
std::list<key*>::iterator it;
|
|
for(it=m_keys.begin();it!=m_keys.end();) {
|
|
key* k = *it;
|
|
it = m_keys.erase(it);
|
|
delete k;
|
|
}
|
|
m_keys.clear();
|
|
}
|
|
|
|
bool save(){
|
|
if(!save_self()) return false;
|
|
tools_vforit(directory*,m_dirs,it) {
|
|
if(!(*it)->save()) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool save_self() {
|
|
// Save Directory keys and header :
|
|
// If the directory has been modified (fModified set), write the keys
|
|
// and the directory header.
|
|
//if (fModified || aForce) {
|
|
// if(!fFile.freeSegments().empty()) {
|
|
// if(!writeKeys()) return false; // Write keys record.
|
|
// if(!writeHeader()) return false; // Update directory record.
|
|
// }
|
|
//}
|
|
if(!write_keys()) return false;
|
|
if(!write_header()) return false;
|
|
return true;
|
|
}
|
|
|
|
key* find_key(const std::string& a_name) {
|
|
if(m_file.verbose()) {
|
|
m_file.out() << "tools::wroot::directory::find_key :"
|
|
<< " " << sout(a_name) << " ..."
|
|
<< std::endl;
|
|
}
|
|
tools_lforcit(key*,m_keys,it) {
|
|
if((*it)->object_name()==a_name) return *it;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
seek seek_keys() const {return m_seek_keys;}
|
|
|
|
uint16 append_key(key* a_key){ //take ownership of a_key
|
|
tools_lforit(key*,m_keys,it) {
|
|
if((*it)->object_name()==a_key->object_name()) {
|
|
m_keys.insert(it,a_key); //a_key will be before *it.
|
|
return ((*it)->cycle() + 1);
|
|
}
|
|
}
|
|
// Not found :
|
|
m_keys.push_back(a_key);
|
|
return 1;
|
|
}
|
|
|
|
bool write_keys(){
|
|
// The list of keys (m_keys) is written as a single data record
|
|
// Delete the old keys structure if it exists
|
|
|
|
//if(fSeekKeys) {
|
|
// if(!fFile.makeFreeSegment
|
|
// (fSeekKeys, fSeekKeys + fNbytesKeys -1)) return false;
|
|
//}
|
|
|
|
// Write new keys record :
|
|
uint32 nkeys = uint32(m_keys.size());
|
|
|
|
// Compute size of all keys
|
|
uint32 nbytes = sizeof(nkeys);
|
|
|
|
{tools_lforit(key*,m_keys,it) {
|
|
nbytes += (*it)->key_length();
|
|
}}
|
|
|
|
key headerkey(m_file.out(),m_file,m_seek_directory,m_name,m_title,"TDirectory",nbytes); // m_file.set_END().
|
|
if(!headerkey.seek_key()) return false;
|
|
|
|
{char* buffer = headerkey.data_buffer();
|
|
wbuf wb(m_file.out(),m_file.byte_swap(),headerkey.eob(),buffer);
|
|
if(!wb.write(nkeys)) return false;
|
|
{tools_lforit(key*,m_keys,it) {
|
|
if(!((*it)->to_buffer(wb,m_file.verbose()))) return false;
|
|
}}}
|
|
|
|
m_seek_keys = headerkey.seek_key();
|
|
m_nbytes_keys = headerkey.number_of_bytes();
|
|
|
|
if(m_file.verbose()) {
|
|
m_file.out() << "tools::wroot::directory::write_keys :"
|
|
<< " write header key"
|
|
<< " " << sout(m_name)
|
|
<< " " << sout(m_title)
|
|
<< " (" << nkeys
|
|
<< ", " << nbytes
|
|
<< ", " << m_seek_keys
|
|
<< ", " << m_nbytes_keys
|
|
<< "):"
|
|
<< std::endl;
|
|
}
|
|
|
|
headerkey.set_cycle(1);
|
|
if(!headerkey.write_self(m_file)) {
|
|
m_file.out() << "tools::wroot::directory::write_keys :"
|
|
<< " key.write_self() failed."
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
|
|
uint32 n;
|
|
return headerkey.write_file(m_file,n);
|
|
}
|
|
|
|
bool write_header(){
|
|
// Overwrite the Directory header record.
|
|
uint32 nbytes = record_size();
|
|
char* header = new char[nbytes];
|
|
char* buffer = header;
|
|
m_date_M = get_date();
|
|
wbuf wb(m_file.out(),m_file.byte_swap(),header+nbytes,buffer);
|
|
if(!to_buffer(wb)) {
|
|
delete [] header;
|
|
return false;
|
|
}
|
|
// do not overwrite the name/title part
|
|
seek pointer = m_seek_directory + m_nbytes_name;
|
|
//fModified = false;
|
|
if(!m_file.set_pos(pointer)) {
|
|
delete [] header;
|
|
return false;
|
|
}
|
|
if(!m_file.write_buffer(header,nbytes)) {
|
|
delete [] header;
|
|
return false;
|
|
}
|
|
if(!m_file.synchronize()) {
|
|
delete [] header;
|
|
return false;
|
|
}
|
|
delete [] header;
|
|
return true;
|
|
}
|
|
|
|
bool write_object(iobject& a_obj,uint32& a_nbytes){
|
|
buffer bref(m_file.out(),m_file.byte_swap(),256*128); //32768
|
|
if(!a_obj.stream(bref)) {
|
|
m_file.out() << "tools::wroot::directory::write_object :"
|
|
<< " cannot stream object of store class name "
|
|
<< " " << sout(a_obj.store_class_name()) << "."
|
|
<< std::endl;
|
|
a_nbytes = 0;
|
|
return false;
|
|
}
|
|
|
|
std::string name = a_obj.name();
|
|
strip(name);
|
|
|
|
//first create the key to get key_length();
|
|
|
|
wroot::key* key = new wroot::key(m_file.out(),m_file,m_seek_directory,
|
|
name,
|
|
a_obj.title(),a_obj.store_class_name(),
|
|
bref.length()); // It does a m_file.set_END().
|
|
|
|
if(!key->seek_key()) {
|
|
delete key;
|
|
return false;
|
|
}
|
|
|
|
if(!bref.displace_mapped(key->key_length())) { //done before compression.
|
|
delete key;
|
|
return false;
|
|
}
|
|
|
|
char* kbuf = 0;
|
|
uint32 klen = 0;
|
|
bool kdelete = false;
|
|
m_file.compress_buffer(bref,kbuf,klen,kdelete);
|
|
|
|
::memcpy(key->data_buffer(),kbuf,klen);
|
|
if(kdelete) delete [] kbuf;
|
|
|
|
{uint32 nkey = key->key_length()+klen;
|
|
m_file.set_END(key->seek_key()+nkey);
|
|
key->set_number_of_bytes(nkey);}
|
|
|
|
uint16 cycle = append_key(key);
|
|
key->set_cycle(cycle);
|
|
|
|
if(!key->write_self(m_file)) {
|
|
m_file.out() << "tools::wroot::directory::write_object :"
|
|
<< " key.write_self() failed."
|
|
<< std::endl;
|
|
return false;
|
|
}
|
|
|
|
//FIXME m_file.sumBuffer(key->object_size()); //uncompressed data size.
|
|
|
|
if(m_file.verbose()) {
|
|
m_file.out() << "tools::wroot::directory::_write_buffer :"
|
|
<< " " << sout(a_obj.name()) << "."
|
|
<< std::endl;
|
|
}
|
|
|
|
return key->write_file(m_file,a_nbytes);
|
|
}
|
|
|
|
protected:
|
|
ifile& m_file;
|
|
directory* m_parent;
|
|
bool m_is_valid;
|
|
std::string m_name;
|
|
std::string m_title;
|
|
std::vector<directory*> m_dirs;
|
|
std::vector<iobject*> m_objs;
|
|
std::list<key*> m_keys;
|
|
// Record (stored in file):
|
|
date m_date_C; //Date and time when directory is created
|
|
date m_date_M; //Date and time of last modification
|
|
uint32 m_nbytes_keys; //Number of bytes for the keys
|
|
uint32 m_nbytes_name; //Number of bytes in TNamed at creation time
|
|
seek m_seek_directory; //Location of directory on file
|
|
seek m_seek_parent; //Location of parent directory on file
|
|
seek m_seek_keys; //Location of Keys record on file
|
|
};
|
|
|
|
}}
|
|
|
|
#endif
|