Panda3D
datagramInputFile.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 datagramInputFile.cxx
10  * @author drose
11  * @date 2000-10-30
12  */
13 
14 #include "datagramInputFile.h"
15 #include "temporaryFile.h"
16 #include "numeric_types.h"
17 #include "datagramIterator.h"
18 #include "profileTimer.h"
19 #include "config_putil.h"
20 #include "config_express.h"
21 #include "virtualFileSystem.h"
22 #include "streamReader.h"
23 #include "thread.h"
24 
25 using std::streampos;
26 using std::streamsize;
27 
28 /**
29  * Opens the indicated filename for reading. Returns true on success, false
30  * 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 == nullptr) {
45  // No such file.
46  return false;
47  }
48  _timestamp = _vfile->get_timestamp();
49  _in = _vfile->open_read_file(true);
50  _owns_in = (_in != nullptr);
51  return _owns_in && !_in->fail();
52 }
53 
54 /**
55  * Starts reading from the indicated stream. Returns true on success, false
56  * on failure. The DatagramInputFile does not take ownership of the stream;
57  * you are responsible for closing or deleting it when you are done.
58  */
60 open(std::istream &in, const Filename &filename) {
61  close();
62 
63  _in = ∈
64  _owns_in = false;
65  _filename = filename;
66  _timestamp = 0;
67 
68  if (!filename.empty()) {
69  _file = new FileReference(filename);
70  }
71 
72  return !_in->fail();
73 }
74 
75 /**
76  * Closes the file. This is also implicitly done when the DatagramInputFile
77  * destructs.
78  */
80 close() {
81  _vfile.clear();
82  if (_owns_in) {
84  vfs->close_read_file(_in);
85  }
86  _in = nullptr;
87  _owns_in = false;
88 
89  _file.clear();
90  _filename = Filename();
91  _timestamp = 0;
92 
93  _read_first_datagram = false;
94  _error = false;
95 }
96 
97 /**
98  * Reads a sequence of bytes from the beginning of the datagram file. This
99  * may be called any number of times after the file has been opened and before
100  * the first datagram is read. It may not be called once the first datagram
101  * has been read.
102  */
104 read_header(std::string &header, size_t num_bytes) {
105  nassertr(!_read_first_datagram, false);
106  nassertr(_in != nullptr, false);
107 
108  char *buffer = (char *)alloca(num_bytes);
109  nassertr(buffer != nullptr, false);
110 
111  _in->read(buffer, num_bytes);
112  if (_in->fail()) {
113  return false;
114  }
115 
116  header = std::string(buffer, num_bytes);
118  return true;
119 }
120 
121 /**
122  * Reads the next datagram from the file. Returns true on success, false if
123  * there is an error or end of file.
124  */
127  nassertr(_in != nullptr, false);
128  _read_first_datagram = true;
129 
130  // First, get the size of the upcoming datagram.
131  StreamReader reader(_in, false);
132  uint32_t num_bytes_32 = reader.get_uint32();
133  if (_in->fail()) {
134  return false;
135  }
136 
137  if (num_bytes_32 == 0) {
138  // A special case for a zero-length datagram: no need to try to read any
139  // data.
140  data.clear();
141  return true;
142  }
143 
144  if (_in->eof()) {
145  return false;
146  }
147 
148  size_t num_bytes = (size_t)num_bytes_32;
149  if (num_bytes_32 == (uint32_t)-1) {
150  // Another special case for a value larger than 32 bits.
151  uint64_t num_bytes_64 = reader.get_uint64();
152 
153  if (_in->fail()) {
154  _error = true;
155  return false;
156  }
157 
158  if (num_bytes_64 == 0) {
159  data.clear();
160  return false;
161  }
162 
163  if (_in->eof()) {
164  return false;
165  }
166 
167  num_bytes = (size_t)num_bytes_64;
168 
169  // Make sure we have a reasonable datagram size for putting into memory.
170  if (num_bytes_64 != (uint64_t)num_bytes) {
171  _error = true;
172  return false;
173  }
174  }
175 
176  // Now, read the datagram itself. We construct an empty datagram, use
177  // pad_bytes to make it big enough, and read *directly* into the datagram's
178  // internal buffer. Doing this saves us a copy operation.
179  data = Datagram();
180 
181  size_t bytes_read = 0;
182  while (bytes_read < num_bytes) {
183  size_t bytes_left = num_bytes - bytes_read;
184 
185  if (_in->eof()) {
186  _error = true;
187  return false;
188  }
189 
190  // Hold up a second - datagrams >4MB are pretty large by bam/network
191  // standards. Let's take it 4MB at a time just in case the length is
192  // corrupt, so we don't allocate potentially a few GBs of RAM only to
193  // find a truncated file.
194  bytes_left = std::min(bytes_left, (size_t)4*1024*1024);
195 
196  PTA_uchar buffer = data.modify_array();
197  buffer.resize(buffer.size() + bytes_left);
198  unsigned char *ptr = &buffer.p()[bytes_read];
199 
200  _in->read((char *)ptr, (streamsize)bytes_left);
201  if (_in->fail()) {
202  _error = true;
203  return false;
204  }
205 
206  bytes_read += bytes_left;
207  }
208 
210 
211  return true;
212 }
213 
214 /**
215  * Skips over the next datagram without extracting it, but saves the relevant
216  * file information in the SubfileInfo object so that its data may be read
217  * later. For non-file-based datagram generators, this may mean creating a
218  * temporary file and copying the contents of the datagram to disk.
219  *
220  * Returns true on success, false on failure or if this method is
221  * unimplemented.
222  */
225  nassertr(_in != nullptr, false);
226  _read_first_datagram = true;
227 
228  // First, get the size of the upcoming datagram.
229  StreamReader reader(_in, false);
230  size_t num_bytes_32 = reader.get_uint32();
231  if (_in->fail() || (_in->eof() && num_bytes_32 > 0)) {
232  return false;
233  }
234 
235  streamsize num_bytes = (streamsize)num_bytes_32;
236  if (num_bytes_32 == (uint32_t)-1) {
237  // Another special case for a value larger than 32 bits.
238  num_bytes = reader.get_uint64();
239  }
240 
241  // If this stream is file-based, we can just point the SubfileInfo directly
242  // into this file.
243  if (_file != nullptr) {
244  info = SubfileInfo(_file, _in->tellg(), num_bytes);
245  _in->seekg(num_bytes, std::ios::cur);
246  return true;
247  }
248 
249  // Otherwise, we have to dump the data into a temporary file.
250  PT(TemporaryFile) tfile = new TemporaryFile(Filename::temporary("", ""));
251  pofstream out;
252  Filename filename = tfile->get_filename();
253  filename.set_binary();
254  if (!filename.open_write(out)) {
255  util_cat.error()
256  << "Couldn't write to " << tfile->get_filename() << "\n";
257  return false;
258  }
259 
260  if (util_cat.is_debug()) {
261  util_cat.debug()
262  << "Copying " << num_bytes << " bytes to " << tfile->get_filename() << "\n";
263  }
264 
265  streamsize num_remaining = num_bytes;
266  static const size_t buffer_size = 4096;
267  char buffer[buffer_size];
268 
269  _in->read(buffer, std::min((streamsize)buffer_size, num_remaining));
270  streamsize count = _in->gcount();
271  while (count != 0) {
272  out.write(buffer, count);
273  if (out.fail()) {
274  util_cat.error()
275  << "Couldn't write " << num_bytes << " bytes to "
276  << tfile->get_filename() << "\n";
277  return false;
278  }
279  num_remaining -= count;
280  if (num_remaining == 0) {
281  break;
282  }
283  _in->read(buffer, std::min((streamsize)buffer_size, num_remaining));
284  count = _in->gcount();
285  }
286 
287  if (num_remaining != 0) {
288  util_cat.error()
289  << "Truncated data stream.\n";
290  return false;
291  }
292 
293  info = SubfileInfo(tfile, 0, num_bytes);
294  return true;
295 }
296 
297 /**
298  * Returns true if the file has reached the end-of-file. This test may only
299  * be made after a call to read_header() or get_datagram() has failed.
300  */
303  return _in != nullptr ? _in->eof() : true;
304 }
305 
306 /**
307  * Returns true if the file has reached an error condition.
308  */
311  if (_in == nullptr) {
312  return true;
313  }
314 
315  if (_in->fail()) {
316  _error = true;
317  }
318  return _error;
319 }
320 
321 /**
322  * Returns the filename that provides the source for these datagrams, if any,
323  * or empty string if the datagrams do not originate from a file on disk.
324  */
327  return _filename;
328 }
329 
330 /**
331  * Returns the on-disk timestamp of the file that was read, at the time it was
332  * opened, if that is available, or 0 if it is not.
333  */
334 time_t DatagramInputFile::
335 get_timestamp() const {
336  return _timestamp;
337 }
338 
339 /**
340  * Returns the FileReference that provides the source for these datagrams, if
341  * any, or NULL if the datagrams do not originate from a file on disk.
342  */
345  return _file;
346 }
347 
348 /**
349  * Returns the VirtualFile that provides the source for these datagrams, if
350  * any, or NULL if the datagrams do not originate from a VirtualFile.
351  */
354  return _vfile;
355 }
356 
357 /**
358  * Returns the current file position within the data stream, if any, or 0 if
359  * the file position is not meaningful or cannot be determined.
360  *
361  * For DatagramInputFiles that return a meaningful file position, this will be
362  * pointing to the first byte following the datagram returned after a call to
363  * get_datagram().
364  */
365 streampos DatagramInputFile::
367  if (_in == nullptr) {
368  return 0;
369  }
370  return _in->tellg();
371 }
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:1899
Keeps a reference-counted pointer to a file on disk.
Definition: fileReference.h:26
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool is_error()
Returns true if the file has reached an error condition.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
A hierarchy of directories and files that appears to be one continuous file system,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual std::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:414
bool read_header(std::string &header, size_t num_bytes)
Reads a sequence of bytes from the beginning of the datagram file.
virtual bool save_datagram(SubfileInfo &info)
Skips over the next datagram without extracting it, but saves the relevant file information in the Su...
uint64_t get_uint64()
Extracts an unsigned 64-bit integer.
Definition: streamReader.I:163
void close()
Closes the file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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:212
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
virtual bool get_datagram(Datagram &data)
Reads the next datagram from the file.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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.
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
This is a special kind of FileReference class that automatically deletes the file in question when it...
Definition: temporaryFile.h:26
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
Definition: streamReader.I:151
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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:26
static Filename temporary(const std::string &dirname, const std::string &prefix, const std::string &suffix=std::string(), Type type=T_general)
Generates a temporary filename within the indicated directory, using the indicated prefix.
Definition: filename.cxx:424
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:28
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
virtual time_t get_timestamp() const
Returns the on-disk timestamp of the file that was read, at the time it was opened,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.