Panda3D
 All Classes Functions Variables Enumerations
datagramInputFile.cxx
00001 // Filename: datagramInputFile.cxx
00002 // Created by:  drose (30Oct00)
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 "datagramInputFile.h"
00016 #include "temporaryFile.h"
00017 #include "numeric_types.h"
00018 #include "datagramIterator.h"
00019 #include "profileTimer.h"
00020 #include "config_util.h"
00021 #include "config_express.h"
00022 #include "virtualFileSystem.h"
00023 #include "streamReader.h"
00024 #include "thread.h"
00025 
00026 ////////////////////////////////////////////////////////////////////
00027 //     Function: DatagramInputFile::open
00028 //       Access: Published
00029 //  Description: Opens the indicated filename for reading.  Returns
00030 //               true on success, false on failure.
00031 ////////////////////////////////////////////////////////////////////
00032 bool DatagramInputFile::
00033 open(const FileReference *file) {
00034   close();
00035 
00036   _file = file;
00037   _filename = _file->get_filename();
00038 
00039   // DatagramInputFiles are always binary.
00040   _filename.set_binary();
00041 
00042   VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00043   _vfile = vfs->get_file(_filename);
00044   if (_vfile == (VirtualFile *)NULL) {
00045     // No such file.
00046     return false;
00047   }
00048   _in = _vfile->open_read_file(true);
00049   _owns_in = (_in != (istream *)NULL);
00050   return _owns_in && !_in->fail();
00051 }
00052 
00053 ////////////////////////////////////////////////////////////////////
00054 //     Function: DatagramInputFile::open
00055 //       Access: Published
00056 //  Description: Starts reading from the indicated stream.  Returns
00057 //               true on success, false on failure.  The
00058 //               DatagramInputFile does not take ownership of the
00059 //               stream; you are responsible for closing or deleting
00060 //               it when you are done.
00061 ////////////////////////////////////////////////////////////////////
00062 bool DatagramInputFile::
00063 open(istream &in, const Filename &filename) {
00064   close();
00065 
00066   _in = ∈
00067   _owns_in = false;
00068   _filename = filename;
00069 
00070   if (!filename.empty()) {
00071     _file = new FileReference(filename);
00072   }
00073 
00074   return !_in->fail();
00075 }
00076 
00077 ////////////////////////////////////////////////////////////////////
00078 //     Function: DatagramInputFile::close
00079 //       Access: Published
00080 //  Description: Closes the file.  This is also implicitly done when
00081 //               the DatagramInputFile destructs.
00082 ////////////////////////////////////////////////////////////////////
00083 void DatagramInputFile::
00084 close() {
00085   _vfile.clear();
00086   if (_owns_in) {
00087     VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00088     vfs->close_read_file(_in);
00089   }
00090   _in = (istream *)NULL;
00091   _owns_in = false;
00092 
00093   _file.clear();
00094   _filename = Filename();
00095 
00096   _read_first_datagram = false;
00097   _error = false;
00098 }
00099 
00100 ////////////////////////////////////////////////////////////////////
00101 //     Function: DatagramInputFile::read_header
00102 //       Access: Published
00103 //  Description: Reads a sequence of bytes from the beginning of the
00104 //               datagram file.  This may be called any number of
00105 //               times after the file has been opened and before the
00106 //               first datagram is read.  It may not be called once
00107 //               the first datagram has been read.
00108 ////////////////////////////////////////////////////////////////////
00109 bool DatagramInputFile::
00110 read_header(string &header, size_t num_bytes) {
00111   nassertr(!_read_first_datagram, false);
00112   nassertr(_in != (istream *)NULL, false);
00113 
00114   char *buffer = (char *)alloca(num_bytes);
00115   nassertr(buffer != (char *)NULL, false);
00116 
00117   _in->read(buffer, num_bytes);
00118   if (_in->fail() || _in->eof()) {
00119     return false;
00120   }
00121 
00122   header = string(buffer, num_bytes);
00123   Thread::consider_yield();
00124   return true;
00125 }
00126 
00127 ////////////////////////////////////////////////////////////////////
00128 //     Function: DatagramInputFile::get_datagram
00129 //       Access: Published, Virtual
00130 //  Description: Reads the next datagram from the file.  Returns true
00131 //               on success, false if there is an error or end of
00132 //               file.
00133 ////////////////////////////////////////////////////////////////////
00134 bool DatagramInputFile::
00135 get_datagram(Datagram &data) {
00136   nassertr(_in != (istream *)NULL, false);
00137   _read_first_datagram = true;
00138 
00139   // First, get the size of the upcoming datagram.
00140   StreamReader reader(_in, false);
00141   size_t num_bytes = reader.get_uint32();
00142   if (_in->fail() || _in->eof()) {
00143     return false;
00144   }
00145 
00146   if (num_bytes == 0) {
00147     // A special case for a zero-length datagram: no need to try to
00148     // read any data.
00149     data.clear();
00150     return true;
00151   }
00152 
00153   // Now, read the datagram itself.
00154 
00155   // If the number of bytes is large, we will need to allocate a
00156   // temporary buffer from the heap.  Otherwise, we can get away with
00157   // allocating it on the stack, via alloca().
00158   if (num_bytes > 65536) {
00159     char *buffer = (char *)PANDA_MALLOC_ARRAY(num_bytes);
00160     nassertr(buffer != (char *)NULL, false);
00161 
00162     _in->read(buffer, num_bytes);
00163     if (_in->fail() || _in->eof()) {
00164       _error = true;
00165       PANDA_FREE_ARRAY(buffer);
00166       return false;
00167     }
00168 
00169     data = Datagram(buffer, num_bytes);
00170     PANDA_FREE_ARRAY(buffer);
00171 
00172   } else {
00173     char *buffer = (char *)alloca(num_bytes);
00174     nassertr(buffer != (char *)NULL, false);
00175 
00176     _in->read(buffer, num_bytes);
00177     if (_in->fail() || _in->eof()) {
00178       _error = true;
00179       return false;
00180     }
00181 
00182     data = Datagram(buffer, num_bytes);
00183   }
00184   Thread::consider_yield();
00185 
00186   return true;
00187 }
00188 
00189 ////////////////////////////////////////////////////////////////////
00190 //     Function: DatagramInputFile::save_datagram
00191 //       Access: Published, Virtual
00192 //  Description: Skips over the next datagram without extracting it,
00193 //               but saves the relevant file information in the
00194 //               SubfileInfo object so that its data may be read
00195 //               later.  For non-file-based datagram generators, this
00196 //               may mean creating a temporary file and copying the
00197 //               contents of the datagram to disk.
00198 //
00199 //               Returns true on success, false on failure or if this
00200 //               method is unimplemented.
00201 ////////////////////////////////////////////////////////////////////
00202 bool DatagramInputFile::
00203 save_datagram(SubfileInfo &info) {
00204   nassertr(_in != (istream *)NULL, false);
00205   _read_first_datagram = true;
00206 
00207   // First, get the size of the upcoming datagram.
00208   StreamReader reader(_in, false);
00209   size_t num_bytes = reader.get_uint32();
00210   if (_in->fail() || _in->eof()) {
00211     return false;
00212   }
00213 
00214   // If this stream is file-based, we can just point the SubfileInfo
00215   // directly into this file.
00216   if (_file != (FileReference *)NULL) {
00217     info = SubfileInfo(_file, _in->tellg(), num_bytes);
00218     _in->seekg(num_bytes, ios::cur);
00219     return true;
00220   }
00221 
00222   // Otherwise, we have to dump the data into a temporary file.
00223   PT(TemporaryFile) tfile = new TemporaryFile(Filename::temporary("", ""));
00224   pofstream out;
00225   Filename filename = tfile->get_filename();
00226   filename.set_binary();
00227   if (!filename.open_write(out)) {
00228     util_cat.error()
00229       << "Couldn't write to " << tfile->get_filename() << "\n";
00230     return false;
00231   }
00232 
00233   if (util_cat.is_debug()) {
00234     util_cat.debug()
00235       << "Copying " << num_bytes << " bytes to " << tfile->get_filename() << "\n";
00236   }
00237 
00238   size_t num_remaining = num_bytes;
00239   static const size_t buffer_size = 4096;
00240   char buffer[buffer_size];
00241   
00242   _in->read(buffer, min(buffer_size, num_remaining));
00243   size_t count = _in->gcount();
00244   while (count != 0) {
00245     out.write(buffer, count);
00246     if (out.fail()) {
00247       util_cat.error()
00248         << "Couldn't write " << num_bytes << " bytes to " 
00249         << tfile->get_filename() << "\n";
00250       return false;
00251     }
00252     num_remaining -= count;
00253     if (num_remaining == 0) {
00254       break;
00255     }
00256     _in->read(buffer, min(buffer_size, num_remaining));
00257     count = _in->gcount();
00258   }
00259 
00260   if (num_remaining != 0) {
00261     util_cat.error()
00262       << "Truncated data stream.\n";
00263     return false;
00264   }
00265 
00266   info = SubfileInfo(tfile, 0, num_bytes);
00267   return true;
00268 }
00269 
00270 ////////////////////////////////////////////////////////////////////
00271 //     Function: DatagramInputFile::is_eof
00272 //       Access: Published, Virtual
00273 //  Description: Returns true if the file has reached the end-of-file.
00274 //               This test may only be made after a call to
00275 //               read_header() or get_datagram() has failed.
00276 ////////////////////////////////////////////////////////////////////
00277 bool DatagramInputFile::
00278 is_eof() {
00279   return _in != (istream *)NULL ? _in->eof() : true;
00280 }
00281 
00282 ////////////////////////////////////////////////////////////////////
00283 //     Function: DatagramInputFile::is_error
00284 //       Access: Published, Virtual
00285 //  Description: Returns true if the file has reached an error
00286 //               condition.
00287 ////////////////////////////////////////////////////////////////////
00288 bool DatagramInputFile::
00289 is_error() {
00290   if (_in == (istream *)NULL) {
00291     return true;
00292   }
00293 
00294   if (_in->fail()) {
00295     _error = true;
00296   }
00297   return _error;
00298 }
00299 
00300 ////////////////////////////////////////////////////////////////////
00301 //     Function: DatagramInputFile::get_filename
00302 //       Access: Published, Virtual
00303 //  Description: Returns the filename that provides the source for
00304 //               these datagrams, if any, or empty string if the
00305 //               datagrams do not originate from a file on disk.
00306 ////////////////////////////////////////////////////////////////////
00307 const Filename &DatagramInputFile::
00308 get_filename() {
00309   return _filename;
00310 }
00311 
00312 ////////////////////////////////////////////////////////////////////
00313 //     Function: DatagramInputFile::get_file
00314 //       Access: Published, Virtual
00315 //  Description: Returns the FileReference that provides the source for
00316 //               these datagrams, if any, or NULL if the datagrams do
00317 //               not originate from a file on disk.
00318 ////////////////////////////////////////////////////////////////////
00319 const FileReference *DatagramInputFile::
00320 get_file() {
00321   return _file;
00322 }
00323 
00324 ////////////////////////////////////////////////////////////////////
00325 //     Function: DatagramInputFile::get_vfile
00326 //       Access: Published, Virtual
00327 //  Description: Returns the VirtualFile that provides the source for
00328 //               these datagrams, if any, or NULL if the datagrams do
00329 //               not originate from a VirtualFile.
00330 ////////////////////////////////////////////////////////////////////
00331 VirtualFile *DatagramInputFile::
00332 get_vfile() {
00333   return _vfile;
00334 }
00335 
00336 ////////////////////////////////////////////////////////////////////
00337 //     Function: DatagramInputFile::get_file_pos
00338 //       Access: Published, Virtual
00339 //  Description: Returns the current file position within the data
00340 //               stream, if any, or 0 if the file position is not
00341 //               meaningful or cannot be determined.
00342 //
00343 //               For DatagramInputFiles that return a meaningful file
00344 //               position, this will be pointing to the first byte
00345 //               following the datagram returned after a call to
00346 //               get_datagram().
00347 ////////////////////////////////////////////////////////////////////
00348 streampos DatagramInputFile::
00349 get_file_pos() {
00350   if (_in == (istream *)NULL) {
00351     return 0;
00352   }
00353   return _in->tellg();
00354 }
 All Classes Functions Variables Enumerations