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  */
126 get_datagram(Datagram &data) {
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  */
224 save_datagram(SubfileInfo &info) {
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  */
302 is_eof() {
303  return _in != nullptr ? _in->eof() : true;
304 }
305 
306 /**
307  * Returns true if the file has reached an error condition.
308  */
310 is_error() {
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  */
326 get_filename() {
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  */
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  */
344 get_file() {
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  */
353 get_vfile() {
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  */
366 get_file_pos() {
367  if (_in == nullptr) {
368  return 0;
369  }
370  return _in->tellg();
371 }
virtual VirtualFile * get_vfile()
Returns the VirtualFile that provides the source for these datagrams, if any, or NULL if the datagram...
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...
virtual time_t get_timestamp() const
Returns the on-disk timestamp of the file that was read, at the time it was opened,...
virtual bool get_datagram(Datagram &data)
Reads the next datagram from the file.
bool read_header(std::string &header, size_t num_bytes)
Reads a sequence of bytes from the beginning of the datagram file.
virtual bool is_eof()
Returns true if the file has reached the end-of-file.
void close()
Closes the file.
bool open(const FileReference *file)
Opens the indicated filename for reading.
virtual const FileReference * get_file()
Returns the FileReference that provides the source for these datagrams, if any, or NULL if the datagr...
virtual const Filename & get_filename()
Returns the filename that provides the source for these datagrams, if any, or empty string if the dat...
virtual bool is_error()
Returns true if the file has reached an error condition.
virtual bool save_datagram(SubfileInfo &info)
Skips over the next datagram without extracting it, but saves the relevant file information in the Su...
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
Keeps a reference-counted pointer to a file on disk.
Definition: fileReference.h:26
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:414
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
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
Definition: filename.cxx:1899
A class to read sequential binary data directly from an istream.
Definition: streamReader.h:28
uint64_t get_uint64()
Extracts an unsigned 64-bit integer.
Definition: streamReader.I:163
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
Definition: streamReader.I:151
This class records a particular byte sub-range within an existing file on disk.
Definition: subfileInfo.h:26
This is a special kind of FileReference class that automatically deletes the file in question when it...
Definition: temporaryFile.h:26
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
A hierarchy of directories and files that appears to be one continuous file system,...
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.