open_toontown_panda3d/panda/src/express/subStreamBuf.cxx

379 lines
8.3 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file subStreamBuf.cxx
* @author drose
* @date 2002-08-02
*/
#include "subStreamBuf.h"
#include "pnotify.h"
#include "memoryHook.h"
using std::ios;
using std::streamoff;
using std::streampos;
using std::streamsize;
static const size_t substream_buffer_size = 4096;
/**
*
*/
SubStreamBuf::
SubStreamBuf() {
_source = nullptr;
_dest = nullptr;
// _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;
// _gpos is the streampos of the end of the read buffer (that is, egptr())
// within the parent stream. By comparing _gpos to gpos(), we can determine
// the actual current file position. _ppos is the similar pos, for the
// write pointer.
_gpos = 0;
_ppos = 0;
#ifdef PHAVE_IOSTREAM
_buffer = (char *)PANDA_MALLOC_ARRAY(substream_buffer_size * 2);
char *ebuf = _buffer + substream_buffer_size * 2;
char *mbuf = _buffer + substream_buffer_size;
setg(_buffer, mbuf, mbuf);
setp(mbuf, ebuf);
#else
allocate();
// Chop the buffer in half. The bottom half goes to the get buffer; the top
// half goes to the put buffer.
char *b = base();
char *t = ebuf();
char *m = b + (t - b) / 2;
setg(b, m, m);
setp(b, m);
#endif
}
/**
*
*/
SubStreamBuf::
~SubStreamBuf() {
close();
#ifdef PHAVE_IOSTREAM
PANDA_FREE_ARRAY(_buffer);
#endif
}
/**
*
*/
void SubStreamBuf::
open(IStreamWrapper *source, OStreamWrapper *dest, streampos start, streampos end, bool append) {
_source = source;
_dest = dest;
_start = start;
_end = end;
_append = append;
_gpos = _start;
_ppos = _start;
if (source != nullptr) {
source->ref();
}
if (dest != nullptr) {
dest->ref();
}
}
/**
*
*/
void SubStreamBuf::
close() {
// Make sure the write buffer is flushed.
sync();
if (_source != nullptr && !_source->unref()) {
delete _source;
}
if (_dest != nullptr && !_dest->unref()) {
delete _dest;
}
_source = nullptr;
_dest = nullptr;
_start = 0;
_end = 0;
_gpos = 0;
_ppos = 0;
pbump(pbase() - pptr());
gbump(egptr() - gptr());
}
/**
* Implements seeking within the stream.
*/
streampos SubStreamBuf::
seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
streampos result = -1;
// Sync the iostream buffer first.
sync();
if (which & ios::in) {
// Determine the current file position.
size_t n = egptr() - gptr();
gbump(n);
_gpos -= n;
nassertr(_gpos >= 0, EOF);
streampos cur_pos = _gpos;
streampos new_pos = cur_pos;
// Now adjust the data pointer appropriately.
switch (dir) {
case ios::beg:
new_pos = (streampos)off + _start;
break;
case ios::cur:
new_pos = (streampos)((streamoff)cur_pos + off);
break;
case ios::end:
if (_end == (streampos)0) {
// If the end of the file is unspecified, we have to seek to find it.
new_pos = _source->seek_gpos_eof() + off;
} else {
new_pos = _end + off;
}
break;
default:
// Shouldn't get here.
break;
}
if (new_pos < _start) {
// Can't seek before beginning of file.
return EOF;
}
if (_end != (streampos)0 && new_pos > _end) {
// Can't seek past end of file.
return EOF;
}
_gpos = new_pos;
nassertr(_gpos >= 0, EOF);
result = new_pos - _start;
}
if (which & ios::out) {
// Determine the current file position.
size_t n = pptr() - pbase();
streampos cur_pos = _ppos + (streamoff)n;
streampos new_pos = cur_pos;
// Now adjust the data pointer appropriately.
switch (dir) {
case ios::beg:
new_pos = (streampos)off + _start;
break;
case ios::cur:
new_pos = (streampos)((streamoff)cur_pos + off);
break;
case ios::end:
if (_end == (streampos)0) {
// If the end of the file is unspecified, we have to seek to find it.
new_pos = _dest->seek_ppos_eof() + off;
} else {
new_pos = _end + off;
}
break;
default:
// Shouldn't get here.
break;
}
if (new_pos < _start) {
// Can't seek before beginning of file.
return EOF;
}
if (_end != (streampos)0 && new_pos > _end) {
// Can't seek past end of file.
return EOF;
}
_ppos = new_pos;
nassertr(_ppos >= 0, EOF);
result = new_pos - _start;
}
return result;
}
/**
* A variant on seekoff() to implement seeking within a stream.
*
* The MSDN Library claims that it is only necessary to redefine seekoff(),
* and not seekpos() as well, as the default implementation of seekpos() is
* supposed to map to seekoff() exactly as I am doing here; but in fact it
* must do something else, because seeking didn't work on Windows until I
* redefined this function as well.
*/
streampos SubStreamBuf::
seekpos(streampos pos, ios_openmode which) {
return seekoff(pos, ios::beg, which);
}
/**
* Called by the system ostream implementation when its internal buffer is
* filled, plus one character.
*/
int SubStreamBuf::
overflow(int ch) {
bool okflag = true;
size_t n = pptr() - pbase();
if (n != 0) {
if (_end != (streampos)0 && _ppos + (streampos)n > _end) {
// Don't allow reading past the end of the file.
n = (size_t)(_end - _ppos);
if (n == 0) {
// No room.
return EOF;
}
}
nassertr(_dest != nullptr, EOF);
bool fail = false;
if (_append) {
_dest->seek_eof_write(pbase(), n, fail);
} else {
_dest->seek_write(_ppos, pbase(), n, fail);
}
_ppos += n;
pbump(-(int)n);
if (fail) {
okflag = false;
}
}
if (okflag && ch != EOF) {
if (pptr() != epptr()) {
// Store the extra character back in the buffer.
*(pptr()) = ch;
pbump(1);
} else {
// No room to store ch.
okflag = false;
}
}
if (!okflag) {
return EOF;
}
return 0;
}
/**
* Called by the system iostream implementation to implement a flush
* operation.
*/
int SubStreamBuf::
sync() {
size_t n = pptr() - pbase();
if (n != 0) {
nassertr(_dest != nullptr, EOF);
bool fail = false;
if (_append) {
_dest->seek_eof_write(pbase(), n, fail);
} else {
_dest->seek_write(_ppos, pbase(), n, fail);
}
_ppos += n;
pbump(-(int)n);
if (fail) {
return EOF;
}
}
return 0;
}
/**
* Called by the system istream implementation when its internal buffer needs
* more characters.
*/
int SubStreamBuf::
underflow() {
// Sometimes underflow() is called even if the buffer is not empty.
if (gptr() >= egptr()) {
sync();
// Mark the buffer filled (with buffer_size bytes).
size_t buffer_size = egptr() - eback();
gbump(-(int)buffer_size);
streamsize num_bytes = buffer_size;
if (_end != (streampos)0 && _gpos + (streampos)num_bytes > _end) {
// Don't allow reading past the end of the file.
streamsize new_num_bytes = _end - _gpos;
if (new_num_bytes == 0) {
gbump(buffer_size);
return EOF;
}
// We won't be filling the entire buffer. Fill in only at the end of
// the buffer.
size_t delta = num_bytes - new_num_bytes;
gbump(delta);
num_bytes = new_num_bytes;
nassertr(egptr() - gptr() == num_bytes, EOF);
}
nassertr(_source != nullptr, EOF);
streamsize read_count;
bool eof;
_source->seek_read(_gpos, gptr(), num_bytes, read_count, eof);
_gpos += read_count;
if (read_count != num_bytes) {
// Oops, we didn't read what we thought we would.
if (read_count == 0) {
gbump(num_bytes);
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);
}
}
return (unsigned char)*gptr();
}