// Filename: dcPackerCatalog.cxx // Created by: drose (21Jun04) // //////////////////////////////////////////////////////////////////// // // 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 "dcPackerCatalog.h" #include "dcPackerInterface.h" #include "dcPacker.h" #include "dcSwitchParameter.h" //////////////////////////////////////////////////////////////////// // Function: DCPackerCatalog::Constructor // Access: Private // Description: The catalog is created only by // DCPackerInterface::get_catalog(). //////////////////////////////////////////////////////////////////// DCPackerCatalog:: 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 // Description: The catalog is destroyed only by // ~DCPackerInterface(). //////////////////////////////////////////////////////////////////// DCPackerCatalog:: ~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; } } //////////////////////////////////////////////////////////////////// // Function: DCPackerCatalog::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:: find_entry_by_name(const string &name) const { EntriesByName::const_iterator ni; ni = _entries_by_name.find(name); if (ni != _entries_by_name.end()) { return (*ni).second; } return -1; } //////////////////////////////////////////////////////////////////// // Function: DCPackerCatalog::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:: find_entry_by_field(const DCPackerInterface *field) const { EntriesByField::const_iterator ni; ni = _entries_by_field.find(field); if (ni != _entries_by_field.end()) { return (*ni).second; } return -1; } //////////////////////////////////////////////////////////////////// // Function: DCPackerCatalog::get_live_catalog // Access: Public // Description: Returns a LiveCatalog object indicating the positions // within the indicated data record of each field within // the catalog. If the catalog's fields are all // fixed-width, this may return a statically-allocated // LiveCatalog object that is the same for all data // records; otherwise, it will allocate a new // LiveCatalog object that must be freed with a later // call to release_live_catalog(). //////////////////////////////////////////////////////////////////// const DCPackerCatalog::LiveCatalog *DCPackerCatalog:: get_live_catalog(const char *data, size_t length) const { if (_live_catalog != (LiveCatalog *)NULL) { // 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; zero_entry._end = 0; for (size_t i = 0; i < _entries.size(); i++) { live_catalog->_live_entries.push_back(zero_entry); } DCPacker packer; packer.set_unpack_data(data, length, false); packer.begin_unpack(_root); const DCSwitchParameter *last_switch = NULL; r_fill_live_catalog(live_catalog, packer, last_switch); bool okflag = packer.end_unpack(); if (!okflag) { delete live_catalog; return NULL; } 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; } return live_catalog; } //////////////////////////////////////////////////////////////////// // Function: DCPackerCatalog::release_live_catalog // Access: Public // Description: Releases the LiveCatalog object that was returned by // an earlier call to get_live_catalog(). If this // represents a newly-allocated live catalog, it will // free it; otherwise, it will do nothing. // // It is therefore always correct (and necessary) to // match a call to get_live_catalog() with a later call // to release_live_catalog(). //////////////////////////////////////////////////////////////////// void DCPackerCatalog:: release_live_catalog(const DCPackerCatalog::LiveCatalog *live_catalog) const { if (live_catalog != _live_catalog) { delete (LiveCatalog *)live_catalog; } } //////////////////////////////////////////////////////////////////// // Function: DCPackerCatalog::add_entry // Access: Private // Description: Called only by DCPackerInterface::r_fill_catalog(), // this adds a new entry to the catalog. //////////////////////////////////////////////////////////////////// void DCPackerCatalog:: add_entry(const string &name, const DCPackerInterface *field, const DCPackerInterface *parent, int field_index) { Entry entry; entry._name = name; entry._field = field; entry._parent = parent; entry._field_index = field_index; int entry_index = (int)_entries.size(); _entries.push_back(entry); _entries_by_name.insert(EntriesByName::value_type(name, entry_index)); _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 (parent != (const DCPackerInterface *)NULL && !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 DCSwitchParameter *switch_parameter = field->as_switch_parameter(); if (switch_parameter != (DCSwitchParameter *)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[switch_parameter] = 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 // Description: Recursively walks through all of the fields on the // catalog and fills the live catalog with the // appropriate offsets. //////////////////////////////////////////////////////////////////// void DCPackerCatalog:: r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer, const DCSwitchParameter *&last_switch) const { const DCPackerInterface *current_field = packer.get_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.get_pack_type() != PT_blob)) { packer.push(); while (packer.more_nested_fields()) { r_fill_live_catalog(live_catalog, packer, last_switch); } packer.pop(); } else { packer.unpack_skip(); } 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); nassertv(switch_catalog != (DCPackerCatalog *)NULL); 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 DCSwitchParameter *switch_parameter, 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(switch_parameter); 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; }