Panda3D
datagramOutputFile.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 datagramOutputFile.cxx
10  * @author drose
11  * @date 2000-10-30
12  */
13 
14 #include "datagramOutputFile.h"
15 #include "streamWriter.h"
16 #include "zStream.h"
17 #include <algorithm>
18 
19 using std::min;
20 using std::streampos;
21 using std::streamsize;
22 
23 /**
24  * Opens the indicated filename for writing. Returns true if successful,
25  * false on failure.
26  */
28 open(const FileReference *file) {
29  close();
30 
31  _file = file;
32  _filename = _file->get_filename();
33 
34  // DatagramOutputFiles are always binary.
35  _filename.set_binary();
36 
38  _vfile = vfs->create_file(_filename);
39  if (_vfile == nullptr) {
40  // No such file.
41  return false;
42  }
43  _out = _vfile->open_write_file(true, true);
44  _owns_out = (_out != nullptr);
45  return _owns_out && !_out->fail();
46 }
47 
48 /**
49  * Starts writing to the indicated stream. Returns true on success, false on
50  * failure. The DatagramOutputFile does not take ownership of the stream; you
51  * are responsible for closing or deleting it when you are done.
52  */
54 open(std::ostream &out, const Filename &filename) {
55  close();
56 
57  _out = &out;
58  _owns_out = false;
59  _filename = filename;
60 
61  if (!filename.empty()) {
62  _file = new FileReference(filename);
63  }
64 
65  return !_out->fail();
66 }
67 
68 /**
69  * Closes the file. This is also implicitly done when the DatagramOutputFile
70  * destructs.
71  */
73 close() {
74  _vfile.clear();
75  if (_owns_out) {
77  vfs->close_write_file(_out);
78  }
79  _out = nullptr;
80  _owns_out = false;
81 
82  _file.clear();
83  _filename = Filename();
84 
85  _wrote_first_datagram = false;
86  _error = false;
87 }
88 
89 /**
90  * Writes a sequence of bytes to the beginning of the datagram file. This may
91  * be called any number of times after the file has been opened and before the
92  * first datagram is written. It may not be called once the first datagram is
93  * written.
94  */
96 write_header(const std::string &header) {
97  nassertr(_out != nullptr, false);
98  nassertr(!_wrote_first_datagram, false);
99 
100  _out->write(header.data(), header.size());
101  thread_consider_yield();
102  return !_out->fail();
103 }
104 
105 /**
106  * Writes the given datagram to the file. Returns true on success, false if
107  * there is an error.
108  */
110 put_datagram(const Datagram &data) {
111  nassertr(_out != nullptr, false);
112  _wrote_first_datagram = true;
113 
114  // First, write the size of the upcoming datagram.
115  StreamWriter writer(_out, false);
116  size_t num_bytes = data.get_length();
117  if (num_bytes == (uint32_t)-1 || num_bytes != (uint32_t)num_bytes) {
118  // Write a large value as a 64-bit size.
119  writer.add_uint32((uint32_t)-1);
120  writer.add_uint64(num_bytes);
121  } else {
122  // Write a value that fits in 32 bits.
123  writer.add_uint32((uint32_t)num_bytes);
124  }
125 
126  // Now, write the datagram itself.
127  _out->write((const char *)data.get_data(), data.get_length());
128  thread_consider_yield();
129 
130  return !_out->fail();
131 }
132 
133 /**
134  * Copies the file data from the entire indicated file (via the vfs) as the
135  * next datagram. This is intended to support potentially very large
136  * datagrams.
137  *
138  * Returns true on success, false on failure or if this method is
139  * unimplemented. On true, fills "result" with the information that
140  * references the copied file, if possible.
141  */
143 copy_datagram(SubfileInfo &result, const Filename &filename) {
144  nassertr(_out != nullptr, false);
145  _wrote_first_datagram = true;
146 
148  PT(VirtualFile) vfile = vfs->get_file(filename);
149  if (vfile == nullptr) {
150  return false;
151  }
152  std::istream *in = vfile->open_read_file(true);
153  if (in == nullptr) {
154  return false;
155  }
156 
157  streamsize size = vfile->get_file_size(in);
158  streamsize num_remaining = size;
159 
160  StreamWriter writer(_out, false);
161  if (num_remaining == (uint32_t)-1 || num_remaining != (uint32_t)num_remaining) {
162  // Write a large value as a 64-bit size.
163  writer.add_uint32((uint32_t)-1);
164  writer.add_uint64(num_remaining);
165  } else {
166  // Write a value that fits in 32 bits.
167  writer.add_uint32((uint32_t)num_remaining);
168  }
169 
170  static const size_t buffer_size = 4096;
171  char buffer[buffer_size];
172 
173  streampos start = _out->tellp();
174  in->read(buffer, min((streamsize)buffer_size, num_remaining));
175  streamsize count = in->gcount();
176  while (count != 0) {
177  _out->write(buffer, count);
178  if (_out->fail()) {
179  vfile->close_read_file(in);
180  return false;
181  }
182  num_remaining -= count;
183  if (num_remaining == 0) {
184  break;
185  }
186  in->read(buffer, min((streamsize)buffer_size, num_remaining));
187  count = in->gcount();
188  }
189 
190  vfile->close_read_file(in);
191 
192  if (num_remaining != 0) {
193  util_cat.error()
194  << "Truncated input stream.\n";
195  return false;
196  }
197 
198  result = SubfileInfo(_file, start, size);
199  return true;
200 }
201 
202 /**
203  * Copies the file data from the range of the indicated file (outside of the
204  * vfs) as the next datagram. This is intended to support potentially very
205  * large datagrams.
206  *
207  * Returns true on success, false on failure or if this method is
208  * unimplemented. On true, fills "result" with the information that
209  * references the copied file, if possible.
210  */
212 copy_datagram(SubfileInfo &result, const SubfileInfo &source) {
213  nassertr(_out != nullptr, false);
214  _wrote_first_datagram = true;
215 
216  pifstream in;
217  if (!source.get_filename().open_read(in)) {
218  return false;
219  }
220 
221  streamsize num_remaining = source.get_size();
222 
223  StreamWriter writer(_out, false);
224  if (num_remaining == (uint32_t)-1 || num_remaining != (uint32_t)num_remaining) {
225  // Write a large value as a 64-bit size.
226  writer.add_uint32((uint32_t)-1);
227  writer.add_uint64(num_remaining);
228  } else {
229  // Write a value that fits in 32 bits.
230  writer.add_uint32((uint32_t)num_remaining);
231  }
232 
233  static const size_t buffer_size = 4096;
234  char buffer[buffer_size];
235 
236  streampos start = _out->tellp();
237  in.seekg(source.get_start());
238  in.read(buffer, min((streamsize)buffer_size, num_remaining));
239  streamsize count = in.gcount();
240  while (count != 0) {
241  _out->write(buffer, count);
242  if (_out->fail()) {
243  return false;
244  }
245  num_remaining -= count;
246  if (num_remaining == 0) {
247  break;
248  }
249  in.read(buffer, min((streamsize)buffer_size, num_remaining));
250  count = in.gcount();
251  }
252 
253  if (num_remaining != 0) {
254  util_cat.error()
255  << "Truncated input stream.\n";
256  return false;
257  }
258 
259  result = SubfileInfo(_file, start, source.get_size());
260  return true;
261 }
262 
263 /**
264  * Returns true if the file has reached an error condition.
265  */
268  if (_out == nullptr) {
269  return true;
270  }
271 
272  if (_out->fail()) {
273  _error = true;
274  }
275  return _error;
276 }
277 
278 /**
279  * Ensures that all datagrams previously written will be visible in the output
280  * file.
281  */
283 flush() {
284  if (_out != nullptr) {
285  _out->flush();
286  }
287 }
288 
289 
290 /**
291  * Returns the filename that provides the target for these datagrams, if any,
292  * or empty string if the datagrams do not get written to a file on disk.
293  */
296  return _filename;
297 }
298 
299 /**
300  * Returns the FileReference that provides the target for these datagrams, if
301  * any, or NULL if the datagrams do not written to a file on disk.
302  */
305  return _file;
306 }
307 
308 /**
309  * Returns the current file position within the data stream, if any, or 0 if
310  * the file position is not meaningful or cannot be determined.
311  *
312  * For DatagramOutputFiles that return a meaningful file position, this will
313  * be pointing to the first byte following the datagram returned after a call
314  * to put_datagram().
315  */
316 streampos DatagramOutputFile::
318  if (_out == nullptr) {
319  return 0;
320  }
321  return _out->tellp();
322 }
A StreamWriter object is used to write sequential binary data directly to an ostream.
Definition: streamWriter.h:29
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...
std::streamsize get_size() const
Returns the number of consecutive bytes, beginning at get_start(), that correspond to this file data.
Definition: subfileInfo.I:109
Keeps a reference-counted pointer to a file on disk.
Definition: fileReference.h:26
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.
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:414
bool write_header(const std::string &header)
Writes a sequence of bytes to the beginning of the datagram file.
bool open_read(std::ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition: filename.cxx:1863
virtual const Filename & get_filename()
Returns the filename that provides the target 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.
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
PointerTo< VirtualFile > create_file(const Filename &filename)
Attempts to create a file by the indicated name in the filesystem, if possible, and returns it.
virtual bool copy_datagram(SubfileInfo &result, const Filename &filename)
Copies the file data from the entire indicated file (via the vfs) as the next datagram.
bool open(const FileReference *file)
Opens the indicated filename for writing.
virtual bool put_datagram(const Datagram &data)
Writes the given datagram to the file.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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 void flush()
Ensures that all datagrams previously written will be visible in the output file.
const Filename & get_filename() const
A shortcut to the filename.
Definition: subfileInfo.I:88
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to open_write_file().
virtual const FileReference * get_file()
Returns the FileReference that provides the target for these datagrams, if any, or NULL if the datagr...
void add_uint64(uint64_t value)
Adds an unsigned 64-bit integer to the stream.
Definition: streamWriter.I:156
This class records a particular byte sub-range within an existing file on disk.
Definition: subfileInfo.h:26
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the stream.
Definition: streamWriter.I:147
void close()
Closes the file.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
std::streampos get_start() const
Returns the offset within the file at which this file data begins.
Definition: subfileInfo.I:100