Panda3D
Loading...
Searching...
No Matches
bamWriter.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 bamWriter.cxx
10 * @author jason
11 * @date 2000-06-08
12 */
13
14#include "pandabase.h"
15#include "pnotify.h"
16
17#include "typedWritable.h"
18#include "config_putil.h"
19#include "bam.h"
20#include "bamWriter.h"
21#include "bamReader.h"
22#include "lightMutexHolder.h"
23#include "simpleHashMap.h"
24
25#include <algorithm>
26
27// Keeps track of older type names in case we want to write out older .bam
28// files.
29struct ObsoleteName {
30 std::string _name;
31 int _before_major;
32 int _before_minor;
33
34 bool operator < (const ObsoleteName &other) const {
35 if (_before_major != other._before_major) {
36 return _before_major < other._before_major;
37 }
38 return _before_minor < other._before_minor;
39 }
40};
41
42// This is a SimpleHashMap to avoid static init ordering issues.
43static SimpleHashMap<TypeHandle, std::set<ObsoleteName> > obsolete_type_names;
44
45/**
46 *
47 */
48BamWriter::
49BamWriter(DatagramSink *target) :
50 _target(target)
51{
52 ++_writing_seq;
53 _next_boc = BOC_adjunct;
54 _needs_init = true;
55
56 // Initialize the next object and PTA ID's. These start counting at 1,
57 // since 0 is reserved for NULL.
58 _next_object_id = 1;
59 _long_object_id = false;
60 _next_pta_id = 1;
61 _long_pta_id = false;
62
63 // Check which version .bam files we should write.
64 if (bam_version.get_num_words() > 0) {
65 if (bam_version.get_num_words() != 2) {
66 util_cat.error()
67 << "bam-version configuration variable requires two arguments.\n";
68 }
69 _file_major = bam_version[0];
70 _file_minor = bam_version[1];
71
72 if (_file_major < _bam_major_ver || _file_minor < 21) {
73 util_cat.error()
74 << "bam-version is set to " << bam_version << ", but this version of "
75 "Panda3D cannot produce .bam files older than 6.21. Set "
76 "bam-version to 6 21 in Config.prc to suppress this error, or "
77 "leave it blank to write version " << _bam_major_ver << "."
78 << _bam_minor_ver << " files.\n";
79 _file_major = 6;
80 _file_minor = 21;
81 bam_version.set_string_value("6 21");
82
83 } else if (_file_major > _bam_major_ver || _file_minor > _bam_last_minor_ver) {
84 util_cat.error()
85 << "bam-version is set to " << bam_version << ", but this version of "
86 "Panda3D cannot produce .bam files newer than " << _bam_major_ver
87 << "." << _bam_last_minor_ver << ". Set bam-version to a supported "
88 "version or leave it blank to write version " << _bam_major_ver
89 << "." << _bam_last_minor_ver << " files.\n";
90
91 _file_major = _bam_major_ver;
92 _file_minor = _bam_last_minor_ver;
93 bam_version.set_word(0, _bam_major_ver);
94 bam_version.set_word(1, _bam_last_minor_ver);
95 }
96 } else {
97 _file_major = _bam_major_ver;
98 _file_minor = _bam_minor_ver;
99 }
100 _file_endian = bam_endian;
101 _file_stdfloat_double = bam_stdfloat_double;
102 _file_texture_mode = bam_texture_mode;
103}
104
105/**
106 *
107 */
108BamWriter::
109~BamWriter() {
110 // Tell all the TypedWritables whose pointer we are still keeping to forget
111 // about us.
112 StateMap::iterator si;
113 for (si = _state_map.begin(); si != _state_map.end(); ++si) {
114 TypedWritable *object = (TypedWritable *)(*si).first;
115 object->remove_bam_writer(this);
116
117 if ((*si).second._refcount != nullptr) {
118 unref_delete((*si).second._refcount);
119 }
120 }
121}
122
123/**
124 * Changes the destination of future datagrams written by the BamWriter. This
125 * also implicitly calls init() if it has not already been called.
126 */
127void BamWriter::
128set_target(DatagramSink *target) {
129 if (_target != nullptr) {
130 _target->flush();
131 }
132 _target = target;
133
134 if (_needs_init && _target != nullptr) {
135 init();
136 }
137}
138
139/**
140 * Initializes the BamWriter prior to writing any objects to its output
141 * stream. This includes writing out the Bam header.
142 *
143 * This returns true if the BamWriter successfully initialized, false
144 * otherwise.
145 */
147init() {
148 nassertr(_target != nullptr, false);
149 nassertr(_needs_init, false);
150 _needs_init = false;
151
152 // Initialize the next object and PTA ID's. These start counting at 1,
153 // since 0 is reserved for NULL.
154 _next_object_id = 1;
155 _long_object_id = false;
156 _next_pta_id = 1;
157 _long_pta_id = false;
158
159 nassertr_always(_file_major == _bam_major_ver, false);
160 nassertr_always(_file_minor <= _bam_last_minor_ver && _file_minor >= 21, false);
161
162 _file_endian = bam_endian;
163 _file_texture_mode = bam_texture_mode;
164
165 // Write out the current major and minor BAM file version numbers.
166 Datagram header;
167
168 header.add_uint16(_file_major);
169 header.add_uint16(_file_minor);
170 header.add_uint8(_file_endian);
171
172 if (_file_major >= 6 || _file_minor >= 27) {
173 header.add_bool(_file_stdfloat_double);
174 } else {
175 _file_stdfloat_double = false;
176 }
177
178 if (!_target->put_datagram(header)) {
179 util_cat.error()
180 << "Unable to write Bam header.\n";
181 return false;
182 }
183 return true;
184}
185
186/**
187 * Writes a single object to the Bam file, so that the
188 * BamReader::read_object() can later correctly restore the object and all its
189 * pointers.
190 *
191 * This implicitly also writes any additional objects this object references
192 * (if they haven't already been written), so that pointers may be fully
193 * resolved.
194 *
195 * This may be called repeatedly to write a sequence of objects to the Bam
196 * file, but typically (especially for scene graph files, indicated with the
197 * .bam extension), only one object is written directly from the Bam file: the
198 * root of the scene graph. The remaining objects will all be written
199 * recursively by the first object.
200 *
201 * Returns true if the object is successfully written, false otherwise.
202 */
204write_object(const TypedWritable *object) {
205 nassertr(_target != nullptr, false);
206
207 // Increment the _writing_seq, so we can check for newly stale objects
208 // during this operation.
209 ++_writing_seq;
210
211 // If there are any freed objects to indicate, write them out now.
212 if (!_freed_object_ids.empty()) {
213 Datagram dg;
214 dg.add_uint8(BOC_remove);
215
216 FreedObjectIds::iterator fi;
217 for (fi = _freed_object_ids.begin(); fi != _freed_object_ids.end(); ++fi) {
218 write_object_id(dg, (*fi));
219 }
220 _freed_object_ids.clear();
221
222 if (!_target->put_datagram(dg)) {
223 util_cat.error()
224 << "Unable to write data to output.\n";
225 return false;
226 }
227 }
228
229 nassertr(_object_queue.empty(), false);
230 _next_boc = BOC_push;
231
232 int object_id = enqueue_object(object);
233 nassertr(object_id != 0, false);
234 if (!flush_queue()) {
235 return false;
236 }
237
238 // Finally, write the closing pop.
239 if (_next_boc != BOC_push) {
240 Datagram dg;
241 dg.add_uint8(BOC_pop);
242 if (!_target->put_datagram(dg)) {
243 util_cat.error()
244 << "Unable to write data to output.\n";
245 return false;
246 }
247 }
248
249 return true;
250}
251
252/**
253 * Returns true if the object has previously been written (or at least
254 * requested to be written) to the bam file, or false if we've never heard of
255 * it before.
256 */
258has_object(const TypedWritable *object) const {
259 StateMap::const_iterator si = _state_map.find(object);
260 return (si != _state_map.end());
261}
262
263/**
264 * Ensures that all data written thus far is manifested on the output stream.
265 */
267flush() {
268 nassertv(_target != nullptr);
269 _target->flush();
270}
271
272/**
273 * Should be called from TypedWritable::update_bam_nested() to recursively
274 * check the entire hiererachy of writable objects for needed updates. This
275 * tests the indicated TypedWritable object and writes it to the bam stream if
276 * it has recently been modified, then recurses through update_bam_nested.
277 */
279consider_update(const TypedWritable *object) {
280 StateMap::iterator si = _state_map.find(object);
281 if (si == _state_map.end()) {
282 // This object has never even been seen before.
283 enqueue_object(object);
284
285 } else if ((*si).second._written_seq.is_initial()) {
286 // This object has not been written yet.
287 enqueue_object(object);
288
289 } else if ((*si).second._written_seq == _writing_seq) {
290 // We have already visited this object this pass, so no need to look
291 // closer.
292
293 } else if ((*si).second._modified != object->get_bam_modified()) {
294 // This object has been recently modified and needs to be rewritten.
295 enqueue_object(object);
296
297 } else {
298 // Mark that we have now visited this object and pronounced it clean.
299 (*si).second._written_seq = _writing_seq;
300
301 // Recurse to child objects.
302 ((TypedWritable *)object)->update_bam_nested(this);
303 }
304}
305
306/**
307 * The interface for writing a pointer to another object to a Bam file. This
308 * is intended to be called by the various objects that write themselves to
309 * the Bam file, within the write_datagram() method.
310 *
311 * This writes the pointer out in such a way that the BamReader will be able
312 * to restore the pointer later. If the pointer is to an object that has not
313 * yet itself been written to the Bam file, that object will automatically be
314 * written.
315 */
317write_pointer(Datagram &packet, const TypedWritable *object) {
318 // If the pointer is NULL, we always simply write a zero for an object ID
319 // and leave it at that.
320 if (object == nullptr) {
321 write_object_id(packet, 0);
322
323 } else {
324 StateMap::iterator si = _state_map.find(object);
325 if (si == _state_map.end()) {
326 // We have not written this pointer out yet. This means we must queue
327 // the object definition up for later.
328 int object_id = enqueue_object(object);
329 write_object_id(packet, object_id);
330
331 } else {
332 // We have already assigned this pointer an ID, so it has previously
333 // been written; but we might still need to rewrite it if it is stale.
334 int object_id = (*si).second._object_id;
335 bool already_written = !(*si).second._written_seq.is_initial();
336 if ((*si).second._written_seq != _writing_seq &&
337 (*si).second._modified != object->get_bam_modified()) {
338 // This object was previously written, but it has since been modified,
339 // so we should write it again.
340 already_written = false;
341 }
342
343 write_object_id(packet, object_id);
344
345 if (!already_written) {
346 // It's stale, so queue the object for rewriting too.
347 enqueue_object(object);
348 } else {
349 // Not stale, but maybe its child object is.
350 ((TypedWritable *)object)->update_bam_nested(this);
351 }
352 }
353 }
354}
355
356/**
357 * Writes a block of auxiliary file data from the indicated file (within the
358 * vfs). This can be a block of arbitrary size, and it is assumed it may be
359 * quite large. This must be balanced by a matching call to read_file_data()
360 * on restore.
361 */
363write_file_data(SubfileInfo &result, const Filename &filename) {
364 // We write file data by preceding with a singleton datagram that contains
365 // only the BOC_file_data token.
366 Datagram dg;
367 dg.add_uint8(BOC_file_data);
368 if (!_target->put_datagram(dg)) {
369 util_cat.error()
370 << "Unable to write data to output.\n";
371 return;
372 }
373
374 // Then we can write the file data itself, as its own (possibly quite large)
375 // followup datagram.
376 if (!_target->copy_datagram(result, filename)) {
377 util_cat.error()
378 << "Unable to write file data to output.\n";
379 return;
380 }
381
382 // Both of those get written to the bam stream prior to the datagram that
383 // represents this particular object, but they'll get pulled out in the same
384 // order and queued up in the BamReader.
385}
386
387/**
388 * Writes a block of auxiliary file data from the indicated file (outside of
389 * the vfs). This can be a block of arbitrary size, and it is assumed it may
390 * be quite large. This must be balanced by a matching call to
391 * read_file_data() on restore.
392 */
394write_file_data(SubfileInfo &result, const SubfileInfo &source) {
395 // We write file data by preceding with a singleton datagram that contains
396 // only the BOC_file_data token.
397 Datagram dg;
398 dg.add_uint8(BOC_file_data);
399 if (!_target->put_datagram(dg)) {
400 util_cat.error()
401 << "Unable to write data to output.\n";
402 return;
403 }
404
405 // Then we can write the file data itself, as its own (possibly quite large)
406 // followup datagram.
407 if (!_target->copy_datagram(result, source)) {
408 util_cat.error()
409 << "Unable to write file data to output.\n";
410 return;
411 }
412
413 // Both of those get written to the bam stream prior to the datagram that
414 // represents this particular object, but they'll get pulled out in the same
415 // order and queued up in the BamReader.
416}
417
418/**
419 * Writes out the indicated CycleData object. This should be used by classes
420 * that store some or all of their data within a CycleData subclass, in
421 * support of pipelining. This will call the virtual
422 * CycleData::write_datagram() method to do the actual writing.
423 */
425write_cdata(Datagram &packet, const PipelineCyclerBase &cycler) {
426 const CycleData *cdata = cycler.read(Thread::get_current_thread());
427 cdata->write_datagram(this, packet);
428 cycler.release_read(cdata);
429}
430
431/**
432 * This version of write_cdata allows passing an additional parameter to
433 * cdata->write_datagram().
434 */
436write_cdata(Datagram &packet, const PipelineCyclerBase &cycler,
437 void *extra_data) {
438 const CycleData *cdata = cycler.read(Thread::get_current_thread());
439 cdata->write_datagram(this, packet, extra_data);
440 cycler.release_read(cdata);
441}
442
443/**
444 * Prepares to write a PointerToArray to the Bam file, unifying references to
445 * the same pointer across the Bam file.
446 *
447 * The writing object should call this prior to writing out a PointerToArray.
448 * It will return true if the same pointer has been written previously, in
449 * which case the writing object need do nothing further; or it will return
450 * false if this particular pointer has not yet been written, in which case
451 * the writing object must then write out the contents of the array.
452 *
453 * Also see the WRITE_PTA() macro, which consolidates the work that must be
454 * done to write a PTA.
455 */
457register_pta(Datagram &packet, const void *ptr) {
458 if (ptr == nullptr) {
459 // A zero for the PTA ID indicates a NULL pointer. This is a special
460 // case.
461 write_pta_id(packet, 0);
462
463/*
464 * We return false to indicate the user must now write out the "definition" of
465 * the NULL pointer. This is necessary because of a quirk in the BamReader's
466 * design, which forces callers to read the definition of every NULL pointer.
467 * Presumably, the caller will be able to write the definition in a concise
468 * way that will clearly indicate a NULL pointer; in the case of a
469 * PointerToArray, this will generally be simply a zero element count.
470 */
471 return false;
472 }
473
474 PTAMap::iterator pi = _pta_map.find(ptr);
475 if (pi == _pta_map.end()) {
476 // We have not encountered this pointer before.
477 int pta_id = _next_pta_id;
478 _next_pta_id++;
479
480 bool inserted = _pta_map.insert(PTAMap::value_type(ptr, pta_id)).second;
481 nassertr(inserted, false);
482
483 write_pta_id(packet, pta_id);
484
485 // Return false to indicate the caller must now write out the array
486 // definition.
487 return false;
488
489 } else {
490 // We have encountered this pointer before.
491 int pta_id = (*pi).second;
492 write_pta_id(packet, pta_id);
493
494 // Return true to indicate the caller need do nothing further.
495 return true;
496 }
497}
498
499/**
500 * Writes a TypeHandle to the file in such a way that the BamReader can read
501 * the same TypeHandle later via read_handle().
502 */
504write_handle(Datagram &packet, TypeHandle type) {
505 // We encode TypeHandles within the Bam file by writing a unique index
506 // number for each one to the file. When we write a particular TypeHandle
507 // for the first time, we assign it a new index number and then immediately
508 // follow it by its definition; when we write the same TypeHandle on
509 // subsequent times we only write the index number.
510
511 // The unique number we choose is actually the internal index number of the
512 // TypeHandle. Why not?
513 int index = type.get_index();
514
515 // Also make sure the index number fits within a uint16_t.
516 nassertv(index <= 0xffff);
517
518 packet.add_uint16(index);
519
520 if (index != 0) {
521 bool inserted = _types_written.insert(index).second;
522
523 if (inserted) {
524 // This is the first time this TypeHandle has been written, so also
525 // write out its definition.
526
527 if (_file_major == _bam_major_ver && _file_minor == _bam_minor_ver) {
528 packet.add_string(type.get_name());
529 } else {
530 // We are writing an older .bam format, so we need to look up whether
531 // we may need to write an older type name.
532 packet.add_string(get_obsolete_type_name(type, _file_major, _file_minor));
533 }
534
535 // We also need to write the derivation of the TypeHandle, in case the
536 // program reading this file later has never heard of this type before.
537 int num_parent_classes = type.get_num_parent_classes();
538 nassertv(num_parent_classes <= 255); // Good grief!
539 packet.add_uint8(num_parent_classes);
540 for (int i = 0; i < num_parent_classes; i++) {
541 write_handle(packet, type.get_parent_class(i));
542 }
543 }
544 }
545}
546
547/**
548 * Returns the name that the given type had in an older .bam version.
549 */
550std::string BamWriter::
551get_obsolete_type_name(TypeHandle type, int major, int minor) {
552 int index = obsolete_type_names.find(type);
553 if (index >= 0) {
554 // Iterate over the names. It is sorted such that the lower versions are
555 // listed first.
556 for (const ObsoleteName &name : obsolete_type_names.get_data((size_t)index)) {
557 if (major < name._before_major ||
558 (major == name._before_major && minor < name._before_minor)) {
559 // We have a hit.
560 return name._name;
561 }
562 }
563 }
564
565 return TypeRegistry::ptr()->get_name(type, nullptr);
566}
567
568/**
569 * Registers the given type as having an older name in .bam files *before* the
570 * indicated version. You can call this multiple times for the same type in
571 * order to establish a history of renames for this type.
572 */
574record_obsolete_type_name(TypeHandle type, std::string name,
575 int before_major, int before_minor) {
576 // Make sure it is registered as alternate name for reading.
578 reg->record_alternate_name(type, name);
579
580 ObsoleteName obsolete_name;
581 obsolete_name._name = std::move(name);
582 obsolete_name._before_major = before_major;
583 obsolete_name._before_minor = before_minor;
584 obsolete_type_names[type].insert(std::move(obsolete_name));
585}
586
587/**
588 * This is called by the TypedWritable destructor. It should remove the
589 * pointer from any structures that keep a reference to it, and also write a
590 * flag to the bam file (if it is open) so that a reader will know the object
591 * id will no longer be used.
592 */
593void BamWriter::
594object_destructs(TypedWritable *object) {
595 StateMap::iterator si = _state_map.find(object);
596 if (si != _state_map.end()) {
597 // We ought to have written out the object by the time it destructs, or
598 // we're in trouble when we do write it out.
599 nassertv(!(*si).second._written_seq.is_initial());
600
601 // This cannot be called if we are still holding a reference to it.
602 nassertv((*si).second._refcount == nullptr);
603
604 int object_id = (*si).second._object_id;
605 _freed_object_ids.push_back(object_id);
606
607 _state_map.erase(si);
608 }
609}
610
611/**
612 * Writes the indicated object id to the datagram.
613 */
614void BamWriter::
615write_object_id(Datagram &dg, int object_id) {
616 if (_long_object_id) {
617 dg.add_uint32(object_id);
618
619 } else {
620 dg.add_uint16(object_id);
621 // Once we fill up our uint16, we write all object id's thereafter with a
622 // uint32.
623 if (object_id == 0xffff) {
624 _long_object_id = true;
625 }
626 }
627}
628
629/**
630 * Writes the indicated pta id to the datagram.
631 */
632void BamWriter::
633write_pta_id(Datagram &dg, int pta_id) {
634 if (_long_pta_id) {
635 dg.add_uint32(pta_id);
636
637 } else {
638 dg.add_uint16(pta_id);
639 // Once we fill up our uint16, we write all pta id's thereafter with a
640 // uint32.
641 if (pta_id == 0xffff) {
642 _long_pta_id = true;
643 }
644 }
645}
646
647/**
648 * Assigns an object ID to the object and queues it up for later writing to
649 * the Bam file.
650 *
651 * The return value is the object ID, or 0 if there is an error.
652 */
653int BamWriter::
654enqueue_object(const TypedWritable *object) {
655 Datagram dg;
656
657 nassertr(object != TypedWritable::Null, 0);
658
659 // No object should ever be written out that is not registered as a child of
660 // TypedWritable. The only way this can happen is if someone failed to
661 // initialize their type correctly in init_type().
662#ifndef NDEBUG
663 if (!object->is_of_type(TypedWritable::get_class_type())) {
664 util_cat.error()
665 << "Type " << object->get_type()
666 << " does not indicate inheritance from TypedWritable.\n"
667 << "(this is almost certainly an oversight in " << object->get_type()
668 << "::init_type().)\n";
669 }
670#endif
671
672 // We need to assign a unique index number to every object we write out.
673 // Has this object been assigned a number yet?
674 int object_id;
675
676 StateMap::iterator si = _state_map.find(object);
677 if (si == _state_map.end()) {
678 // No, it hasn't, so assign it the next number in sequence arbitrarily.
679 object_id = _next_object_id;
680
681 StateMap::iterator si;
682 bool inserted;
683 tie(si, inserted) =
684 _state_map.insert(StateMap::value_type(object, StoreState(_next_object_id)));
685 nassertr(inserted, false);
686
687 // Store ourselves on the TypedWritable so that we get notified when it
688 // destructs.
689 (const_cast<TypedWritable*>(object))->add_bam_writer(this);
690 _next_object_id++;
691
692 // Increase the reference count if this inherits from ReferenceCount,
693 // until we get a chance to write this object for the first time.
694 const ReferenceCount *rc = ((TypedWritable *)object)->as_reference_count();
695 if (rc != nullptr) {
696 rc->ref();
697 (*si).second._refcount = rc;
698 }
699
700 } else {
701 // Yes, it has; get the object ID.
702 object_id = (*si).second._object_id;
703 }
704
705 _object_queue.push_back(object);
706 return object_id;
707}
708
709/**
710 * Writes all of the objects on the _object_queue to the bam stream, until the
711 * queue is empty.
712 *
713 * Returns true on success, false on failure.
714 */
715bool BamWriter::
716flush_queue() {
717 nassertr(_target != nullptr, false);
718 // Each object we write may append more to the queue.
719 while (!_object_queue.empty()) {
720 const TypedWritable *object = _object_queue.front();
721 _object_queue.pop_front();
722
723 // Look up the object in the map. It had better be there!
724 StateMap::iterator si = _state_map.find(object);
725 nassertr(si != _state_map.end(), false);
726
727 if ((*si).second._written_seq == _writing_seq) {
728 // We have already visited this object; no need to consider it again.
729 continue;
730 }
731
732 int object_id = (*si).second._object_id;
733 bool already_written = !(*si).second._written_seq.is_initial();
734 if ((*si).second._modified != object->get_bam_modified()) {
735 // This object was previously written, but it has since been modified,
736 // so we should write it again.
737 already_written = false;
738 }
739
740 Datagram dg;
741 dg.set_stdfloat_double(_file_stdfloat_double);
742 dg.add_uint8(_next_boc);
743 _next_boc = BOC_adjunct;
744
745 if (!already_written) {
746 // The first time we write a particular object, or when we update the
747 // same object later, we do so by writing its TypeHandle (which had
748 // better not be TypeHandle::none(), since that's our code for a
749 // previously-written object), followed by the object ID number,
750 // followed by the object definition.
751
752 TypeHandle type = object->get_type();
753 nassertr(type != TypeHandle::none(), false);
754
755 // Determine what the nearest kind of type is that the reader will be
756 // able to handle, and write that instead.
757 TypeHandle registered_type =
759 if (registered_type == TypeHandle::none()) {
760 // We won't be able to read this type again.
761 util_cat.warning()
762 << "Objects of type " << type << " cannot be read; bam file is invalid.\n";
763 } else if (registered_type != type) {
764 util_cat.info()
765 << "Writing " << registered_type << " instead of " << type << "\n";
766 type = registered_type;
767
768 } else if (util_cat.is_debug()) {
769 util_cat.debug()
770 << "Writing " << type << " object id " << object_id
771 << " to bam file\n";
772 }
773
774 write_handle(dg, type);
775 write_object_id(dg, object_id);
776
777 // We cast the const pointer to non-const so that we may call
778 // write_datagram() on it. Really, write_datagram() should be a const
779 // method anyway, but there may be times when a class object wants to
780 // update some transparent cache value during writing or something like
781 // that, so it's more convenient to cheat and define it as a non-const
782 // method.
783 ((TypedWritable *)object)->write_datagram(this, dg);
784
785 (*si).second._written_seq = _writing_seq;
786 (*si).second._modified = object->get_bam_modified();
787
788 // Now release any reference we hold to it, so that it may destruct.
789 const ReferenceCount *rc = (*si).second._refcount;
790 if (rc != nullptr) {
791 // We need to assign this pointer to null before deleting the object,
792 // since that may end up calling object_destructs.
793 (*si).second._refcount = nullptr;
794 unref_delete(rc);
795 }
796
797 } else {
798 // On subsequent times when we write a particular object, we write
799 // simply TypeHandle::none(), followed by the object ID. The occurrence
800 // of TypeHandle::none() is an indicator to the BamReader that this is a
801 // previously-written object.
802
803 write_handle(dg, TypeHandle::none());
804 write_object_id(dg, object_id);
805
806 // The object has not been modified, but maybe one of its child objects
807 // has.
808 ((TypedWritable *)object)->update_bam_nested(this);
809 }
810
811 if (!_target->put_datagram(dg)) {
812 util_cat.error()
813 << "Unable to write data to output.\n";
814 return false;
815 }
816 }
817
818 return true;
819}
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition bamReader.I:177
void flush()
Ensures that all data written thus far is manifested on the output stream.
bool has_object(const TypedWritable *obj) const
Returns true if the object has previously been written (or at least requested to be written) to the b...
bool write_object(const TypedWritable *obj)
Writes a single object to the Bam file, so that the BamReader::read_object() can later correctly rest...
void consider_update(const TypedWritable *obj)
Should be called from TypedWritable::update_bam_nested() to recursively check the entire hiererachy o...
void write_file_data(SubfileInfo &result, const Filename &filename)
Writes a block of auxiliary file data from the indicated file (within the vfs).
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
bool init()
Initializes the BamWriter prior to writing any objects to its output stream.
bool register_pta(Datagram &packet, const void *ptr)
Prepares to write a PointerToArray to the Bam file, unifying references to the same pointer across th...
static void record_obsolete_type_name(TypeHandle type, std::string name, int before_major, int before_minor)
Registers the given type as having an older name in .bam files *before* the indicated version.
void write_handle(Datagram &packet, TypeHandle type)
Writes a TypeHandle to the file in such a way that the BamReader can read the same TypeHandle later v...
static std::string get_obsolete_type_name(TypeHandle type, int major, int minor)
Returns the name that the given type had in an older .bam version.
set_target
Changes the destination of future datagrams written by the BamWriter.
Definition bamWriter.h:94
void set_word(size_t n, int value)
Reassigns the variable's nth value.
size_t get_num_words() const
Returns the number of words in the variable's value.
void set_string_value(const std::string &value)
Changes the value assigned to this variable.
A single page of data maintained by a PipelineCycler.
Definition cycleData.h:50
virtual void write_datagram(BamWriter *, Datagram &) const
Writes the contents of this object to the datagram for shipping out to a Bam file.
Definition cycleData.cxx:32
This class defines the abstract interface to sending datagrams to any target, whether it be into a fi...
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.
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition datagram.h:38
void add_uint32(uint32_t value)
Adds an unsigned 32-bit integer to the datagram.
Definition datagram.I:94
void set_stdfloat_double(bool stdfloat_double)
Changes the stdfloat_double flag, which defines the operation performed by add_stdfloat() and Datagra...
Definition datagram.I:395
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition datagram.I:50
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition datagram.I:34
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition datagram.I:85
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition datagram.I:219
TypeHandle find_registered_type(TypeHandle handle)
Returns the TypeHandle given, if it is a registered type, or if it is not registered,...
The name of a file, such as a texture file or an Egg file.
Definition filename.h:44
A base class for all things that want to be reference-counted.
void ref() const
Explicitly increments the reference count.
This template class implements an unordered map of keys to data, implemented as a hashtable.
This class records a particular byte sub-range within an existing file on disk.
Definition subfileInfo.h:26
get_current_thread
Returns a pointer to the currently-executing Thread object.
Definition thread.h:109
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
get_parent_class
Returns the nth parent class of this type.
Definition typeHandle.h:137
get_name
Returns the name of the type.
Definition typeHandle.h:136
get_index
Returns the integer index associated with this TypeHandle.
Definition typeHandle.h:135
get_num_parent_classes
Returns the number of parent classes that this type is known to have.
Definition typeHandle.h:137
The TypeRegistry class maintains all the assigned TypeHandles in a given system.
void record_alternate_name(TypeHandle type, const std::string &name)
Indicates an alternate name for the same type.
static TypeRegistry * ptr()
Returns the pointer to the global TypeRegistry object.
std::string get_name(TypeHandle type, TypedObject *object) const
Returns the name of the indicated type.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition typedObject.I:28
Base class for objects that can be written to and read from Bam files.
UpdateSeq get_bam_modified() const
Returns the current bam_modified counter.
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.
void unref_delete(RefCountType *ptr)
This global helper function will unref the given ReferenceCount object, and if the reference count re...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the trivial, non-threaded implementation of PipelineCyclerBase.
const CycleData * read(Thread *current_thread) const
Returns a const CycleData pointer, filled with the data for the current stage of the pipeline as seen...
void release_read(const CycleData *pointer) const
Releases a pointer previously obtained via a call to read().
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.