Panda3D
datagramInputFile.cxx
1 // Filename: datagramInputFile.cxx
2 // Created by: drose (30Oct00)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "datagramInputFile.h"
16 #include "temporaryFile.h"
17 #include "numeric_types.h"
18 #include "datagramIterator.h"
19 #include "profileTimer.h"
20 #include "config_util.h"
21 #include "config_express.h"
22 #include "virtualFileSystem.h"
23 #include "streamReader.h"
24 #include "thread.h"
25 
26 ////////////////////////////////////////////////////////////////////
27 // Function: DatagramInputFile::open
28 // Access: Published
29 // Description: Opens the indicated filename for reading. Returns
30 // true on success, false on failure.
31 ////////////////////////////////////////////////////////////////////
33 open(const FileReference *file) {
34  close();
35 
36  _file = file;
37  _filename = _file->get_filename();
38 
39  // DatagramInputFiles are always binary.
40  _filename.set_binary();
41 
43  _vfile = vfs->get_file(_filename);
44  if (_vfile == (VirtualFile *)NULL) {
45  // No such file.
46  return false;
47  }
48  _timestamp = _vfile->get_timestamp();
49  _in = _vfile->open_read_file(true);
50  _owns_in = (_in != (istream *)NULL);
51  return _owns_in && !_in->fail();
52 }
53 
54 ////////////////////////////////////////////////////////////////////
55 // Function: DatagramInputFile::open
56 // Access: Published
57 // Description: Starts reading from the indicated stream. Returns
58 // true on success, false on failure. The
59 // DatagramInputFile does not take ownership of the
60 // stream; you are responsible for closing or deleting
61 // it when you are done.
62 ////////////////////////////////////////////////////////////////////
64 open(istream &in, const Filename &filename) {
65  close();
66 
67  _in = ∈
68  _owns_in = false;
69  _filename = filename;
70  _timestamp = 0;
71 
72  if (!filename.empty()) {
73  _file = new FileReference(filename);
74  }
75 
76  return !_in->fail();
77 }
78 
79 ////////////////////////////////////////////////////////////////////
80 // Function: DatagramInputFile::close
81 // Access: Published
82 // Description: Closes the file. This is also implicitly done when
83 // the DatagramInputFile destructs.
84 ////////////////////////////////////////////////////////////////////
86 close() {
87  _vfile.clear();
88  if (_owns_in) {
90  vfs->close_read_file(_in);
91  }
92  _in = (istream *)NULL;
93  _owns_in = false;
94 
95  _file.clear();
96  _filename = Filename();
97  _timestamp = 0;
98 
99  _read_first_datagram = false;
100  _error = false;
101 }
102 
103 ////////////////////////////////////////////////////////////////////
104 // Function: DatagramInputFile::read_header
105 // Access: Published
106 // Description: Reads a sequence of bytes from the beginning of the
107 // datagram file. This may be called any number of
108 // times after the file has been opened and before the
109 // first datagram is read. It may not be called once
110 // the first datagram has been read.
111 ////////////////////////////////////////////////////////////////////
113 read_header(string &header, size_t num_bytes) {
114  nassertr(!_read_first_datagram, false);
115  nassertr(_in != (istream *)NULL, false);
116 
117  char *buffer = (char *)alloca(num_bytes);
118  nassertr(buffer != (char *)NULL, false);
119 
120  _in->read(buffer, num_bytes);
121  if (_in->fail() || _in->eof()) {
122  return false;
123  }
124 
125  header = string(buffer, num_bytes);
127  return true;
128 }
129 
130 ////////////////////////////////////////////////////////////////////
131 // Function: DatagramInputFile::get_datagram
132 // Access: Published, Virtual
133 // Description: Reads the next datagram from the file. Returns true
134 // on success, false if there is an error or end of
135 // file.
136 ////////////////////////////////////////////////////////////////////
139  nassertr(_in != (istream *)NULL, false);
140  _read_first_datagram = true;
141 
142  // First, get the size of the upcoming datagram.
143  StreamReader reader(_in, false);
144  PN_uint32 num_bytes_32 = reader.get_uint32();
145  if (_in->fail() || _in->eof()) {
146  return false;
147  }
148 
149  if (num_bytes_32 == 0) {
150  // A special case for a zero-length datagram: no need to try to
151  // read any data.
152  data.clear();
153  return true;
154  }
155 
156  streamsize num_bytes = (streamsize)num_bytes_32;
157  if (num_bytes_32 == (PN_uint32)-1) {
158  // Another special case for a value larger than 32 bits.
159  num_bytes = reader.get_uint64();
160  }
161 
162  // Make sure we have a reasonable datagram size for putting into
163  // memory.
164  nassertr(num_bytes == (size_t)num_bytes, false);
165 
166  // Now, read the datagram itself.
167 
168  // If the number of bytes is large, we will need to allocate a
169  // temporary buffer from the heap. Otherwise, we can get away with
170  // allocating it on the stack, via alloca().
171  if (num_bytes > 65536) {
172  char *buffer = (char *)PANDA_MALLOC_ARRAY(num_bytes);
173  nassertr(buffer != (char *)NULL, false);
174 
175  _in->read(buffer, num_bytes);
176  if (_in->fail() || _in->eof()) {
177  _error = true;
178  PANDA_FREE_ARRAY(buffer);
179  return false;
180  }
181 
182  data = Datagram(buffer, num_bytes);
183  PANDA_FREE_ARRAY(buffer);
184 
185  } else {
186  char *buffer = (char *)alloca(num_bytes);
187  nassertr(buffer != (char *)NULL, false);
188 
189  _in->read(buffer, num_bytes);
190  if (_in->fail() || _in->eof()) {
191  _error = true;
192  return false;
193  }
194 
195  data = Datagram(buffer, num_bytes);
196  }
198 
199  return true;
200 }
201 
202 ////////////////////////////////////////////////////////////////////
203 // Function: DatagramInputFile::save_datagram
204 // Access: Published, Virtual
205 // Description: Skips over the next datagram without extracting it,
206 // but saves the relevant file information in the
207 // SubfileInfo object so that its data may be read
208 // later. For non-file-based datagram generators, this
209 // may mean creating a temporary file and copying the
210 // contents of the datagram to disk.
211 //
212 // Returns true on success, false on failure or if this
213 // method is unimplemented.
214 ////////////////////////////////////////////////////////////////////
217  nassertr(_in != (istream *)NULL, false);
218  _read_first_datagram = true;
219 
220  // First, get the size of the upcoming datagram.
221  StreamReader reader(_in, false);
222  size_t num_bytes_32 = reader.get_uint32();
223  if (_in->fail() || _in->eof()) {
224  return false;
225  }
226 
227  streamsize num_bytes = (streamsize)num_bytes_32;
228  if (num_bytes_32 == (PN_uint32)-1) {
229  // Another special case for a value larger than 32 bits.
230  num_bytes = reader.get_uint64();
231  }
232 
233  // If this stream is file-based, we can just point the SubfileInfo
234  // directly into this file.
235  if (_file != (FileReference *)NULL) {
236  info = SubfileInfo(_file, _in->tellg(), num_bytes);
237  _in->seekg(num_bytes, ios::cur);
238  return true;
239  }
240 
241  // Otherwise, we have to dump the data into a temporary file.
242  PT(TemporaryFile) tfile = new TemporaryFile(Filename::temporary("", ""));
243  pofstream out;
244  Filename filename = tfile->get_filename();
245  filename.set_binary();
246  if (!filename.open_write(out)) {
247  util_cat.error()
248  << "Couldn't write to " << tfile->get_filename() << "\n";
249  return false;
250  }
251 
252  if (util_cat.is_debug()) {
253  util_cat.debug()
254  << "Copying " << num_bytes << " bytes to " << tfile->get_filename() << "\n";
255  }
256 
257  streamsize num_remaining = num_bytes;
258  static const size_t buffer_size = 4096;
259  char buffer[buffer_size];
260 
261  _in->read(buffer, min((streamsize)buffer_size, num_remaining));
262  streamsize count = _in->gcount();
263  while (count != 0) {
264  out.write(buffer, count);
265  if (out.fail()) {
266  util_cat.error()
267  << "Couldn't write " << num_bytes << " bytes to "
268  << tfile->get_filename() << "\n";
269  return false;
270  }
271  num_remaining -= count;
272  if (num_remaining == 0) {
273  break;
274  }
275  _in->read(buffer, min((streamsize)buffer_size, num_remaining));
276  count = _in->gcount();
277  }
278 
279  if (num_remaining != 0) {
280  util_cat.error()
281  << "Truncated data stream.\n";
282  return false;
283  }
284 
285  info = SubfileInfo(tfile, 0, num_bytes);
286  return true;
287 }
288 
289 ////////////////////////////////////////////////////////////////////
290 // Function: DatagramInputFile::is_eof
291 // Access: Published, Virtual
292 // Description: Returns true if the file has reached the end-of-file.
293 // This test may only be made after a call to
294 // read_header() or get_datagram() has failed.
295 ////////////////////////////////////////////////////////////////////
298  return _in != (istream *)NULL ? _in->eof() : true;
299 }
300 
301 ////////////////////////////////////////////////////////////////////
302 // Function: DatagramInputFile::is_error
303 // Access: Published, Virtual
304 // Description: Returns true if the file has reached an error
305 // condition.
306 ////////////////////////////////////////////////////////////////////
309  if (_in == (istream *)NULL) {
310  return true;
311  }
312 
313  if (_in->fail()) {
314  _error = true;
315  }
316  return _error;
317 }
318 
319 ////////////////////////////////////////////////////////////////////
320 // Function: DatagramInputFile::get_filename
321 // Access: Published, Virtual
322 // Description: Returns the filename that provides the source for
323 // these datagrams, if any, or empty string if the
324 // datagrams do not originate from a file on disk.
325 ////////////////////////////////////////////////////////////////////
328  return _filename;
329 }
330 
331 ////////////////////////////////////////////////////////////////////
332 // Function: DatagramInputFile::get_timestamp
333 // Access: Published, Virtual
334 // Description: Returns the on-disk timestamp of the file that was
335 // read, at the time it was opened, if that is
336 // available, or 0 if it is not.
337 ////////////////////////////////////////////////////////////////////
338 time_t DatagramInputFile::
339 get_timestamp() const {
340  return _timestamp;
341 }
342 
343 ////////////////////////////////////////////////////////////////////
344 // Function: DatagramInputFile::get_file
345 // Access: Published, Virtual
346 // Description: Returns the FileReference that provides the source for
347 // these datagrams, if any, or NULL if the datagrams do
348 // not originate from a file on disk.
349 ////////////////////////////////////////////////////////////////////
352  return _file;
353 }
354 
355 ////////////////////////////////////////////////////////////////////
356 // Function: DatagramInputFile::get_vfile
357 // Access: Published, Virtual
358 // Description: Returns the VirtualFile that provides the source for
359 // these datagrams, if any, or NULL if the datagrams do
360 // not originate from a VirtualFile.
361 ////////////////////////////////////////////////////////////////////
364  return _vfile;
365 }
366 
367 ////////////////////////////////////////////////////////////////////
368 // Function: DatagramInputFile::get_file_pos
369 // Access: Published, Virtual
370 // Description: Returns the current file position within the data
371 // stream, if any, or 0 if the file position is not
372 // meaningful or cannot be determined.
373 //
374 // For DatagramInputFiles that return a meaningful file
375 // position, this will be pointing to the first byte
376 // following the datagram returned after a call to
377 // get_datagram().
378 ////////////////////////////////////////////////////////////////////
379 streampos DatagramInputFile::
381  if (_in == (istream *)NULL) {
382  return 0;
383  }
384  return _in->tellg();
385 }
Keeps a reference-counted pointer to a file on disk.
Definition: fileReference.h:29
bool open_write(ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:2045
virtual bool is_error()
Returns true if the file has reached an error condition.
A hierarchy of directories and files that appears to be one continuous file system, even though the files may originate from several different sources that may not be related to the actual OS&#39;s file system.
virtual streampos get_file_pos()
Returns the current file position within the data stream, if any, or 0 if the file position is not me...
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:494
PN_uint32 get_uint32()
Extracts an unsigned 32-bit integer.
Definition: streamReader.I:183
virtual bool save_datagram(SubfileInfo &info)
Skips over the next datagram without extracting it, but saves the relevant file information in the Su...
virtual void clear()
Resets the datagram to empty, in preparation for building up a new datagram.
Definition: datagram.cxx:41
void close()
Closes the file.
bool open(const FileReference *file)
Opens the indicated filename for reading.
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
Definition: thread.I:263
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:37
virtual bool get_datagram(Datagram &data)
Reads the next datagram from the file.
bool read_header(string &header, size_t num_bytes)
Reads a sequence of bytes from the beginning of the datagram file.
static void close_read_file(istream *stream)
Closes a file opened by a previous call to open_read_file().
static Filename temporary(const string &dirname, const string &prefix, const string &suffix=string(), Type type=T_general)
Generates a temporary filename within the indicated directory, using the indicated prefix...
Definition: filename.cxx:439
virtual VirtualFile * get_vfile()
Returns the VirtualFile that provides the source for these datagrams, if any, or NULL if the datagram...
virtual bool is_eof()
Returns true if the file has reached the end-of-file.
PN_uint64 get_uint64()
Extracts an unsigned 64-bit integer.
Definition: streamReader.I:197
This is a special kind of FileReference class that automatically deletes the file in question when it...
Definition: temporaryFile.h:29
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
virtual const Filename & get_filename()
Returns the filename that provides the source for these datagrams, if any, or empty string if the dat...
This class records a particular byte sub-range within an existing file on disk.
Definition: subfileInfo.h:29
virtual const FileReference * get_file()
Returns the FileReference that provides the source for these datagrams, if any, or NULL if the datagr...
A class to read sequential binary data directly from an istream.
Definition: streamReader.h:30
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:43
virtual time_t get_timestamp() const
Returns the on-disk timestamp of the file that was read, at the time it was opened, if that is available, or 0 if it is not.