Panda3D
 All Classes Functions Variables Enumerations
subStreamBuf.cxx
1 // Filename: subStreamBuf.cxx
2 // Created by: drose (02Aug02)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "subStreamBuf.h"
16 #include "pnotify.h"
17 #include "memoryHook.h"
18 
19 #ifndef HAVE_STREAMSIZE
20 // Some compilers (notably SGI) don't define this for us
21 typedef int streamsize;
22 #endif /* HAVE_STREAMSIZE */
23 
24 static const size_t substream_buffer_size = 4096;
25 
26 ////////////////////////////////////////////////////////////////////
27 // Function: SubStreamBuf::Constructor
28 // Access: Public
29 // Description:
30 ////////////////////////////////////////////////////////////////////
31 SubStreamBuf::
32 SubStreamBuf() {
33  _source = (IStreamWrapper *)NULL;
34  _dest = (OStreamWrapper *)NULL;
35 
36  // _start is the streampos of the first byte of the SubStream within
37  // its parent stream.
38  _start = 0;
39 
40  // _end is the streampos of the byte following the last byte of the
41  // SubStream within its parent stream. If _end is 0, the SubStream
42  // continues to the end of the parent stream, wherever that is.
43  _end = 0;
44 
45  // _gpos is the streampos of the end of the read buffer (that is,
46  // egptr()) within the parent stream. By comparing _gpos to gpos(),
47  // we can determine the actual current file position. _ppos is the
48  // similar pos, for the write pointer.
49  _gpos = 0;
50  _ppos = 0;
51 
52 #ifdef PHAVE_IOSTREAM
53  _buffer = (char *)PANDA_MALLOC_ARRAY(substream_buffer_size * 2);
54  char *ebuf = _buffer + substream_buffer_size * 2;
55  char *mbuf = _buffer + substream_buffer_size;
56  setg(_buffer, mbuf, mbuf);
57  setp(mbuf, ebuf);
58 
59 #else
60  allocate();
61  // Chop the buffer in half. The bottom half goes to the get buffer;
62  // the top half goes to the put buffer.
63  char *b = base();
64  char *t = ebuf();
65  char *m = b + (t - b) / 2;
66  setg(b, m, m);
67  setp(b, m);
68 #endif
69 }
70 
71 ////////////////////////////////////////////////////////////////////
72 // Function: SubStreamBuf::Destructor
73 // Access: Public, Virtual
74 // Description:
75 ////////////////////////////////////////////////////////////////////
76 SubStreamBuf::
77 ~SubStreamBuf() {
78  close();
79 #ifdef PHAVE_IOSTREAM
80  PANDA_FREE_ARRAY(_buffer);
81 #endif
82 }
83 
84 ////////////////////////////////////////////////////////////////////
85 // Function: SubStreamBuf::open
86 // Access: Public
87 // Description:
88 ////////////////////////////////////////////////////////////////////
89 void SubStreamBuf::
90 open(IStreamWrapper *source, OStreamWrapper *dest, streampos start, streampos end, bool append) {
91  _source = source;
92  _dest = dest;
93  _start = start;
94  _end = end;
95  _append = append;
96  _gpos = _start;
97  _ppos = _start;
98 }
99 
100 ////////////////////////////////////////////////////////////////////
101 // Function: SubStreamBuf::close
102 // Access: Public
103 // Description:
104 ////////////////////////////////////////////////////////////////////
105 void SubStreamBuf::
106 close() {
107  // Make sure the write buffer is flushed.
108  sync();
109 
110  _source = (IStreamWrapper *)NULL;
111  _dest = (OStreamWrapper *)NULL;
112  _start = 0;
113  _end = 0;
114 
115  _gpos = 0;
116  _ppos = 0;
117 
118  pbump(pbase() - pptr());
119  gbump(egptr() - gptr());
120 }
121 
122 ////////////////////////////////////////////////////////////////////
123 // Function: SubStreamBuf::seekoff
124 // Access: Public, Virtual
125 // Description: Implements seeking within the stream.
126 ////////////////////////////////////////////////////////////////////
127 streampos SubStreamBuf::
128 seekoff(streamoff off, ios_seekdir dir, ios_openmode which) {
129  streampos result = -1;
130 
131  // Sync the iostream buffer first.
132  sync();
133 
134  if (which & ios::in) {
135  // Determine the current file position.
136  size_t n = egptr() - gptr();
137  gbump(n);
138  _gpos -= n;
139  nassertr(_gpos >= 0, EOF);
140  streampos cur_pos = _gpos;
141  streampos new_pos = cur_pos;
142 
143  // Now adjust the data pointer appropriately.
144  switch (dir) {
145  case ios::beg:
146  new_pos = (streampos)off + _start;
147  break;
148 
149  case ios::cur:
150  new_pos = (streampos)((streamoff)cur_pos + off);
151  break;
152 
153  case ios::end:
154  if (_end == (streampos)0) {
155  // If the end of the file is unspecified, we have to seek to
156  // find it.
157  new_pos = _source->seek_gpos_eof() + off;
158 
159  } else {
160  new_pos = _end + off;
161  }
162  break;
163 
164  default:
165  // Shouldn't get here.
166  break;
167  }
168 
169  if (new_pos < _start) {
170  // Can't seek before beginning of file.
171  return EOF;
172  }
173 
174  if (_end != (streampos)0 && new_pos > _end) {
175  // Can't seek past end of file.
176  return EOF;
177  }
178 
179  _gpos = new_pos;
180  nassertr(_gpos >= 0, EOF);
181  result = new_pos - _start;
182  }
183 
184  if (which & ios::out) {
185  // Determine the current file position.
186  size_t n = pptr() - pbase();
187  streampos cur_pos = _ppos + (streamoff)n;
188  streampos new_pos = cur_pos;
189 
190  // Now adjust the data pointer appropriately.
191  switch (dir) {
192  case ios::beg:
193  new_pos = (streampos)off + _start;
194  break;
195 
196  case ios::cur:
197  new_pos = (streampos)((streamoff)cur_pos + off);
198  break;
199 
200  case ios::end:
201  if (_end == (streampos)0) {
202  // If the end of the file is unspecified, we have to seek to
203  // find it.
204  new_pos = _dest->seek_ppos_eof() + off;
205 
206  } else {
207  new_pos = _end + off;
208  }
209  break;
210 
211  default:
212  // Shouldn't get here.
213  break;
214  }
215 
216  if (new_pos < _start) {
217  // Can't seek before beginning of file.
218  return EOF;
219  }
220 
221  if (_end != (streampos)0 && new_pos > _end) {
222  // Can't seek past end of file.
223  return EOF;
224  }
225 
226  _ppos = new_pos;
227  nassertr(_ppos >= 0, EOF);
228  result = new_pos - _start;
229  }
230 
231  return result;
232 }
233 
234 ////////////////////////////////////////////////////////////////////
235 // Function: SubStreamBuf::seekpos
236 // Access: Public, Virtual
237 // Description: A variant on seekoff() to implement seeking within a
238 // stream.
239 //
240 // The MSDN Library claims that it is only necessary to
241 // redefine seekoff(), and not seekpos() as well, as the
242 // default implementation of seekpos() is supposed to
243 // map to seekoff() exactly as I am doing here; but in
244 // fact it must do something else, because seeking
245 // didn't work on Windows until I redefined this
246 // function as well.
247 ////////////////////////////////////////////////////////////////////
248 streampos SubStreamBuf::
249 seekpos(streampos pos, ios_openmode which) {
250  return seekoff(pos, ios::beg, which);
251 }
252 
253 ////////////////////////////////////////////////////////////////////
254 // Function: SubStreamBuf::overflow
255 // Access: Protected, Virtual
256 // Description: Called by the system ostream implementation when its
257 // internal buffer is filled, plus one character.
258 ////////////////////////////////////////////////////////////////////
259 int SubStreamBuf::
260 overflow(int ch) {
261  bool okflag = true;
262 
263  size_t n = pptr() - pbase();
264  if (n != 0) {
265  if (_end != (streampos)0 && _ppos + (streampos)n > _end) {
266  // Don't allow reading past the end of the file.
267  n = (size_t)(_end - _ppos);
268  if (n == 0) {
269  // No room.
270  return EOF;
271  }
272  }
273 
274  nassertr(_dest != NULL, EOF);
275  bool fail = false;
276  if (_append) {
277  _dest->seek_eof_write(pbase(), n, fail);
278  } else {
279  _dest->seek_write(_ppos, pbase(), n, fail);
280  }
281  _ppos += n;
282  pbump(-(int)n);
283  if (fail) {
284  okflag = false;
285  }
286  }
287 
288  if (okflag && ch != EOF) {
289  if (pptr() != epptr()) {
290  // Store the extra character back in the buffer.
291  *(pptr()) = ch;
292  pbump(1);
293  } else {
294  // No room to store ch.
295  okflag = false;
296  }
297  }
298 
299  if (!okflag) {
300  return EOF;
301  }
302  return 0;
303 }
304 
305 ////////////////////////////////////////////////////////////////////
306 // Function: SubStreamBuf::sync
307 // Access: Protected, Virtual
308 // Description: Called by the system iostream implementation to
309 // implement a flush operation.
310 ////////////////////////////////////////////////////////////////////
311 int SubStreamBuf::
312 sync() {
313  size_t n = pptr() - pbase();
314 
315  if (n != 0) {
316  nassertr(_dest != NULL, EOF);
317  bool fail = false;
318  if (_append) {
319  _dest->seek_eof_write(pbase(), n, fail);
320  } else {
321  _dest->seek_write(_ppos, pbase(), n, fail);
322  }
323  _ppos += n;
324  pbump(-(int)n);
325 
326  if (fail) {
327  return EOF;
328  }
329  }
330 
331  return 0;
332 }
333 
334 ////////////////////////////////////////////////////////////////////
335 // Function: SubStreamBuf::underflow
336 // Access: Protected, Virtual
337 // Description: Called by the system istream implementation when its
338 // internal buffer needs more characters.
339 ////////////////////////////////////////////////////////////////////
340 int SubStreamBuf::
341 underflow() {
342  // Sometimes underflow() is called even if the buffer is not empty.
343  if (gptr() >= egptr()) {
344  sync();
345 
346  // Mark the buffer filled (with buffer_size bytes).
347  size_t buffer_size = egptr() - eback();
348  gbump(-(int)buffer_size);
349 
350  streamsize num_bytes = buffer_size;
351  if (_end != (streampos)0 && _gpos + (streampos)num_bytes > _end) {
352  // Don't allow reading past the end of the file.
353  streamsize new_num_bytes = _end - _gpos;
354  if (new_num_bytes == 0) {
355  gbump(buffer_size);
356  return EOF;
357  }
358 
359  // We won't be filling the entire buffer. Fill in only at the
360  // end of the buffer.
361  size_t delta = num_bytes - new_num_bytes;
362  gbump(delta);
363  num_bytes = new_num_bytes;
364  nassertr(egptr() - gptr() == num_bytes, EOF);
365  }
366 
367  nassertr(_source != NULL, EOF);
368  streamsize read_count;
369  bool eof;
370  _source->seek_read(_gpos, gptr(), num_bytes, read_count, eof);
371  _gpos += read_count;
372 
373  if (read_count != num_bytes) {
374  // Oops, we didn't read what we thought we would.
375  if (read_count == 0) {
376  gbump(num_bytes);
377  return EOF;
378  }
379 
380  // Slide what we did read to the top of the buffer.
381  nassertr(read_count < num_bytes, EOF);
382  size_t delta = num_bytes - read_count;
383  memmove(gptr() + delta, gptr(), read_count);
384  gbump(delta);
385  }
386  }
387 
388  return (unsigned char)*gptr();
389 }
void seek_write(streamsize pos, const char *buffer, streamsize num_bytes, bool &fail)
Atomically seeks to a particular offset from the beginning of the file, and writes a number of bytes ...
void seek_eof_write(const char *buffer, streamsize num_bytes, bool &fail)
Atomically seeks to the end of the file, and writes a number of bytes to the stream.
void seek_read(streamsize pos, char *buffer, streamsize num_bytes, streamsize &read_bytes, bool &eof)
Atomically seeks to a particular offset from the beginning of the file, and reads a number of bytes f...
streamsize seek_gpos_eof()
Atomically seeks to EOF and returns the gpos there; that is, returns the file size.
virtual streampos seekpos(streampos pos, ios_openmode which)
A variant on seekoff() to implement seeking within a stream.
This class provides a locking wrapper around an arbitrary istream pointer.
Definition: streamWrapper.h:53
streamsize seek_ppos_eof()
Atomically seeks to EOF and returns the ppos there; that is, returns the file size.
This class provides a locking wrapper around an arbitrary ostream pointer.
Definition: streamWrapper.h:81
virtual streampos seekoff(streamoff off, ios_seekdir dir, ios_openmode which)
Implements seeking within the stream.