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 ////////////////////////////////////////////////////////////////////
00024 //     Function: SubStreamBuf::Constructor
00025 //       Access: Public
00026 //  Description:
00027 ////////////////////////////////////////////////////////////////////
00028 SubStreamBuf::
00029 SubStreamBuf() {
00030   _source = (IStreamWrapper *)NULL;
00031 
00032   // _start is the streampos of the first byte of the SubStream within
00033   // its parent stream.
00034   _start = 0;
00035 
00036   // _end is the streampos of the byte following the last byte of the
00037   // SubStream within its parent stream.  If _end is 0, the SubStream
00038   // continues to the end of the parent stream, wherever that is.
00039   _end = 0;
00040 
00041   // _cur is the streampos of the end of the read buffer (that is,
00042   // egptr()) within the parent stream.  By comparing _cur to gpos(),
00043   // we can determine the actual current file position.
00044   _cur = 0;
00045 
00046   // _unused counts the number of bytes at the beginning of the buffer
00047   // that are unused.  Usually this is 0 after a read, but when we
00048   // reach the end of the file we might not need the whole buffer to
00049   // read the last bit, so the first part of the buffer is unused.
00050   // This is important to prevent us from inadvertently seeking into
00051   // the unused part of the buffer.
00052   _unused = 0;
00053 
00054 #ifdef PHAVE_IOSTREAM
00055   // The new-style iostream library doesn't seem to support allocate().
00056   _buffer = (char *)PANDA_MALLOC_ARRAY(4096);
00057   char *ebuf = _buffer + 4096;
00058   setg(_buffer, ebuf, ebuf);
00059 
00060 #else
00061   allocate();
00062   setg(base(), ebuf(), ebuf());
00063 #endif
00064 }
00065 
00066 ////////////////////////////////////////////////////////////////////
00067 //     Function: SubStreamBuf::Destructor
00068 //       Access: Public, Virtual
00069 //  Description:
00070 ////////////////////////////////////////////////////////////////////
00071 SubStreamBuf::
00072 ~SubStreamBuf() {
00073   close();
00074 #ifdef PHAVE_IOSTREAM
00075   PANDA_FREE_ARRAY(_buffer);
00076 #endif
00077 }
00078 
00079 ////////////////////////////////////////////////////////////////////
00080 //     Function: SubStreamBuf::open
00081 //       Access: Public
00082 //  Description:
00083 ////////////////////////////////////////////////////////////////////
00084 void SubStreamBuf::
00085 open(IStreamWrapper *source, streampos start, streampos end) {
00086   _source = source;
00087   _start = start;
00088   _end = end;
00089   _cur = _start;
00090 
00091   // Initially, the entire buffer is unused.  Duh.
00092   _unused = egptr() - eback();
00093 }
00094 
00095 ////////////////////////////////////////////////////////////////////
00096 //     Function: SubStreamBuf::close
00097 //       Access: Public
00098 //  Description:
00099 ////////////////////////////////////////////////////////////////////
00100 void SubStreamBuf::
00101 close() {
00102   _source = (IStreamWrapper *)NULL;
00103   _start = 0;
00104   _end = 0;
00105   _cur = 0;
00106   _unused = 0;
00107 }
00108 
00109 ////////////////////////////////////////////////////////////////////
00110 //     Function: SubStreamBuf::seekoff
00111 //       Access: Public, Virtual
00112 //  Description: Implements seeking within the stream.
00113 ////////////////////////////////////////////////////////////////////
00114 streampos SubStreamBuf::
00115 seekoff(streamoff off, ios_seekdir dir, ios_openmode mode) {
00116   // Invariant: _cur points to the file location of the buffer at
00117   // egptr().
00118 
00119   // Use this to determine the actual file position right now.
00120   size_t n = egptr() - gptr();
00121   streampos cur_pos = _cur - (streampos)n;
00122   streampos new_pos = cur_pos;
00123 
00124   // Now adjust the data pointer appropriately.
00125 
00126   // Casting this to int to prevent GCC 3.2 compiler warnings.  Very
00127   // suspicious, need to investigate further.
00128   switch ((int)dir) {
00129   case ios::beg:
00130     new_pos = _start + off;
00131     break;
00132 
00133   case ios::cur:
00134     new_pos = cur_pos + off;
00135     break;
00136 
00137   case ios::end:
00138     if (_end == (streampos)0) {
00139       // If the end of the file is unspecified, we have to seek to
00140       // find it.
00141       new_pos = _source->seek_gpos_eof() + off;
00142 
00143     } else {
00144       new_pos = _end + off;
00145     }
00146     break;
00147   }
00148 
00149   new_pos = max(_start, new_pos);
00150   streamsize delta = new_pos - cur_pos;
00151 
00152   if (gptr() + delta >= eback() + _unused && gptr() + delta <= egptr()) {
00153     // If we can get away with just bumping the gptr within the
00154     // buffer, do so.
00155     gbump(delta);
00156 
00157   } else {
00158     // Otherwise, empty the buffer and force it to call underflow().
00159     gbump(n);
00160     _cur = new_pos;
00161   }
00162 
00163   return new_pos - _start;
00164 }
00165 
00166 ////////////////////////////////////////////////////////////////////
00167 //     Function: SubStreamBuf::seekpos
00168 //       Access: Public, Virtual
00169 //  Description: A variant on seekoff() to implement seeking within a
00170 //               stream.
00171 //
00172 //               The MSDN Library claims that it is only necessary to
00173 //               redefine seekoff(), and not seekpos() as well, as the
00174 //               default implementation of seekpos() is supposed to
00175 //               map to seekoff() exactly as I am doing here; but in
00176 //               fact it must do something else, because seeking
00177 //               didn't work on Windows until I redefined this
00178 //               function as well.
00179 ////////////////////////////////////////////////////////////////////
00180 streampos SubStreamBuf::
00181 seekpos(streampos pos, ios_openmode mode) {
00182   return seekoff(pos, ios::beg, mode);
00183 }
00184 
00185 ////////////////////////////////////////////////////////////////////
00186 //     Function: SubStreamBuf::overflow
00187 //       Access: Protected, Virtual
00188 //  Description: Called by the system ostream implementation when its
00189 //               internal buffer is filled, plus one character.
00190 ////////////////////////////////////////////////////////////////////
00191 int SubStreamBuf::
00192 overflow(int c) {
00193   // We don't support ostream.
00194   return 0;
00195 }
00196 
00197 ////////////////////////////////////////////////////////////////////
00198 //     Function: SubStreamBuf::sync
00199 //       Access: Protected, Virtual
00200 //  Description: Called by the system iostream implementation to
00201 //               implement a flush operation.
00202 ////////////////////////////////////////////////////////////////////
00203 int SubStreamBuf::
00204 sync() {
00205   size_t n = egptr() - gptr();
00206   gbump(n);
00207 
00208   return 0;
00209 }
00210 
00211 ////////////////////////////////////////////////////////////////////
00212 //     Function: SubStreamBuf::underflow
00213 //       Access: Protected, Virtual
00214 //  Description: Called by the system istream implementation when its
00215 //               internal buffer needs more characters.
00216 ////////////////////////////////////////////////////////////////////
00217 int SubStreamBuf::
00218 underflow() {
00219   // Sometimes underflow() is called even if the buffer is not empty.
00220   if (gptr() >= egptr()) {
00221     if (_end != (streampos)0 && _cur >= _end) {
00222       // We're done.
00223       return EOF;
00224     }
00225     
00226     size_t buffer_size = egptr() - eback();
00227     size_t num_bytes;
00228     if (_end == (streampos)0 || _end - _cur > (streampos)buffer_size) {
00229       // We have enough bytes in the input stream to fill our buffer.
00230       num_bytes = buffer_size;
00231     } else {
00232       // We won't quite fill the buffer.
00233       num_bytes = (size_t)(_end - _cur);
00234     }
00235 
00236     gbump(-(int)num_bytes);
00237     nassertr(gptr() + num_bytes <= egptr(), EOF);
00238 
00239     streamsize read_count;
00240     bool eof;
00241     _source->seek_read(_cur, gptr(), num_bytes, read_count, eof);
00242 
00243     if (read_count != (streamsize)num_bytes) {
00244       // Oops, we didn't read what we thought we would.
00245       if (read_count == 0) {
00246         _unused = buffer_size;
00247         if (_end != (streampos)0) {
00248           _end = _cur;
00249         }
00250         gbump(num_bytes);
00251         return EOF;
00252       }
00253 
00254       // Slide what we did read to the top of the buffer.
00255       nassertr(read_count < (int)num_bytes, EOF);
00256       size_t delta = num_bytes - read_count;
00257       memmove(gptr() + delta, gptr(), read_count);
00258       gbump(delta);
00259     }
00260 
00261     // Now record whatever bytes at the beginning of the buffer are
00262     // unused, so we won't try to seek into that area.
00263     _unused = buffer_size - read_count;
00264 
00265     // Invariant: _cur points to the file location of the buffer at
00266     // egptr().
00267 
00268     _cur += read_count;
00269   }
00270 
00271   return (unsigned char)*gptr();
00272 }
 All Classes Functions Variables Enumerations