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