Panda3D
Loading...
Searching...
No Matches
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
18using std::ios;
19using std::streamoff;
20using std::streampos;
21using std::streamsize;
22
23static const size_t substream_buffer_size = 4096;
24
25/**
26 *
27 */
28SubStreamBuf::
29SubStreamBuf() {
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 */
71SubStreamBuf::
72~SubStreamBuf() {
73 close();
74#ifdef PHAVE_IOSTREAM
75 PANDA_FREE_ARRAY(_buffer);
76#endif
77}
78
79/**
80 *
81 */
82void SubStreamBuf::
83open(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 */
103void SubStreamBuf::
104close() {
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 */
130seekoff(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 */
244seekpos(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 */
252int SubStreamBuf::
253overflow(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 */
302int SubStreamBuf::
303sync() {
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 */
329int SubStreamBuf::
330underflow() {
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}
This class provides a locking wrapper around an arbitrary istream pointer.
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...
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 ostream pointer.
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 ...
std::streamsize seek_ppos_eof()
Atomically seeks to EOF and returns the ppos there; that is, returns the file size.
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.
bool unref() const
Decrements the reference count.
void ref() const
Increments the reference count.
virtual std::streampos seekoff(std::streamoff off, ios_seekdir dir, ios_openmode which)
Implements seeking within the stream.
virtual std::streampos seekpos(std::streampos pos, ios_openmode which)
A variant on seekoff() to implement seeking within a stream.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.