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_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_minor_ver << ". Set bam-version to a supported "
88  "version or leave it blank to write version " << _bam_major_ver
89  << "." << _bam_minor_ver << " files.\n";
90 
91  _file_major = _bam_major_ver;
92  _file_minor = _bam_minor_ver;
93  bam_version.set_word(0, _bam_major_ver);
94  bam_version.set_word(1, _bam_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  */
146 bool BamWriter::
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_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  */
203 bool BamWriter::
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  */
257 bool BamWriter::
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  */
266 void BamWriter::
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  */
278 void BamWriter::
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  */
316 void BamWriter::
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  */
362 void BamWriter::
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  */
393 void BamWriter::
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  */
424 void BamWriter::
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  */
435 void BamWriter::
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  */
456 bool BamWriter::
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  */
503 void BamWriter::
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  */
573 void BamWriter::
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.
void set_word(size_t n, int value)
Reassigns the variable's nth value.
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
UpdateSeq get_bam_modified() const
Returns the current bam_modified counter.
Definition: typedWritable.I:52
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:47
std::string get_name(TypeHandle type, TypedObject *object) const
Returns the name of the indicated type.
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void record_alternate_name(TypeHandle type, const std::string &name)
Indicates an alternate name for the same type.
void write_cdata(Datagram &packet, const PipelineCyclerBase &cycler)
Writes out the indicated CycleData object.
Definition: bamWriter.cxx:425
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the trivial, non-threaded implementation of PipelineCyclerBase.
This template class implements an unordered map of keys to data, implemented as a hashtable.
Definition: simpleHashMap.h:81
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_name
Returns the name of the type.
Definition: typeHandle.h:136
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 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
size_t get_num_words() const
Returns the number of words in the variable's value.
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
void add_uint16(uint16_t value)
Adds an unsigned 16-bit integer to the datagram.
Definition: datagram.I:85
void add_bool(bool value)
Adds a boolean value to the datagram.
Definition: datagram.I:34
This class defines the abstract interface to sending datagrams to any target, whether it be into a fi...
Definition: datagramSink.h:29
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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
get_index
Returns the integer index associated with this TypeHandle.
Definition: typeHandle.h:135
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.
void add_string(const std::string &str)
Adds a variable-length string to the datagram.
Definition: datagram.I:219
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
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
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
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
get_parent_class
Returns the nth parent class of this type.
Definition: typeHandle.h:137
void ref() const
Explicitly increments the reference count.
static TypeRegistry * ptr()
Returns the pointer to the global TypeRegistry object.
Definition: typeRegistry.I:30
A base class for all things that want to be reference-counted.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
The TypeRegistry class maintains all the assigned TypeHandles in a given system.
Definition: typeRegistry.h:36
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...
This class records a particular byte sub-range within an existing file on disk.
Definition: subfileInfo.h:26
void add_uint8(uint8_t value)
Adds an unsigned 8-bit integer to the datagram.
Definition: datagram.I:50
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
void flush()
Ensures that all data written thus far is manifested on the output stream.
Definition: bamWriter.cxx:267
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
get_num_parent_classes
Returns the number of parent classes that this type is known to have.
Definition: typeHandle.h:137
set_target
Changes the destination of future datagrams written by the BamWriter.
Definition: bamWriter.h:91
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
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition: datagram.h:38
bool init()
Initializes the BamWriter prior to writing any objects to its output stream.
Definition: bamWriter.cxx:147
void release_read(const CycleData *pointer) const
Releases a pointer previously obtained via a call to read().
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.
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
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