Panda3D
dcPacker.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 dcPacker.cxx
10  * @author drose
11  * @date 2004-06-15
12  */
13 
14 #include "dcPacker.h"
15 #include "dcSwitch.h"
16 #include "dcParserDefs.h"
17 #include "dcLexerDefs.h"
18 #include "dcClassParameter.h"
19 #include "dcSwitchParameter.h"
20 #include "dcClass.h"
21 
22 #ifdef HAVE_PYTHON
23 #include "py_panda.h"
24 #endif
25 
26 using std::istream;
27 using std::istringstream;
28 using std::ostream;
29 using std::ostringstream;
30 using std::string;
31 
32 DCPacker::StackElement *DCPacker::StackElement::_deleted_chain = nullptr;
33 int DCPacker::StackElement::_num_ever_allocated = 0;
34 
35 /**
36  *
37  */
38 DCPacker::
39 DCPacker() {
40  _mode = M_idle;
41  _unpack_data = nullptr;
42  _unpack_length = 0;
43  _owns_unpack_data = false;
44  _unpack_p = 0;
45  _live_catalog = nullptr;
46  _parse_error = false;
47  _pack_error = false;
48  _range_error = false;
49  _stack = nullptr;
50 
51  clear();
52 }
53 
54 /**
55  *
56  */
57 DCPacker::
58 ~DCPacker() {
59  clear_data();
60  clear();
61 }
62 
63 /**
64  * Begins a packing session. The parameter is the DC object that describes
65  * the packing format; it may be a DCParameter or DCField.
66  *
67  * Unless you call clear_data() between sessions, multiple packing sessions
68  * will be concatenated together into the same buffer. If you wish to add
69  * bytes to the buffer between packing sessions, use append_data() or
70  * get_write_pointer().
71  */
72 void DCPacker::
74  nassertv(_mode == M_idle);
75 
76  _mode = M_pack;
77  _parse_error = false;
78  _pack_error = false;
79  _range_error = false;
80 
81  _root = root;
82  _catalog = nullptr;
83  _live_catalog = nullptr;
84 
85  _current_field = root;
86  _current_parent = nullptr;
87  _current_field_index = 0;
88  _num_nested_fields = 0;
89 }
90 
91 /**
92  * Finishes a packing session.
93  *
94  * The return value is true on success, or false if there has been some error
95  * during packing.
96  */
97 bool DCPacker::
99  nassertr(_mode == M_pack, false);
100 
101  _mode = M_idle;
102 
103  if (_stack != nullptr || _current_field != nullptr || _current_parent != nullptr) {
104  _pack_error = true;
105  }
106 
107  clear();
108 
109  return !had_error();
110 }
111 
112 /**
113  * Sets up the unpack_data pointer. You may call this before calling the
114  * version of begin_unpack() that takes only one parameter.
115  */
116 void DCPacker::
117 set_unpack_data(const vector_uchar &data) {
118  nassertv(_mode == M_idle);
119 
120  char *buffer = new char[data.size()];
121  memcpy(buffer, data.data(), data.size());
122  set_unpack_data(buffer, data.size(), true);
123 }
124 
125 /**
126  * Sets up the unpack_data pointer. You may call this before calling the
127  * version of begin_unpack() that takes only one parameter.
128  */
129 void DCPacker::
130 set_unpack_data(const char *unpack_data, size_t unpack_length,
131  bool owns_unpack_data) {
132  nassertv(_mode == M_idle);
133 
134  if (_owns_unpack_data) {
135  delete[] _unpack_data;
136  }
137  _unpack_data = unpack_data;
138  _unpack_length = unpack_length;
139  _owns_unpack_data = owns_unpack_data;
140  _unpack_p = 0;
141 }
142 
143 /**
144  * Begins an unpacking session. You must have previously called
145  * set_unpack_data() to specify a buffer to unpack.
146  *
147  * If there was data left in the buffer after a previous begin_unpack() ..
148  * end_unpack() session, the new session will resume from the current point.
149  * This method may be used, therefore, to unpack a sequence of objects from
150  * the same buffer.
151  */
152 void DCPacker::
154  nassertv(_mode == M_idle);
155  nassertv(_unpack_data != nullptr);
156 
157  _mode = M_unpack;
158  _parse_error = false;
159  _pack_error = false;
160  _range_error = false;
161 
162  _root = root;
163  _catalog = nullptr;
164  _live_catalog = nullptr;
165 
166  _current_field = root;
167  _current_parent = nullptr;
168  _current_field_index = 0;
169  _num_nested_fields = 0;
170 }
171 
172 /**
173  * Finishes the unpacking session.
174  *
175  * The return value is true on success, or false if there has been some error
176  * during unpacking (or if all fields have not been unpacked).
177  */
178 bool DCPacker::
180  nassertr(_mode == M_unpack, false);
181 
182  _mode = M_idle;
183 
184  if (_stack != nullptr || _current_field != nullptr || _current_parent != nullptr) {
185  // This happens if we have not unpacked all of the fields. However, this
186  // is not an error if we have called seek() during the unpack session (in
187  // which case the _catalog will be non-NULL). On the other hand, if the
188  // catalog is still NULL, then we have never called seek() and it is an
189  // error not to unpack all values.
190  if (_catalog == nullptr) {
191  _pack_error = true;
192  }
193  }
194 
195  clear();
196 
197  return !had_error();
198 }
199 
200 /**
201  * Begins a repacking session. You must have previously called
202  * set_unpack_data() to specify a buffer to unpack.
203  *
204  * Unlike begin_pack() or begin_unpack() you may not concatenate the results
205  * of multiple begin_repack() sessions in one buffer.
206  *
207  * Also, unlike in packing or unpacking modes, you may not walk through the
208  * fields from beginning to end, or even pack two consecutive fields at once.
209  * Instead, you must call seek() for each field you wish to modify and pack
210  * only that one field; then call seek() again to modify another field.
211  */
212 void DCPacker::
214  nassertv(_mode == M_idle);
215  nassertv(_unpack_data != nullptr);
216  nassertv(_unpack_p == 0);
217 
218  _mode = M_repack;
219  _parse_error = false;
220  _pack_error = false;
221  _range_error = false;
222  _pack_data.clear();
223 
224  // In repack mode, we immediately get the catalog, since we know we'll need
225  // it.
226  _root = root;
227  _catalog = _root->get_catalog();
228  _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length);
229  if (_live_catalog == nullptr) {
230  _pack_error = true;
231  }
232 
233  // We don't begin at the first field in repack mode. Instead, you must
234  // explicitly call seek().
235  _current_field = nullptr;
236  _current_parent = nullptr;
237  _current_field_index = 0;
238  _num_nested_fields = 0;
239 }
240 
241 /**
242  * Finishes the repacking session.
243  *
244  * The return value is true on success, or false if there has been some error
245  * during repacking (or if all fields have not been repacked).
246  */
247 bool DCPacker::
249  nassertr(_mode == M_repack, false);
250 
251  // Put the rest of the data onto the pack stream.
252  _pack_data.append_data(_unpack_data + _unpack_p, _unpack_length - _unpack_p);
253 
254  _mode = M_idle;
255  clear();
256 
257  return !had_error();
258 }
259 
260 /**
261  * Sets the current unpack (or repack) position to the named field. In unpack
262  * mode, the next call to unpack_*() or push() will begin to read the named
263  * field. In repack mode, the next call to pack_*() or push() will modify the
264  * named field.
265  *
266  * Returns true if successful, false if the field is not known (or if the
267  * packer is in an invalid mode).
268  */
269 bool DCPacker::
270 seek(const string &field_name) {
271  if (_catalog == nullptr) {
272  _catalog = _root->get_catalog();
273  _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length);
274  }
275  nassertr(_catalog != nullptr, false);
276  if (_live_catalog == nullptr) {
277  _pack_error = true;
278  return false;
279  }
280 
281  int seek_index = _live_catalog->find_entry_by_name(field_name);
282  if (seek_index < 0) {
283  // The field was not known.
284  _pack_error = true;
285  return false;
286  }
287 
288  return seek(seek_index);
289 }
290 
291 /**
292  * Seeks to the field indentified by seek_index, which was returned by an
293  * earlier call to DCField::find_seek_index() to get the index of some nested
294  * field. Also see the version of seek() that accepts a field name.
295  *
296  * Returns true if successful, false if the field is not known (or if the
297  * packer is in an invalid mode).
298  */
299 bool DCPacker::
300 seek(int seek_index) {
301  if (_catalog == nullptr) {
302  _catalog = _root->get_catalog();
303  _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length);
304  }
305  nassertr(_catalog != nullptr, false);
306  if (_live_catalog == nullptr) {
307  _pack_error = true;
308  return false;
309  }
310 
311  if (_mode == M_unpack) {
312  const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(seek_index);
313 
314  // If we are seeking, we don't need to remember our current stack
315  // position.
316  clear_stack();
317  _current_field = entry._field;
318  _current_parent = entry._parent;
319  _current_field_index = entry._field_index;
320  _num_nested_fields = _current_parent->get_num_nested_fields();
321  _unpack_p = _live_catalog->get_begin(seek_index);
322 
323  // We don't really need _push_marker and _pop_marker now, except that we
324  // should set _push_marker in case we have just seeked to a switch
325  // parameter, and we should set _pop_marker to 0 just so it won't get in
326  // the way.
327  _push_marker = _unpack_p;
328  _pop_marker = 0;
329 
330  return true;
331 
332  } else if (_mode == M_repack) {
333  nassertr(_catalog != nullptr, false);
334 
335  if (_stack != nullptr || _current_field != nullptr) {
336  // It is an error to reseek while the stack is nonempty--that means we
337  // haven't finished packing the current field.
338  _pack_error = true;
339  return false;
340  }
341  const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(seek_index);
342 
343  if (entry._parent->as_switch_parameter() != nullptr) {
344  // If the parent is a DCSwitch, that can only mean that the seeked field
345  // is a switch parameter. We can't support seeking to a switch
346  // parameter and modifying it directly--what would happen to all of the
347  // related fields? Instead, you'll have to seek to the switch itself
348  // and repack the whole entity.
349  _pack_error = true;
350  return false;
351  }
352 
353  size_t begin = _live_catalog->get_begin(seek_index);
354  if (begin < _unpack_p) {
355  // Whoops, we are seeking fields out-of-order. That means we need to
356  // write the entire record and start again.
357  _pack_data.append_data(_unpack_data + _unpack_p, _unpack_length - _unpack_p);
358  size_t length = _pack_data.get_length();
359  char *buffer = _pack_data.take_data();
360  set_unpack_data(buffer, length, true);
361  _unpack_p = 0;
362 
363  _catalog->release_live_catalog(_live_catalog);
364  _live_catalog = _catalog->get_live_catalog(_unpack_data, _unpack_length);
365 
366  if (_live_catalog == nullptr) {
367  _pack_error = true;
368  return false;
369  }
370 
371  begin = _live_catalog->get_begin(seek_index);
372  }
373 
374  // Now copy the bytes from _unpack_p to begin from the _unpack_data to the
375  // _pack_data. These are the bytes we just skipped over with the call to
376  // seek().
377  _pack_data.append_data(_unpack_data + _unpack_p, begin - _unpack_p);
378 
379  // And set the packer up to pack the indicated field (but no subsequent
380  // fields).
381  _current_field = entry._field;
382  _current_parent = entry._parent;
383  _current_field_index = entry._field_index;
384  _num_nested_fields = 1;
385  _unpack_p = _live_catalog->get_end(seek_index);
386 
387  // Set up push_marker and pop_marker so we won't try to advance beyond
388  // this field.
389  _push_marker = begin;
390  _pop_marker = _live_catalog->get_end(seek_index);
391 
392  return true;
393  }
394 
395  // Invalid mode.
396  _pack_error = true;
397  return false;
398 }
399 
400 /**
401  * Marks the beginning of a nested series of fields.
402  *
403  * This must be called before filling the elements of an array or the
404  * individual fields in a structure field. It must also be balanced by a
405  * matching pop().
406  *
407  * It is necessary to use push() / pop() only if has_nested_fields() returns
408  * true.
409  */
410 void DCPacker::
411 push() {
412  if (!has_nested_fields()) {
413  _pack_error = true;
414 
415  } else {
416  StackElement *element = new StackElement;
417  element->_current_parent = _current_parent;
418  element->_current_field_index = _current_field_index;
419  element->_push_marker = _push_marker;
420  element->_pop_marker = _pop_marker;
421  element->_next = _stack;
422  _stack = element;
423  _current_parent = _current_field;
424 
425 
426  // Now deal with the length prefix that might or might not be before a
427  // sequence of nested fields.
428  int num_nested_fields = _current_parent->get_num_nested_fields();
429  size_t length_bytes = _current_parent->get_num_length_bytes();
430 
431  if (_mode == M_pack || _mode == M_repack) {
432  // Reserve length_bytes for when we figure out what the length is.
433  _push_marker = _pack_data.get_length();
434  _pop_marker = 0;
435  _pack_data.append_junk(length_bytes);
436 
437  } else if (_mode == M_unpack) {
438  // Read length_bytes to determine the end of this nested sequence.
439  _push_marker = _unpack_p;
440  _pop_marker = 0;
441 
442  if (length_bytes != 0) {
443  if (_unpack_p + length_bytes > _unpack_length) {
444  _pack_error = true;
445 
446  } else {
447  size_t length;
448  if (length_bytes == 4) {
449  length = DCPackerInterface::do_unpack_uint32
450  (_unpack_data + _unpack_p);
451  _unpack_p += 4;
452  } else {
453  length = DCPackerInterface::do_unpack_uint16
454  (_unpack_data + _unpack_p);
455  _unpack_p += 2;
456  }
457  _pop_marker = _unpack_p + length;
458 
459  // The explicit length trumps the number of nested fields reported
460  // by get_num_nested_fields().
461  if (length == 0) {
462  num_nested_fields = 0;
463  } else {
464  num_nested_fields = _current_parent->calc_num_nested_fields(length);
465  }
466  }
467  }
468  } else {
469  _pack_error = true;
470  }
471 
472 
473  // Now point to the first field in the nested range.
474  _num_nested_fields = num_nested_fields;
475  _current_field_index = 0;
476 
477  if (_num_nested_fields >= 0 &&
478  _current_field_index >= _num_nested_fields) {
479  _current_field = nullptr;
480 
481  } else {
482  _current_field = _current_parent->get_nested_field(_current_field_index);
483  }
484  }
485 }
486 
487 /**
488  * Marks the end of a nested series of fields.
489  *
490  * This must be called to match a previous push() only after all the expected
491  * number of nested fields have been packed. It is an error to call it too
492  * early, or too late.
493  */
494 void DCPacker::
495 pop() {
496  if (_current_field != nullptr && _num_nested_fields >= 0) {
497  // Oops, didn't pack or unpack enough values.
498  _pack_error = true;
499 
500  } else if (_mode == M_unpack && _pop_marker != 0 &&
501  _unpack_p != _pop_marker) {
502  // Didn't unpack the right number of values.
503  _pack_error = true;
504  }
505 
506  if (_stack == nullptr) {
507  // Unbalanced pop().
508  _pack_error = true;
509 
510  } else {
511  if (!_current_parent->validate_num_nested_fields(_current_field_index)) {
512  // Incorrect number of nested elements.
513  _pack_error = true;
514  }
515 
516  if (_mode == M_pack || _mode == M_repack) {
517  size_t length_bytes = _current_parent->get_num_length_bytes();
518  if (length_bytes != 0) {
519  // Now go back and fill in the length of the array.
520  size_t length = _pack_data.get_length() - _push_marker - length_bytes;
521  if (length_bytes == 4) {
522  DCPackerInterface::do_pack_uint32
523  (_pack_data.get_rewrite_pointer(_push_marker, 4), length);
524  } else {
525  DCPackerInterface::validate_uint_limits(length, 16, _range_error);
526  DCPackerInterface::do_pack_uint16
527  (_pack_data.get_rewrite_pointer(_push_marker, 2), length);
528  }
529  }
530  }
531 
532  _current_field = _current_parent;
533  _current_parent = _stack->_current_parent;
534  _current_field_index = _stack->_current_field_index;
535  _push_marker = _stack->_push_marker;
536  _pop_marker = _stack->_pop_marker;
537  _num_nested_fields = (_current_parent == nullptr) ? 0 : _current_parent->get_num_nested_fields();
538 
539  StackElement *next = _stack->_next;
540  delete _stack;
541  _stack = next;
542  }
543 
544  advance();
545 }
546 
547 /**
548  * Adds the default value for the current element into the stream. If no
549  * default has been set for the current element, creates a sensible default.
550  */
551 void DCPacker::
553  nassertv(_mode == M_pack || _mode == M_repack);
554  if (_current_field == nullptr) {
555  _pack_error = true;
556  } else {
557  if (_current_field->pack_default_value(_pack_data, _pack_error)) {
558  advance();
559 
560  } else {
561  // If the single field didn't know how to pack a default value, try
562  // packing nested fields.
563  push();
564  while (more_nested_fields()) {
566  }
567  pop();
568  }
569  }
570 }
571 
572 /**
573  * Internally unpacks the current numeric or string value and validates it
574  * against the type range limits, but does not return the value. If the
575  * current field contains nested fields, validates all of them.
576  */
577 void DCPacker::
579  nassertv(_mode == M_unpack);
580  if (_current_field == nullptr) {
581  _pack_error = true;
582 
583  } else {
584  if (_current_field->unpack_validate(_unpack_data, _unpack_length, _unpack_p,
585  _pack_error, _range_error)) {
586  advance();
587  } else {
588  // If the single field couldn't be validated, try validating nested
589  // fields.
590  push();
591  while (more_nested_fields()) {
592  unpack_validate();
593  }
594  pop();
595  }
596  }
597 }
598 
599 /**
600  * Skips the current field without unpacking it and advances to the next
601  * field. If the current field contains nested fields, skips all of them.
602  */
603 void DCPacker::
605  nassertv(_mode == M_unpack);
606  if (_current_field == nullptr) {
607  _pack_error = true;
608 
609  } else {
610  if (_current_field->unpack_skip(_unpack_data, _unpack_length, _unpack_p,
611  _pack_error)) {
612  advance();
613 
614  } else {
615  // If the single field couldn't be skipped, try skipping nested fields.
616  push();
617  while (more_nested_fields()) {
618  unpack_skip();
619  }
620  pop();
621  }
622  }
623 }
624 
625 #ifdef HAVE_PYTHON
626 /**
627  * Packs the Python object of whatever type into the packer. Each numeric
628  * object and string object maps to the corresponding pack_value() call; a
629  * tuple or sequence maps to a push() followed by all of the tuple's contents
630  * followed by a pop().
631  */
632 void DCPacker::
633 pack_object(PyObject *object) {
634  nassertv(_mode == M_pack || _mode == M_repack);
635  DCPackType pack_type = get_pack_type();
636 
637  // had to add this for basic 64 and unsigned data to get packed right .. Not
638  // sure if we can just do the rest this way..
639 
640  switch(pack_type)
641  {
642  case PT_int64:
643  if(PyLong_Check(object))
644  {
645  pack_int64(PyLong_AsLongLong(object));
646  return;
647  }
648 #if PY_MAJOR_VERSION < 3
649  else if (PyInt_Check(object))
650  {
651  pack_int64(PyInt_AsLong(object));
652  return;
653  }
654 #endif
655  break;
656  case PT_uint64:
657  if(PyLong_Check(object))
658  {
659  pack_uint64(PyLong_AsUnsignedLongLong(object));
660  return;
661  }
662 #if PY_MAJOR_VERSION < 3
663  else if(PyInt_Check(object))
664  {
665  PyObject *obj1 = PyNumber_Long(object);
666  pack_int(PyLong_AsUnsignedLongLong(obj1));
667  Py_DECREF(obj1);
668  return;
669  }
670 #endif
671  break;
672  case PT_int:
673  if(PyLong_Check(object))
674  {
675  pack_int(PyLong_AsLong(object));
676  return;
677  }
678 #if PY_MAJOR_VERSION < 3
679  else if (PyInt_Check(object))
680  {
681  pack_int(PyInt_AsLong(object));
682  return;
683  }
684 #endif
685  break;
686  case PT_uint:
687  if(PyLong_Check(object))
688  {
689  pack_uint(PyLong_AsUnsignedLong(object));
690  return;
691  }
692 #if PY_MAJOR_VERSION < 3
693  else if (PyInt_Check(object))
694  {
695  PyObject *obj1 = PyNumber_Long(object);
696  pack_uint(PyLong_AsUnsignedLong(obj1));
697  Py_DECREF(obj1);
698  return;
699  }
700 #endif
701  break;
702  default:
703  break;
704  }
705  if (PyLong_Check(object)) {
706  pack_int(PyLong_AsLong(object));
707 #if PY_MAJOR_VERSION < 3
708  } else if (PyInt_Check(object)) {
709  pack_int(PyInt_AS_LONG(object));
710 #endif
711  } else if (PyFloat_Check(object)) {
712  pack_double(PyFloat_AS_DOUBLE(object));
713  } else if (PyLong_Check(object)) {
714  pack_int64(PyLong_AsLongLong(object));
715 #if PY_MAJOR_VERSION >= 3
716  } else if (PyUnicode_Check(object)) {
717  const char *buffer;
718  Py_ssize_t length;
719  buffer = PyUnicode_AsUTF8AndSize(object, &length);
720  if (buffer) {
721  pack_string(string(buffer, length));
722  }
723  } else if (PyBytes_Check(object)) {
724  const unsigned char *buffer;
725  Py_ssize_t length;
726  PyBytes_AsStringAndSize(object, (char **)&buffer, &length);
727  if (buffer) {
728  pack_blob(vector_uchar(buffer, buffer + length));
729  }
730 #else
731  } else if (PyString_Check(object) || PyUnicode_Check(object)) {
732  char *buffer;
733  Py_ssize_t length;
734  PyString_AsStringAndSize(object, &buffer, &length);
735  if (buffer) {
736  pack_string(string(buffer, length));
737  }
738 #endif
739  } else {
740  // For some reason, PySequence_Check() is incorrectly reporting that a
741  // class instance is a sequence, even if it doesn't provide __len__, so we
742  // double-check by testing for __len__ explicitly.
743  bool is_sequence =
744  (PySequence_Check(object) != 0) &&
745  (PyObject_HasAttrString(object, "__len__") != 0);
746  bool is_instance = false;
747 
748  const DCClass *dclass = nullptr;
749  const DCPackerInterface *current_field = get_current_field();
750  if (current_field != nullptr) {
751  const DCClassParameter *class_param = get_current_field()->as_class_parameter();
752  if (class_param != nullptr) {
753  dclass = class_param->get_class();
754 
755  if (dclass->has_class_def()) {
756  PyObject *class_def = dclass->get_class_def();
757  is_instance = (PyObject_IsInstance(object, dclass->get_class_def()) != 0);
758  Py_DECREF(class_def);
759  }
760  }
761  }
762 
763  // If dclass is not NULL, the packer is expecting a class object. There
764  // are then two cases: (1) the user has supplied a matching class object,
765  // or (2) the user has supplied a sequence object. Unfortunately, it may
766  // be difficult to differentiate these two cases, since a class object may
767  // also be a sequence object.
768 
769  // The rule to differentiate them is:
770 
771  // (1) If the supplied class object is an instance of the expected class
772  // object, it is considered to be a class object.
773 
774  // (2) Otherwise, if the supplied class object has a __len__() method
775  // (i.e. PySequence_Check() returns true), then it is considered to be a
776  // sequence.
777 
778  // (3) Otherwise, it is considered to be a class object.
779 
780  if (dclass != nullptr && (is_instance || !is_sequence)) {
781  // The supplied object is either an instance of the expected class
782  // object, or it is not a sequence--this is case (1) or (3).
783  pack_class_object(dclass, object);
784  } else if (is_sequence) {
785  // The supplied object is not an instance of the expected class object,
786  // but it is a sequence. This is case (2).
787  push();
788  int size = PySequence_Size(object);
789  for (int i = 0; i < size; ++i) {
790  PyObject *element = PySequence_GetItem(object, i);
791  if (element != nullptr) {
792  pack_object(element);
793  Py_DECREF(element);
794  } else {
795  std::cerr << "Unable to extract item " << i << " from sequence.\n";
796  }
797  }
798  pop();
799  } else {
800  // The supplied object is not a sequence, and we weren't expecting a
801  // class parameter. This is none of the above, an error.
802  ostringstream strm;
803  strm << "Don't know how to pack object: "
804  << DCField::get_pystr(object);
805  nassert_raise(strm.str());
806  _pack_error = true;
807  }
808  }
809 }
810 #endif // HAVE_PYTHON
811 
812 #ifdef HAVE_PYTHON
813 /**
814  * Unpacks a Python object of the appropriate type from the stream for the
815  * current field. This may be an integer or a string for a simple field
816  * object; if the current field represents a list of fields it will be a
817  * tuple.
818  */
819 PyObject *DCPacker::
820 unpack_object() {
821  PyObject *object = nullptr;
822 
823  DCPackType pack_type = get_pack_type();
824 
825  switch (pack_type) {
826  case PT_invalid:
827  object = Py_None;
828  Py_INCREF(object);
829  unpack_skip();
830  break;
831 
832  case PT_double:
833  {
834  double value = unpack_double();
835  object = PyFloat_FromDouble(value);
836  }
837  break;
838 
839  case PT_int:
840  {
841  int value = unpack_int();
842 #if PY_MAJOR_VERSION >= 3
843  object = PyLong_FromLong(value);
844 #else
845  object = PyInt_FromLong(value);
846 #endif
847  }
848  break;
849 
850  case PT_uint:
851  {
852  unsigned int value = unpack_uint();
853 #if PY_MAJOR_VERSION >= 3
854  object = PyLong_FromLong(value);
855 #else
856  if (value & 0x80000000) {
857  object = PyLong_FromUnsignedLong(value);
858  } else {
859  object = PyInt_FromLong(value);
860  }
861 #endif
862  }
863  break;
864 
865  case PT_int64:
866  {
867  int64_t value = unpack_int64();
868  object = PyLong_FromLongLong(value);
869  }
870  break;
871 
872  case PT_uint64:
873  {
874  uint64_t value = unpack_uint64();
875  object = PyLong_FromUnsignedLongLong(value);
876  }
877  break;
878 
879  case PT_blob:
880 #if PY_MAJOR_VERSION >= 3
881  {
882  string str;
883  unpack_string(str);
884  object = PyBytes_FromStringAndSize(str.data(), str.size());
885  }
886  break;
887 #endif
888  // On Python 2, fall through to below.
889 
890  case PT_string:
891  {
892  string str;
893  unpack_string(str);
894 #if PY_MAJOR_VERSION >= 3
895  object = PyUnicode_FromStringAndSize(str.data(), str.size());
896 #else
897  object = PyString_FromStringAndSize(str.data(), str.size());
898 #endif
899  }
900  break;
901 
902  case PT_class:
903  {
904  const DCClassParameter *class_param = get_current_field()->as_class_parameter();
905  if (class_param != nullptr) {
906  const DCClass *dclass = class_param->get_class();
907  if (dclass->has_class_def()) {
908  // If we know what kind of class object this is and it has a valid
909  // constructor, create the class object instead of just a tuple.
910  object = unpack_class_object(dclass);
911  if (object == nullptr) {
912  std::cerr << "Unable to construct object of class "
913  << dclass->get_name() << "\n";
914  } else {
915  break;
916  }
917  }
918  }
919  }
920  // Fall through (if no constructor)
921 
922  // If we don't know what kind of class object it is, or it doesn't have a
923  // constructor, fall through and make a tuple.
924  default:
925  {
926  // First, build up a list from the nested objects.
927  object = PyList_New(0);
928 
929  push();
930  while (more_nested_fields()) {
931  PyObject *element = unpack_object();
932  PyList_Append(object, element);
933  Py_DECREF(element);
934  }
935  pop();
936 
937  if (pack_type != PT_array) {
938  // For these other kinds of objects, we'll convert the list into a
939  // tuple.
940  PyObject *tuple = PyList_AsTuple(object);
941  Py_DECREF(object);
942  object = tuple;
943  }
944  }
945  break;
946  }
947 
948  nassertr(object != nullptr, nullptr);
949  return object;
950 }
951 #endif // HAVE_PYTHON
952 
953 
954 /**
955  * Parses an object's value according to the DC file syntax (e.g. as a
956  * default value string) and packs it. Returns true on success, false on a
957  * parse error.
958  */
959 bool DCPacker::
960 parse_and_pack(const string &formatted_object) {
961  istringstream strm(formatted_object);
962  return parse_and_pack(strm);
963 }
964 
965 /**
966  * Parses an object's value according to the DC file syntax (e.g. as a
967  * default value string) and packs it. Returns true on success, false on a
968  * parse error.
969  */
970 bool DCPacker::
971 parse_and_pack(istream &in) {
972  dc_init_parser_parameter_value(in, "parse_and_pack", *this);
973  dcyyparse();
974  dc_cleanup_parser();
975 
976  bool parse_error = (dc_error_count() != 0);
977  if (parse_error) {
978  _parse_error = true;
979  }
980 
981  return !parse_error;
982 }
983 
984 /**
985  * Unpacks an object and formats its value into a syntax suitable for parsing
986  * in the dc file (e.g. as a default value), or as an input to parse_object.
987  */
988 string DCPacker::
989 unpack_and_format(bool show_field_names) {
990  ostringstream strm;
991  unpack_and_format(strm, show_field_names);
992  return strm.str();
993 }
994 
995 /**
996  * Unpacks an object and formats its value into a syntax suitable for parsing
997  * in the dc file (e.g. as a default value), or as an input to parse_object.
998  */
999 void DCPacker::
1000 unpack_and_format(ostream &out, bool show_field_names) {
1001  DCPackType pack_type = get_pack_type();
1002 
1003  if (show_field_names && !get_current_field_name().empty()) {
1004  nassertv(_current_field != nullptr);
1005  const DCField *field = _current_field->as_field();
1006  if (field != nullptr &&
1007  field->as_parameter() != nullptr) {
1008  out << field->get_name() << " = ";
1009  }
1010  }
1011 
1012  switch (pack_type) {
1013  case PT_invalid:
1014  out << "<invalid>";
1015  break;
1016 
1017  case PT_double:
1018  out << unpack_double();
1019  break;
1020 
1021  case PT_int:
1022  out << unpack_int();
1023  break;
1024 
1025  case PT_uint:
1026  out << unpack_uint();
1027  break;
1028 
1029  case PT_int64:
1030  out << unpack_int64();
1031  break;
1032 
1033  case PT_uint64:
1034  out << unpack_uint64();
1035  break;
1036 
1037  case PT_string:
1038  enquote_string(out, '"', unpack_string());
1039  break;
1040 
1041  case PT_blob:
1043  break;
1044 
1045  default:
1046  {
1047  switch (pack_type) {
1048  case PT_array:
1049  out << '[';
1050  break;
1051 
1052  case PT_field:
1053  case PT_switch:
1054  out << '(';
1055  break;
1056 
1057  case PT_class:
1058  default:
1059  out << '{';
1060  break;
1061  }
1062 
1063  push();
1064  while (more_nested_fields() && !had_pack_error()) {
1065  unpack_and_format(out, show_field_names);
1066 
1067  if (more_nested_fields()) {
1068  out << ", ";
1069  }
1070  }
1071  pop();
1072 
1073  switch (pack_type) {
1074  case PT_array:
1075  out << ']';
1076  break;
1077 
1078  case PT_field:
1079  case PT_switch:
1080  out << ')';
1081  break;
1082 
1083  case PT_class:
1084  default:
1085  out << '}';
1086  break;
1087  }
1088  }
1089  break;
1090  }
1091 }
1092 
1093 /**
1094  * Outputs the indicated string within quotation marks.
1095  */
1096 void DCPacker::
1097 enquote_string(ostream &out, char quote_mark, const string &str) {
1098  out << quote_mark;
1099  for (string::const_iterator pi = str.begin();
1100  pi != str.end();
1101  ++pi) {
1102  if ((*pi) == quote_mark || (*pi) == '\\') {
1103  out << '\\' << (*pi);
1104 
1105  } else if (!isprint(*pi)) {
1106  char buffer[10];
1107  sprintf(buffer, "%02x", (unsigned char)(*pi));
1108  out << "\\x" << buffer;
1109 
1110  } else {
1111  out << (*pi);
1112  }
1113  }
1114  out << quote_mark;
1115 }
1116 
1117 /**
1118  * Outputs the indicated string as a hex constant.
1119  */
1120 void DCPacker::
1121 output_hex_string(ostream &out, const vector_uchar &str) {
1122  out << '<';
1123  for (vector_uchar::const_iterator pi = str.begin();
1124  pi != str.end();
1125  ++pi) {
1126  char buffer[10];
1127  sprintf(buffer, "%02x", (unsigned char)(*pi));
1128  out << buffer;
1129  }
1130  out << '>';
1131 }
1132 
1133 /**
1134  * When we advance past the key field on a switch record, we suddenly have
1135  * more fields available--all the appropriate alternate fields in the switch.
1136  *
1137  * This function is called when we detect this condition; it switches the
1138  * _current_parent to the appropriate case of the switch record.
1139  */
1140 void DCPacker::
1141 handle_switch(const DCSwitchParameter *switch_parameter) {
1142  // First, get the value from the key. This is either found in the unpack or
1143  // the pack data, depending on what mode we're in.
1144  const DCPackerInterface *new_parent = nullptr;
1145 
1146  if (_mode == M_pack || _mode == M_repack) {
1147  const char *data = _pack_data.get_data();
1148  new_parent = switch_parameter->apply_switch
1149  (data + _push_marker, _pack_data.get_length() - _push_marker);
1150 
1151  } else if (_mode == M_unpack) {
1152  new_parent = switch_parameter->apply_switch
1153  (_unpack_data + _push_marker, _unpack_p - _push_marker);
1154  }
1155 
1156  if (new_parent == nullptr) {
1157  // This means an invalid value was packed for the key.
1158  _range_error = true;
1159  return;
1160  }
1161 
1162  _last_switch = switch_parameter;
1163 
1164  // Now substitute in the switch case for the previous parent (which replaces
1165  // the switch node itself). This will suddenly make a slew of new fields
1166  // appear.
1167  _current_parent = new_parent;
1168  _num_nested_fields = _current_parent->get_num_nested_fields();
1169 
1170  if (_num_nested_fields < 0 ||
1171  _current_field_index < _num_nested_fields) {
1172  _current_field = _current_parent->get_nested_field(_current_field_index);
1173  }
1174 }
1175 
1176 /**
1177  * Resets the data structures after a pack or unpack sequence.
1178  */
1179 void DCPacker::
1180 clear() {
1181  clear_stack();
1182  _current_field = nullptr;
1183  _current_parent = nullptr;
1184  _current_field_index = 0;
1185  _num_nested_fields = 0;
1186  _push_marker = 0;
1187  _pop_marker = 0;
1188  _last_switch = nullptr;
1189 
1190  if (_live_catalog != nullptr) {
1191  _catalog->release_live_catalog(_live_catalog);
1192  _live_catalog = nullptr;
1193  }
1194  _catalog = nullptr;
1195  _root = nullptr;
1196 }
1197 
1198 /**
1199  * Empties the stack.
1200  */
1201 void DCPacker::
1202 clear_stack() {
1203  while (_stack != nullptr) {
1204  StackElement *next = _stack->_next;
1205  delete _stack;
1206  _stack = next;
1207  }
1208 }
1209 
1210 #ifdef HAVE_PYTHON
1211 /**
1212  * Given that the current element is a ClassParameter for a Python class
1213  * object, try to extract the appropriate values from the class object and
1214  * pack in.
1215  */
1216 void DCPacker::
1217 pack_class_object(const DCClass *dclass, PyObject *object) {
1218  push();
1219  while (more_nested_fields() && !_pack_error) {
1220  const DCField *field = get_current_field()->as_field();
1221  nassertv(field != nullptr);
1222  get_class_element(dclass, object, field);
1223  }
1224  pop();
1225 }
1226 #endif // HAVE_PYTHON
1227 
1228 #ifdef HAVE_PYTHON
1229 /**
1230  * Given that the current element is a ClassParameter for a Python class for
1231  * which we have a valid constructor, unpack it and fill in its values.
1232  */
1233 PyObject *DCPacker::
1234 unpack_class_object(const DCClass *dclass) {
1235  PyObject *class_def = dclass->get_class_def();
1236  nassertr(class_def != nullptr, nullptr);
1237 
1238  PyObject *object = nullptr;
1239 
1240  if (!dclass->has_constructor()) {
1241  // If the class uses a default constructor, go ahead and create the Python
1242  // object for it now.
1243  object = PyObject_CallObject(class_def, nullptr);
1244  if (object == nullptr) {
1245  return nullptr;
1246  }
1247  }
1248 
1249  push();
1250  if (object == nullptr && more_nested_fields()) {
1251  // The first nested field will be the constructor.
1252  const DCField *field = get_current_field()->as_field();
1253  nassertr(field != nullptr, object);
1254  nassertr(field == dclass->get_constructor(), object);
1255 
1256  set_class_element(class_def, object, field);
1257 
1258  // By now, the object should have been constructed.
1259  if (object == nullptr) {
1260  return nullptr;
1261  }
1262  }
1263  while (more_nested_fields()) {
1264  const DCField *field = get_current_field()->as_field();
1265  nassertr(field != nullptr, object);
1266 
1267  set_class_element(class_def, object, field);
1268  }
1269  pop();
1270 
1271  return object;
1272 }
1273 #endif // HAVE_PYTHON
1274 
1275 
1276 #ifdef HAVE_PYTHON
1277 /**
1278  * Unpacks the current element and stuffs it on the Python class object in
1279  * whatever way is appropriate.
1280  */
1281 void DCPacker::
1282 set_class_element(PyObject *class_def, PyObject *&object,
1283  const DCField *field) {
1284  string field_name = field->get_name();
1285  DCPackType pack_type = get_pack_type();
1286 
1287  if (field_name.empty()) {
1288  switch (pack_type) {
1289  case PT_class:
1290  case PT_switch:
1291  // If the field has no name, but it is one of these container objects,
1292  // we want to unpack its nested objects directly into the class.
1293  push();
1294  while (more_nested_fields()) {
1295  const DCField *field = get_current_field()->as_field();
1296  nassertv(field != nullptr);
1297  nassertv(object != nullptr);
1298  set_class_element(class_def, object, field);
1299  }
1300  pop();
1301  break;
1302 
1303  default:
1304  // Otherwise, we just skip over the field.
1305  unpack_skip();
1306  }
1307 
1308  } else {
1309  // If the field does have a name, we will want to store it on the class,
1310  // either by calling a method (for a PT_field pack_type) or by setting a
1311  // value (for any other kind of pack_type).
1312 
1313  PyObject *element = unpack_object();
1314 
1315  if (pack_type == PT_field) {
1316  if (object == nullptr) {
1317  // If the object hasn't been constructed yet, assume this is the
1318  // constructor.
1319  object = PyObject_CallObject(class_def, element);
1320 
1321  } else {
1322  if (PyObject_HasAttrString(object, (char *)field_name.c_str())) {
1323  PyObject *func = PyObject_GetAttrString(object, (char *)field_name.c_str());
1324  if (func != nullptr) {
1325  PyObject *result = PyObject_CallObject(func, element);
1326  Py_XDECREF(result);
1327  Py_DECREF(func);
1328  }
1329  }
1330  }
1331 
1332  } else {
1333  nassertv(object != nullptr);
1334  PyObject_SetAttrString(object, (char *)field_name.c_str(), element);
1335  }
1336 
1337  Py_DECREF(element);
1338  }
1339 }
1340 #endif // HAVE_PYTHON
1341 
1342 
1343 #ifdef HAVE_PYTHON
1344 /**
1345  * Gets the current element from the Python object and packs it.
1346  */
1347 void DCPacker::
1348 get_class_element(const DCClass *dclass, PyObject *object,
1349  const DCField *field) {
1350  string field_name = field->get_name();
1351  DCPackType pack_type = get_pack_type();
1352 
1353  if (field_name.empty()) {
1354  switch (pack_type) {
1355  case PT_class:
1356  case PT_switch:
1357  // If the field has no name, but it is one of these container objects,
1358  // we want to get its nested objects directly from the class.
1359  push();
1360  while (more_nested_fields() && !_pack_error) {
1361  const DCField *field = get_current_field()->as_field();
1362  nassertv(field != nullptr);
1363  get_class_element(dclass, object, field);
1364  }
1365  pop();
1366  break;
1367 
1368  default:
1369  // Otherwise, we just pack the default value.
1371  }
1372 
1373  } else {
1374  // If the field does have a name, we will want to get it from the class
1375  // and pack it. It just so happens that there's already a method that
1376  // does this on DCClass.
1377 
1378  if (!dclass->pack_required_field(*this, object, field)) {
1379  _pack_error = true;
1380  }
1381  }
1382 }
1383 #endif // HAVE_PYTHON
uint64_t unpack_uint64()
Unpacks the current numeric or string value from the stream.
Definition: dcPacker.I:328
bool had_error() const
Returns true if there has been any error (either a pack error or a range error) since the most recent...
Definition: dcPacker.I:563
DCPackType get_pack_type() const
Returns the type of value expected by the current field.
Definition: dcPacker.I:114
void append_data(const char *buffer, size_t size)
Adds the indicated bytes to the end of the data.
Definition: dcPackData.I:47
void pack_int(int value)
Packs the indicated numeric or string value into the stream.
Definition: dcPacker.I:153
virtual bool pack_default_value(DCPackData &pack_data, bool &pack_error) const
Packs the field's specified default value (or a sensible default if no value is specified) into the s...
bool end_repack()
Finishes the repacking session.
Definition: dcPacker.cxx:248
const std::string & get_name() const
Returns the name of this field, or empty string if the field is unnamed.
void release_live_catalog(const LiveCatalog *live_catalog) const
Releases the LiveCatalog object that was returned by an earlier call to get_live_catalog().
void pack_blob(const vector_uchar &value)
Packs the indicated numeric or string value into the stream.
Definition: dcPacker.I:223
This represents a class (or struct) object used as a parameter itself.
A single field of a Distributed Class, either atomic or molecular.
Definition: dcField.h:37
void clear()
Empties the contents of the data (without necessarily freeing its allocated memory).
Definition: dcPackData.I:39
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
const DCPackerInterface * get_current_field() const
Returns the field that will be referenced by the next call to pack_*() or unpack_*().
Definition: dcPacker.I:87
bool seek(const std::string &field_name)
Sets the current unpack (or repack) position to the named field.
Definition: dcPacker.cxx:270
void append_junk(size_t size)
Adds some uninitialized bytes to the end of the data.
Definition: dcPackData.I:66
int get_num_nested_fields() const
Returns the number of nested fields required by this field type.
unsigned int unpack_uint()
Unpacks the current numeric or string value from the stream.
Definition: dcPacker.I:290
Defines a particular DistributedClass as read from an input .dc file.
Definition: dcClass.h:44
virtual DCPackerInterface * get_nested_field(int n) const
Returns the DCPackerInterface object that represents the nth nested field.
void pack_uint(unsigned int value)
Packs the indicated numeric or string value into the stream.
Definition: dcPacker.I:167
bool had_pack_error() const
Returns true if there has been an packing error since the most recent call to begin(); in particular,...
Definition: dcPacker.I:539
const DCClass * get_class() const
Returns the class object this parameter represents.
void pack_uint64(uint64_t value)
Packs the indicated numeric or string value into the stream.
Definition: dcPacker.I:195
int64_t unpack_int64()
Unpacks the current numeric or string value from the stream.
Definition: dcPacker.I:309
int find_entry_by_name(const std::string &name) const
Returns the index number of the entry with the indicated name, or -1 if no entry has the indicated na...
void begin_repack(const DCPackerInterface *root)
Begins a repacking session.
Definition: dcPacker.cxx:213
double unpack_double()
Unpacks the current numeric or string value from the stream.
Definition: dcPacker.I:252
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string get_current_field_name() const
Returns the name of the current field, if it has a name, or the empty string if the field does not ha...
Definition: dcPacker.I:127
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool has_nested_fields() const
Returns true if the current field has any nested fields (and thus expects a push() .
Definition: dcPacker.I:37
static void output_hex_string(std::ostream &out, const vector_uchar &str)
Outputs the indicated string as a hex constant.
Definition: dcPacker.cxx:1121
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool unpack_validate(const char *data, size_t length, size_t &p, bool &pack_error, bool &range_error) const
Internally unpacks the current numeric or string value and validates it against the type range limits...
void set_unpack_data(const vector_uchar &data)
Sets up the unpack_data pointer.
Definition: dcPacker.cxx:117
void pack_default_value()
Adds the default value for the current element into the stream.
Definition: dcPacker.cxx:552
bool more_nested_fields() const
Returns true if there are more nested fields to pack or unpack in the current push sequence,...
Definition: dcPacker.I:67
const DCPackerCatalog * get_catalog() const
Returns the DCPackerCatalog associated with this field, listing all of the nested fields by name.
size_t get_num_length_bytes() const
Returns the number of bytes that should be written into the stream on a push() to record the number o...
void push()
Marks the beginning of a nested series of fields.
Definition: dcPacker.cxx:411
virtual bool unpack_skip(const char *data, size_t length, size_t &p, bool &pack_error) const
Increments p to the end of the current field without actually unpacking any data or performing any ra...
char * take_data()
Returns the pointer to the beginning of the data buffer, and transfers ownership of the buffer to the...
Definition: dcPackData.I:129
size_t get_length() const
Returns the current length of the buffer.
Definition: dcPackData.I:102
static void enquote_string(std::ostream &out, char quote_mark, const std::string &str)
Outputs the indicated string within quotation marks.
Definition: dcPacker.cxx:1097
DCField * get_constructor() const
Returns the constructor method for this class if it is defined, or NULL if the class uses the default...
Definition: dcClass.cxx:162
std::string unpack_string()
Unpacks the current numeric or string value from the stream.
Definition: dcPacker.I:347
std::string unpack_and_format(bool show_field_names=true)
Unpacks an object and formats its value into a syntax suitable for parsing in the dc file (e....
Definition: dcPacker.cxx:989
size_t get_end(int n) const
Returns the end of the indicated field (the byte position of the first following field) within the li...
void begin_pack(const DCPackerInterface *root)
Begins a packing session.
Definition: dcPacker.cxx:73
int unpack_int()
Unpacks the current numeric or string value from the stream.
Definition: dcPacker.I:271
size_t get_begin(int n) const
Returns the beginning of the indicated field within the live data.
const char * get_data() const
Returns the beginning of the data buffer.
Definition: dcPackData.I:115
const std::string & get_name() const
Returns the name of this class.
Definition: dcClass.I:26
virtual int calc_num_nested_fields(size_t length_bytes) const
This flavor of get_num_nested_fields is used during unpacking.
bool parse_and_pack(const std::string &formatted_object)
Parses an object's value according to the DC file syntax (e.g.
Definition: dcPacker.cxx:960
bool has_constructor() const
Returns true if this class has a constructor method, false if it just uses the default constructor.
Definition: dcClass.cxx:153
void unpack_skip()
Skips the current field without unpacking it and advances to the next field.
Definition: dcPacker.cxx:604
char * get_rewrite_pointer(size_t position, size_t size)
Returns a pointer into the middle of the data at the indicated point.
Definition: dcPackData.I:84
vector_uchar unpack_literal_value()
Returns the literal string that represents the packed value of the current field, and advances the fi...
Definition: dcPacker.I:386
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
virtual bool validate_num_nested_fields(int num_nested_fields) const
After a number of fields have been packed via push() .
bool end_pack()
Finishes a packing session.
Definition: dcPacker.cxx:98
void pack_double(double value)
Packs the indicated numeric or string value into the stream.
Definition: dcPacker.I:139
void pop()
Marks the end of a nested series of fields.
Definition: dcPacker.cxx:495
const Entry & get_entry(int n) const
Returns the nth entry in the catalog.
const LiveCatalog * get_live_catalog(const char *data, size_t length) const
Returns a LiveCatalog object indicating the positions within the indicated data record of each field ...
This defines the internal interface for packing values into a DCField.
void pack_string(const std::string &value)
Packs the indicated numeric or string value into the stream.
Definition: dcPacker.I:209
void clear_data()
Empties the data in the pack buffer and unpack buffer.
Definition: dcPacker.I:20
void begin_unpack(const DCPackerInterface *root)
Begins an unpacking session.
Definition: dcPacker.cxx:153
static void validate_uint_limits(unsigned int value, int num_bits, bool &range_error)
Confirms that the unsigned value fits within num_bits bits.
bool end_unpack()
Finishes the unpacking session.
Definition: dcPacker.cxx:179
void pack_int64(int64_t value)
Packs the indicated numeric or string value into the stream.
Definition: dcPacker.I:181
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void unpack_validate()
Internally unpacks the current numeric or string value and validates it against the type range limits...
Definition: dcPacker.cxx:578