Panda3D
Loading...
Searching...
No Matches
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
25using std::streampos;
26using std::streamsize;
27
28/**
29 * Opens the indicated filename for reading. Returns true on success, false
30 * on failure.
31 */
33open(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 */
60open(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 */
80close() {
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 */
104read_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 */
126get_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 */
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 */
302is_eof() {
303 return _in != nullptr ? _in->eof() : true;
304}
305
306/**
307 * Returns true if the file has reached an error condition.
308 */
310is_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 */
326get_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 */
335get_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 */
344get_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 */
353get_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 */
366get_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.
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
void set_binary()
Indicates that the filename represents a binary file.
Definition filename.I:414
bool open_write(std::ofstream &stream, bool truncate=true) const
Opens the indicated ifstream for writing the file, if possible.
A class to read sequential binary data directly from an istream.
uint64_t get_uint64()
Extracts an unsigned 64-bit integer.
uint32_t get_uint32()
Extracts an unsigned 32-bit integer.
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...
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.