浏览代码

fix seek within subStream

David Rose 23 年之前
父节点
当前提交
c5103b67e5
共有 2 个文件被更改,包括 87 次插入38 次删除
  1. 86 38
      panda/src/express/subStreamBuf.cxx
  2. 1 0
      panda/src/express/subStreamBuf.h

+ 86 - 38
panda/src/express/subStreamBuf.cxx

@@ -31,9 +31,29 @@ typedef int streamsize;
 SubStreamBuf::
 SubStreamBuf() {
   _source = (istream *)NULL;
+
+  // _start is the streampos of the first byte of the SubStream within
+  // its parent stream.
   _start = 0;
+
+  // _end is the streampos of the byte following the last byte of the
+  // SubStream within its parent stream.  If _end is 0, the SubStream
+  // continues to the end of the parent stream, wherever that is.
   _end = 0;
+
+  // _cur is the streampos of the end of the read buffer (that is,
+  // egptr()) within the parent stream.  By comparing _cur to gpos(),
+  // we can determine the actual current file position.
   _cur = 0;
+
+  // _unused counts the number of bytes at the beginning of the buffer
+  // that are unused.  Usually this is 0, but when we reach the end of
+  // the file we might not need the whole buffer to read the last bit,
+  // so the first part of the buffer is unused.  This is important to
+  // prevent us from inadvertently seeking into the unused part of the
+  // buffer.
+  _unused = 0;
+
 #ifndef WIN32_VC
   // These lines, which are essential on Irix and Linux, seem to be
   // unnecessary and not understood on Windows.
@@ -63,6 +83,7 @@ open(istream *source, streampos start, streampos end) {
   _start = start;
   _end = end;
   _cur = _start;
+  _unused = 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -72,11 +93,11 @@ open(istream *source, streampos start, streampos end) {
 ////////////////////////////////////////////////////////////////////
 void SubStreamBuf::
 close() {
-  sync();
   _source = (istream *)NULL;
   _start = 0;
   _end = 0;
   _cur = 0;
+  _unused = 0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -86,22 +107,52 @@ close() {
 ////////////////////////////////////////////////////////////////////
 streampos SubStreamBuf::
 seekoff(streamoff off, ios::seek_dir dir, int mode) {
+  // Invariant: _cur points to the file location of the buffer at
+  // egptr().
+
+  // Use this to determine the actual file position right now.
+  streamsize n = egptr() - gptr();
+  streampos cur_pos = _cur - n;
+  streampos new_pos = cur_pos;
+
+  // Now adjust the data pointer appropriately.
   switch (dir) {
   case ios::beg:
-    _cur = _start + off;
+    new_pos = _start + off;
     break;
 
   case ios::cur:
-    _cur += off;
+    new_pos = cur_pos + off;
     break;
 
   case ios::end:
-    _cur = _end + off;
+    if (_end == 0) {
+      // If the end of the file is unspecified, we have to seek to
+      // find it.
+      _source->seekg(off, ios::end);
+      new_pos = _source->tellg();
+
+    } else {
+      new_pos = _end + off;
+    }
     break;
   }
 
-  _cur = max(_start, _cur);
-  return _cur - _start;
+  new_pos = max(_start, new_pos);
+  streamsize delta = new_pos - cur_pos;
+
+  if (gptr() + delta >= eback() + _unused && gptr() + delta <= egptr()) {
+    // If we can get away with just bumping the gptr within the
+    // buffer, do so.
+    gbump(delta);
+
+  } else {
+    // Otherwise, empty the buffer and force it to call underflow().
+    gbump(n);
+    _cur = new_pos;
+  }
+
+  return new_pos - _start;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -113,19 +164,6 @@ seekoff(streamoff off, ios::seek_dir dir, int mode) {
 int SubStreamBuf::
 overflow(int c) {
   // We don't support ostream.
-
-  /*
-  streamsize n = pptr() - pbase();
-  if (n != 0) {
-    write_chars(pbase(), n, false);
-    pbump(-n);  // reset pptr()
-  }
-  if (c != EOF) {
-    // write one more character
-    char ch = c;
-    write_chars(&ch, 1, false);
-  }
-  */
   return 0;
 }
 
@@ -137,19 +175,9 @@ overflow(int c) {
 ////////////////////////////////////////////////////////////////////
 int SubStreamBuf::
 sync() {
-  /*
-  streamsize n = pptr() - pbase();
-  if (n != 0) {
-    write_chars(pbase(), n, false);
-    pbump(-n);
-  }
-  */
-
   streamsize n = egptr() - gptr();
-  if (n != 0) {
-    gbump(n);
-    _cur += n;
-  }
+  gbump(n);
+
   return 0;
 }
 
@@ -172,14 +200,14 @@ underflow() {
 
   // Sometimes underflow() is called even if the buffer is not empty.
   if (gptr() >= egptr()) {
-    if (_cur >= _end) {
+    if (_end != 0 && _cur >= _end) {
       // We're done.
       return EOF;
     }
     
     size_t buffer_size = egptr() - eback();
     size_t num_bytes;
-    if (_end - _cur > (streampos)buffer_size) {
+    if (_end == 0 || _end - _cur > (streampos)buffer_size) {
       // We have enough bytes in the input stream to fill our buffer.
       num_bytes = buffer_size;
     } else {
@@ -192,13 +220,33 @@ underflow() {
     nassertr(gptr() + num_bytes <= egptr(), EOF);
 
     _source->read(gptr(), num_bytes);
-    if (_source->gcount() != num_bytes) {
-      // Oops, something screwed up.
-      _cur = _end;
-      return EOF;
+    size_t read_count = _source->gcount();
+
+    if (read_count != num_bytes) {
+      // Oops, we didn't read what we thought we would.
+      if (read_count == 0) {
+        _unused = buffer_size;
+        if (_end != 0) {
+          _end = _cur;
+        }
+        return EOF;
+      }
+
+      // Slide what we did read to the top of the buffer.
+      nassertr(read_count < num_bytes, EOF);
+      size_t delta = num_bytes - read_count;
+      memmove(gptr() + delta, gptr(), read_count);
+      gbump(delta);
     }
 
-    _cur += num_bytes;
+    // Now record whatever bytes at the beginning of the buffer are
+    // unused, so we won't try to seek into that area.
+    _unused = buffer_size - read_count;
+
+    // Invariant: _cur points to the file location of the buffer at
+    // egptr().
+
+    _cur += read_count;
   }
 
   return (unsigned char)*gptr();

+ 1 - 0
panda/src/express/subStreamBuf.h

@@ -45,6 +45,7 @@ private:
   streampos _start;
   streampos _end;
   streampos _cur;
+  size_t _unused;
 };
 
 #endif