Geant4 Cross Reference

Cross-Referencing   Geant4
Geant4/externals/g4tools/include/tools/rroot/key

Version: [ ReleaseNotes ] [ 1.0 ] [ 1.1 ] [ 2.0 ] [ 3.0 ] [ 3.1 ] [ 3.2 ] [ 4.0 ] [ 4.0.p1 ] [ 4.0.p2 ] [ 4.1 ] [ 4.1.p1 ] [ 5.0 ] [ 5.0.p1 ] [ 5.1 ] [ 5.1.p1 ] [ 5.2 ] [ 5.2.p1 ] [ 5.2.p2 ] [ 6.0 ] [ 6.0.p1 ] [ 6.1 ] [ 6.2 ] [ 6.2.p1 ] [ 6.2.p2 ] [ 7.0 ] [ 7.0.p1 ] [ 7.1 ] [ 7.1.p1 ] [ 8.0 ] [ 8.0.p1 ] [ 8.1 ] [ 8.1.p1 ] [ 8.1.p2 ] [ 8.2 ] [ 8.2.p1 ] [ 8.3 ] [ 8.3.p1 ] [ 8.3.p2 ] [ 9.0 ] [ 9.0.p1 ] [ 9.0.p2 ] [ 9.1 ] [ 9.1.p1 ] [ 9.1.p2 ] [ 9.1.p3 ] [ 9.2 ] [ 9.2.p1 ] [ 9.2.p2 ] [ 9.2.p3 ] [ 9.2.p4 ] [ 9.3 ] [ 9.3.p1 ] [ 9.3.p2 ] [ 9.4 ] [ 9.4.p1 ] [ 9.4.p2 ] [ 9.4.p3 ] [ 9.4.p4 ] [ 9.5 ] [ 9.5.p1 ] [ 9.5.p2 ] [ 9.6 ] [ 9.6.p1 ] [ 9.6.p2 ] [ 9.6.p3 ] [ 9.6.p4 ] [ 10.0 ] [ 10.0.p1 ] [ 10.0.p2 ] [ 10.0.p3 ] [ 10.0.p4 ] [ 10.1 ] [ 10.1.p1 ] [ 10.1.p2 ] [ 10.1.p3 ] [ 10.2 ] [ 10.2.p1 ] [ 10.2.p2 ] [ 10.2.p3 ] [ 10.3 ] [ 10.3.p1 ] [ 10.3.p2 ] [ 10.3.p3 ] [ 10.4 ] [ 10.4.p1 ] [ 10.4.p2 ] [ 10.4.p3 ] [ 10.5 ] [ 10.5.p1 ] [ 10.6 ] [ 10.6.p1 ] [ 10.6.p2 ] [ 10.6.p3 ] [ 10.7 ] [ 10.7.p1 ] [ 10.7.p2 ] [ 10.7.p3 ] [ 10.7.p4 ] [ 11.0 ] [ 11.0.p1 ] [ 11.0.p2 ] [ 11.0.p3, ] [ 11.0.p4 ] [ 11.1 ] [ 11.1.1 ] [ 11.1.2 ] [ 11.1.3 ] [ 11.2 ] [ 11.2.1 ] [ 11.2.2 ] [ 11.3.0 ]

  1 // Copyright (C) 2010, Guy Barrand. All rights reserved.
  2 // See the file tools.license for terms.
  3 
  4 #ifndef tools_rroot_key
  5 #define tools_rroot_key
  6 
  7 #include "rbuf"
  8 #include "seek"
  9 #include "date"
 10 #include "ifile"
 11 #include "../sout"
 12 
 13 #ifdef TOOLS_MEM
 14 #include "../mem"
 15 #endif
 16 
 17 #include <map>
 18 #include <ostream>
 19 #include <cstring> //memcpy
 20 
 21 //#include <zlib.h>
 22 
 23 #ifdef TOOLS_USE_CSZ
 24 // CSZ code where used as default compressor in old CERN-ROOT, then it may be needed
 25 // to read old file (as the pawdemo.root one).
 26 //FIXME : arrange to have csz coming from the "outside" (as zip).
 27 extern "C" {
 28   void csz__Init_Inflate(long,unsigned char*,long,unsigned char*);
 29   int csz__Inflate();
 30   unsigned char* csz__obufptr();
 31 }
 32 #endif
 33 
 34 namespace tools {
 35 namespace rroot {
 36 
 37 class key {
 38   static uint32 class_version() {return 2;}
 39 public:
 40   static uint32 std_string_record_size(const std::string& x) {
 41     // Returns size string will occupy on I/O buffer.
 42     if (x.size() > 254)
 43       return uint32(x.size()+sizeof(unsigned char)+sizeof(int));
 44     else
 45       return uint32(x.size()+sizeof(unsigned char));
 46   }
 47 
 48 public:
 49   key(std::ostream& a_out)
 50   :m_out(a_out)
 51   ,m_buf_size(0)
 52   ,m_buffer(0)
 53   // Record :
 54   ,m_nbytes(0)
 55   ,m_version(class_version())
 56   ,m_object_size(0)
 57   ,m_date(0)
 58   ,m_key_length(0)
 59   ,m_cycle(0)
 60   ,m_seek_key(0)
 61   ,m_seek_parent_dir(0)
 62   //,m_object_class
 63   //,m_object_name
 64   //,m_object_title
 65   {
 66 #ifdef TOOLS_MEM
 67     mem::increment(s_class().c_str());
 68 #endif
 69     m_key_length = record_size(m_version);
 70     //fDate.setDate(0);
 71   }
 72 
 73   key(std::ostream& a_out,seek a_pos,uint32 a_nbytes)
 74   :m_out(a_out)
 75   ,m_buf_size(0)
 76   ,m_buffer(0)
 77   // Record :
 78   ,m_nbytes(a_nbytes) //key len + compressed object size
 79   ,m_version(class_version())
 80   ,m_object_size(0)
 81   ,m_date(0)
 82   ,m_key_length(0)
 83   ,m_cycle(0)
 84   ,m_seek_key(a_pos)
 85   ,m_seek_parent_dir(0)
 86   //,m_object_class
 87   //,m_object_name
 88   //,m_object_title
 89   {
 90 #ifdef TOOLS_MEM
 91     mem::increment(s_class().c_str());
 92 #endif
 93     if(a_pos>START_BIG_FILE) m_version += big_file_version_tag();
 94     m_buffer = new char[a_nbytes];
 95     if(!m_buffer) {
 96       m_out << "tools::rroot::key::key(cpcstor) :"
 97                    << " can't alloc " << a_nbytes << "."
 98                    << std::endl;
 99     } else {
100       m_buf_size = a_nbytes;
101     }
102   }
103   virtual ~key(){
104     delete [] m_buffer;
105 #ifdef TOOLS_MEM
106     mem::decrement(s_class().c_str());
107 #endif
108   }
109 protected:
110   key(const key& a_from)
111   :m_out(a_from.m_out)
112   ,m_buf_size(0)
113   ,m_buffer(0)
114   ,m_nbytes(a_from.m_nbytes)
115   ,m_version(a_from.m_version)
116   ,m_object_size(a_from.m_object_size)
117   ,m_date(a_from.m_date)
118   ,m_key_length(a_from.m_key_length)
119   ,m_cycle(a_from.m_cycle)
120   ,m_seek_key(a_from.m_seek_key)
121   ,m_seek_parent_dir(a_from.m_seek_parent_dir)
122   ,m_object_class(a_from.m_object_class)
123   ,m_object_name(a_from.m_object_name)
124   ,m_object_title(a_from.m_object_title)
125   {
126 #ifdef TOOLS_MEM
127     mem::increment(s_class().c_str());
128 #endif
129     if(a_from.m_buf_size && a_from.m_buffer) {
130       m_buffer = new char[a_from.m_buf_size];
131       if(!m_buffer) {
132         m_out << "tools::rroot::key::key(cpcstor) :"
133                      << " can't alloc " << a_from.m_buf_size << "."
134                      << std::endl;
135       } else {
136         m_buf_size = a_from.m_buf_size;
137         ::memcpy(m_buffer,a_from.m_buffer,a_from.m_buf_size);
138       }
139     }
140   }
141 public:
142   key& operator=(const key& a_from){
143     if(&a_from==this) return *this;
144     m_nbytes = a_from.m_nbytes;
145     m_version = a_from.m_version;
146     m_object_size = a_from.m_object_size;
147     m_date = a_from.m_date;
148     m_key_length = a_from.m_key_length;
149     m_cycle = a_from.m_cycle;
150     m_seek_key = a_from.m_seek_key;
151     m_seek_parent_dir = a_from.m_seek_parent_dir;
152     m_object_class = a_from.m_object_class;
153     m_object_name = a_from.m_object_name;
154     m_object_title = a_from.m_object_title;
155 
156     delete [] m_buffer;
157     m_buffer = 0;
158     m_buf_size = 0;
159 
160     if(a_from.m_buf_size && a_from.m_buffer) {
161       m_buffer = new char[a_from.m_buf_size];
162       if(!m_buffer) {
163         m_out << "tools::rroot::key::operator=() :"
164                      << " can't alloc " << a_from.m_buf_size << "."
165                      << std::endl;
166       } else {
167         m_buf_size = a_from.m_buf_size;
168         ::memcpy(m_buffer,a_from.m_buffer,a_from.m_buf_size);
169       }
170     }
171 
172     return *this;
173   }
174 
175 public:
176   std::ostream& out() const {return m_out;}
177 
178   uint32 nbytes() const {return m_nbytes;}
179   seek seek_key() const {return m_seek_key;}
180   uint32 object_size() const {return m_object_size;}
181 
182   const std::string& object_name() const {return m_object_name;}
183   const std::string& object_title() const {return m_object_title;}
184   const std::string& object_class() const {return m_object_class;}
185 
186   bool read_file(ifile& a_file){
187     // Read the key structure from the file.
188     if(!a_file.set_pos(m_seek_key)) return false;
189     if(!a_file.read_buffer(m_buffer,m_nbytes)) return false;
190     if(a_file.verbose()) {
191       m_out << "tools::rroot::key::read_file :"
192                    << " reading " << m_nbytes << " bytes"
193                    << " at position " << m_seek_key
194                    << "."
195                    << std::endl;
196     }
197     return true;
198   }
199 
200   char* buf() const {return m_buffer;}
201   char* data_buffer() const {return m_buffer + m_key_length;}
202   const char* eob() const {return m_buffer + m_buf_size;}
203   uint32 buf_size() const {return m_buf_size;}
204   uint32 key_length() const {return m_key_length;}
205 
206   bool from_buffer(bool a_byte_swap,const char* aEOB,char*& a_pos,bool a_verbose) {
207     rbuf rb(m_out,a_byte_swap,aEOB,a_pos);
208     int _nbytes;
209     if(!rb.read(_nbytes)) return false;
210 /*
211     if(m_nbytes) {
212       if(_nbytes!=int(m_nbytes)) {
213         out << "tools::rroot::key::from_buffer :"
214             << " nbytes not consistent."
215             << " read " << _nbytes
216             << ", expected " << m_nbytes
217             << ". Continue with " << _nbytes
218             << std::endl;
219         m_nbytes = _nbytes;
220       }
221     } else {
222 */
223     m_nbytes = _nbytes;
224     //}
225     short version;
226     if(!rb.read(version)) return false;
227     m_version = version;
228    {int v;
229     if(!rb.read(v)) return false;
230     m_object_size = v;}
231     unsigned int _date;
232     if(!rb.read(_date)) return false;
233     //fDate.setDate(_date);
234    {short v;
235     if(!rb.read(v)) return false;
236     m_key_length = v;}
237    {short v;
238     if(!rb.read(v)) return false;
239     m_cycle = v;}
240     if(version>(short)big_file_version_tag()) {
241       if(!rb.read(m_seek_key)) return false;
242       if(!rb.read(m_seek_parent_dir)) return false;
243     } else {
244      {seek32 i;
245       if(!rb.read(i)) return false;
246       m_seek_key = i;}
247      {seek32 i;
248       if(!rb.read(i)) return false;
249       m_seek_parent_dir = i;}
250     }
251     if(!rb.read(m_object_class)) return false;
252     if(!rb.read(m_object_name)) return false;
253     if(!rb.read(m_object_title)) return false;
254     if(a_verbose) {
255       m_out << "tools::rroot::key::from_buffer :"
256                    << " nbytes : " << m_nbytes
257                    << ", object class : " << sout(m_object_class)
258                    << ", object name : " << sout(m_object_name)
259                    << ", object title : " << sout(m_object_title)
260                    << ", object size : " << m_object_size
261                    << "."
262                    << std::endl;
263     }
264     return true;
265   }
266 
267   char* get_object_buffer(ifile& a_file,uint32& a_size) {
268     if(!m_key_length) {
269       m_out << "tools::rroot::key::get_object_buffer :"
270                    << " WARNING : m_key_length is zero."
271                    << std::endl;
272       //delete [] m_buffer;
273       //m_buffer = 0;
274       //m_buf_size = 0;
275       //a_size = 0;
276       //return 0;
277     }
278     if(!m_nbytes) {
279       m_out << "tools::rroot::key::get_object_buffer :"
280                    << " m_nbytes is zero."
281                    << std::endl;
282       delete [] m_buffer;
283       m_buffer = 0;
284       m_buf_size = 0;
285       a_size = 0;
286       return 0;
287     }
288     if(!m_object_size) {
289       m_out << "tools::rroot::key::get_object_buffer :"
290                    << " WARNING : m_object_size is zero."
291                    << std::endl;
292     }
293 
294     if(a_file.verbose()) {
295       m_out << "tools::rroot::key::get_object_buffer :"
296                    << " m_nbytes : " << m_nbytes
297                    << " m_key_length : " << m_key_length
298                    << " m_object_size : " << m_object_size << "."
299                    << " m_seek_key : " << m_seek_key << "."
300                    << std::endl;
301     }
302 
303     if(m_object_size <= (m_nbytes-m_key_length)) {
304       delete [] m_buffer;
305       m_buf_size = m_key_length+m_object_size;
306       if(m_buf_size<m_nbytes) {
307         m_out << "tools::rroot::key::get_object_buffer :"
308                      << " WARNING : m_buf_size<m_nbytes."
309                      << " m_buf_size " << m_buf_size
310                      << " m_nbytes " << m_nbytes
311                      << ". Raise m_buf_size to " << m_nbytes << "."
312                      << std::endl;
313         m_buf_size = m_nbytes; //for read_file()
314       }
315       m_buffer = new char[m_buf_size];
316       if(!m_buffer) {
317         m_out << "tools::rroot::key::get_object_buffer :"
318                      << " can't alloc " << m_buf_size
319                      << std::endl;
320         m_buffer = 0;
321         m_buf_size = 0;
322         a_size = 0;
323         return 0;
324       }
325 
326       if(!read_file(a_file)) {
327         delete [] m_buffer;
328         m_buffer = 0;
329         m_buf_size = 0;
330         a_size = 0;
331         return 0;
332       }
333 
334     } else {
335       // have to decompress. Need a second buffer.
336 
337       uint32 decsiz = m_key_length+m_object_size;
338       char* decbuf = new char[decsiz];
339       if(!decbuf) {
340         m_out << "tools::rroot::key::get_object_buffer :"
341                      << " can't alloc " << decsiz
342                      << std::endl;
343         a_size = 0;
344         return 0;
345       }
346 
347       delete [] m_buffer;
348       m_buffer = new char[m_nbytes];
349       m_buf_size = m_nbytes;
350       if(!read_file(a_file)) {
351         delete [] decbuf;
352         decbuf = 0;
353         delete [] m_buffer;
354         m_buffer = 0;
355         m_buf_size = 0;
356         a_size = 0;
357         return 0;
358       }
359 
360       ::memcpy(decbuf,m_buffer,m_key_length);
361 
362       // decompress :
363       unsigned char* objbuf = (unsigned char*)(decbuf+m_key_length);
364       unsigned char* bufcur = (unsigned char*)(m_buffer+m_key_length);
365       int nout = 0;
366       uint32 noutot = 0;
367       while(true) {
368         int nin = 9 + ((int)bufcur[3] | ((int)bufcur[4] << 8) | ((int)bufcur[5] << 16));
369         int nbuf = (int)bufcur[6] | ((int)bufcur[7] << 8) | ((int)bufcur[8] << 16);
370         if(!unzip(m_out,a_file,nin,bufcur,nbuf,objbuf,nout)) break;
371         if(!nout) break;
372         noutot += nout;
373         if(noutot >= m_object_size) break;
374         bufcur += nin;
375         objbuf += nout;
376       }
377 
378       delete [] m_buffer;
379       m_buffer = 0;
380       m_buf_size = 0;
381 
382       if(!noutot) {
383         m_out << "tools::rroot::key::get_object_buffer :"
384                      << " nothing from decompression."
385                      << std::endl;
386         delete [] decbuf;
387         decbuf = 0;
388         a_size = 0;
389         return 0;
390       }
391       if(noutot!=m_object_size) {
392         m_out << "tools::rroot::key::get_object_buffer :"
393                      << " decompression mismatch."
394                      << " noutot " << noutot
395                      << " m_object_size " << m_object_size
396                      << std::endl;
397         delete [] decbuf;
398         decbuf = 0;
399         a_size = 0;
400         return 0;
401       }
402 
403       m_buffer = decbuf;
404       m_buf_size = decsiz;
405 
406     }
407     a_size = m_object_size;
408     return m_buffer+m_key_length;
409   }
410   //NOTE : print is a Python keyword.
411   void dump(std::ostream& a_out) const {
412     a_out << "class : " << sout(m_object_class)
413           << ", name : " << sout(m_object_name)
414           << ", title : " << sout(m_object_title)
415           << ", size : " << m_object_size
416           << "."
417           << std::endl;
418   }
419 
420 protected:
421   static const uint32 START_BIG_FILE = 2000000000;
422 protected:
423   uint32 record_size(uint32 a_version) const {
424     // Return the size in bytes of the key header structure.
425     uint32 _nbytes = sizeof(m_nbytes);
426     _nbytes += sizeof(short);         //2
427     _nbytes += sizeof(m_object_size);
428     _nbytes += sizeof(date);
429     _nbytes += sizeof(m_key_length);
430     _nbytes += sizeof(m_cycle);       //2+4*4=18
431     if(a_version>big_file_version_tag()) {
432       _nbytes += sizeof(seek);
433       _nbytes += sizeof(seek);        //18+2*8=34
434     } else {
435       _nbytes += sizeof(seek32);
436       _nbytes += sizeof(seek32);      //18+2*4=26
437     }
438     _nbytes += std_string_record_size(m_object_class);
439     _nbytes += std_string_record_size(m_object_name);
440     _nbytes += std_string_record_size(m_object_title);
441     //::printf("debug : record_size %d\n",_nbytes);
442     return _nbytes;
443   }
444 
445   bool unzip(std::ostream& a_out,ifile& a_file,
446              int a_srcsize,unsigned char* a_src,int a_tgtsize,unsigned char* a_tgt,int& a_irep) {
447 
448     // Author: E.Chernyaev (IHEP/Protvino)
449     // Input: scrsize - size of input buffer
450     //       src     - input buffer
451     //       tgtsize - size of target buffer
452     //
453     // Output: tgt - target buffer (decompressed)
454     //        irep - size of decompressed data
455     //        0 - if error
456 
457     a_irep = 0;
458 
459     // C H E C K   H E A D E R
460     const int HDRSIZE = 9;
461 
462     if (a_srcsize < HDRSIZE) {
463       a_out << "tools::rroot::key::unzip : too small source" << std::endl;
464       return false;
465     }
466 
467     unsigned char DEFLATE = 8;
468 
469     if ((a_src[0] != 'C' && a_src[0] != 'Z') ||
470         (a_src[1] != 'S' && a_src[1] != 'L') ||
471         a_src[2] != DEFLATE) {
472       a_out << "tools::rroot::key::unzip : error in header" << std::endl;
473       return false;
474     }
475 
476     long _ibufcnt = (long)a_src[3] | ((long)a_src[4] << 8) | ((long)a_src[5] << 16);
477     long isize = (long)a_src[6] | ((long)a_src[7] << 8) | ((long)a_src[8] << 16);
478 
479     if(a_tgtsize<isize) {
480       a_out << "tools::rroot::key::unzip : too small target." << std::endl;
481       return false;
482     }
483 
484     if(_ibufcnt + HDRSIZE != a_srcsize) {
485       a_out << "tools::rroot::key::unzip :"
486             << " discrepancy in source length." << std::endl;
487       return false;
488     }
489 
490     // D E C O M P R E S S   D A T A
491 
492     if (a_src[0] == 'Z' && a_src[1] == 'L') { //compressed with zlib.
493       decompress_func func;
494       if(!a_file.unziper('Z',func)) {
495         a_out << "tools::rroot::key::unzip : "
496               << " zlib unziper not found." << std::endl;
497         return false;
498       }
499 
500       unsigned int irep;
501       char* src = (char*)(a_src + HDRSIZE);
502       if(!func(a_out,
503                (unsigned int)a_srcsize,src,
504                (unsigned int)a_tgtsize,(char*)a_tgt,irep)) {
505         a_out << "tools::rroot::key::unzip : "
506               << " unzip function failed." << std::endl;
507         a_irep = 0;
508         return false;
509       }
510       a_irep = irep;
511 
512 #ifdef TOOLS_USE_CSZ
513     } else if (a_src[0] == 'C' && a_src[1] == 'S') {
514       //compressed with Chernyaev & Smirnov
515 
516       csz__Init_Inflate(_ibufcnt,a_src + HDRSIZE,a_tgtsize,a_tgt);
517 
518       if (csz__Inflate()) {
519         a_out << "tools::rroot::key::unzip :"
520               << " error during decompression." << std::endl;
521         return false;
522       }
523 
524       unsigned char* obufptr = csz__obufptr();
525 
526       // if (obufptr - a_tgt != isize) {
527       // There are some rare cases when a few more bytes are required
528       if (obufptr - a_tgt > a_tgtsize) {
529         a_out << "tools::rroot::key::_unzip :"
530               << " discrepancy " << (int)(obufptr - a_tgt)
531               << " with initial size: " << (int)isize
532               << ", tgtsize= " << a_tgtsize
533               << std::endl;
534         a_irep = int(obufptr - a_tgt);
535         //return false;
536       }
537       a_irep = isize;
538 
539       //a_out << "tools::rroot::key::unzip : CS : ok "
540       //        << a_irep << std::endl;
541 #endif
542     } else {
543       a_out << "tools::rroot::key::_unzip : unknown a_src[0,1]."
544             << " [0] = " << a_src[0] << ", [1] = " << a_src[1]
545             << std::endl;
546       a_irep = 0;
547       return false;
548     }
549     return true;
550   }
551 
552   static const std::string& s_class() {
553     static const std::string s_v("tools::rroot::key");
554     return s_v;
555   }
556 protected:
557   std::ostream& m_out;
558   uint32 m_buf_size;
559   char* m_buffer;
560   // Record (stored in file) :
561   uint32 m_nbytes;               //Number of bytes for the object on file
562   uint32 m_version;              //Key version identifier
563   uint32 m_object_size;          //Length of uncompressed object in bytes
564   date m_date;                //Date/Time of insertion in file
565   uint16 m_key_length;         //Number of bytes for the key itself
566   uint16 m_cycle;              //Cycle number
567   seek m_seek_key;            //Location of object on file
568   seek m_seek_parent_dir;     //Location of parent directory on file
569   std::string m_object_class; //Object Class name.
570   std::string m_object_name;  //name of the object.
571   std::string m_object_title; //title of the object.
572 };
573 
574 }}
575 
576 #endif
577 
578 //doc :
579 //////////////////////////////////////////////////////////////////////////
580 //                                                                      //
581 //  The Key class includes functions to book space on a file,           //
582 //   to create I/O buffers, to fill these buffers                       //
583 //   to compress/uncompress data buffers.                               //
584 //                                                                      //
585 //  Before saving (making persistent) an object on a file, a key must   //
586 //  be created. The key structure contains all the information to       //
587 //  uniquely identify a persistent object on a file.                    //
588 //     fNbytes    = number of bytes for the compressed object+key       //
589 //     version of the Key class                                         //
590 //     fObjlen    = Length of uncompressed object                       //
591 //     fDatime    = Date/Time when the object was written               //
592 //     fKeylen    = number of bytes for the key structure               //
593 //     fCycle     = cycle number of the object                          //
594 //     fSeekKey   = Address of the object on file (points to fNbytes)   //
595 //                  This is a redundant information used to cross-check //
596 //                  the data base integrity.                            //
597 //     fSeekPdir  = Pointer to the directory supporting this object     //
598 //     fClassName = Object class name                                   //
599 //     fName      = name of the object                                  //
600 //     fTitle     = title of the object                                 //
601 //                                                                      //
602 //  The Key class is used by ROOT to:                                   //
603 //    - to write an object in the Current Directory                     //
604 //    - to write a new ntuple buffer                                    //
605 //                                                                      //
606 //////////////////////////////////////////////////////////////////////////