00001 // Filename: subStreamBuf.cxx 00002 // Created by: drose (02Aug02) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved 00008 // 00009 // All use of this software is subject to the terms of the Panda 3d 00010 // Software license. You should have received a copy of this license 00011 // along with this source code; you will also find a current copy of 00012 // the license at http://www.panda3d.org/license.txt . 00013 // 00014 // To contact the maintainers of this program write to 00015 // panda3d@yahoogroups.com . 00016 // 00017 //////////////////////////////////////////////////////////////////// 00018 00019 #include "subStreamBuf.h" 00020 00021 #ifndef HAVE_STREAMSIZE 00022 // Some compilers (notably SGI) don't define this for us 00023 typedef int streamsize; 00024 #endif /* HAVE_STREAMSIZE */ 00025 00026 // Temporary hack to make these thread-safe. 00027 //#define SUBSTREAM_THREAD_SAFE 00028 00029 #ifdef SUBSTREAM_THREAD_SAFE 00030 // This requires NSPR for now. 00031 #include <prlock.h> 00032 PRLock *substream_mutex = (PRLock *)NULL; 00033 00034 inline void init_lock() { 00035 if (substream_mutex == (PRLock *)NULL) { 00036 substream_mutex = PR_NewLock(); 00037 } 00038 } 00039 inline void grab_lock() { 00040 PR_Lock(substream_mutex); 00041 } 00042 inline void release_lock() { 00043 PR_Unlock(substream_mutex); 00044 } 00045 #else // SUBSTREAM_THREAD_SAFE 00046 inline void init_lock() { 00047 } 00048 inline void grab_lock() { 00049 } 00050 inline void release_lock() { 00051 } 00052 #endif // SUBSTREAM_THREAD_SAFE 00053 00054 //////////////////////////////////////////////////////////////////// 00055 // Function: SubStreamBuf::Constructor 00056 // Access: Public 00057 // Description: 00058 //////////////////////////////////////////////////////////////////// 00059 SubStreamBuf:: 00060 SubStreamBuf() { 00061 _source = (istream *)NULL; 00062 00063 // _start is the streampos of the first byte of the SubStream within 00064 // its parent stream. 00065 _start = 0; 00066 00067 // _end is the streampos of the byte following the last byte of the 00068 // SubStream within its parent stream. If _end is 0, the SubStream 00069 // continues to the end of the parent stream, wherever that is. 00070 _end = 0; 00071 00072 // _cur is the streampos of the end of the read buffer (that is, 00073 // egptr()) within the parent stream. By comparing _cur to gpos(), 00074 // we can determine the actual current file position. 00075 _cur = 0; 00076 00077 // _unused counts the number of bytes at the beginning of the buffer 00078 // that are unused. Usually this is 0 after a read, but when we 00079 // reach the end of the file we might not need the whole buffer to 00080 // read the last bit, so the first part of the buffer is unused. 00081 // This is important to prevent us from inadvertently seeking into 00082 // the unused part of the buffer. 00083 _unused = 0; 00084 00085 init_lock(); 00086 00087 #ifdef HAVE_IOSTREAM 00088 // The new-style iostream library doesn't seem to support allocate(). 00089 char *buf = new char[4096]; 00090 char *ebuf = buf + 4096; 00091 setg(buf, ebuf, ebuf); 00092 00093 #else 00094 allocate(); 00095 setg(base(), ebuf(), ebuf()); 00096 #endif 00097 } 00098 00099 //////////////////////////////////////////////////////////////////// 00100 // Function: SubStreamBuf::Destructor 00101 // Access: Public, Virtual 00102 // Description: 00103 //////////////////////////////////////////////////////////////////// 00104 SubStreamBuf:: 00105 ~SubStreamBuf() { 00106 close(); 00107 } 00108 00109 //////////////////////////////////////////////////////////////////// 00110 // Function: SubStreamBuf::open 00111 // Access: Public 00112 // Description: 00113 //////////////////////////////////////////////////////////////////// 00114 void SubStreamBuf:: 00115 open(istream *source, streampos start, streampos end) { 00116 _source = source; 00117 _start = start; 00118 _end = end; 00119 _cur = _start; 00120 00121 // Initially, the entire buffer is unused. Duh. 00122 _unused = egptr() - eback(); 00123 } 00124 00125 //////////////////////////////////////////////////////////////////// 00126 // Function: SubStreamBuf::close 00127 // Access: Public 00128 // Description: 00129 //////////////////////////////////////////////////////////////////// 00130 void SubStreamBuf:: 00131 close() { 00132 _source = (istream *)NULL; 00133 _start = 0; 00134 _end = 0; 00135 _cur = 0; 00136 _unused = 0; 00137 } 00138 00139 //////////////////////////////////////////////////////////////////// 00140 // Function: SubStreamBuf::seekoff 00141 // Access: Public, Virtual 00142 // Description: Implements seeking within the stream. 00143 //////////////////////////////////////////////////////////////////// 00144 streampos SubStreamBuf:: 00145 seekoff(streamoff off, ios_seekdir dir, ios_openmode mode) { 00146 // Invariant: _cur points to the file location of the buffer at 00147 // egptr(). 00148 00149 // Use this to determine the actual file position right now. 00150 size_t n = egptr() - gptr(); 00151 streampos cur_pos = _cur - (streampos)n; 00152 streampos new_pos = cur_pos; 00153 00154 // Now adjust the data pointer appropriately. 00155 00156 // Casting this to int to prevent GCC 3.2 compiler warnings. Very 00157 // suspicious, need to investigate further. 00158 switch ((int)dir) { 00159 case ios::beg: 00160 new_pos = _start + off; 00161 break; 00162 00163 case ios::cur: 00164 new_pos = cur_pos + off; 00165 break; 00166 00167 case ios::end: 00168 if (_end == (streampos)0) { 00169 // If the end of the file is unspecified, we have to seek to 00170 // find it. 00171 grab_lock(); 00172 _source->seekg(off, ios::end); 00173 new_pos = _source->tellg(); 00174 release_lock(); 00175 00176 } else { 00177 new_pos = _end + off; 00178 } 00179 break; 00180 } 00181 00182 new_pos = max(_start, new_pos); 00183 streamsize delta = new_pos - cur_pos; 00184 00185 if (gptr() + delta >= eback() + _unused && gptr() + delta <= egptr()) { 00186 // If we can get away with just bumping the gptr within the 00187 // buffer, do so. 00188 gbump(delta); 00189 00190 } else { 00191 // Otherwise, empty the buffer and force it to call underflow(). 00192 gbump(n); 00193 _cur = new_pos; 00194 } 00195 00196 return new_pos - _start; 00197 } 00198 00199 //////////////////////////////////////////////////////////////////// 00200 // Function: SubStreamBuf::seekpos 00201 // Access: Public, Virtual 00202 // Description: A variant on seekoff() to implement seeking within a 00203 // stream. 00204 // 00205 // The MSDN Library claims that it is only necessary to 00206 // redefine seekoff(), and not seekpos() as well, as the 00207 // default implementation of seekpos() is supposed to 00208 // map to seekoff() exactly as I am doing here; but in 00209 // fact it must do something else, because seeking 00210 // didn't work on Windows until I redefined this 00211 // function as well. 00212 //////////////////////////////////////////////////////////////////// 00213 streampos SubStreamBuf:: 00214 seekpos(streampos pos, ios_openmode mode) { 00215 return seekoff(pos, ios::beg, mode); 00216 } 00217 00218 //////////////////////////////////////////////////////////////////// 00219 // Function: SubStreamBuf::overflow 00220 // Access: Protected, Virtual 00221 // Description: Called by the system ostream implementation when its 00222 // internal buffer is filled, plus one character. 00223 //////////////////////////////////////////////////////////////////// 00224 int SubStreamBuf:: 00225 overflow(int c) { 00226 // We don't support ostream. 00227 return 0; 00228 } 00229 00230 //////////////////////////////////////////////////////////////////// 00231 // Function: SubStreamBuf::sync 00232 // Access: Protected, Virtual 00233 // Description: Called by the system iostream implementation to 00234 // implement a flush operation. 00235 //////////////////////////////////////////////////////////////////// 00236 int SubStreamBuf:: 00237 sync() { 00238 size_t n = egptr() - gptr(); 00239 gbump(n); 00240 00241 return 0; 00242 } 00243 00244 //////////////////////////////////////////////////////////////////// 00245 // Function: SubStreamBuf::underflow 00246 // Access: Protected, Virtual 00247 // Description: Called by the system istream implementation when its 00248 // internal buffer needs more characters. 00249 //////////////////////////////////////////////////////////////////// 00250 int SubStreamBuf:: 00251 underflow() { 00252 // Sometimes underflow() is called even if the buffer is not empty. 00253 if (gptr() >= egptr()) { 00254 if (_end != (streampos)0 && _cur >= _end) { 00255 // We're done. 00256 return EOF; 00257 } 00258 00259 size_t buffer_size = egptr() - eback(); 00260 size_t num_bytes; 00261 if (_end == (streampos)0 || _end - _cur > (streampos)buffer_size) { 00262 // We have enough bytes in the input stream to fill our buffer. 00263 num_bytes = buffer_size; 00264 } else { 00265 // We won't quite fill the buffer. 00266 num_bytes = (size_t)(_end - _cur); 00267 } 00268 00269 gbump(-(int)num_bytes); 00270 nassertr(gptr() + num_bytes <= egptr(), EOF); 00271 00272 grab_lock(); 00273 _source->seekg(_cur); 00274 _source->read(gptr(), num_bytes); 00275 size_t read_count = _source->gcount(); 00276 release_lock(); 00277 00278 if (read_count != num_bytes) { 00279 // Oops, we didn't read what we thought we would. 00280 if (read_count == 0) { 00281 _unused = buffer_size; 00282 if (_end != (streampos)0) { 00283 _end = _cur; 00284 } 00285 gbump(num_bytes); 00286 return EOF; 00287 } 00288 00289 // Slide what we did read to the top of the buffer. 00290 nassertr(read_count < num_bytes, EOF); 00291 size_t delta = num_bytes - read_count; 00292 memmove(gptr() + delta, gptr(), read_count); 00293 gbump(delta); 00294 } 00295 00296 // Now record whatever bytes at the beginning of the buffer are 00297 // unused, so we won't try to seek into that area. 00298 _unused = buffer_size - read_count; 00299 00300 // Invariant: _cur points to the file location of the buffer at 00301 // egptr(). 00302 00303 _cur += read_count; 00304 } 00305 00306 return (unsigned char)*gptr(); 00307 }