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
19using std::min;
20using std::streampos;
21using std::streamsize;
22
23/**
24 * Opens the indicated filename for writing. Returns true if successful,
25 * false on failure.
26 */
28open(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 */
54open(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 */
73close() {
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 */
96write_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 */
110put_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 */
143copy_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 */
212copy_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 */
267is_error() {
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 */
283flush() {
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 */
295get_filename() {
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 */
304get_file() {
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 */
317get_file_pos() {
318 if (_out == nullptr) {
319 return 0;
320 }
321 return _out->tellp();
322}
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.
virtual void flush()
Ensures that all datagrams previously written will be visible in the output file.
bool open(const FileReference *file)
Opens the indicated filename for writing.
void close()
Closes the file.
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 const FileReference * get_file()
Returns the FileReference that provides the target for these datagrams, if any, or NULL if the datagr...
virtual bool is_error()
Returns true if the file has reached an error condition.
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 put_datagram(const Datagram &data)
Writes the given datagram to the file.
bool write_header(const std::string &header)
Writes a sequence of bytes to the beginning of the datagram file.
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
bool open_read(std::ifstream &stream) const
Opens the indicated ifstream for reading the file, if possible.
Definition: filename.cxx:1863
void set_binary()
Indicates that the filename represents a binary file.
Definition: filename.I:414
A StreamWriter object is used to write sequential binary data directly to an ostream.
Definition: streamWriter.h:29
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the stream.
Definition: streamWriter.I:147
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
const Filename & get_filename() const
A shortcut to the filename.
Definition: subfileInfo.I:88
std::streampos get_start() const
Returns the offset within the file at which this file data begins.
Definition: subfileInfo.I:100
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
A hierarchy of directories and files that appears to be one continuous file system,...
static void close_write_file(std::ostream *stream)
Closes a file opened by a previous call to open_write_file().
PointerTo< VirtualFile > create_file(const Filename &filename)
Attempts to create a file by the indicated name in the filesystem, if possible, and returns it.
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.