Panda3D
Loading...
Searching...
No Matches
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
26using std::istream;
27using std::istringstream;
28using std::ostream;
29using std::ostringstream;
30using std::string;
31
32DCPacker::StackElement *DCPacker::StackElement::_deleted_chain = nullptr;
33int DCPacker::StackElement::_num_ever_allocated = 0;
34
35/**
36 *
37 */
38DCPacker::
39DCPacker() {
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 */
57DCPacker::
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 */
73begin_pack(const DCPackerInterface *root) {
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 */
98end_pack() {
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 */
117set_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 */
130set_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 */
153begin_unpack(const DCPackerInterface *root) {
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 */
179end_unpack() {
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 */
213begin_repack(const DCPackerInterface *root) {
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 */
248end_repack() {
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 */
270seek(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 */
300seek(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 */
411push() {
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 */
495pop() {
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 */
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 */
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()) {
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 */
604unpack_skip() {
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 */
632void DCPacker::
633pack_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 case PT_int64:
642 if (PyLong_Check(object)) {
643 pack_int64(PyLong_AsLongLong(object));
644 return;
645 }
646#if PY_MAJOR_VERSION < 3
647 else if (PyInt_Check(object)) {
648 pack_int64(PyInt_AsLong(object));
649 return;
650 }
651#endif
652 break;
653
654 case PT_uint64:
655 if (PyLong_Check(object)) {
656 pack_uint64(PyLong_AsUnsignedLongLong(object));
657 return;
658 }
659#if PY_MAJOR_VERSION < 3
660 else if (PyInt_Check(object)) {
661 PyObject *obj1 = PyNumber_Long(object);
662 pack_int(PyLong_AsUnsignedLongLong(obj1));
663 Py_DECREF(obj1);
664 return;
665 }
666#endif
667 break;
668
669 case PT_int:
670 if (PyLong_Check(object)) {
671 pack_int(PyLong_AsLong(object));
672 return;
673 }
674#if PY_MAJOR_VERSION < 3
675 else if (PyInt_Check(object)) {
676 pack_int(PyInt_AsLong(object));
677 return;
678 }
679#endif
680 break;
681
682 case PT_uint:
683 if (PyLong_Check(object)) {
684 pack_uint(PyLong_AsUnsignedLong(object));
685 return;
686 }
687#if PY_MAJOR_VERSION < 3
688 else if (PyInt_Check(object)) {
689 PyObject *obj1 = PyNumber_Long(object);
690 pack_uint(PyLong_AsUnsignedLong(obj1));
691 Py_DECREF(obj1);
692 return;
693 }
694#endif
695 break;
696
697 default:
698 break;
699 }
700
701 if (PyLong_Check(object)) {
702 pack_int(PyLong_AsLong(object));
703#if PY_MAJOR_VERSION < 3
704 } else if (PyInt_Check(object)) {
705 pack_int(PyInt_AS_LONG(object));
706#endif
707 } else if (PyFloat_Check(object)) {
708 pack_double(PyFloat_AS_DOUBLE(object));
709 } else if (PyLong_Check(object)) {
710 pack_int64(PyLong_AsLongLong(object));
711#if PY_MAJOR_VERSION >= 3
712 } else if (PyUnicode_Check(object)) {
713 const char *buffer;
714 Py_ssize_t length;
715 buffer = PyUnicode_AsUTF8AndSize(object, &length);
716 if (buffer) {
717 pack_string(string(buffer, length));
718 }
719 } else if (PyBytes_Check(object)) {
720 const unsigned char *buffer;
721 Py_ssize_t length;
722 PyBytes_AsStringAndSize(object, (char **)&buffer, &length);
723 if (buffer) {
724 pack_blob(vector_uchar(buffer, buffer + length));
725 }
726#else
727 } else if (PyString_Check(object) || PyUnicode_Check(object)) {
728 char *buffer;
729 Py_ssize_t length;
730 PyString_AsStringAndSize(object, &buffer, &length);
731 if (buffer) {
732 pack_string(string(buffer, length));
733 }
734#endif
735 } else {
736 // For some reason, PySequence_Check() is incorrectly reporting that a
737 // class instance is a sequence, even if it doesn't provide __len__, so we
738 // double-check by testing for __len__ explicitly.
739 bool is_sequence =
740 (PySequence_Check(object) != 0) &&
741 (PyObject_HasAttrString(object, "__len__") != 0);
742 bool is_instance = false;
743
744 const DCClass *dclass = nullptr;
745 const DCPackerInterface *current_field = get_current_field();
746 if (current_field != nullptr) {
747 const DCClassParameter *class_param = get_current_field()->as_class_parameter();
748 if (class_param != nullptr) {
749 dclass = class_param->get_class();
750
751 if (dclass->has_class_def()) {
752 PyObject *class_def = dclass->get_class_def();
753 is_instance = (PyObject_IsInstance(object, dclass->get_class_def()) != 0);
754 Py_DECREF(class_def);
755 }
756 }
757 }
758
759 // If dclass is not NULL, the packer is expecting a class object. There
760 // are then two cases: (1) the user has supplied a matching class object,
761 // or (2) the user has supplied a sequence object. Unfortunately, it may
762 // be difficult to differentiate these two cases, since a class object may
763 // also be a sequence object.
764
765 // The rule to differentiate them is:
766
767 // (1) If the supplied class object is an instance of the expected class
768 // object, it is considered to be a class object.
769
770 // (2) Otherwise, if the supplied class object has a __len__() method
771 // (i.e. PySequence_Check() returns true), then it is considered to be a
772 // sequence.
773
774 // (3) Otherwise, it is considered to be a class object.
775
776 if (dclass != nullptr && (is_instance || !is_sequence)) {
777 // The supplied object is either an instance of the expected class
778 // object, or it is not a sequence--this is case (1) or (3).
779 pack_class_object(dclass, object);
780 } else if (is_sequence) {
781 // The supplied object is not an instance of the expected class object,
782 // but it is a sequence. This is case (2).
783 push();
784 int size = PySequence_Size(object);
785 for (int i = 0; i < size; ++i) {
786 PyObject *element = PySequence_GetItem(object, i);
787 if (element != nullptr) {
788 pack_object(element);
789 Py_DECREF(element);
790 } else {
791 std::cerr << "Unable to extract item " << i << " from sequence.\n";
792 }
793 }
794 pop();
795 } else {
796 // The supplied object is not a sequence, and we weren't expecting a
797 // class parameter. This is none of the above, an error.
798 ostringstream strm;
799 strm << "Don't know how to pack object: "
800 << DCField::get_pystr(object);
801 nassert_raise(strm.str());
802 _pack_error = true;
803 }
804 }
805}
806#endif // HAVE_PYTHON
807
808#ifdef HAVE_PYTHON
809/**
810 * Unpacks a Python object of the appropriate type from the stream for the
811 * current field. This may be an integer or a string for a simple field
812 * object; if the current field represents a list of fields it will be a
813 * tuple.
814 */
815PyObject *DCPacker::
816unpack_object() {
817 PyObject *object = nullptr;
818
819 DCPackType pack_type = get_pack_type();
820
821 switch (pack_type) {
822 case PT_invalid:
823 object = Py_None;
824 Py_INCREF(object);
825 unpack_skip();
826 break;
827
828 case PT_double:
829 {
830 double value = unpack_double();
831 object = PyFloat_FromDouble(value);
832 }
833 break;
834
835 case PT_int:
836 {
837 int value = unpack_int();
838#if PY_MAJOR_VERSION >= 3
839 object = PyLong_FromLong(value);
840#else
841 object = PyInt_FromLong(value);
842#endif
843 }
844 break;
845
846 case PT_uint:
847 {
848 unsigned int value = unpack_uint();
849#if PY_MAJOR_VERSION >= 3
850 object = PyLong_FromUnsignedLong(value);
851#else
852 if (value & 0x80000000) {
853 object = PyLong_FromUnsignedLong(value);
854 } else {
855 object = PyInt_FromLong(value);
856 }
857#endif
858 }
859 break;
860
861 case PT_int64:
862 {
863 int64_t value = unpack_int64();
864 object = PyLong_FromLongLong(value);
865 }
866 break;
867
868 case PT_uint64:
869 {
870 uint64_t value = unpack_uint64();
871 object = PyLong_FromUnsignedLongLong(value);
872 }
873 break;
874
875 case PT_blob:
876#if PY_MAJOR_VERSION >= 3
877 {
878 string str;
879 unpack_string(str);
880 object = PyBytes_FromStringAndSize(str.data(), str.size());
881 }
882 break;
883#endif
884 // On Python 2, fall through to below.
885
886 case PT_string:
887 {
888 string str;
889 unpack_string(str);
890#if PY_MAJOR_VERSION >= 3
891 object = PyUnicode_FromStringAndSize(str.data(), str.size());
892 if (object == nullptr) {
893 nassert_raise("Unable to decode UTF-8 string; use blob type for binary data");
894 return nullptr;
895 }
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 */
960parse_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 */
971parse_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 */
989unpack_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 */
1000unpack_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 */
1097enquote_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) || (*pi) == '\t') {
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 */
1121output_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 */
1140void DCPacker::
1141handle_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 */
1179void DCPacker::
1180clear() {
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 */
1201void DCPacker::
1202clear_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 */
1216void DCPacker::
1217pack_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 */
1233PyObject *DCPacker::
1234unpack_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 */
1281void DCPacker::
1282set_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 */
1347void DCPacker::
1348get_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
This represents a class (or struct) object used as a parameter itself.
const DCClass * get_class() const
Returns the class object this parameter represents.
Defines a particular DistributedClass as read from an input .dc file.
Definition dcClass.h:44
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
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
const std::string & get_name() const
Returns the name of this class.
Definition dcClass.I:26
A single field of a Distributed Class, either atomic or molecular.
Definition dcField.h:37
const char * get_data() const
Returns the beginning of the data buffer.
Definition dcPackData.I:115
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
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
void append_junk(size_t size)
Adds some uninitialized bytes to the end of the data.
Definition dcPackData.I:66
void append_data(const char *buffer, size_t size)
Adds the indicated bytes to the end of the data.
Definition dcPackData.I:47
size_t get_length() const
Returns the current length of the buffer.
Definition dcPackData.I:102
void clear()
Empties the contents of the data (without necessarily freeing its allocated memory).
Definition dcPackData.I:39
size_t get_begin(int n) const
Returns the beginning of the indicated field within the live data.
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...
const Entry & get_entry(int n) const
Returns the nth entry in the catalog.
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 release_live_catalog(const LiveCatalog *live_catalog) const
Releases the LiveCatalog object that was returned by an earlier call to get_live_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.
static void validate_uint_limits(unsigned int value, int num_bits, bool &range_error)
Confirms that the unsigned value fits within num_bits bits.
const DCPackerCatalog * get_catalog() const
Returns the DCPackerCatalog associated with this field, listing all of the nested fields by name.
const std::string & get_name() const
Returns the name of this field, or empty string if the field is unnamed.
int get_num_nested_fields() const
Returns the number of nested fields required by this field type.
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...
virtual int calc_num_nested_fields(size_t length_bytes) const
This flavor of get_num_nested_fields is used during unpacking.
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...
virtual bool validate_num_nested_fields(int num_nested_fields) const
After a number of fields have been packed via push() .
virtual DCPackerInterface * get_nested_field(int n) const
Returns the DCPackerInterface object that represents the nth nested field.
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...
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...
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
void unpack_validate()
Internally unpacks the current numeric or string value and validates it against the type range limits...
Definition dcPacker.cxx:578
void push()
Marks the beginning of a nested series of fields.
Definition dcPacker.cxx:411
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
std::string unpack_string()
Unpacks the current numeric or string value from the stream.
Definition dcPacker.I:347
void clear_data()
Empties the data in the pack buffer and unpack buffer.
Definition dcPacker.I:20
void pack_blob(const std::vector< unsigned char > &value)
Packs the indicated numeric or string value into the stream.
Definition dcPacker.I:223
int unpack_int()
Unpacks the current numeric or string value from the stream.
Definition dcPacker.I:271
void pack_uint(unsigned int value)
Packs the indicated numeric or string value into the stream.
Definition dcPacker.I:167
std::vector< unsigned char > unpack_literal_value()
Returns the literal string that represents the packed value of the current field, and advances the fi...
Definition dcPacker.I:386
void begin_pack(const DCPackerInterface *root)
Begins a packing session.
Definition dcPacker.cxx:73
void pack_int(int value)
Packs the indicated numeric or string value into the stream.
Definition dcPacker.I:153
void begin_unpack(const DCPackerInterface *root)
Begins an unpacking session.
Definition dcPacker.cxx:153
void begin_repack(const DCPackerInterface *root)
Begins a repacking session.
Definition dcPacker.cxx:213
bool end_repack()
Finishes the repacking session.
Definition dcPacker.cxx:248
DCPackType get_pack_type() const
Returns the type of value expected by the current field.
Definition dcPacker.I:114
bool end_unpack()
Finishes the unpacking session.
Definition dcPacker.cxx:179
bool has_nested_fields() const
Returns true if the current field has any nested fields (and thus expects a push() .
Definition dcPacker.I:37
void pack_uint64(uint64_t value)
Packs the indicated numeric or string value into the stream.
Definition dcPacker.I:195
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
double unpack_double()
Unpacks the current numeric or string value from the stream.
Definition dcPacker.I:252
void pop()
Marks the end of a nested series of fields.
Definition dcPacker.cxx:495
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
bool end_pack()
Finishes a packing session.
Definition dcPacker.cxx:98
void pack_int64(int64_t value)
Packs the indicated numeric or string value into the stream.
Definition dcPacker.I:181
void unpack_skip()
Skips the current field without unpacking it and advances to the next field.
Definition dcPacker.cxx:604
static void output_hex_string(std::ostream &out, const std::vector< unsigned char > &str)
Outputs the indicated string as a hex constant.
bool seek(const std::string &field_name)
Sets the current unpack (or repack) position to the named field.
Definition dcPacker.cxx:270
void set_unpack_data(const std::vector< unsigned char > &data)
Sets up the unpack_data pointer.
Definition dcPacker.cxx:117
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
void pack_default_value()
Adds the default value for the current element into the stream.
Definition dcPacker.cxx:552
static void enquote_string(std::ostream &out, char quote_mark, const std::string &str)
Outputs the indicated string within quotation marks.
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
void pack_double(double value)
Packs the indicated numeric or string value into the stream.
Definition dcPacker.I:139
int64_t unpack_int64()
Unpacks the current numeric or string value from the stream.
Definition dcPacker.I:309
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
unsigned int unpack_uint()
Unpacks the current numeric or string value from the stream.
Definition dcPacker.I:290
uint64_t unpack_uint64()
Unpacks the current numeric or string value from the stream.
Definition dcPacker.I:328
void pack_string(const std::string &value)
Packs the indicated numeric or string value into the stream.
Definition dcPacker.I:209
This represents a switch object used as a parameter itself, which packs the appropriate fields of the...
const DCPackerInterface * apply_switch(const char *value_data, size_t length) const
Returns the DCPackerInterface that presents the alternative fields for the case indicated by the give...
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.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.