Panda3D
|
00001 // Filename: subStreamBuf.cxx 00002 // Created by: drose (02Aug02) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "subStreamBuf.h" 00016 #include "pnotify.h" 00017 00018 #ifndef HAVE_STREAMSIZE 00019 // Some compilers (notably SGI) don't define this for us 00020 typedef int streamsize; 00021 #endif /* HAVE_STREAMSIZE */ 00022 00023 static const size_t substream_buffer_size = 4096; 00024 00025 //////////////////////////////////////////////////////////////////// 00026 // Function: SubStreamBuf::Constructor 00027 // Access: Public 00028 // Description: 00029 //////////////////////////////////////////////////////////////////// 00030 SubStreamBuf:: 00031 SubStreamBuf() { 00032 _source = (IStreamWrapper *)NULL; 00033 _dest = (OStreamWrapper *)NULL; 00034 00035 // _start is the streampos of the first byte of the SubStream within 00036 // its parent stream. 00037 _start = 0; 00038 00039 // _end is the streampos of the byte following the last byte of the 00040 // SubStream within its parent stream. If _end is 0, the SubStream 00041 // continues to the end of the parent stream, wherever that is. 00042 _end = 0; 00043 00044 // _gpos is the streampos of the end of the read buffer (that is, 00045 // egptr()) within the parent stream. By comparing _gpos to gpos(), 00046 // we can determine the actual current file position. _ppos is the 00047 // similar pos, for the write pointer. 00048 _gpos = 0; 00049 _ppos = 0; 00050 00051 #ifdef PHAVE_IOSTREAM 00052 _buffer = (char *)PANDA_MALLOC_ARRAY(substream_buffer_size * 2); 00053 char *ebuf = _buffer + substream_buffer_size * 2; 00054 char *mbuf = _buffer + substream_buffer_size; 00055 setg(_buffer, mbuf, mbuf); 00056 setp(mbuf, ebuf); 00057 00058 #else 00059 allocate(); 00060 // Chop the buffer in half. The bottom half goes to the get buffer; 00061 // the top half goes to the put buffer. 00062 char *b = base(); 00063 char *t = ebuf(); 00064 char *m = b + (t - b) / 2; 00065 setg(b, m, m); 00066 setp(b, m); 00067 #endif 00068 } 00069 00070 //////////////////////////////////////////////////////////////////// 00071 // Function: SubStreamBuf::Destructor 00072 // Access: Public, Virtual 00073 // Description: 00074 //////////////////////////////////////////////////////////////////// 00075 SubStreamBuf:: 00076 ~SubStreamBuf() { 00077 close(); 00078 #ifdef PHAVE_IOSTREAM 00079 PANDA_FREE_ARRAY(_buffer); 00080 #endif 00081 } 00082 00083 //////////////////////////////////////////////////////////////////// 00084 // Function: SubStreamBuf::open 00085 // Access: Public 00086 // Description: 00087 //////////////////////////////////////////////////////////////////// 00088 void SubStreamBuf:: 00089 open(IStreamWrapper *source, OStreamWrapper *dest, streampos start, streampos end, bool append) { 00090 _source = source; 00091 _dest = dest; 00092 _start = start; 00093 _end = end; 00094 _append = append; 00095 _gpos = _start; 00096 _ppos = _start; 00097 } 00098 00099 //////////////////////////////////////////////////////////////////// 00100 // Function: SubStreamBuf::close 00101 // Access: Public 00102 // Description: 00103 //////////////////////////////////////////////////////////////////// 00104 void SubStreamBuf:: 00105 close() { 00106 // Make sure the write buffer is flushed. 00107 sync(); 00108 00109 _source = (IStreamWrapper *)NULL; 00110 _dest = (OStreamWrapper *)NULL; 00111 _start = 0; 00112 _end = 0; 00113 00114 _gpos = 0; 00115 _ppos = 0; 00116 00117 pbump(pbase() - pptr()); 00118 gbump(egptr() - gptr()); 00119 } 00120 00121 //////////////////////////////////////////////////////////////////// 00122 // Function: SubStreamBuf::seekoff 00123 // Access: Public, Virtual 00124 // Description: Implements seeking within the stream. 00125 //////////////////////////////////////////////////////////////////// 00126 streampos SubStreamBuf:: 00127 seekoff(streamoff off, ios_seekdir dir, ios_openmode which) { 00128 streampos result = -1; 00129 00130 // Sync the iostream buffer first. 00131 sync(); 00132 00133 if (which & ios::in) { 00134 // Determine the current file position. 00135 size_t n = egptr() - gptr(); 00136 gbump(n); 00137 _gpos -= n; 00138 nassertr(_gpos >= 0, EOF); 00139 streampos cur_pos = _gpos; 00140 streampos new_pos = cur_pos; 00141 00142 // Now adjust the data pointer appropriately. 00143 switch (dir) { 00144 case ios::beg: 00145 new_pos = (streampos)off + _start; 00146 break; 00147 00148 case ios::cur: 00149 new_pos = (streampos)((streamoff)cur_pos + off); 00150 break; 00151 00152 case ios::end: 00153 if (_end == (streampos)0) { 00154 // If the end of the file is unspecified, we have to seek to 00155 // find it. 00156 new_pos = _source->seek_gpos_eof() + off; 00157 00158 } else { 00159 new_pos = _end + off; 00160 } 00161 break; 00162 00163 default: 00164 // Shouldn't get here. 00165 break; 00166 } 00167 00168 if (new_pos < _start) { 00169 // Can't seek before beginning of file. 00170 return EOF; 00171 } 00172 00173 if (_end != (streampos)0 && new_pos > _end) { 00174 // Can't seek past end of file. 00175 return EOF; 00176 } 00177 00178 _gpos = new_pos; 00179 nassertr(_gpos >= 0, EOF); 00180 result = new_pos - _start; 00181 } 00182 00183 if (which & ios::out) { 00184 // Determine the current file position. 00185 size_t n = pptr() - pbase(); 00186 streampos cur_pos = _ppos + (streamoff)n; 00187 streampos new_pos = cur_pos; 00188 00189 // Now adjust the data pointer appropriately. 00190 switch (dir) { 00191 case ios::beg: 00192 new_pos = (streampos)off + _start; 00193 break; 00194 00195 case ios::cur: 00196 new_pos = (streampos)((streamoff)cur_pos + off); 00197 break; 00198 00199 case ios::end: 00200 if (_end == (streampos)0) { 00201 // If the end of the file is unspecified, we have to seek to 00202 // find it. 00203 new_pos = _dest->seek_ppos_eof() + off; 00204 00205 } else { 00206 new_pos = _end + off; 00207 } 00208 break; 00209 00210 default: 00211 // Shouldn't get here. 00212 break; 00213 } 00214 00215 if (new_pos < _start) { 00216 // Can't seek before beginning of file. 00217 return EOF; 00218 } 00219 00220 if (_end != (streampos)0 && new_pos > _end) { 00221 // Can't seek past end of file. 00222 return EOF; 00223 } 00224 00225 _ppos = new_pos; 00226 nassertr(_ppos >= 0, EOF); 00227 result = new_pos - _start; 00228 } 00229 00230 return result; 00231 } 00232 00233 //////////////////////////////////////////////////////////////////// 00234 // Function: SubStreamBuf::seekpos 00235 // Access: Public, Virtual 00236 // Description: A variant on seekoff() to implement seeking within a 00237 // stream. 00238 // 00239 // The MSDN Library claims that it is only necessary to 00240 // redefine seekoff(), and not seekpos() as well, as the 00241 // default implementation of seekpos() is supposed to 00242 // map to seekoff() exactly as I am doing here; but in 00243 // fact it must do something else, because seeking 00244 // didn't work on Windows until I redefined this 00245 // function as well. 00246 //////////////////////////////////////////////////////////////////// 00247 streampos SubStreamBuf:: 00248 seekpos(streampos pos, ios_openmode which) { 00249 return seekoff(pos, ios::beg, which); 00250 } 00251 00252 //////////////////////////////////////////////////////////////////// 00253 // Function: SubStreamBuf::overflow 00254 // Access: Protected, Virtual 00255 // Description: Called by the system ostream implementation when its 00256 // internal buffer is filled, plus one character. 00257 //////////////////////////////////////////////////////////////////// 00258 int SubStreamBuf:: 00259 overflow(int ch) { 00260 bool okflag = true; 00261 00262 size_t n = pptr() - pbase(); 00263 if (n != 0) { 00264 if (_end != (streampos)0 && _ppos + (streampos)n > _end) { 00265 // Don't allow reading past the end of the file. 00266 n = (size_t)(_end - _ppos); 00267 if (n == 0) { 00268 // No room. 00269 return EOF; 00270 } 00271 } 00272 00273 nassertr(_dest != NULL, EOF); 00274 bool fail = false; 00275 if (_append) { 00276 _dest->seek_eof_write(pbase(), n, fail); 00277 } else { 00278 _dest->seek_write(_ppos, pbase(), n, fail); 00279 } 00280 _ppos += n; 00281 pbump(-(int)n); 00282 if (fail) { 00283 okflag = false; 00284 } 00285 } 00286 00287 if (okflag && ch != EOF) { 00288 if (pptr() != epptr()) { 00289 // Store the extra character back in the buffer. 00290 *(pptr()) = ch; 00291 pbump(1); 00292 } else { 00293 // No room to store ch. 00294 okflag = false; 00295 } 00296 } 00297 00298 if (!okflag) { 00299 return EOF; 00300 } 00301 return 0; 00302 } 00303 00304 //////////////////////////////////////////////////////////////////// 00305 // Function: SubStreamBuf::sync 00306 // Access: Protected, Virtual 00307 // Description: Called by the system iostream implementation to 00308 // implement a flush operation. 00309 //////////////////////////////////////////////////////////////////// 00310 int SubStreamBuf:: 00311 sync() { 00312 size_t n = pptr() - pbase(); 00313 00314 if (n != 0) { 00315 nassertr(_dest != NULL, EOF); 00316 bool fail = false; 00317 if (_append) { 00318 _dest->seek_eof_write(pbase(), n, fail); 00319 } else { 00320 _dest->seek_write(_ppos, pbase(), n, fail); 00321 } 00322 _ppos += n; 00323 pbump(-(int)n); 00324 00325 if (fail) { 00326 return EOF; 00327 } 00328 } 00329 00330 return 0; 00331 } 00332 00333 //////////////////////////////////////////////////////////////////// 00334 // Function: SubStreamBuf::underflow 00335 // Access: Protected, Virtual 00336 // Description: Called by the system istream implementation when its 00337 // internal buffer needs more characters. 00338 //////////////////////////////////////////////////////////////////// 00339 int SubStreamBuf:: 00340 underflow() { 00341 // Sometimes underflow() is called even if the buffer is not empty. 00342 if (gptr() >= egptr()) { 00343 sync(); 00344 00345 // Mark the buffer filled (with buffer_size bytes). 00346 size_t buffer_size = egptr() - eback(); 00347 gbump(-(int)buffer_size); 00348 00349 streamsize num_bytes = buffer_size; 00350 if (_end != (streampos)0 && _gpos + (streampos)num_bytes > _end) { 00351 // Don't allow reading past the end of the file. 00352 streamsize new_num_bytes = _end - _gpos; 00353 if (new_num_bytes == 0) { 00354 gbump(buffer_size); 00355 return EOF; 00356 } 00357 00358 // We won't be filling the entire buffer. Fill in only at the 00359 // end of the buffer. 00360 size_t delta = num_bytes - new_num_bytes; 00361 gbump(delta); 00362 num_bytes = new_num_bytes; 00363 nassertr(egptr() - gptr() == num_bytes, EOF); 00364 } 00365 00366 nassertr(_source != NULL, EOF); 00367 streamsize read_count; 00368 bool eof; 00369 _source->seek_read(_gpos, gptr(), num_bytes, read_count, eof); 00370 _gpos += read_count; 00371 00372 if (read_count != num_bytes) { 00373 // Oops, we didn't read what we thought we would. 00374 if (read_count == 0) { 00375 gbump(num_bytes); 00376 return EOF; 00377 } 00378 00379 // Slide what we did read to the top of the buffer. 00380 nassertr(read_count < num_bytes, EOF); 00381 size_t delta = num_bytes - read_count; 00382 memmove(gptr() + delta, gptr(), read_count); 00383 gbump(delta); 00384 } 00385 } 00386 00387 return (unsigned char)*gptr(); 00388 }