Panda3D
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.
29 struct 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.
43 static SimpleHashMap<TypeHandle, std::set<ObsoleteName> > obsolete_type_names;
44 
45 /**
46  *
47  */
48 BamWriter::
49 BamWriter(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  */
108 BamWriter::
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  */
127 void BamWriter::
128 set_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  */
147 init() {
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  */
204 write_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  */
258 has_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  */
267 flush() {
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  */
279 consider_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  */
317 write_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  */
363 write_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  */
394 write_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  */
425 write_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  */
436 write_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  */
457 register_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  */
504 write_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  */
550 std::string BamWriter::
551 get_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  */
574 record_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  */
593 void BamWriter::
594 object_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  */
614 void BamWriter::
615 write_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  */
632 void BamWriter::
633 write_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  */
653 int BamWriter::
654 enqueue_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  */
715 bool BamWriter::
716 flush_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.
Definition: bamWriter.cxx:267
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...
Definition: bamWriter.cxx:258
bool write_object(const TypedWritable *obj)
Writes a single object to the Bam file, so that the BamReader::read_object() can later correctly rest...
Definition: bamWriter.cxx:204
void consider_update(const TypedWritable *obj)
Should be called from TypedWritable::update_bam_nested() to recursively check the entire hiererachy o...
Definition: bamWriter.cxx:279
void write_file_data(SubfileInfo &result, const Filename &filename)
Writes a block of auxiliary file data from the indicated file (within the vfs).
Definition: bamWriter.cxx:363
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:425
void write_pointer(Datagram &packet, const TypedWritable *dest)
The interface for writing a pointer to another object to a Bam file.
Definition: bamWriter.cxx:317
bool init()
Initializes the BamWriter prior to writing any objects to its output stream.
Definition: bamWriter.cxx:147
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...
Definition: bamWriter.cxx:457
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.
Definition: bamWriter.cxx:574
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...
Definition: bamWriter.cxx:504
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.
Definition: bamWriter.cxx:551
set_target
Changes the destination of future datagrams written by the BamWriter.
Definition: bamWriter.h:91
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...
Definition: datagramSink.h:29
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,...
Definition: factoryBase.cxx:90
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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.
Definition: simpleHashMap.h:81
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.
Definition: typeRegistry.h:36
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.
Definition: typeRegistry.I:30
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.
Definition: typedWritable.h:35
UpdateSeq get_bam_modified() const
Returns the current bam_modified counter.
Definition: typedWritable.I:52
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.