Panda3D
Loading...
Searching...
No Matches
dcField.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 dcField.cxx
10 * @author drose
11 * @date 2000-10-11
12 */
13
14#include "dcField.h"
15#include "dcFile.h"
16#include "dcPacker.h"
17#include "dcClass.h"
18#include "hashGenerator.h"
19#include "dcmsgtypes.h"
20
21#ifdef HAVE_PYTHON
22#include "py_panda.h"
23#endif
24
25#ifdef WITHIN_PANDA
26#include "pStatTimer.h"
27#endif
28
29using std::string;
30
31/**
32 *
33 */
34DCField::
35DCField() :
36 _dclass(nullptr)
37#ifdef WITHIN_PANDA
38 ,
39 _field_update_pcollector("DCField")
40#endif
41{
42 _number = -1;
43 _default_value_stale = true;
44 _has_default_value = false;
45
46 _bogus_field = false;
47
48 _has_nested_fields = true;
49 _num_nested_fields = 0;
50 _pack_type = PT_field;
51
52 _has_fixed_byte_size = true;
53 _fixed_byte_size = 0;
54 _has_fixed_structure = true;
55}
56
57/**
58 *
59 */
60DCField::
61DCField(const string &name, DCClass *dclass) :
63 _dclass(dclass)
64#ifdef WITHIN_PANDA
65 ,
66 _field_update_pcollector(dclass->_class_update_pcollector, name)
67#endif
68{
69 _number = -1;
70 _has_default_value = false;
71 _default_value_stale = true;
72
73 _bogus_field = false;
74
75 _has_nested_fields = true;
76 _num_nested_fields = 0;
77 _pack_type = PT_field;
78
79 _has_fixed_byte_size = true;
80 _fixed_byte_size = 0;
81 _has_fixed_structure = true;
82}
83
84/**
85 *
86 */
87DCField::
88~DCField() {
89}
90
91/**
92 *
93 */
94DCField *DCField::
95as_field() {
96 return this;
97}
98
99/**
100 *
101 */
102const DCField *DCField::
103as_field() const {
104 return this;
105}
106
107/**
108 * Returns the same field pointer converted to an atomic field pointer, if
109 * this is in fact an atomic field; otherwise, returns NULL.
110 */
113 return nullptr;
114}
115
116/**
117 * Returns the same field pointer converted to an atomic field pointer, if
118 * this is in fact an atomic field; otherwise, returns NULL.
119 */
121as_atomic_field() const {
122 return nullptr;
123}
124
125/**
126 * Returns the same field pointer converted to a molecular field pointer, if
127 * this is in fact a molecular field; otherwise, returns NULL.
128 */
131 return nullptr;
132}
133
134/**
135 * Returns the same field pointer converted to a molecular field pointer, if
136 * this is in fact a molecular field; otherwise, returns NULL.
137 */
139as_molecular_field() const {
140 return nullptr;
141}
142
143/**
144 *
145 */
146DCParameter *DCField::
147as_parameter() {
148 return nullptr;
149}
150
151/**
152 *
153 */
154const DCParameter *DCField::
155as_parameter() const {
156 return nullptr;
157}
158
159/**
160 * Given a blob that represents the packed data for this field, returns a
161 * string formatting it for human consumption. Returns empty string if there
162 * is an error.
163 */
165format_data(const vector_uchar &packed_data, bool show_field_names) {
166 DCPacker packer;
167 packer.set_unpack_data(packed_data);
168 packer.begin_unpack(this);
169 string result = packer.unpack_and_format(show_field_names);
170 if (!packer.end_unpack()) {
171 return string();
172 }
173 return result;
174}
175
176/**
177 * Given a human-formatted string (for instance, as returned by format_data(),
178 * above) that represents the value of this field, parse the string and return
179 * the corresponding packed data. Returns empty string if there is an error.
180 */
181vector_uchar DCField::
182parse_string(const string &formatted_string) {
183 DCPacker packer;
184 packer.begin_pack(this);
185 if (!packer.parse_and_pack(formatted_string)) {
186 // Parse error.
187 return vector_uchar();
188 }
189 if (!packer.end_pack()) {
190 // Data type mismatch.
191 return vector_uchar();
192 }
193
194 return packer.get_bytes();
195}
196
197/**
198 * Verifies that all of the packed values in the field data are within the
199 * specified ranges and that there are no extra bytes on the end of the
200 * record. Returns true if all fields are valid, false otherwise.
201 */
203validate_ranges(const vector_uchar &packed_data) const {
204 DCPacker packer;
205 packer.set_unpack_data(packed_data);
206 packer.begin_unpack(this);
207 packer.unpack_validate();
208 if (!packer.end_unpack()) {
209 return false;
210 }
211
212 return (packer.get_num_unpacked_bytes() == packed_data.size());
213}
214
215#ifdef HAVE_PYTHON
216/**
217 * Packs the Python arguments from the indicated tuple into the packer.
218 * Returns true on success, false on failure.
219 *
220 * It is assumed that the packer is currently positioned on this field.
221 */
222bool DCField::
223pack_args(DCPacker &packer, PyObject *sequence) const {
224 nassertr(!packer.had_error(), false);
225 nassertr(packer.get_current_field() == this, false);
226
227 packer.pack_object(sequence);
228 if (!packer.had_error()) {
229 /*
230 cerr << "pack " << get_name() << get_pystr(sequence) << "\n";
231 */
232
233 return true;
234 }
235
236 if (!Notify::ptr()->has_assert_failed()) {
237 std::ostringstream strm;
238 PyObject *exc_type = PyExc_Exception;
239
240 if (as_parameter() != nullptr) {
241 // If it's a parameter-type field, the value may or may not be a
242 // sequence.
243 if (packer.had_pack_error()) {
244 strm << "Incorrect arguments to field: " << get_name()
245 << " = " << get_pystr(sequence);
246 exc_type = PyExc_TypeError;
247 } else {
248 strm << "Value out of range on field: " << get_name()
249 << " = " << get_pystr(sequence);
250 exc_type = PyExc_ValueError;
251 }
252
253 } else {
254 // If it's a molecular or atomic field, the value should be a sequence.
255 PyObject *tuple = PySequence_Tuple(sequence);
256 if (tuple == nullptr) {
257 strm << "Value for " << get_name() << " not a sequence: " \
258 << get_pystr(sequence);
259 exc_type = PyExc_TypeError;
260
261 } else {
262 if (packer.had_pack_error()) {
263 strm << "Incorrect arguments to field: " << get_name()
264 << get_pystr(sequence);
265 exc_type = PyExc_TypeError;
266 } else {
267 strm << "Value out of range on field: " << get_name()
268 << get_pystr(sequence);
269 exc_type = PyExc_ValueError;
270 }
271
272 Py_DECREF(tuple);
273 }
274 }
275
276 string message = strm.str();
277 PyErr_SetString(exc_type, message.c_str());
278 }
279 return false;
280}
281#endif // HAVE_PYTHON
282
283#ifdef HAVE_PYTHON
284/**
285 * Unpacks the values from the packer, beginning at the current point in the
286 * unpack_buffer, into a Python tuple and returns the tuple.
287 *
288 * It is assumed that the packer is currently positioned on this field.
289 */
290PyObject *DCField::
291unpack_args(DCPacker &packer) const {
292 nassertr(!packer.had_error(), nullptr);
293 nassertr(packer.get_current_field() == this, nullptr);
294
295 size_t start_byte = packer.get_num_unpacked_bytes();
296 PyObject *object = packer.unpack_object();
297
298 if (!packer.had_error()) {
299 // Successfully unpacked.
300 /*
301 cerr << "recv " << get_name() << get_pystr(object) << "\n";
302 */
303
304 return object;
305 }
306
307 if (!Notify::ptr()->has_assert_failed()) {
308 std::ostringstream strm;
309 PyObject *exc_type = PyExc_Exception;
310
311 if (packer.had_pack_error()) {
312 strm << "Data error unpacking field ";
313 output(strm, true);
314 size_t length = packer.get_unpack_length() - start_byte;
315 strm << "\nGot data (" << (int)length << " bytes):\n";
316 Datagram dg(packer.get_unpack_data() + start_byte, length);
317 dg.dump_hex(strm);
318 size_t error_byte = packer.get_num_unpacked_bytes() - start_byte;
319 strm << "Error detected on byte " << error_byte
320 << " (" << std::hex << error_byte << std::dec << " hex)";
321
322 exc_type = PyExc_RuntimeError;
323 } else {
324 strm << "Value outside specified range when unpacking field "
325 << get_name() << ": " << get_pystr(object);
326 exc_type = PyExc_ValueError;
327 }
328
329 string message = strm.str();
330 PyErr_SetString(exc_type, message.c_str());
331 }
332
333 Py_XDECREF(object);
334 return nullptr;
335}
336#endif // HAVE_PYTHON
337
338#ifdef HAVE_PYTHON
339/**
340 * Extracts the update message out of the datagram and applies it to the
341 * indicated object by calling the appropriate method.
342 */
343void DCField::
344receive_update(DCPacker &packer, PyObject *distobj) const {
345 if (as_parameter() != nullptr) {
346 // If it's a parameter-type field, just store a new value on the object.
347 PyObject *value = unpack_args(packer);
348 if (value != nullptr) {
349 PyObject_SetAttrString(distobj, (char *)_name.c_str(), value);
350 }
351 Py_DECREF(value);
352
353 } else {
354 // Otherwise, it must be an atomic or molecular field, so call the
355 // corresponding method.
356
357 if (!PyObject_HasAttrString(distobj, (char *)_name.c_str())) {
358 // If there's no Python method to receive this message, don't bother
359 // unpacking it to a Python tuple--just skip past the message.
360 packer.unpack_skip();
361
362 } else {
363 // Otherwise, get a Python tuple from the args and call the Python
364 // method.
365 PyObject *args = unpack_args(packer);
366
367 if (args != nullptr) {
368 PyObject *func = PyObject_GetAttrString(distobj, (char *)_name.c_str());
369 nassertv(func != nullptr);
370
371 PyObject *result;
372 {
373#ifdef WITHIN_PANDA
374 PStatTimer timer(((DCField *)this)->_field_update_pcollector);
375#endif
376 result = PyObject_CallObject(func, args);
377 }
378 Py_XDECREF(result);
379 Py_DECREF(func);
380 Py_DECREF(args);
381 }
382 }
383 }
384}
385#endif // HAVE_PYTHON
386
387#ifdef HAVE_PYTHON
388/**
389 * Generates a datagram containing the message necessary to send an update for
390 * the indicated distributed object from the client.
391 */
392Datagram DCField::
393client_format_update(DOID_TYPE do_id, PyObject *args) const {
394 DCPacker packer;
395
396 packer.raw_pack_uint16(CLIENT_OBJECT_SET_FIELD);
397 packer.raw_pack_uint32(do_id);
398 packer.raw_pack_uint16(_number);
399
400 packer.begin_pack(this);
401 pack_args(packer, args);
402 if (!packer.end_pack()) {
403 return Datagram();
404 }
405
406 return Datagram(packer.get_data(), packer.get_length());
407}
408#endif // HAVE_PYTHON
409
410#ifdef HAVE_PYTHON
411/**
412 * Generates a datagram containing the message necessary to send an update for
413 * the indicated distributed object from the AI.
414 */
415Datagram DCField::
416ai_format_update(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, PyObject *args) const {
417 DCPacker packer;
418
419 packer.raw_pack_uint8(1);
420 packer.RAW_PACK_CHANNEL(to_id);
421 packer.RAW_PACK_CHANNEL(from_id);
422 packer.raw_pack_uint16(STATESERVER_OBJECT_SET_FIELD);
423 packer.raw_pack_uint32(do_id);
424 packer.raw_pack_uint16(_number);
425
426 packer.begin_pack(this);
427 pack_args(packer, args);
428 if (!packer.end_pack()) {
429 return Datagram();
430 }
431
432 return Datagram(packer.get_data(), packer.get_length());
433}
434#endif // HAVE_PYTHON
435
436#ifdef HAVE_PYTHON
437/**
438 * Generates a datagram containing the message necessary to send an update,
439 * with the msg type, for the indicated distributed object from the AI.
440 */
441Datagram DCField::
442ai_format_update_msg_type(DOID_TYPE do_id, CHANNEL_TYPE to_id, CHANNEL_TYPE from_id, int msg_type, PyObject *args) const {
443 DCPacker packer;
444
445 packer.raw_pack_uint8(1);
446 packer.RAW_PACK_CHANNEL(to_id);
447 packer.RAW_PACK_CHANNEL(from_id);
448 packer.raw_pack_uint16(msg_type);
449 packer.raw_pack_uint32(do_id);
450 packer.raw_pack_uint16(_number);
451
452 packer.begin_pack(this);
453 pack_args(packer, args);
454 if (!packer.end_pack()) {
455 return Datagram();
456 }
457
458 return Datagram(packer.get_data(), packer.get_length());
459}
460#endif // HAVE_PYTHON
461
462
463/**
464 * Accumulates the properties of this field into the hash.
465 */
467generate_hash(HashGenerator &hashgen) const {
468 // It shouldn't be necessary to explicitly add _number to the hash--this is
469 // computed based on the relative position of this field with the other
470 // fields, so adding it explicitly will be redundant. However, the field
471 // name is significant.
472 hashgen.add_string(_name);
473
474 // Actually, we add _number anyway, since we need to ensure the hash code
475 // comes out different in the dc_multiple_inheritance case.
476 if (dc_multiple_inheritance) {
477 hashgen.add_int(_number);
478 }
479}
480
481/**
482 * Packs the field's specified default value (or a sensible default if no
483 * value is specified) into the stream. Returns true if the default value is
484 * packed, false if the field doesn't know how to pack its default value.
485 */
487pack_default_value(DCPackData &pack_data, bool &) const {
488 // The default behavior is to pack the default value if we got it;
489 // otherwise, to return false and let the packer visit our nested elements.
490 if (!_default_value_stale) {
491 pack_data.append_data((const char *)_default_value.data(), _default_value.size());
492 return true;
493 }
494
495 return false;
496}
497
498/**
499 * Sets the name of this field.
500 */
502set_name(const string &name) {
504 if (_dclass != nullptr) {
505 _dclass->_dc_file->mark_inherited_fields_stale();
506 }
507}
508
509#ifdef HAVE_PYTHON
510/**
511 * Returns the string representation of the indicated Python object.
512 */
513string DCField::
514get_pystr(PyObject *value) {
515 if (value == nullptr) {
516 return "(null)";
517 }
518
519 PyObject *str = PyObject_Str(value);
520 if (str != nullptr) {
521#if PY_MAJOR_VERSION >= 3
522 string result = PyUnicode_AsUTF8(str);
523#else
524 string result = PyString_AsString(str);
525#endif
526 Py_DECREF(str);
527 return result;
528 }
529
530 PyObject *repr = PyObject_Repr(value);
531 if (repr != nullptr) {
532#if PY_MAJOR_VERSION >= 3
533 string result = PyUnicode_AsUTF8(repr);
534#else
535 string result = PyString_AsString(repr);
536#endif
537 Py_DECREF(repr);
538 return result;
539 }
540
541 if (value->ob_type != nullptr) {
542 PyObject *typestr = PyObject_Str((PyObject *)(value->ob_type));
543 if (typestr != nullptr) {
544#if PY_MAJOR_VERSION >= 3
545 string result = PyUnicode_AsUTF8(typestr);
546#else
547 string result = PyString_AsString(typestr);
548#endif
549 Py_DECREF(typestr);
550 return result;
551 }
552 }
553
554 return "(invalid object)";
555}
556#endif // HAVE_PYTHON
557
558/**
559 * Recomputes the default value of the field by repacking it.
560 */
561void DCField::
562refresh_default_value() {
563 DCPacker packer;
564 packer.begin_pack(this);
565 packer.pack_default_value();
566 if (!packer.end_pack()) {
567 std::cerr << "Error while packing default value for " << get_name() << "\n";
568 } else {
569 const unsigned char *data = (const unsigned char *)packer.get_data();
570 _default_value = vector_uchar(data, data + packer.get_length());
571 }
572 _default_value_stale = false;
573}
A single atomic field of a Distributed Class, as read from a .dc file.
Defines a particular DistributedClass as read from an input .dc file.
Definition dcClass.h:44
A single field of a Distributed Class, either atomic or molecular.
Definition dcField.h:37
std::string format_data(const std::vector< unsigned char > &packed_data, bool show_field_names=true)
Given a blob that represents the packed data for this field, returns a string formatting it for human...
Definition dcField.cxx:165
virtual void set_name(const std::string &name)
Sets the name of this field.
Definition dcField.cxx:502
std::vector< unsigned char > parse_string(const std::string &formatted_string)
Given a human-formatted string (for instance, as returned by format_data(), above) that represents th...
Definition dcField.cxx:182
virtual void generate_hash(HashGenerator &hashgen) const
Accumulates the properties of this field into the hash.
Definition dcField.cxx:467
virtual DCAtomicField * as_atomic_field()
Returns the same field pointer converted to an atomic field pointer, if this is in fact an atomic fie...
Definition dcField.cxx:112
void output(std::ostream &out) const
Write a string representation of this instance to <out>.
Definition dcField.I:141
virtual DCMolecularField * as_molecular_field()
Returns the same field pointer converted to a molecular field pointer, if this is in fact a molecular...
Definition dcField.cxx:130
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...
Definition dcField.cxx:487
bool validate_ranges(const std::vector< unsigned char > &packed_data) const
Verifies that all of the packed values in the field data are within the specified ranges and that the...
Definition dcField.cxx:203
void mark_inherited_fields_stale()
Indicates that something has changed in one or more of the inheritance chains or the set of fields; t...
Definition dcFile.I:40
A single molecular field of a Distributed Class, as read from a .dc file.
This is a block of data that receives the results of DCPacker.
Definition dcPackData.h:22
void append_data(const char *buffer, size_t size)
Adds the indicated bytes to the end of the data.
Definition dcPackData.I:47
This defines the internal interface for packing values into a DCField.
const std::string & get_name() const
Returns the name of this field, or empty string if the field is unnamed.
virtual void set_name(const std::string &name)
Sets the name of this field.
This class can be used for packing a series of numeric and string data into a binary stream,...
Definition dcPacker.h:34
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
std::vector< unsigned char > get_bytes() const
Returns the packed data buffer as a bytes object.
Definition dcPacker.I:599
void unpack_validate()
Internally unpacks the current numeric or string value and validates it against the type range limits...
Definition dcPacker.cxx:578
void begin_pack(const DCPackerInterface *root)
Begins a packing session.
Definition dcPacker.cxx:73
void begin_unpack(const DCPackerInterface *root)
Begins an unpacking session.
Definition dcPacker.cxx:153
bool end_unpack()
Finishes the unpacking session.
Definition dcPacker.cxx:179
void raw_pack_uint32(unsigned int value)
Packs the data into the buffer between packing sessions.
Definition dcPacker.I:758
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
void raw_pack_uint16(unsigned int value)
Packs the data into the buffer between packing sessions.
Definition dcPacker.I:749
void raw_pack_uint8(unsigned int value)
Packs the data into the buffer between packing sessions.
Definition dcPacker.I:740
size_t get_unpack_length() const
Returns the total number of bytes in the unpack data buffer.
Definition dcPacker.I:610
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
const char * get_data() const
Returns the beginning of the data buffer.
Definition dcPacker.I:641
void unpack_skip()
Skips the current field without unpacking it and advances to the next field.
Definition dcPacker.cxx:604
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
const char * get_unpack_data() const
Returns a read pointer to the unpack data buffer.
Definition dcPacker.I:686
size_t get_num_unpacked_bytes() const
Returns the number of bytes that have been unpacked so far, or after unpack_end(),...
Definition dcPacker.I:574
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_length() const
Returns the current length of the buffer.
Definition dcPacker.I:583
Represents the type specification for a single parameter within a field specification.
Definition dcParameter.h:35
An ordered list of data elements, formatted in memory for transmission over a socket or writing to a ...
Definition datagram.h:38
This class generates an arbitrary hash number from a sequence of ints.
void add_int(int num)
Adds another integer to the hash so far.
void add_string(const std::string &str)
Adds a string to the hash, by breaking it down into a sequence of integers.
static Notify * ptr()
Returns the pointer to the global Notify object.
Definition notify.cxx:293
A lightweight class that can be used to automatically start and stop a PStatCollector around a sectio...
Definition pStatTimer.h:30
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.