inlib
1.2.0
|
00001 // Copyright (C) 2010, Guy Barrand. All rights reserved. 00002 // See the file inlib.license for terms. 00003 00004 #ifndef inlib_wroot_file 00005 #define inlib_wroot_file 00006 00007 // inheritance : 00008 #include "ifile" 00009 00010 #include "directory" 00011 00012 #include "infos" 00013 00014 #include "../platform" 00015 00016 #include "../path" 00017 00018 #include <map> 00019 00020 #include <fcntl.h> 00021 #include <errno.h> 00022 #include <sys/stat.h> 00023 00024 #if defined(WIN32) && !defined(__GNUC__) 00025 #include <direct.h> 00026 #define ssize_t int 00027 #include <io.h> 00028 // disable the warning about the usage of "this" in the constructor. 00029 #pragma warning(disable:4355) 00030 #endif 00031 00032 namespace inlib { 00033 namespace wroot { 00034 00035 class file : public virtual ifile { 00036 static const std::string& s_class() { 00037 static const std::string s_v("inlib::wroot::file"); 00038 return s_v; 00039 } 00040 static int not_open() {return -1;} 00041 static uint32 kBegin() {return 64;} 00042 static uint32 START_BIG_FILE() {return 2000000000;} 00043 public: //ifile 00044 virtual bool verbose() const {return m_verbose;} 00045 virtual std::ostream& out() {return m_out;} 00046 00047 virtual bool byte_swap() const {return is_little_endian();} 00048 virtual bool set_pos(seek a_offset = 0,from a_from = begin){ 00049 int whence = 0; 00050 switch(a_from) { 00051 case begin: 00052 whence = SEEK_SET; 00053 break; 00054 case current: 00055 whence = SEEK_CUR; 00056 break; 00057 case end: 00058 whence = SEEK_END; 00059 break; 00060 } 00061 00062 #if defined(__linux__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 2) 00063 if (::lseek64(m_file, a_offset, whence) < 0) { 00064 #elif defined(WIN32) 00065 if (::_lseeki64(m_file, a_offset, whence) < 0) { 00066 #else 00067 if (::lseek(m_file, a_offset, whence) < 0) { 00068 #endif 00069 m_out << "inlib::wroot::file::set_pos :" 00070 << " cannot set position " << a_offset 00071 << " in file " << sout(m_path) << "." 00072 << std::endl; 00073 return false; 00074 } 00075 return true; 00076 } 00077 00078 virtual seek END() const {return m_END;} 00079 virtual void set_END(seek a_end){m_END = a_end;} 00080 00081 virtual bool write_buffer(const char* a_buffer,uint32 a_length) { 00082 // Write a buffer to the file. This is the basic low level write operation. 00083 ssize_t siz; 00084 while ((siz = ::write(m_file,a_buffer,a_length)) < 0 && 00085 error_number() == EINTR) reset_error_number(); 00086 00087 if(siz < 0) { 00088 m_out << "inlib::wroot::file::write_buffer :" 00089 << " error writing to file " << sout(m_path) << "." 00090 << std::endl; 00091 return false; 00092 } 00093 if(siz!=(ssize_t)a_length) { 00094 m_out << "inlib::wroot::file::write_buffer :" 00095 << "error writing all requested bytes to file " << sout(m_path) 00096 << ", wrote " << long2s(siz) << " of " << a_length 00097 << std::endl; 00098 return false; 00099 } 00100 //m_bytes_write += siz; 00101 return true; 00102 } 00103 00104 virtual uint32 version() const { 00105 // Return version id as an integer, i.e. "2.22/04" -> 22204. 00106 static const uint32 ROOT_MAJOR_VERSION = 4; 00107 static const uint32 ROOT_MINOR_VERSION = 0; 00108 static const uint32 ROOT_PATCH_VERSION = 0; 00109 return 00110 10000 * ROOT_MAJOR_VERSION + 00111 100 * ROOT_MINOR_VERSION + 00112 ROOT_PATCH_VERSION; 00113 } 00114 00115 virtual bool synchronize(){ 00116 // Synchornize a file's in-core and on-disk states. 00117 #ifdef WIN32 00118 return true; 00119 #else 00120 if (::fsync(m_file) < 0) { 00121 m_out << "inlib::wroot::file::synchronize :" 00122 << " error flushing file " << sout(m_path) << "." 00123 << std::endl; 00124 return false; 00125 } 00126 return true; 00127 #endif 00128 } 00129 00130 virtual bool ziper(char a_key,zip_func& a_func) const { 00131 std::map<char,zip_func>::const_iterator it = m_zipers.find(a_key); 00132 if(it==m_zipers.end()) { 00133 a_func = 0; 00134 return false; 00135 } 00136 a_func = (*it).second; 00137 return true; 00138 } 00139 virtual uint32 compression() const {return m_compress;} 00140 virtual void compress_buffer(const buffer& a_buffer, 00141 char*& a_kbuf,uint32& a_klen,bool& a_kdel){ 00142 //NOTE : if(kdelete) delete [] kbuf; 00143 00144 a_kbuf = 0; 00145 a_klen = 0; 00146 a_kdel = false; 00147 00148 uint32 nbytes = a_buffer.length(); 00149 uint32 cxlevel = m_compress; 00150 if(cxlevel && (nbytes>256)) { 00151 ifile::zip_func func; 00152 if(!ziper('Z',func)) { 00153 //m_out << "inlib::wroot::directory::write_object :" 00154 // << " zlib ziper not found." 00155 // << std::endl; 00156 a_kbuf = (char*)a_buffer.buf(); 00157 a_klen = a_buffer.length(); 00158 a_kdel = false; 00159 } else { 00160 const uint32 kMAXBUF = 0xffffff; 00161 const uint32 HDRSIZE = 9; 00162 uint32 nbuffers = nbytes/kMAXBUF; 00163 uint32 buflen = nbytes+HDRSIZE*(nbuffers+1); 00164 a_kbuf = new char[buflen]; 00165 a_kdel = true; 00166 char* src = (char*)a_buffer.buf(); 00167 char* tgt = a_kbuf; 00168 uint32 nzip = 0; 00169 for(uint32 i=0;i<=nbuffers;i++) { 00170 uint32 bufmax = ((i == nbuffers) ? nbytes - nzip : kMAXBUF); 00171 uint32 nout; 00172 if(!zip(m_out,func,cxlevel,bufmax,src,bufmax,tgt,nout)) { 00173 delete [] a_kbuf; 00174 a_kbuf = (char*)a_buffer.buf(); 00175 a_klen = a_buffer.length(); 00176 a_kdel = false; 00177 break; 00178 } 00179 tgt += nout; //nout includes HDRSIZE 00180 a_klen += nout; 00181 src += kMAXBUF; 00182 nzip += kMAXBUF; 00183 } 00184 //::printf("debug : compress : end : %u %u\n",nbytes,klen); 00185 } 00186 } else { 00187 a_kbuf = (char*)a_buffer.buf(); 00188 a_klen = a_buffer.length(); 00189 a_kdel = false; 00190 } 00191 } 00192 public: 00193 file(std::ostream& a_out, 00194 const std::string& a_path, 00195 bool a_verbose = false) 00196 :m_out(a_out) 00197 ,m_path(a_path) 00198 ,m_verbose(a_verbose) 00199 ,m_file(not_open()) 00200 //,m_bytes_write(0) 00201 ,m_root_directory(*this,nosuffix(a_path),m_title) 00202 // begin of record : 00203 ,m_version(0) 00204 ,m_BEGIN(0) 00205 ,m_END(0) 00206 ,m_seek_free(0) 00207 ,m_nbytes_free(0) 00208 ,m_nbytes_name(0) 00209 ,m_units(4) 00210 ,m_compress(1) 00211 ,m_seek_info(0) 00212 ,m_nbytes_info(0) 00213 { 00214 #ifdef INLIB_MEM 00215 mem::increment(s_class().c_str()); 00216 #endif 00217 00218 m_version = version(); 00219 00220 if(access_path(m_path,kFileExists)) unlink(m_path); 00221 00222 if(!m_root_directory.is_valid()) { 00223 m_out << "inlib::wroot::file::file :" 00224 << " " << sout(m_path) << " root directory badly created." 00225 << std::endl; 00226 return; 00227 } 00228 00229 m_file = _open(a_path.c_str(), 00230 #ifdef WIN32 00231 O_RDWR | O_CREAT | O_BINARY,S_IREAD | S_IWRITE 00232 #else 00233 O_RDWR | O_CREAT,0644 00234 #endif 00235 ); 00236 if(m_file==not_open()) { 00237 m_out << "inlib::wroot::file::file :" 00238 << " can't open " << sout(a_path) << "." 00239 << std::endl; 00240 return; 00241 } 00242 00243 //initialize : 00244 m_BEGIN = kBegin(); // First used word in file following the file header. 00245 m_END = m_BEGIN; // Pointer to end of file. 00246 00247 // Write Directory info : 00248 uint32 namelen = 00249 key::std_string_record_size(m_path) + 00250 key::std_string_record_size(m_title); 00251 uint32 nbytes = namelen + m_root_directory.record_size(); 00252 wroot::key key(*this,0,m_path,m_title,"TFile",nbytes); //set m_END. 00253 00254 // m_nbytes_name = start point of directory info from key head. 00255 m_nbytes_name = key.key_length() + namelen; 00256 m_root_directory.set_nbytes_name(m_nbytes_name); 00257 m_root_directory.set_seek_directory(key.seek_key()); //at EOF. 00258 00259 //the below write 45 bytes at BOF (Begin Of File). 00260 if(!write_header()) { //need m_nbytes_name, m_END after key written. 00261 m_out << "inlib::wroot::file::file :" 00262 << " can't write file header." 00263 << std::endl; 00264 return; 00265 } 00266 00267 {char* pos = key.data_buffer(); 00268 wbuf wb(m_out,byte_swap(),key.eob(),pos); 00269 if(!wb.write(m_path)) return; 00270 if(!wb.write(m_title)) return; 00271 if(!m_root_directory.to_buffer(wb)) return;} 00272 00273 if(m_verbose) { 00274 m_out << "inlib::wroot::file::file :" 00275 << " write key (" 00276 << namelen 00277 << ", " 00278 << m_root_directory.record_size() 00279 << ", " 00280 << nbytes 00281 << ", " 00282 << m_nbytes_name 00283 << ", " 00284 << key.seek_key() 00285 << ")." 00286 << std::endl; 00287 } 00288 00289 key.set_cycle(1); 00290 if(!key.write_self()) { 00291 m_out << "inlib::wroot::file::file :" 00292 << " key.write_self() failed." 00293 << std::endl; 00294 return; 00295 } 00296 00297 //the below write at kBegin + nbytes. 00298 //64+52 00299 uint32 n; 00300 if(!key.write_file(n)) { 00301 m_out << "inlib::wroot::file::file :" 00302 << " can't write key in file." 00303 << std::endl; 00304 return; 00305 } 00306 //::printf("debug : file::file : write key : %d\n",n); 00307 00308 } 00309 virtual ~file() { 00310 close(); 00311 #ifdef INLIB_MEM 00312 mem::decrement(s_class().c_str()); 00313 #endif 00314 } 00315 protected: 00316 file(const file& a_from) 00317 :ifile(a_from) 00318 ,m_out(a_from.m_out) 00319 ,m_root_directory(*this) 00320 { 00321 #ifdef INLIB_MEM 00322 mem::increment(s_class().c_str()); 00323 #endif 00324 } 00325 file& operator=(const file&){return *this;} 00326 public: 00327 void set_compression(uint32 a_level) { 00328 // level = 0 objects written to this file will not be compressed. 00329 // level = 1 minimal compression level but fast. 00330 // .... 00331 // level = 9 maximal compression level but slow. 00332 m_compress = a_level; 00333 if(m_compress>9) m_compress = 9; 00334 } 00335 00336 void close() { 00337 if(m_file==not_open()) return; 00338 m_root_directory.close(); 00339 ::close(m_file); 00340 m_file = not_open(); 00341 } 00342 00343 directory& dir() {return m_root_directory;} 00344 const directory& dir() const {return m_root_directory;} 00345 00346 bool write(uint32& a_nbytes){ 00347 // Write memory objects to this file : 00348 // Loop on all objects in m_root_directory (including subdirectories). 00349 // A new key is created in the directories m_keys linked list 00350 // for each object. 00351 // The list of keys is then saved on the file (via write_keys) 00352 // as a single data record. 00353 // The directory header info is rewritten on the directory header record. 00354 // //The linked list of FREE segments is written. 00355 // The file header is written (bytes 1->m_BEGIN). 00356 a_nbytes = 0; 00357 00358 if(m_verbose) { 00359 m_out << "inlib::wroot::file::write :" 00360 << " writing Name=" << sout(m_path) 00361 << " Title=" << sout(m_title) << "." 00362 << std::endl; 00363 } 00364 00365 uint32 nbytes; 00366 if(!m_root_directory.write(nbytes)) return false; // Write directory tree 00367 00368 if(!write_streamer_infos()) { 00369 m_out << "inlib::wroot::file::write :" 00370 << " write_streamer_infos failed." 00371 << std::endl; 00372 return false; 00373 } 00374 00375 //if(!writeFreeSegments()) return false; // Write free segments. 00376 00377 if(!write_header()) { //write 45 bytes at BOF. 00378 m_out << "inlib::wroot::file::write :" 00379 << " can't write file header." 00380 << std::endl; 00381 return false; 00382 } 00383 00384 a_nbytes = nbytes; 00385 return true; 00386 } 00387 00388 bool add_ziper(char a_key,zip_func a_func){ 00389 std::map<char,zip_func>::iterator it = m_zipers.find(a_key); 00390 if(it!=m_zipers.end()) { 00391 //(*it).second = a_func; //override ? 00392 return false; 00393 } else { 00394 m_zipers[a_key] = a_func; 00395 return true; 00396 } 00397 } 00398 protected: 00399 enum EAccessMode { 00400 kFileExists = 0, 00401 kExecutePermission = 1, 00402 kWritePermission = 2, 00403 kReadPermission = 4 00404 }; 00405 static bool access_path(const std::string& a_path,EAccessMode a_mode){ 00406 // Returns true if one can access a file using the specified access mode. 00407 // Mode is the same as for the WinNT access(2) function. 00408 #ifdef WIN32 00409 return (::_access(a_path.c_str(),a_mode) == 0) ? true : false; 00410 #else 00411 return (::access(a_path.c_str(),a_mode) == 0) ? true : false; 00412 #endif 00413 } 00414 static bool unlink(const std::string& a_path){ 00415 // Unlink, i.e. remove, a file or directory. Returns true when succesfull, 00416 // false in case of failure. 00417 struct stat finfo; 00418 if (::stat(a_path.c_str(),&finfo) < 0) return false; 00419 #ifdef WIN32 00420 if (finfo.st_mode & S_IFDIR) 00421 return (::_rmdir(a_path.c_str())==-1 ? false : true); 00422 else 00423 return (::unlink(a_path.c_str())==-1 ? false : true); 00424 #else 00425 if (S_ISDIR(finfo.st_mode)) 00426 return (::rmdir(a_path.c_str())==-1 ? false : true); 00427 else 00428 return (::unlink(a_path.c_str())==-1 ? false : true); 00429 #endif 00430 } 00431 00432 static int _open(const char* a_name,int a_flags,unsigned int a_mode) { 00433 #if defined(__linux__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ >= 2) 00434 return ::open64(a_name,a_flags,a_mode); 00435 #else 00436 return ::open(a_name,a_flags,a_mode); 00437 #endif 00438 } 00439 static std::string sout(const std::string& a_string) { 00440 return std::string("\"")+a_string+"\""; 00441 } 00442 bool write_header() { 00443 const char root[] = "root"; 00444 //char psave[kBegin()]; 00445 char psave[128]; 00446 const char* eob = psave + kBegin(); 00447 char* pos = psave; 00448 ::memcpy(pos,root,4); pos += 4; 00449 uint32 version = m_version; 00450 if((m_END>START_BIG_FILE()) || 00451 (m_seek_free>START_BIG_FILE()) || 00452 (m_seek_info>START_BIG_FILE()) ){ 00453 version += 1000000; 00454 m_units = 8; 00455 } 00456 wbuf wb(m_out,byte_swap(),eob,pos); 00457 if(!wb.write(version)) return false; 00458 if(!wb.write((seek32)m_BEGIN)) return false; 00459 if(version>1000000) { 00460 if(!wb.write(m_END)) return false; 00461 if(!wb.write(m_seek_free)) return false; 00462 } else { 00463 if(!wb.write((seek32)m_END)) return false; 00464 if(!wb.write((seek32)m_seek_free)) return false; 00465 } 00466 if(!wb.write(m_nbytes_free)) return false; 00467 //int nfree = fFreeSegments.size(); 00468 uint32 nfree = 0; //FIXME 00469 if(!wb.write(nfree)) return false; 00470 if(!wb.write(m_nbytes_name)) return false; 00471 if(!wb.write(m_units)) return false; 00472 if(!wb.write(m_compress)) return false; 00473 if(version>1000000) { 00474 if(!wb.write(m_seek_info)) return false; 00475 } else { 00476 if(!wb.write((seek32)m_seek_info)) return false; 00477 } 00478 if(!wb.write(m_nbytes_info)) return false; 00479 if(!set_pos()) return false; //BOF 00480 uint32 nbytes = pos - psave; 00481 //::printf("debug : write_header : %d\n",nbytes); 00482 if(!write_buffer(psave,nbytes)) return false; 00483 if(!synchronize()) return false; 00484 return true; 00485 } 00486 00487 bool write_streamer_infos() { 00488 List<StreamerInfo> sinfos; 00489 fill_infos(sinfos,m_out); 00490 00491 if(sinfos.empty()) return false; 00492 00493 buffer bref(m_out,byte_swap(),256); 00494 00495 if(!sinfos.stream(bref)) { 00496 m_out << "inlib::wroot::file::write_streamer_infos :" 00497 << " cannot stream List<StreamerInfo>." 00498 << std::endl; 00499 return false; 00500 } 00501 uint32 nbytes = bref.length(); 00502 00503 wroot::key key(*this, 00504 m_root_directory.seek_directory(), 00505 "StreamerInfo","", 00506 sinfos.store_cls(), 00507 nbytes); //set m_END 00508 if(!key.seek_key()) return false; 00509 00510 if(!bref.displace_mapped(key.key_length())) return false; 00511 00512 ::memcpy(key.data_buffer(),bref.buf(),nbytes); 00513 00514 //key.set_cycle(1); 00515 if(!key.write_self()) { 00516 m_out << "inlib::wroot::file::write_streamer_infos :" 00517 << " key.write_self() failed." 00518 << std::endl; 00519 return false; 00520 } 00521 00522 m_seek_info = key.seek_key(); 00523 m_nbytes_info = key.number_of_bytes(); 00524 //FIXME sumBuffer(key.objectSize()); 00525 00526 00527 uint32 n; 00528 if(!key.write_file(n)) return false; 00529 if(!n) return false; 00530 00531 return true; 00532 } 00533 00534 static bool zip(std::ostream& a_out, 00535 ifile::zip_func a_func, 00536 int a_level, 00537 uint32 a_srcsize,char* a_src, 00538 uint32 a_tgtsize,char* a_tgt, 00539 uint32& a_irep){ 00540 00541 // from Rio/Bits/R__zip using zlib. 00542 00543 const uint32 HDRSIZE = 9; 00544 00545 if(a_tgtsize<HDRSIZE) { 00546 a_out << "inlib::rroot::directory::zip :" 00547 << " target buffer too small." 00548 << std::endl; 00549 a_irep = 0; 00550 return false; 00551 } 00552 if(a_srcsize>0xffffff) { 00553 a_out << "inlib::rroot::directory::zip :" 00554 << " source buffer too big." 00555 << std::endl; 00556 a_irep = 0; 00557 return false; 00558 } 00559 00560 uint32 out_size; 00561 if(!a_func(a_out,a_level, 00562 a_srcsize,a_src, 00563 a_tgtsize,a_tgt+HDRSIZE, 00564 out_size)) { 00565 a_out << "inlib::rroot::directory::zip :" 00566 << " zipper failed." 00567 << std::endl; 00568 a_irep = 0; 00569 return false; 00570 } 00571 if((HDRSIZE+out_size)>a_tgtsize) { 00572 a_out << "inlib::rroot::directory::zip :" 00573 << " target buffer overflow." 00574 << std::endl; 00575 a_irep = 0; 00576 return false; 00577 } 00578 00579 // HEADER : 00580 a_tgt[0] = 'Z'; // Signature ZLib 00581 a_tgt[1] = 'L'; 00582 a_tgt[2] = 8; //DEFLATE 00583 00584 a_tgt[3] = (char)(out_size & 0xff); 00585 a_tgt[4] = (char)((out_size >> 8) & 0xff); 00586 a_tgt[5] = (char)((out_size >> 16) & 0xff); 00587 00588 a_tgt[6] = (char)(a_srcsize & 0xff); 00589 a_tgt[7] = (char)((a_srcsize >> 8) & 0xff); 00590 a_tgt[8] = (char)((a_srcsize >> 16) & 0xff); 00591 00592 a_irep = HDRSIZE+out_size; 00593 00594 return true; 00595 } 00596 00597 #if defined(__sun) && !defined(__linux__) && (__SUNPRO_CC > 0x420) 00598 int error_number() {return ::errno;} 00599 void reset_error_number() {::errno = 0;} 00600 #else 00601 int error_number() {return errno;} 00602 void reset_error_number() {errno = 0;} 00603 #endif 00604 00605 protected: 00606 std::ostream& m_out; 00607 std::string m_path; 00608 bool m_verbose; 00609 int m_file; 00610 //uint64 m_bytes_write; //Number of bytes write in this file 00611 std::string m_title; //must be before the below. 00612 directory m_root_directory; 00613 std::map<char,zip_func> m_zipers; 00614 // begin of record : 00615 // "root" 00616 uint32 m_version; //File format version 00617 seek m_BEGIN; //First used byte in file 00618 seek m_END; //Last used byte in file 00619 seek m_seek_free; //Location on disk of free segments structure 00620 uint32 m_nbytes_free; //Number of bytes for free segments structure 00621 //int nfree 00622 uint32 m_nbytes_name; //Number of bytes in TNamed at creation time 00623 char m_units; //Number of bytes for file pointers 00624 uint32 m_compress; //(=1 file is compressed, 0 otherwise) 00625 seek m_seek_info; //Location on disk of StreamerInfo record 00626 uint32 m_nbytes_info; //Number of bytes for StreamerInfo record 00627 }; 00628 00629 00630 }} 00631 00632 #endif 00633 00634 //doc 00635 // 00636 // A ROOT file is a suite of consecutive data records with the following 00637 // format (see also the TKey class); 00638 // TKey --------------------- 00639 // byte 1->4 Nbytes = Length of compressed object (in bytes) 00640 // 5->6 Version = TKey version identifier 00641 // 7->10 ObjLen = Length of uncompressed object 00642 // 11->14 Datime = Date and time when object was written to file 00643 // 15->16 KeyLen = Length of the key structure (in bytes) 00644 // 17->18 Cycle = Cycle of key 00645 // 19->22 SeekKey = Pointer to record itself (consistency check) 00646 // 23->26 SeekPdir = Pointer to directory header 00647 // 27->27 lname = Number of bytes in the class name 00648 // 28->.. ClassName = Object Class Name 00649 // ..->.. lname = Number of bytes in the object name 00650 // ..->.. Name = lName bytes with the name of the object 00651 // ..->.. lTitle = Number of bytes in the object title 00652 // ..->.. Title = Title of the object 00653 // -----> DATA = Data bytes associated to the object 00654 // 00655 // The first data record starts at byte fBEGIN (currently set to kBegin) 00656 // Bytes 1->kBegin contain the file description: 00657 // byte 1->4 "root" = Root file identifier 00658 // 5->8 fVersion = File format version 00659 // 9->12 fBEGIN = Pointer to first data record 00660 // 13->16 fEND = Pointer to first free word at the EOF 00661 // 17->20 fSeekFree = Pointer to FREE data record 00662 // 21->24 fNbytesFree = Number of bytes in FREE data record 00663 // 25->28 nfree = Number of free data records 00664 // 29->32 fNbytesName = Number of bytes in TNamed at creation time 00665 // 33->33 fUnits = Number of bytes for file pointers 00666 // 34->37 fCompress = Zip compression level 00667 //