diff --git a/direct/src/dcparser/Sources.pp b/direct/src/dcparser/Sources.pp index 96573de445..c20b00a595 100644 --- a/direct/src/dcparser/Sources.pp +++ b/direct/src/dcparser/Sources.pp @@ -26,6 +26,7 @@ dcPackerInterface.h dcPackerInterface.I \ dcParameter.h dcClassParameter.h dcArrayParameter.h dcSimpleParameter.h \ dcNumericRange.h dcNumericRange.I \ + dcSwitch.h \ dcTypedef.h \ dcPython.h \ dcbase.h dcindent.h hashGenerator.h \ @@ -42,6 +43,7 @@ dcPackerInterface.cxx \ dcParameter.cxx dcClassParameter.cxx \ dcArrayParameter.cxx dcSimpleParameter.cxx \ + dcSwitch.cxx \ dcTypedef.cxx \ dcindent.cxx \ hashGenerator.cxx primeNumberGenerator.cxx diff --git a/direct/src/dcparser/dcArrayParameter.cxx b/direct/src/dcparser/dcArrayParameter.cxx index 1d7780c212..467ac5cbab 100644 --- a/direct/src/dcparser/dcArrayParameter.cxx +++ b/direct/src/dcparser/dcArrayParameter.cxx @@ -40,6 +40,7 @@ DCArrayParameter(DCParameter *element_type, const DCUnsignedIntRange &size) : if (_array_size >= 0 && _element_type->has_fixed_byte_size()) { _has_fixed_byte_size = true; _fixed_byte_size = _array_size * _element_type->get_fixed_byte_size(); + _has_fixed_structure = true; } else { // We only need to store the length bytes if the array has a diff --git a/direct/src/dcparser/dcAtomicField.cxx b/direct/src/dcparser/dcAtomicField.cxx index c920bb69f9..512eb34c84 100644 --- a/direct/src/dcparser/dcAtomicField.cxx +++ b/direct/src/dcparser/dcAtomicField.cxx @@ -317,7 +317,7 @@ write(ostream &out, bool brief, int indent_level) const { } out << ";"; - if (!brief) { + if (!brief && _number >= 0) { out << " // field " << _number; } out << "\n"; @@ -372,6 +372,9 @@ add_element(const DCAtomicField::ElementType &element) { _has_fixed_byte_size = element._param->has_fixed_byte_size(); _fixed_byte_size += element._param->get_fixed_byte_size(); } + if (_has_fixed_structure) { + _has_fixed_structure = element._param->has_fixed_structure(); + } } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/dcparser/dcClass.cxx b/direct/src/dcparser/dcClass.cxx index 00140884b0..92f53415b4 100644 --- a/direct/src/dcparser/dcClass.cxx +++ b/direct/src/dcparser/dcClass.cxx @@ -22,6 +22,41 @@ #include "dcindent.h" #include "dcmsgtypes.h" +//////////////////////////////////////////////////////////////////// +// Function: DCClass::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DCClass:: +DCClass(const string &name, bool is_struct, bool bogus_class) : + _name(name), + _is_struct(is_struct), + _bogus_class(bogus_class) +{ + _number = -1; + +#ifdef HAVE_PYTHON + _class_def = NULL; +#endif +} + +//////////////////////////////////////////////////////////////////// +// Function: DCClass::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DCClass:: +~DCClass() { + Fields::iterator fi; + for (fi = _fields.begin(); fi != _fields.end(); ++fi) { + delete (*fi); + } + +#ifdef HAVE_PYTHON + Py_XDECREF(_class_def); +#endif +} + //////////////////////////////////////////////////////////////////// // Function: DCClass::get_name // Access: Published @@ -540,39 +575,6 @@ ai_format_generate(PyObject *distobj, int do_id, } #endif // HAVE_PYTHON -//////////////////////////////////////////////////////////////////// -// Function: DCClass::Constructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -DCClass:: -DCClass(const string &name, bool is_struct, bool bogus_class) : - _name(name), - _is_struct(is_struct), - _bogus_class(bogus_class) -{ -#ifdef HAVE_PYTHON - _class_def = NULL; -#endif -} - -//////////////////////////////////////////////////////////////////// -// Function: DCClass::Destructor -// Access: Public -// Description: -//////////////////////////////////////////////////////////////////// -DCClass:: -~DCClass() { - Fields::iterator fi; - for (fi = _fields.begin(); fi != _fields.end(); ++fi) { - delete (*fi); - } - -#ifdef HAVE_PYTHON - Py_XDECREF(_class_def); -#endif -} - //////////////////////////////////////////////////////////////////// // Function: DCClass::write // Access: Public, Virtual @@ -599,7 +601,7 @@ write(ostream &out, bool brief, int indent_level) const { } out << " {"; - if (!brief) { + if (!brief && _number >= 0) { out << " // index " << _number; } out << "\n"; diff --git a/direct/src/dcparser/dcClass.h b/direct/src/dcparser/dcClass.h index a7eb6f373d..32022999d9 100644 --- a/direct/src/dcparser/dcClass.h +++ b/direct/src/dcparser/dcClass.h @@ -33,6 +33,10 @@ class DCParameter; // input .dc file. //////////////////////////////////////////////////////////////////// class EXPCL_DIRECT DCClass : public DCDeclaration { +public: + DCClass(const string &name, bool is_struct, bool bogus_class); + ~DCClass(); + PUBLISHED: const string &get_name() const; int get_number() const; @@ -76,9 +80,6 @@ PUBLISHED: #endif public: - DCClass(const string &name, bool is_struct, bool bogus_class); - ~DCClass(); - virtual void write(ostream &out, bool brief, int indent_level) const; void generate_hash(HashGenerator &hashgen) const; diff --git a/direct/src/dcparser/dcClassParameter.cxx b/direct/src/dcparser/dcClassParameter.cxx index e1a1cdcf50..a8c3585344 100644 --- a/direct/src/dcparser/dcClassParameter.cxx +++ b/direct/src/dcparser/dcClassParameter.cxx @@ -40,10 +40,12 @@ DCClassParameter(DCClass *dclass) : // fields). _has_fixed_byte_size = true; _fixed_byte_size = 0; + _has_fixed_structure = true; for (int i = 0; i < _num_nested_fields && _has_fixed_byte_size; i++) { DCPackerInterface *field = get_nested_field(i); - _has_fixed_byte_size = field->has_fixed_byte_size(); + _has_fixed_byte_size = _has_fixed_byte_size && field->has_fixed_byte_size(); _fixed_byte_size += field->get_fixed_byte_size(); + _has_fixed_structure = _has_fixed_structure && field->has_fixed_structure(); } } diff --git a/direct/src/dcparser/dcField.cxx b/direct/src/dcparser/dcField.cxx index 15a51d7595..3ec9f17acb 100644 --- a/direct/src/dcparser/dcField.cxx +++ b/direct/src/dcparser/dcField.cxx @@ -28,13 +28,15 @@ //////////////////////////////////////////////////////////////////// DCField:: DCField(const string &name) : DCPackerInterface(name) { - _number = 0; + _number = -1; + _has_nested_fields = true; _num_nested_fields = 0; _pack_type = PT_field; _has_fixed_byte_size = true; _fixed_byte_size = 0; + _has_fixed_structure = true; } //////////////////////////////////////////////////////////////////// @@ -92,6 +94,16 @@ as_parameter() { return (DCParameter *)NULL; } +//////////////////////////////////////////////////////////////////// +// Function: DCField::as_switch +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +DCSwitch *DCField:: +as_switch() { + return (DCSwitch *)NULL; +} + //////////////////////////////////////////////////////////////////// // Function: DCField::format_data // Access: Published diff --git a/direct/src/dcparser/dcField.h b/direct/src/dcparser/dcField.h index 8fb56fb4d7..480f29995c 100644 --- a/direct/src/dcparser/dcField.h +++ b/direct/src/dcparser/dcField.h @@ -26,6 +26,7 @@ class DCAtomicField; class DCMolecularField; class DCParameter; +class DCSwitch; class HashGenerator; //////////////////////////////////////////////////////////////////// @@ -44,6 +45,7 @@ PUBLISHED: virtual DCAtomicField *as_atomic_field(); virtual DCMolecularField *as_molecular_field(); virtual DCParameter *as_parameter(); + virtual DCSwitch *as_switch(); string format_data(const string &packed_data); string parse_string(const string &formatted_string); diff --git a/direct/src/dcparser/dcLexer.lxx b/direct/src/dcparser/dcLexer.lxx index 8eba29594d..51dd9843ce 100644 --- a/direct/src/dcparser/dcLexer.lxx +++ b/direct/src/dcparser/dcLexer.lxx @@ -370,6 +370,16 @@ REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?) return KW_TYPEDEF; } +"switch" { + accept(); + return KW_SWITCH; +} + +"case" { + accept(); + return KW_CASE; +} + "int8" { accept(); return KW_INT8; diff --git a/direct/src/dcparser/dcMolecularField.cxx b/direct/src/dcparser/dcMolecularField.cxx index 06e0e30787..1a0b6740a3 100644 --- a/direct/src/dcparser/dcMolecularField.cxx +++ b/direct/src/dcparser/dcMolecularField.cxx @@ -96,6 +96,9 @@ add_atomic(DCAtomicField *atomic) { _has_fixed_byte_size = atomic->has_fixed_byte_size(); _fixed_byte_size += atomic->get_fixed_byte_size(); } + if (_has_fixed_structure) { + _has_fixed_structure = atomic->has_fixed_structure(); + } } //////////////////////////////////////////////////////////////////// diff --git a/direct/src/dcparser/dcPacker.I b/direct/src/dcparser/dcPacker.I index 908292a2b3..8211e272ef 100755 --- a/direct/src/dcparser/dcPacker.I +++ b/direct/src/dcparser/dcPacker.I @@ -45,6 +45,12 @@ has_nested_fields() const { // The return value may be -1 to indicate that a // variable number of nested fields are accepted by this // field type (e.g. a variable-length array). +// +// Note that this method is unreliable to determine how +// many fields you must traverse before you can call +// pop(), since particularly in the presence of a +// DCSwitch, it may change during traversal. Use +// more_nested_fields() instead. //////////////////////////////////////////////////////////////////// INLINE int DCPacker:: get_num_nested_fields() const { @@ -63,6 +69,19 @@ more_nested_fields() const { return (_current_field != (DCPackerInterface *)NULL); } +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::get_current_parent +// Access: Published +// Description: Returns the field that we left in our last call to +// push(): the owner of the current level of fields. +// This may be NULL at the beginning of the pack +// operation. +//////////////////////////////////////////////////////////////////// +INLINE const DCPackerInterface *DCPacker:: +get_current_parent() const { + return _current_parent; +} + //////////////////////////////////////////////////////////////////// // Function: DCPacker::get_current_field // Access: Published @@ -76,6 +95,24 @@ get_current_field() const { return _current_field; } +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::get_last_switch +// Access: Published +// Description: Returns a pointer to the last DCSwitch instance that +// we have passed by and selected one case of during the +// pack/unpack process. Each time we encounter a new +// DCSwitch and select a case, this will change state. +// +// This may be used to detect when a DCSwitch has been +// selected. At the moment this changes state, +// get_current_parent() will contain the particular +// SwitchCase that was selected by the switch. +//////////////////////////////////////////////////////////////////// +INLINE const DCSwitch *DCPacker:: +get_last_switch() const { + return _last_switch; +} + //////////////////////////////////////////////////////////////////// // Function: DCPacker::get_pack_type // Access: Published @@ -470,8 +507,16 @@ advance() { // call pop(). _current_field = NULL; - } else if (_mode == M_unpack && _push_marker != 0 && - _unpack_p >= _push_marker) { + // But if the parent is a switch record, we make a special case so + // we can get the alternate fields. + if (_current_parent != (DCPackerInterface *)NULL) { + const DCSwitch *dswitch = ((DCPackerInterface *)_current_parent)->as_switch(); + if (dswitch != (DCSwitch *)NULL) { + handle_switch(dswitch); + } + } + + } else if (_pop_marker != 0 && _unpack_p >= _pop_marker) { // Done with all the fields on this parent. The caller must now // call pop(). _current_field = NULL; diff --git a/direct/src/dcparser/dcPacker.cxx b/direct/src/dcparser/dcPacker.cxx index 93134442d9..24aa9dc9af 100755 --- a/direct/src/dcparser/dcPacker.cxx +++ b/direct/src/dcparser/dcPacker.cxx @@ -17,6 +17,7 @@ //////////////////////////////////////////////////////////////////// #include "dcPacker.h" +#include "dcSwitch.h" #include "dcParserDefs.h" #include "dcLexerDefs.h" @@ -32,15 +33,11 @@ DCPacker() { _unpack_length = 0; _owns_unpack_data = false; _unpack_p = 0; - _root = NULL; - _catalog = NULL; _live_catalog = NULL; - _current_field = NULL; - _current_parent = NULL; - _current_field_index = 0; - _num_nested_fields = 0; _pack_error = false; _range_error = false; + + clear(); } //////////////////////////////////////////////////////////////////// @@ -272,14 +269,14 @@ seek(const string &field_name) { } nassertr(_catalog != (DCPackerCatalog *)NULL, false); - int entry_index = _catalog->find_entry_by_name(field_name); + int entry_index = _live_catalog->find_entry_by_name(field_name); if (entry_index < 0) { // The field was not known. _pack_error = true; return false; } - const DCPackerCatalog::Entry &entry = _catalog->get_entry(entry_index); + const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(entry_index); // If we are seeking, we don't need to remember our current stack // position. @@ -290,6 +287,13 @@ seek(const string &field_name) { _num_nested_fields = _current_parent->get_num_nested_fields(); _unpack_p = _live_catalog->get_begin(entry_index); + // We don't really need _push_marker and _pop_marker now, except + // that we should set _push_marker in case we have just seeked to + // a switch parameter, and we should set _pop_marker to 0 just so + // it won't get in the way. + _push_marker = _unpack_p; + _pop_marker = 0; + return true; } else if (_mode == M_repack) { @@ -302,14 +306,24 @@ seek(const string &field_name) { return false; } - int entry_index = _catalog->find_entry_by_name(field_name); + int entry_index = _live_catalog->find_entry_by_name(field_name); if (entry_index < 0) { // The field was not known. _pack_error = true; return false; } - const DCPackerCatalog::Entry &entry = _catalog->get_entry(entry_index); + const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(entry_index); + + if (((DCPackerInterface *)entry._parent)->as_switch() != (DCSwitch *)NULL) { + // If the parent is a DCSwitch, that can only mean that the + // seeked field is a switch parameter. We can't support seeking + // to a switch parameter and modifying it directly--what would + // happen to all of the related fields? Instead, you'll have to + // seek to the switch itself and repack the whole entity. + _pack_error = true; + return false; + } size_t begin = _live_catalog->get_begin(entry_index); if (begin < _unpack_p) { @@ -337,9 +351,14 @@ seek(const string &field_name) { _current_field = entry._field; _current_parent = entry._parent; _current_field_index = entry._field_index; - _num_nested_fields = 0; // this makes advance() stop after this field. + _num_nested_fields = 1; _unpack_p = _live_catalog->get_end(entry_index); + // Set up push_marker and pop_marker so we won't try to advance + // beyond this field. + _push_marker = begin; + _pop_marker = _live_catalog->get_end(entry_index); + return true; } @@ -370,6 +389,7 @@ push() { element._current_parent = _current_parent; element._current_field_index = _current_field_index; element._push_marker = _push_marker; + element._pop_marker = _pop_marker; _stack.push_back(element); _current_parent = _current_field; @@ -383,12 +403,14 @@ push() { // Reserve length_bytes for when we figure out what the length // is. _push_marker = _pack_data.get_length(); + _pop_marker = 0; _pack_data.append_junk(length_bytes); } else if (_mode == M_unpack) { // Read length_bytes to determine the end of this nested // sequence. - _push_marker = 0; + _push_marker = _unpack_p; + _pop_marker = 0; if (length_bytes != 0) { if (_unpack_p + length_bytes > _unpack_length) { @@ -400,13 +422,13 @@ push() { length = DCPackerInterface::do_unpack_uint32 (_unpack_data + _unpack_p); _unpack_p += 4; - _push_marker = _unpack_p + length; + _pop_marker = _unpack_p + length; } else { length = DCPackerInterface::do_unpack_uint16 (_unpack_data + _unpack_p); _unpack_p += 2; } - _push_marker = _unpack_p + length; + _pop_marker = _unpack_p + length; // The explicit length trumps the number of nested fields // reported by get_num_nested_fields(). @@ -452,8 +474,8 @@ pop() { // Oops, didn't pack or unpack enough values. _pack_error = true; - } else if (_mode == M_unpack && _push_marker != 0 && - _unpack_p != _push_marker) { + } else if (_mode == M_unpack && _pop_marker != 0 && + _unpack_p != _pop_marker) { // Didn't unpack the right number of values. _pack_error = true; } @@ -488,6 +510,7 @@ pop() { _current_parent = _stack.back()._current_parent; _current_field_index = _stack.back()._current_field_index; _push_marker = _stack.back()._push_marker; + _pop_marker = _stack.back()._pop_marker; _num_nested_fields = (_current_parent == NULL) ? 0 : _current_parent->get_num_nested_fields(); _stack.pop_back(); } @@ -791,6 +814,7 @@ unpack_and_format(ostream &out) { break; case PT_field: + case PT_switch: out << '('; break; @@ -816,6 +840,7 @@ unpack_and_format(ostream &out) { break; case PT_field: + case PT_switch: out << ')'; break; @@ -829,6 +854,54 @@ unpack_and_format(ostream &out) { } } +//////////////////////////////////////////////////////////////////// +// Function: DCPacker::handle_switch +// Access: Private +// Description: When we advance past the key field on a switch +// record, we suddenly have more fields available--all +// the appropriate alternate fields in the switch. +// +// This function is called when we detect this +// condition; it switches the _current_parent to the +// appropriate case of the switch record. +//////////////////////////////////////////////////////////////////// +void DCPacker:: +handle_switch(const DCSwitch *dswitch) { + // First, get the value from the key. This is either found in the + // unpack or the pack data, depending on what mode we're in. + const DCPackerInterface *new_parent = NULL; + + if (_mode == M_pack || _mode == M_repack) { + const char *data = _pack_data.get_data(); + new_parent = dswitch->apply_switch(data + _push_marker, + _pack_data.get_length() - _push_marker); + + } else if (_mode == M_unpack) { + new_parent = dswitch->apply_switch(_unpack_data + _push_marker, + _unpack_p - _push_marker); + + } + + if (new_parent == (DCPackerInterface *)NULL) { + // This means an invalid value was packed for the key. + _pack_error = true; + return; + } + + _last_switch = dswitch; + + // Now substitute in the switch case for the previous parent (which + // replaces the switch node itself). This will suddenly make a slew + // of new fields appear. + _current_parent = new_parent; + _num_nested_fields = _current_parent->get_num_nested_fields(); + + if (_num_nested_fields < 0 || + _current_field_index < _num_nested_fields) { + _current_field = _current_parent->get_nested_field(_current_field_index); + } +} + //////////////////////////////////////////////////////////////////// // Function: DCPacker::clear // Access: Private @@ -842,6 +915,9 @@ clear() { _current_parent = NULL; _current_field_index = 0; _num_nested_fields = 0; + _push_marker = 0; + _pop_marker = 0; + _last_switch = NULL; if (_live_catalog != (DCPackerCatalog::LiveCatalog *)NULL) { _catalog->release_live_catalog(_live_catalog); diff --git a/direct/src/dcparser/dcPacker.h b/direct/src/dcparser/dcPacker.h index 0f85900eb4..aa10232878 100755 --- a/direct/src/dcparser/dcPacker.h +++ b/direct/src/dcparser/dcPacker.h @@ -26,6 +26,8 @@ #include "dcPackerCatalog.h" #include "dcPython.h" +class DCSwitch; + //////////////////////////////////////////////////////////////////// // Class : DCPacker // Description : This class can be used for packing a series of @@ -64,7 +66,9 @@ PUBLISHED: INLINE int get_num_nested_fields() const; INLINE bool more_nested_fields() const; + INLINE const DCPackerInterface *get_current_parent() const; INLINE const DCPackerInterface *get_current_field() const; + INLINE const DCSwitch *get_last_switch() const; INLINE DCPackType get_pack_type() const; void push(); @@ -109,6 +113,7 @@ public: private: INLINE void advance(); + void handle_switch(const DCSwitch *dswitch); void clear(); void set_unpack_data(const char *unpack_data, size_t unpack_length, bool owns_unpack_data); @@ -138,6 +143,7 @@ private: const DCPackerInterface *_current_parent; int _current_field_index; size_t _push_marker; + size_t _pop_marker; }; typedef pvector Stack; @@ -146,12 +152,17 @@ private: const DCPackerInterface *_current_parent; int _current_field_index; - // In pack mode, _push_marker marks the beginning of the push record - // (so we can go back and write in the length later). In unpack - // mode, it marks the end of the push record (so we know when we've - // reached the end). + // _push_marker marks the beginning of the push record (so we can go + // back and write in the length later, or figure out the switch + // parameter). size_t _push_marker; + // _pop_marker is used in unpack mode with certain data structures + // (like dynamic arrays) to mark the end of the push record (so we + // know when we've reached the end). It is zero when it is not in + // use. + size_t _pop_marker; int _num_nested_fields; + const DCSwitch *_last_switch; bool _pack_error; bool _range_error; diff --git a/direct/src/dcparser/dcPackerCatalog.I b/direct/src/dcparser/dcPackerCatalog.I index 5f8dedbef6..ec03a42661 100644 --- a/direct/src/dcparser/dcPackerCatalog.I +++ b/direct/src/dcparser/dcPackerCatalog.I @@ -42,6 +42,52 @@ get_end(int n) const { return _live_entries[n]._end; } +//////////////////////////////////////////////////////////////////// +// Function: DCPackerCatalog::LiveCatalog::get_num_entries +// Access: Public +// Description: Returns the number of entries in the catalog. +//////////////////////////////////////////////////////////////////// +INLINE int DCPackerCatalog::LiveCatalog:: +get_num_entries() const { + return _catalog->get_num_entries(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPackerCatalog::LiveCatalog::get_entry +// Access: Public +// Description: Returns the nth entry in the catalog. +//////////////////////////////////////////////////////////////////// +INLINE const DCPackerCatalog::Entry &DCPackerCatalog::LiveCatalog:: +get_entry(int n) const { + return _catalog->get_entry(n); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPackerCatalog::LiveCatalog::find_entry_by_name +// Access: Public +// Description: Returns the index number of the entry with the +// indicated name, or -1 if no entry has the indicated +// name. The return value is suitable for passing to +// get_entry(). +//////////////////////////////////////////////////////////////////// +int DCPackerCatalog::LiveCatalog:: +find_entry_by_name(const string &name) const { + return _catalog->find_entry_by_name(name); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPackerCatalog::LiveCatalog::find_entry_by_field +// Access: Public +// Description: Returns the index number of the entry with the +// indicated field, or -1 if no entry has the indicated +// field. The return value is suitable for passing to +// get_entry(). +//////////////////////////////////////////////////////////////////// +int DCPackerCatalog::LiveCatalog:: +find_entry_by_field(const DCPackerInterface *field) const { + return _catalog->find_entry_by_field(field); +} + //////////////////////////////////////////////////////////////////// // Function: DCPackerCatalog::get_num_entries // Access: Public diff --git a/direct/src/dcparser/dcPackerCatalog.cxx b/direct/src/dcparser/dcPackerCatalog.cxx index 829d3838ee..0879ab96c8 100644 --- a/direct/src/dcparser/dcPackerCatalog.cxx +++ b/direct/src/dcparser/dcPackerCatalog.cxx @@ -19,6 +19,7 @@ #include "dcPackerCatalog.h" #include "dcPackerInterface.h" #include "dcPacker.h" +#include "dcSwitch.h" //////////////////////////////////////////////////////////////////// // Function: DCPackerCatalog::Constructor @@ -31,6 +32,22 @@ DCPackerCatalog(const DCPackerInterface *root) : _root(root) { _live_catalog = NULL; } +//////////////////////////////////////////////////////////////////// +// Function: DCPackerCatalog::Copy Constructor +// Access: Private +// Description: The copy constructor is used only internally, in +// update_switch_fields(). +//////////////////////////////////////////////////////////////////// +DCPackerCatalog:: +DCPackerCatalog(const DCPackerCatalog ©) : + _root(copy._root), + _entries(copy._entries), + _entries_by_name(copy._entries_by_name), + _entries_by_field(copy._entries_by_field) +{ + _live_catalog = NULL; +} + //////////////////////////////////////////////////////////////////// // Function: DCPackerCatalog::Destructor // Access: Private @@ -42,6 +59,11 @@ DCPackerCatalog:: if (_live_catalog != (LiveCatalog *)NULL) { delete _live_catalog; } + + SwitchCatalogs::iterator si; + for (si = _switch_catalogs.begin(); si != _switch_catalogs.end(); ++si) { + delete (*si).second; + } } //////////////////////////////////////////////////////////////////// @@ -95,11 +117,13 @@ find_entry_by_field(const DCPackerInterface *field) const { const DCPackerCatalog::LiveCatalog *DCPackerCatalog:: get_live_catalog(const char *data, size_t length) const { if (_live_catalog != (LiveCatalog *)NULL) { - // Return the previously-allocated static catalog. + // Return the previously-allocated live catalog; it will be the + // same as this one since it's based on a fixed-length field. return _live_catalog; } LiveCatalog *live_catalog = new LiveCatalog; + live_catalog->_catalog = this; live_catalog->_live_entries.reserve(_entries.size()); LiveCatalogEntry zero_entry; zero_entry._begin = 0; @@ -110,13 +134,14 @@ get_live_catalog(const char *data, size_t length) const { DCPacker packer; packer.begin_unpack(data, length, _root); - r_fill_live_catalog(live_catalog, packer); + const DCSwitch *last_switch = NULL; + r_fill_live_catalog(live_catalog, packer, last_switch); bool okflag = packer.end_unpack(); nassertr(okflag, live_catalog); - if (_root->has_fixed_byte_size()) { - // If our root field has a fixed byte size, then the live catalog + if (_root->has_fixed_structure()) { + // If our root field has a fixed structure, then the live catalog // will always be the same every time, so we might as well keep // this one around as an optimization. ((DCPackerCatalog *)this)->_live_catalog = live_catalog; @@ -165,6 +190,50 @@ add_entry(const string &name, const DCPackerInterface *field, _entries_by_field.insert(EntriesByField::value_type(field, entry_index)); } +//////////////////////////////////////////////////////////////////// +// Function: DCPackerCatalog::r_fill_catalog +// Access: Private +// Description: Called by DCPackerInterface to recursively fill up a +// newly-allocated reference catalog. Also called by +// update_switch_fields to append fields to a catalog +// after a DCSwitch node is selected. +//////////////////////////////////////////////////////////////////// +void DCPackerCatalog:: +r_fill_catalog(const string &name_prefix, const DCPackerInterface *field, + const DCPackerInterface *parent, int field_index) { + string next_name_prefix = name_prefix; + + if (!field->get_name().empty()) { + // Record this entry in the catalog. + next_name_prefix += field->get_name(); + add_entry(next_name_prefix, field, parent, field_index); + + next_name_prefix += "."; + } + + const DCSwitch *dswitch = ((DCPackerInterface *)field)->as_switch(); + if (dswitch != (DCSwitch *)NULL) { + // If we come upon a DCSwitch while building the catalog, save the + // name_prefix at this point so we'll have it again when we later + // encounter the switch while unpacking a live record (and so we + // can return to this point in the recursion from + // update_switch_fields). + _switch_prefixes[dswitch] = next_name_prefix; + } + + // Add any children. + if (field->has_nested_fields()) { + int num_nested = field->get_num_nested_fields(); + // It's ok if num_nested is -1. + for (int i = 0; i < num_nested; i++) { + DCPackerInterface *nested = field->get_nested_field(i); + if (nested != (DCPackerInterface *)NULL) { + r_fill_catalog(next_name_prefix, nested, field, i); + } + } + } +} + //////////////////////////////////////////////////////////////////// // Function: DCPackerCatalog::r_fill_live_catalog // Access: Private @@ -173,18 +242,20 @@ add_entry(const string &name, const DCPackerInterface *field, // appropriate offsets. //////////////////////////////////////////////////////////////////// void DCPackerCatalog:: -r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer) const { +r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer, + const DCSwitch *&last_switch) const { const DCPackerInterface *current_field = packer.get_current_field(); - int field_index = find_entry_by_field(current_field); + int field_index = live_catalog->find_entry_by_field(current_field); if (field_index >= 0) { + nassertv(field_index < (int)live_catalog->_live_entries.size()); live_catalog->_live_entries[field_index]._begin = packer.get_num_unpacked_bytes(); } if (packer.has_nested_fields() && packer.get_pack_type() != PT_string) { packer.push(); while (packer.more_nested_fields()) { - r_fill_live_catalog(live_catalog, packer); + r_fill_live_catalog(live_catalog, packer, last_switch); } packer.pop(); @@ -195,4 +266,84 @@ r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer) const { if (field_index >= 0) { live_catalog->_live_entries[field_index]._end = packer.get_num_unpacked_bytes(); } + + if (last_switch != packer.get_last_switch()) { + // We've just invoked a new DCSwitch. That means we must add the + // new fields revealed by the switch to the reference catalog. + last_switch = packer.get_last_switch(); + + const DCPackerInterface *switch_case = packer.get_current_parent(); + nassertv(switch_case != (DCPackerInterface *)NULL); + const DCPackerCatalog *switch_catalog = + live_catalog->_catalog->update_switch_fields(last_switch, switch_case); + live_catalog->_catalog = switch_catalog; + + // And we also have to expand the live catalog to hold the new + // entries. + LiveCatalogEntry zero_entry; + zero_entry._begin = 0; + zero_entry._end = 0; + for (size_t i = live_catalog->_live_entries.size(); + i < switch_catalog->_entries.size(); + i++) { + live_catalog->_live_entries.push_back(zero_entry); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DCPackerCatalog::update_switch_fields +// Access: Private +// Description: Returns a new DCPackerCatalog that includes all of +// the fields in this object, with the addition of the +// fields named by switch_case. +// +// This is used to implement switches, which change the +// set of fields they make available according to the +// data in the record, and therefore present a different +// catalog under different circumstances. +// +// This returned pointer is allocated one time for each +// different switch_case instance; if a given same +// switch_case is supplied twice, the same pointer is +// returned both times. The ownership of the returned +// pointer is kept by this object. +//////////////////////////////////////////////////////////////////// +const DCPackerCatalog *DCPackerCatalog:: +update_switch_fields(const DCSwitch *dswitch, + const DCPackerInterface *switch_case) const { + SwitchCatalogs::const_iterator si = _switch_catalogs.find(switch_case); + if (si != _switch_catalogs.end()) { + return (*si).second; + } + + // Look up the name_prefix will we use for all of the fields that + // descend from this switch. This should be stored in this record + // because we must have come across the DCSwitch when building the + // catalog the first time. + SwitchPrefixes::const_iterator pi = _switch_prefixes.find(dswitch); + nassertr(pi != _switch_prefixes.end(), NULL); + string name_prefix = (*pi).second; + + // Start by creating a new DCPackerCatalog object that contains all + // of the fields that this one contains. + DCPackerCatalog *switch_catalog = new DCPackerCatalog(*this); + + // Now record all of the fields of the switch case in the new + // catalog. We start with the second field of the switch case, + // since the first field will be the switch parameter itself, which + // we would have already recorded the first time around. + int num_nested = switch_case->get_num_nested_fields(); + for (int i = 1; i < num_nested; i++) { + DCPackerInterface *nested = switch_case->get_nested_field(i); + if (nested != (DCPackerInterface *)NULL) { + switch_catalog->r_fill_catalog(name_prefix, nested, switch_case, i); + } + } + + // Store the newly-generated switch catalog in the record so the + // same pointer can be returned in the future. + ((DCPackerCatalog *)this)->_switch_catalogs[switch_case] = switch_catalog; + + return switch_catalog; } diff --git a/direct/src/dcparser/dcPackerCatalog.h b/direct/src/dcparser/dcPackerCatalog.h index 7d9be397d5..7de471b940 100644 --- a/direct/src/dcparser/dcPackerCatalog.h +++ b/direct/src/dcparser/dcPackerCatalog.h @@ -23,6 +23,7 @@ class DCPackerInterface; class DCPacker; +class DCSwitch; //////////////////////////////////////////////////////////////////// // Class : DCPackerCatalog @@ -35,6 +36,7 @@ class DCPacker; class EXPCL_DIRECT DCPackerCatalog { private: DCPackerCatalog(const DCPackerInterface *root); + DCPackerCatalog(const DCPackerCatalog ©); ~DCPackerCatalog(); public: @@ -62,9 +64,16 @@ public: INLINE size_t get_begin(int n) const; INLINE size_t get_end(int n) const; + INLINE int get_num_entries() const; + INLINE const Entry &get_entry(int n) const; + INLINE int find_entry_by_name(const string &name) const; + INLINE int find_entry_by_field(const DCPackerInterface *field) const; + private: typedef pvector LiveEntries; LiveEntries _live_entries; + + const DCPackerCatalog *_catalog; friend class DCPackerCatalog; }; @@ -75,11 +84,19 @@ public: const LiveCatalog *get_live_catalog(const char *data, size_t length) const; void release_live_catalog(const LiveCatalog *live_catalog) const; - + private: void add_entry(const string &name, const DCPackerInterface *field, const DCPackerInterface *parent, int field_index); - void r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer) const; + + void r_fill_catalog(const string &name_prefix, const DCPackerInterface *field, + const DCPackerInterface *parent, int field_index); + void r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer, + const DCSwitch *&last_switch) const; + + const DCPackerCatalog *update_switch_fields(const DCSwitch *dswitch, + const DCPackerInterface *switch_case) const; + const DCPackerInterface *_root; LiveCatalog *_live_catalog; @@ -93,6 +110,12 @@ private: typedef pmap EntriesByField; EntriesByField _entries_by_field; + typedef pmap SwitchCatalogs; + SwitchCatalogs _switch_catalogs; + + typedef pmap SwitchPrefixes; + SwitchPrefixes _switch_prefixes; + friend class DCPackerInterface; }; diff --git a/direct/src/dcparser/dcPackerInterface.I b/direct/src/dcparser/dcPackerInterface.I index 7167cfead2..da214f2fcb 100644 --- a/direct/src/dcparser/dcPackerInterface.I +++ b/direct/src/dcparser/dcPackerInterface.I @@ -60,6 +60,24 @@ get_fixed_byte_size() const { return _fixed_byte_size; } +//////////////////////////////////////////////////////////////////// +// Function: DCPackerInterface::has_fixed_structure +// Access: Public +// Description: Returns true if this field type always has the same +// structure regardless of the data in the stream, or +// false if its structure may vary. This is almost, but +// not quite, the same thing as has_fixed_byte_size. +// The difference is that a DCSwitch may have multiple +// cases all with the same byte size, but they will +// still (presumably) have different structures, in the +// sense that the actual list of fields varies according +// to the live data. +//////////////////////////////////////////////////////////////////// +INLINE bool DCPackerInterface:: +has_fixed_structure() const { + return _has_fixed_structure; +} + //////////////////////////////////////////////////////////////////// // Function: DCPackerInterface::get_num_length_bytes // Access: Public diff --git a/direct/src/dcparser/dcPackerInterface.cxx b/direct/src/dcparser/dcPackerInterface.cxx index f76d2f691c..308e217e09 100755 --- a/direct/src/dcparser/dcPackerInterface.cxx +++ b/direct/src/dcparser/dcPackerInterface.cxx @@ -30,6 +30,7 @@ DCPackerInterface(const string &name) : { _has_fixed_byte_size = false; _fixed_byte_size = 0; + _has_fixed_structure = false; _num_length_bytes = 0; _has_nested_fields = false; _num_nested_fields = -1; @@ -47,6 +48,7 @@ DCPackerInterface(const DCPackerInterface ©) : _name(copy._name), _has_fixed_byte_size(copy._has_fixed_byte_size), _fixed_byte_size(copy._fixed_byte_size), + _has_fixed_structure(copy._has_fixed_structure), _num_length_bytes(copy._num_length_bytes), _has_nested_fields(copy._has_nested_fields), _num_nested_fields(copy._num_nested_fields), @@ -67,6 +69,16 @@ DCPackerInterface:: } } +//////////////////////////////////////////////////////////////////// +// Function: DCPackerInterface::as_switch +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +DCSwitch *DCPackerInterface:: +as_switch() { + return (DCSwitch *)NULL; +} + //////////////////////////////////////////////////////////////////// // Function: DCPackerInterface::calc_num_nested_fields // Access: Public, Virtual @@ -303,39 +315,7 @@ make_catalog() { for (int i = 0; i < num_nested; i++) { DCPackerInterface *nested = get_nested_field(i); if (nested != (DCPackerInterface *)NULL) { - nested->r_fill_catalog(_catalog, "", this, i); - } - } - } -} - -//////////////////////////////////////////////////////////////////// -// Function: DCPackerInterface::r_fill_catalog -// Access: Private -// Description: Called internally to recursively fill up the new -// DCPackerCatalog object. -//////////////////////////////////////////////////////////////////// -void DCPackerInterface:: -r_fill_catalog(DCPackerCatalog *catalog, const string &name_prefix, - DCPackerInterface *parent, int field_index) { - string next_name_prefix = name_prefix; - - if (!get_name().empty()) { - // Record this entry in the catalog. - next_name_prefix += get_name(); - catalog->add_entry(next_name_prefix, this, parent, field_index); - - next_name_prefix += "."; - } - - // Add any children. - if (has_nested_fields()) { - int num_nested = get_num_nested_fields(); - // As above, it's ok if num_nested is -1. - for (int i = 0; i < num_nested; i++) { - DCPackerInterface *nested = get_nested_field(i); - if (nested != (DCPackerInterface *)NULL) { - nested->r_fill_catalog(catalog, next_name_prefix, this, i); + _catalog->r_fill_catalog("", nested, this, i); } } } diff --git a/direct/src/dcparser/dcPackerInterface.h b/direct/src/dcparser/dcPackerInterface.h index d611af7ec4..4228ebd157 100755 --- a/direct/src/dcparser/dcPackerInterface.h +++ b/direct/src/dcparser/dcPackerInterface.h @@ -22,6 +22,7 @@ #include "dcbase.h" #include "dcSubatomicType.h" +class DCSwitch; class DCPackData; class DCPackerCatalog; @@ -48,6 +49,7 @@ enum DCPackType { PT_array, PT_field, PT_class, + PT_switch, }; END_PUBLISH @@ -70,10 +72,13 @@ public: PUBLISHED: INLINE const string &get_name() const; + virtual DCSwitch *as_switch(); + public: INLINE void set_name(const string &name); INLINE bool has_fixed_byte_size() const; INLINE size_t get_fixed_byte_size() const; + INLINE bool has_fixed_structure() const; INLINE size_t get_num_length_bytes() const; INLINE bool has_nested_fields() const; @@ -152,6 +157,7 @@ protected: string _name; bool _has_fixed_byte_size; size_t _fixed_byte_size; + bool _has_fixed_structure; size_t _num_length_bytes; bool _has_nested_fields; int _num_nested_fields; @@ -159,9 +165,6 @@ protected: private: void make_catalog(); - void r_fill_catalog(DCPackerCatalog *catalog, const string &name_prefix, - DCPackerInterface *parent, int field_index); - DCPackerCatalog *_catalog; }; diff --git a/direct/src/dcparser/dcParameter.cxx b/direct/src/dcparser/dcParameter.cxx index cf127884ff..500a8d1006 100644 --- a/direct/src/dcparser/dcParameter.cxx +++ b/direct/src/dcparser/dcParameter.cxx @@ -31,6 +31,7 @@ DCParameter:: DCParameter() { _typedef = NULL; _has_fixed_byte_size = false; + _has_fixed_structure = false; _num_nested_fields = -1; } @@ -139,9 +140,12 @@ output(ostream &out, bool brief) const { // Description: //////////////////////////////////////////////////////////////////// void DCParameter:: -write(ostream &out, bool brief, int indent_level) const { +write(ostream &out, bool, int indent_level) const { + // we must always output the name when the parameter occurs by + // itself within a class, so we ignore brief and pass false up to + // output(). indent(out, indent_level); - output(out, brief); + output(out, false); out << ";\n"; } @@ -168,4 +172,6 @@ output_typedef_name(ostream &out, const string &prename, const string &name, //////////////////////////////////////////////////////////////////// void DCParameter:: generate_hash(HashGenerator &) const { + // We specifically don't call up to DCField::generate_hash(), since + // the parameter name is not actually significant to the hash. } diff --git a/direct/src/dcparser/dcParser.yxx b/direct/src/dcparser/dcParser.yxx index 321faa6d8c..b25c32fe1f 100644 --- a/direct/src/dcparser/dcParser.yxx +++ b/direct/src/dcparser/dcParser.yxx @@ -8,6 +8,7 @@ #include "dcParserDefs.h" #include "dcFile.h" #include "dcClass.h" +#include "dcSwitch.h" #include "dcAtomicField.h" #include "dcMolecularField.h" #include "dcClassParameter.h" @@ -27,6 +28,7 @@ static DCFile *dc_file = (DCFile *)NULL; static DCClass *current_class = (DCClass *)NULL; +static DCSwitch *current_switch = (DCSwitch *)NULL; static DCAtomicField *current_atomic = (DCAtomicField *)NULL; static DCMolecularField *current_molecular = (DCMolecularField *)NULL; static DCAtomicField::ElementType atomic_element(new DCSimpleParameter(ST_invalid)); @@ -71,6 +73,8 @@ dc_cleanup_parser() { %token KW_FROM %token KW_IMPORT %token KW_TYPEDEF +%token KW_SWITCH +%token KW_CASE %token KW_INT8 %token KW_INT16 @@ -111,6 +115,11 @@ dc_cleanup_parser() { %type kw_struct_or_kw_dclass %type dclass_name %type atomic_name +%type dclass_field +%type switch_field +%type atomic_field +%type molecular_field +%type switch %type type_token %type type_name %type type_definition @@ -120,6 +129,7 @@ dc_cleanup_parser() { %type parameter_definition %type import_identifier %type slash_identifier +%type optional_name %type number %% @@ -263,13 +273,7 @@ base_list: dclass_fields: empty | dclass_fields ';' - | dclass_fields atomic_field - | dclass_fields molecular_field - | dclass_fields unnamed_parameter ';' -{ - current_class->add_field($2); -} - | dclass_fields named_parameter + | dclass_fields dclass_field { if (!current_class->add_field($2)) { yyerror("Duplicate field name: " + $2->get_name()); @@ -277,32 +281,30 @@ dclass_fields: } ; +dclass_field: + atomic_field + | molecular_field + | switch + | unnamed_parameter ';' +{ + $$ = $1; +} + | named_parameter +{ + $$ = $1; +} + ; + atomic_field: IDENTIFIER '(' { current_atomic = new DCAtomicField($1); - if (!current_class->add_field(current_atomic)) { - yyerror("Duplicate field name: " + current_atomic->get_name()); - } } parameter_list ')' atomic_flags - ; - -atomic_name: - IDENTIFIER { - DCField *field = current_class->get_field_by_name($1); - $$ = (DCAtomicField *)NULL; - if (field == (DCField *)NULL) { - yyerror("Unknown field: " + $1); - } else { - $$ = field->as_atomic_field(); - if ($$ == (DCAtomicField *)NULL) { - yyerror("Not an atomic field: " + $1); - } - } + $$ = current_atomic; } - ; + ; parameter_list: empty @@ -794,13 +796,29 @@ molecular_field: IDENTIFIER ':' { current_molecular = new DCMolecularField($1); - if (!current_class->add_field(current_molecular)) { - yyerror("Duplicate field name: " + current_molecular->get_name()); - } } molecular_atom_list +{ + $$ = current_molecular; +} ; +atomic_name: + IDENTIFIER +{ + DCField *field = current_class->get_field_by_name($1); + $$ = (DCAtomicField *)NULL; + if (field == (DCField *)NULL) { + yyerror("Unknown field: " + $1); + } else { + $$ = field->as_atomic_field(); + if ($$ == (DCAtomicField *)NULL) { + yyerror("Not an atomic field: " + $1); + } + } +} + ; + molecular_atom_list: atomic_name { @@ -821,6 +839,70 @@ molecular_atom_list: } ; +optional_name: + empty +{ + $$ = ""; +} + | IDENTIFIER + ; + +switch: + KW_SWITCH optional_name '(' parameter ')' '{' +{ + $$ = current_switch; + current_switch = new DCSwitch($2, $4); +} + switch_fields '}' +{ + $$ = current_switch; + current_switch = (DCSwitch *)$7; +} + ; + + +switch_fields: + empty + | switch_fields ';' + | switch_fields switch_case + | switch_fields switch_field +{ + if (current_switch->get_num_cases() == 0) { + yyerror("case declaration required before first element"); + } else if (!current_switch->add_field($2)) { + yyerror("Duplicate field name: " + $2->get_name()); + } +} + ; + +switch_case: + KW_CASE +{ + current_packer = &default_packer; + current_packer->begin_pack(current_switch->get_switch_parameter()); +} + parameter_value ':' +{ + if (!current_packer->end_pack()) { + yyerror("Invalid value for switch parameter"); + } else { + current_switch->add_case(current_packer->get_string()); + } +} + ; + +switch_field: + switch + | unnamed_parameter ';' +{ + $$ = $1; +} + | named_parameter +{ + $$ = $1; +} + ; + empty: ; diff --git a/direct/src/dcparser/dcParserDefs.h b/direct/src/dcparser/dcParserDefs.h index cb984c4c4f..eab54d7ab8 100644 --- a/direct/src/dcparser/dcParserDefs.h +++ b/direct/src/dcparser/dcParserDefs.h @@ -24,6 +24,7 @@ class DCFile; class DCClass; +class DCField; class DCAtomicField; class DCParameter; class DCPacker; @@ -48,6 +49,7 @@ public: double real; bool flag; DCClass *dclass; + DCField *field; DCAtomicField *atomic; DCSubatomicType subatomic; DCParameter *parameter; diff --git a/direct/src/dcparser/dcSimpleParameter.cxx b/direct/src/dcparser/dcSimpleParameter.cxx index d59ce7e62c..81ac59a904 100644 --- a/direct/src/dcparser/dcSimpleParameter.cxx +++ b/direct/src/dcparser/dcSimpleParameter.cxx @@ -166,6 +166,7 @@ DCSimpleParameter(DCSubatomicType type, int divisor) : case ST_invalid: break; } + _has_fixed_structure = _has_fixed_byte_size; set_divisor(divisor); @@ -412,9 +413,11 @@ set_range(const DCDoubleRange &range) { _num_length_bytes = 0; _has_fixed_byte_size = true; _fixed_byte_size = _uint_range.get_one_value(); + _has_fixed_structure = true; } else { _num_length_bytes = 2; _has_fixed_byte_size = false; + _has_fixed_structure = false; } break; @@ -431,9 +434,11 @@ set_range(const DCDoubleRange &range) { _num_length_bytes = 0; _has_fixed_byte_size = true; _fixed_byte_size = _uint_range.get_one_value(); + _has_fixed_structure = true; } else { _num_length_bytes = 4; _has_fixed_byte_size = false; + _has_fixed_structure = false; } break; diff --git a/direct/src/dcparser/dcSwitch.cxx b/direct/src/dcparser/dcSwitch.cxx new file mode 100644 index 0000000000..a33e084832 --- /dev/null +++ b/direct/src/dcparser/dcSwitch.cxx @@ -0,0 +1,397 @@ +// Filename: dcSwitch.cxx +// Created by: drose (23Jun04) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "dcSwitch.h" +#include "dcField.h" +#include "dcindent.h" + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::Constructor +// Access: Public +// Description: The switch_parameter must be recently allocated via +// new; it will be deleted via delete when the switch +// destructs. +//////////////////////////////////////////////////////////////////// +DCSwitch:: +DCSwitch(const string &name, DCParameter *switch_parameter) : + DCField(name), + _switch_parameter(switch_parameter) +{ + _has_fixed_byte_size = _switch_parameter->has_fixed_byte_size(); + _fixed_byte_size = _switch_parameter->get_fixed_byte_size(); + _has_fixed_structure = false; + + // The DCSwitch presents just one nested field initially, which is + // the switch parameter. When we pack or unpack that, the DCPacker + // calls apply_switch(), which returns a new record that presents + // the remaining nested fields. + _has_nested_fields = true; + _num_nested_fields = 1; + + _pack_type = PT_switch; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::Destructor +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +DCSwitch:: +~DCSwitch() { + delete _switch_parameter; + + Cases::iterator ci; + for (ci = _cases.begin(); ci != _cases.end(); ++ci) { + const SwitchCase *dcase = (*ci); + delete dcase; + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::as_switch +// Access: Published, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +DCSwitch *DCSwitch:: +as_switch() { + return this; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::get_switch_parameter +// Access: Published +// Description: Returns the key parameter on which the switch is +// based. The value of this parameter in the record +// determines which one of the several cases within the +// switch will be used. +//////////////////////////////////////////////////////////////////// +DCParameter *DCSwitch:: +get_switch_parameter() const { + return _switch_parameter; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::get_num_cases +// Access: Published +// Description: Returns the number of different cases within the +// switch. The legal values for case_index range from 0 +// to get_num_cases() - 1. +//////////////////////////////////////////////////////////////////// +int DCSwitch:: +get_num_cases() const { + return _cases.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::get_case_by_value +// Access: Published +// Description: Returns the index number of the case with the +// indicated packed value, or -1 if no case has this +// value. +//////////////////////////////////////////////////////////////////// +int DCSwitch:: +get_case_by_value(const string &case_value) const { + CasesByValue::const_iterator vi; + vi = _cases_by_value.find(case_value); + if (vi != _cases_by_value.end()) { + return (*vi).second; + } + + return -1; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::get_value +// Access: Published +// Description: Returns the packed value associated with the +// indicated case. +//////////////////////////////////////////////////////////////////// +string DCSwitch:: +get_value(int case_index) const { + nassertr(case_index >= 0 && case_index < (int)_cases.size(), string()); + return _cases[case_index]->_value; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::get_num_fields +// Access: Published +// Description: Returns the number of fields in the indicated case. +//////////////////////////////////////////////////////////////////// +int DCSwitch:: +get_num_fields(int case_index) const { + nassertr(case_index >= 0 && case_index < (int)_cases.size(), 0); + return _cases[case_index]->_fields.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::get_num_fields +// Access: Published +// Description: Returns the nth field in the indicated case. +//////////////////////////////////////////////////////////////////// +DCField *DCSwitch:: +get_field(int case_index, int n) const { + nassertr(case_index >= 0 && case_index < (int)_cases.size(), NULL); + nassertr(n >= 0 && n < (int)_cases[case_index]->_fields.size(), NULL); + return _cases[case_index]->_fields[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::get_field_by_name +// Access: Published +// Description: Returns the field with the given name from the +// indicated case, or NULL if no field has this name. +//////////////////////////////////////////////////////////////////// +DCField *DCSwitch:: +get_field_by_name(int case_index, const string &name) const { + nassertr(case_index >= 0 && case_index < (int)_cases.size(), NULL); + + const FieldsByName &fields_by_name = _cases[case_index]->_fields_by_name; + FieldsByName::const_iterator ni; + ni = fields_by_name.find(name); + if (ni != fields_by_name.end()) { + return (*ni).second; + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::get_nested_field +// Access: Public, Virtual +// Description: Returns the DCPackerInterface object that represents +// the nth nested field. This may return NULL if there +// is no such field (but it shouldn't do this if n is in +// the range 0 <= n < get_num_nested_fields()). +//////////////////////////////////////////////////////////////////// +DCPackerInterface *DCSwitch:: +get_nested_field(int) const { + // The DCSwitch presents just one nested field initially, which is + // the switch parameter. When we pack or unpack that, the DCPacker + // calls apply_switch(), which returns a new record that presents + // the remaining nested fields. + return _switch_parameter; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::add_case +// Access: Public +// Description: Adds a new case to the switch with the indicated +// value, and returns the new case_index. If the value +// has already been used for another case, returns -1. +// This is normally called only by the parser. +//////////////////////////////////////////////////////////////////// +int DCSwitch:: +add_case(const string &value) { + int case_index = (int)_cases.size(); + if (!_cases_by_value.insert(CasesByValue::value_type(value, case_index)).second) { + return -1; + } + + SwitchCase *dcase = new SwitchCase(_name, value); + dcase->add_field(_switch_parameter); + _cases.push_back(dcase); + return case_index; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::add_field +// Access: Public +// Description: Adds a field to the case most recently added via +// add_case(). Returns true if successful, false if the +// field duplicates a field already named within this +// case. It is an error to call this before calling +// add_case(). This is normally called only by the +// parser. +//////////////////////////////////////////////////////////////////// +bool DCSwitch:: +add_field(DCField *field) { + nassertr(!_cases.empty(), false); + + if (!_cases.back()->add_field(field)) { + return false; + } + + // See if we now have a fixed byte size for the overall switch. + // This will be true only if all of the individual cases have the + // same fixed byte size. + _fixed_byte_size = _cases.back()->get_fixed_byte_size(); + + Cases::const_iterator ci; + for (ci = _cases.begin(); ci != _cases.end(); ++ci) { + const SwitchCase *dcase = (*ci); + if (!dcase->has_fixed_byte_size() || + dcase->get_fixed_byte_size() != _fixed_byte_size) { + + // Nope, we have a variable byte size. + _has_fixed_byte_size = false; + return true; + } + } + + // Sonofagun, we do have a fixed byte size. + _has_fixed_byte_size = true; + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::apply_switch +// Access: Public +// Description: Returns the DCPackerInterface that presents the +// alternative fields for the case indicated by the +// given packed value string, or NULL if the value +// string does not match one of the expected cases. +//////////////////////////////////////////////////////////////////// +const DCPackerInterface *DCSwitch:: +apply_switch(const char *value_data, size_t length) const { + CasesByValue::const_iterator vi; + vi = _cases_by_value.find(string(value_data, length)); + if (vi != _cases_by_value.end()) { + return _cases[(*vi).second]; + } + + // Invalid value. + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::write +// Access: Public, Virtual +// Description: Generates a parseable description of the object to +// the indicated output stream. +//////////////////////////////////////////////////////////////////// +void DCSwitch:: +write(ostream &out, bool brief, int indent_level) const { + indent(out, indent_level) + << "switch"; + if (!_name.empty()) { + out << " " << _name; + } + out << " ("; + _switch_parameter->output(out, brief); + out << ") {\n"; + + Cases::const_iterator ci; + for (ci = _cases.begin(); ci != _cases.end(); ++ci) { + const SwitchCase *dcase = (*ci); + indent(out, indent_level) + << "case " << _switch_parameter->format_data(dcase->_value) << ":\n"; + + Fields::const_iterator fi; + if (!dcase->_fields.empty()) { + fi = dcase->_fields.begin(); + ++fi; + while (fi != dcase->_fields.end()) { + (*fi)->write(out, brief, indent_level + 2); + ++fi; + } + } + } + indent(out, indent_level) + << "};\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::generate_hash +// Access: Public, Virtual +// Description: Accumulates the properties of this switch into the +// hash. +//////////////////////////////////////////////////////////////////// +void DCSwitch:: +generate_hash(HashGenerator &hashgen) const { + DCField::generate_hash(hashgen); + + _switch_parameter->generate_hash(hashgen); + + hashgen.add_int(_cases.size()); + Cases::const_iterator ci; + for (ci = _cases.begin(); ci != _cases.end(); ++ci) { + const SwitchCase *dcase = (*ci); + hashgen.add_string(dcase->_value); + + hashgen.add_int(dcase->_fields.size()); + Fields::const_iterator fi; + for (fi = dcase->_fields.begin(); fi != dcase->_fields.end(); ++fi) { + (*fi)->generate_hash(hashgen); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::SwitchCase::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +DCSwitch::SwitchCase:: +SwitchCase(const string &name, const string &value) : + DCPackerInterface(name), + _value(value) +{ + _has_nested_fields = true; + _num_nested_fields = 0; + _pack_type = PT_switch; + + _has_fixed_byte_size = true; + _fixed_byte_size = 0; + _has_fixed_structure = true; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::SwitchCase::get_nested_field +// Access: Public, Virtual +// Description: Returns the DCPackerInterface object that represents +// the nth nested field. This may return NULL if there +// is no such field (but it shouldn't do this if n is in +// the range 0 <= n < get_num_nested_fields()). +//////////////////////////////////////////////////////////////////// +DCPackerInterface *DCSwitch::SwitchCase:: +get_nested_field(int n) const { + nassertr(n >= 0 && n < (int)_fields.size(), NULL); + return _fields[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: DCSwitch::SwitchCase::add_field +// Access: Public +// Description: Adds a field to this case. Returns true if +// successful, false if the field duplicates a field +// already named within this case. This is normally +// called only by the parser. +//////////////////////////////////////////////////////////////////// +bool DCSwitch::SwitchCase:: +add_field(DCField *field) { + bool inserted = _fields_by_name.insert + (FieldsByName::value_type(field->get_name(), field)).second; + + if (!inserted) { + return false; + } + + _fields.push_back(field); + + _num_nested_fields = (int)_fields.size(); + + // See if we still have a fixed byte size. + if (_has_fixed_byte_size) { + _has_fixed_byte_size = field->has_fixed_byte_size(); + _fixed_byte_size += field->get_fixed_byte_size(); + } + if (_has_fixed_structure) { + _has_fixed_structure = field->has_fixed_structure(); + } + return true; +} diff --git a/direct/src/dcparser/dcSwitch.h b/direct/src/dcparser/dcSwitch.h new file mode 100644 index 0000000000..b3c1f0cb4b --- /dev/null +++ b/direct/src/dcparser/dcSwitch.h @@ -0,0 +1,86 @@ +// Filename: dcSwitch.h +// Created by: drose (23Jun04) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef DCSWITCH_H +#define DCSWITCH_H + +#include "dcbase.h" +#include "dcField.h" + +//////////////////////////////////////////////////////////////////// +// Class : DCSwitch +// Description : This represents a switch statement, which can appear +// inside a class body and represents two or more +// alternative unpacking schemes based on the first +// field read. +//////////////////////////////////////////////////////////////////// +class EXPCL_DIRECT DCSwitch : public DCField { +public: + DCSwitch(const string &name, DCParameter *switch_parameter); + virtual ~DCSwitch(); + +PUBLISHED: + virtual DCSwitch *as_switch(); + + DCParameter *get_switch_parameter() const; + + int get_num_cases() const; + int get_case_by_value(const string &case_value) const; + + string get_value(int case_index) const; + int get_num_fields(int case_index) const; + DCField *get_field(int case_index, int n) const; + DCField *get_field_by_name(int case_index, const string &name) const; + +public: + virtual DCPackerInterface *get_nested_field(int n) const; + + int add_case(const string &value); + bool add_field(DCField *field); + + const DCPackerInterface *apply_switch(const char *value_data, size_t length) const; + + virtual void write(ostream &out, bool brief, int indent_level) const; + virtual void generate_hash(HashGenerator &hashgen) const; + +private: + DCParameter *_switch_parameter; + + typedef pvector Fields; + typedef pmap FieldsByName; + + class SwitchCase : public DCPackerInterface { + public: + SwitchCase(const string &name, const string &value); + virtual DCPackerInterface *get_nested_field(int n) const; + + bool add_field(DCField *field); + + string _value; + Fields _fields; + FieldsByName _fields_by_name; + }; + + typedef pvector Cases; + Cases _cases; + + typedef pmap CasesByValue; + CasesByValue _cases_by_value; +}; + +#endif diff --git a/direct/src/dcparser/dcparser_composite2.cxx b/direct/src/dcparser/dcparser_composite2.cxx index 4b1e960e2c..3d13c9d39d 100644 --- a/direct/src/dcparser/dcparser_composite2.cxx +++ b/direct/src/dcparser/dcparser_composite2.cxx @@ -7,4 +7,5 @@ #include "dcFile.cxx" #include "dcMolecularField.cxx" #include "dcSubatomicType.cxx" +#include "dcSwitch.cxx" #include "dcTypedef.cxx"