Panda3D

subStreamBuf.cxx

00001 // Filename: subStreamBuf.cxx
00002 // Created by:  drose (02Aug02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "subStreamBuf.h"
00016 #include "pnotify.h"
00017 
00018 #ifndef HAVE_STREAMSIZE
00019 // Some compilers (notably SGI) don't define this for us
00020 typedef int streamsize;
00021 #endif /* HAVE_STREAMSIZE */
00022 
00023 static const size_t substream_buffer_size = 4096;
00024 
00025 ////////////////////////////////////////////////////////////////////
00026 //     Function: SubStreamBuf::Constructor
00027 //       Access: Public
00028 //  Description:
00029 ////////////////////////////////////////////////////////////////////
00030 SubStreamBuf::
00031 SubStreamBuf() {
00032   _source = (IStreamWrapper *)NULL;
00033   _dest = (OStreamWrapper *)NULL;
00034 
00035   // _start is the streampos of the first byte of the SubStream within
00036   // its parent stream.
00037   _start = 0;
00038 
00039   // _end is the streampos of the byte following the last byte of the
00040   // SubStream within its parent stream.  If _end is 0, the SubStream
00041   // continues to the end of the parent stream, wherever that is.
00042   _end = 0;
00043 
00044   // _gpos is the streampos of the end of the read buffer (that is,
00045   // egptr()) within the parent stream.  By comparing _gpos to gpos(),
00046   // we can determine the actual current file position.  _ppos is the
00047   // similar pos, for the write pointer.
00048   _gpos = 0;
00049   _ppos = 0;
00050 
00051 #ifdef PHAVE_IOSTREAM
00052   _buffer = (char *)PANDA_MALLOC_ARRAY(substream_buffer_size * 2);
00053   char *ebuf = _buffer + substream_buffer_size * 2;
00054   char *mbuf = _buffer + substream_buffer_size;
00055   setg(_buffer, mbuf, mbuf);
00056   setp(mbuf, ebuf);
00057 
00058 #else
00059   allocate();
00060   // Chop the buffer in half.  The bottom half goes to the get buffer;
00061   // the top half goes to the put buffer.
00062   char *b = base();
00063   char *t = ebuf();
00064   char *m = b + (t - b) / 2;
00065   setg(b, m, m);
00066   setp(b, m);
00067 #endif
00068 }
00069 
00070 ////////////////////////////////////////////////////////////////////
00071 //     Function: SubStreamBuf::Destructor
00072 //       Access: Public, Virtual
00073 //  Description:
00074 ////////////////////////////////////////////////////////////////////
00075 SubStreamBuf::
00076 ~SubStreamBuf() {
00077   close();
00078 #ifdef PHAVE_IOSTREAM
00079   PANDA_FREE_ARRAY(_buffer);
00080 #endif
00081 }
00082 
00083 ////////////////////////////////////////////////////////////////////
00084 //     Function: SubStreamBuf::open
00085 //       Access: Public
00086 //  Description:
00087 ////////////////////////////////////////////////////////////////////
00088 void SubStreamBuf::
00089 open(IStreamWrapper *source, OStreamWrapper *dest, streampos start, streampos end, bool append) {
00090   _source = source;
00091   _dest = dest;
00092   _start = start;
00093   _end = end;
00094   _append = append;
00095   _gpos = _start;
00096   _ppos = _start;
00097 }
00098 
00099 ////////////////////////////////////////////////////////////////////
00100 //     Function: SubStreamBuf::close
00101 //       Access: Public
00102 //  Description:
00103 ////////////////////////////////////////////////////////////////////
00104 void SubStreamBuf::
00105 close() {
00106   // Make sure the write buffer is flushed.
00107   sync();
00108 
00109   _source = (IStreamWrapper *)NULL;
00110   _dest = (OStreamWrapper *)NULL;
00111   _start = 0;
00112   _end = 0;
00113 
00114   _gpos = 0;
00115   _ppos = 0;
00116 
00117   pbump(pbase() - pptr());
00118   gbump(egptr() - gptr());
00119 }
00120 
00121 ////////////////////////////////////////////////////////////////////
00122 //     Function: SubStreamBuf::seekoff
00123 //       Access: Public, Virtual
00124 //  Description: Implements seeking within the stream.
00125 ////////////////////////////////////////////////////////////////////
00126 streampos SubStreamBuf::
00127 seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
00128   streampos result = -1;
00129 
00130   // Sync the iostream buffer first.
00131   sync();
00132 
00133   if (which & ios::in) {
00134     // Determine the current file position.
00135     size_t n = egptr() - gptr();
00136     gbump(n);
00137     _gpos -= n;
00138     nassertr(_gpos >= 0, EOF);
00139     streampos cur_pos = _gpos;
00140     streampos new_pos = cur_pos;
00141     
00142     // Now adjust the data pointer appropriately.
00143     switch (dir) {
00144     case ios::beg:
00145       new_pos = (streampos)off + _start;
00146       break;
00147       
00148     case ios::cur:
00149       new_pos = (streampos)((streamoff)cur_pos + off);
00150       break;
00151       
00152     case ios::end:
00153       if (_end == (streampos)0) {
00154         // If the end of the file is unspecified, we have to seek to
00155         // find it.
00156         new_pos = _source->seek_gpos_eof() + off;
00157         
00158       } else {
00159         new_pos = _end + off;
00160       }
00161       break;
00162       
00163     default:
00164       // Shouldn't get here.
00165       break;
00166     }
00167 
00168     if (new_pos < _start) {
00169       // Can't seek before beginning of file.
00170       return EOF;
00171     }
00172 
00173     if (_end != (streampos)0 && new_pos > _end) {
00174       // Can't seek past end of file.
00175       return EOF;
00176     }
00177 
00178     _gpos = new_pos;
00179     nassertr(_gpos >= 0, EOF);
00180     result = new_pos - _start;
00181   }
00182 
00183   if (which & ios::out) {
00184     // Determine the current file position.
00185     size_t n = pptr() - pbase();
00186     streampos cur_pos = _ppos + (streamoff)n;
00187     streampos new_pos = cur_pos;
00188     
00189     // Now adjust the data pointer appropriately.
00190     switch (dir) {
00191     case ios::beg:
00192       new_pos = (streampos)off + _start;
00193       break;
00194       
00195     case ios::cur:
00196       new_pos = (streampos)((streamoff)cur_pos + off);
00197       break;
00198       
00199     case ios::end:
00200       if (_end == (streampos)0) {
00201         // If the end of the file is unspecified, we have to seek to
00202         // find it.
00203         new_pos = _dest->seek_ppos_eof() + off;
00204         
00205       } else {
00206         new_pos = _end + off;
00207       }
00208       break;
00209 
00210     default:
00211       // Shouldn't get here.
00212       break;
00213     }
00214 
00215     if (new_pos < _start) {
00216       // Can't seek before beginning of file.
00217       return EOF;
00218     }
00219 
00220     if (_end != (streampos)0 && new_pos > _end) {
00221       // Can't seek past end of file.
00222       return EOF;
00223     }
00224 
00225     _ppos = new_pos;
00226     nassertr(_ppos >= 0, EOF);
00227     result = new_pos - _start;
00228   }
00229 
00230   return result;
00231 }
00232 
00233 ////////////////////////////////////////////////////////////////////
00234 //     Function: SubStreamBuf::seekpos
00235 //       Access: Public, Virtual
00236 //  Description: A variant on seekoff() to implement seeking within a
00237 //               stream.
00238 //
00239 //               The MSDN Library claims that it is only necessary to
00240 //               redefine seekoff(), and not seekpos() as well, as the
00241 //               default implementation of seekpos() is supposed to
00242 //               map to seekoff() exactly as I am doing here; but in
00243 //               fact it must do something else, because seeking
00244 //               didn't work on Windows until I redefined this
00245 //               function as well.
00246 ////////////////////////////////////////////////////////////////////
00247 streampos SubStreamBuf::
00248 seekpos(streampos pos, ios_openmode which) {
00249   return seekoff(pos, ios::beg, which);
00250 }
00251 
00252 ////////////////////////////////////////////////////////////////////
00253 //     Function: SubStreamBuf::overflow
00254 //       Access: Protected, Virtual
00255 //  Description: Called by the system ostream implementation when its
00256 //               internal buffer is filled, plus one character.
00257 ////////////////////////////////////////////////////////////////////
00258 int SubStreamBuf::
00259 overflow(int ch) {
00260   bool okflag = true;
00261 
00262   size_t n = pptr() - pbase();
00263   if (n != 0) {
00264     if (_end != (streampos)0 && _ppos + (streampos)n > _end) {
00265       // Don't allow reading past the end of the file.
00266       n = (size_t)(_end - _ppos);
00267       if (n == 0) {
00268         // No room.
00269         return EOF;
00270       }
00271     }
00272 
00273     nassertr(_dest != NULL, EOF); 
00274     bool fail = false;
00275     if (_append) { 
00276       _dest->seek_eof_write(pbase(), n, fail);
00277     } else {
00278       _dest->seek_write(_ppos, pbase(), n, fail);
00279     }
00280     _ppos += n;
00281     pbump(-(int)n);
00282     if (fail) {
00283       okflag = false;
00284     }
00285   }
00286 
00287   if (okflag && ch != EOF) {
00288     if (pptr() != epptr()) {
00289       // Store the extra character back in the buffer.
00290       *(pptr()) = ch;
00291       pbump(1);
00292     } else {
00293       // No room to store ch.
00294       okflag = false;
00295     }
00296   }
00297 
00298   if (!okflag) {
00299     return EOF;
00300   }
00301   return 0;
00302 }
00303 
00304 ////////////////////////////////////////////////////////////////////
00305 //     Function: SubStreamBuf::sync
00306 //       Access: Protected, Virtual
00307 //  Description: Called by the system iostream implementation to
00308 //               implement a flush operation.
00309 ////////////////////////////////////////////////////////////////////
00310 int SubStreamBuf::
00311 sync() {
00312   size_t n = pptr() - pbase();
00313 
00314   if (n != 0) {
00315     nassertr(_dest != NULL, EOF); 
00316     bool fail = false;
00317     if (_append) { 
00318       _dest->seek_eof_write(pbase(), n, fail);
00319     } else {
00320       _dest->seek_write(_ppos, pbase(), n, fail);
00321     }
00322     _ppos += n;
00323     pbump(-(int)n);
00324     
00325     if (fail) {
00326       return EOF;
00327     }
00328   }
00329 
00330   return 0;
00331 }
00332 
00333 ////////////////////////////////////////////////////////////////////
00334 //     Function: SubStreamBuf::underflow
00335 //       Access: Protected, Virtual
00336 //  Description: Called by the system istream implementation when its
00337 //               internal buffer needs more characters.
00338 ////////////////////////////////////////////////////////////////////
00339 int SubStreamBuf::
00340 underflow() {
00341   // Sometimes underflow() is called even if the buffer is not empty.
00342   if (gptr() >= egptr()) {
00343     sync();
00344 
00345     // Mark the buffer filled (with buffer_size bytes).
00346     size_t buffer_size = egptr() - eback();
00347     gbump(-(int)buffer_size);
00348 
00349     streamsize num_bytes = buffer_size;
00350     if (_end != (streampos)0 && _gpos + (streampos)num_bytes > _end) {
00351       // Don't allow reading past the end of the file.
00352       streamsize new_num_bytes = _end - _gpos;
00353       if (new_num_bytes == 0) {
00354         gbump(buffer_size);
00355         return EOF;
00356       }
00357 
00358       // We won't be filling the entire buffer.  Fill in only at the
00359       // end of the buffer.
00360       size_t delta = num_bytes - new_num_bytes;
00361       gbump(delta);
00362       num_bytes = new_num_bytes;
00363       nassertr(egptr() - gptr() == num_bytes, EOF);
00364     }
00365       
00366     nassertr(_source != NULL, EOF);
00367     streamsize read_count;
00368     bool eof;
00369     _source->seek_read(_gpos, gptr(), num_bytes, read_count, eof);
00370     _gpos += read_count;
00371 
00372     if (read_count != num_bytes) {
00373       // Oops, we didn't read what we thought we would.
00374       if (read_count == 0) {
00375         gbump(num_bytes);
00376         return EOF;
00377       }
00378 
00379       // Slide what we did read to the top of the buffer.
00380       nassertr(read_count < num_bytes, EOF);
00381       size_t delta = num_bytes - read_count;
00382       memmove(gptr() + delta, gptr(), read_count);
00383       gbump(delta);
00384     }
00385   }
00386 
00387   return (unsigned char)*gptr();
00388 }
 All Classes Functions Variables Enumerations