Panda3D

dcPackerCatalog.cxx

00001 // Filename: dcPackerCatalog.cxx
00002 // Created by:  drose (21Jun04)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "dcPackerCatalog.h"
00016 #include "dcPackerInterface.h"
00017 #include "dcPacker.h"
00018 #include "dcSwitchParameter.h"
00019 
00020 ////////////////////////////////////////////////////////////////////
00021 //     Function: DCPackerCatalog::Constructor
00022 //       Access: Private
00023 //  Description: The catalog is created only by
00024 //               DCPackerInterface::get_catalog().
00025 ////////////////////////////////////////////////////////////////////
00026 DCPackerCatalog::
00027 DCPackerCatalog(const DCPackerInterface *root) : _root(root) {
00028   _live_catalog = NULL;
00029 }
00030 
00031 ////////////////////////////////////////////////////////////////////
00032 //     Function: DCPackerCatalog::Copy Constructor
00033 //       Access: Private
00034 //  Description: The copy constructor is used only internally, in
00035 //               update_switch_fields().
00036 ////////////////////////////////////////////////////////////////////
00037 DCPackerCatalog::
00038 DCPackerCatalog(const DCPackerCatalog &copy) :
00039   _root(copy._root),
00040   _entries(copy._entries),
00041   _entries_by_name(copy._entries_by_name),
00042   _entries_by_field(copy._entries_by_field)
00043 {
00044   _live_catalog = NULL;
00045 }
00046 
00047 ////////////////////////////////////////////////////////////////////
00048 //     Function: DCPackerCatalog::Destructor
00049 //       Access: Private
00050 //  Description: The catalog is destroyed only by
00051 //               ~DCPackerInterface().
00052 ////////////////////////////////////////////////////////////////////
00053 DCPackerCatalog::
00054 ~DCPackerCatalog() {
00055   if (_live_catalog != (LiveCatalog *)NULL) {
00056     delete _live_catalog;
00057   }
00058 
00059   SwitchCatalogs::iterator si;
00060   for (si = _switch_catalogs.begin(); si != _switch_catalogs.end(); ++si) {
00061     delete (*si).second;
00062   }
00063 }
00064 
00065 ////////////////////////////////////////////////////////////////////
00066 //     Function: DCPackerCatalog::find_entry_by_name
00067 //       Access: Public
00068 //  Description: Returns the index number of the entry with the
00069 //               indicated name, or -1 if no entry has the indicated
00070 //               name.  The return value is suitable for passing to
00071 //               get_entry().
00072 ////////////////////////////////////////////////////////////////////
00073 int DCPackerCatalog::
00074 find_entry_by_name(const string &name) const {
00075   EntriesByName::const_iterator ni;
00076   ni = _entries_by_name.find(name);
00077   if (ni != _entries_by_name.end()) {
00078     return (*ni).second;
00079   }
00080   return -1;
00081 }
00082 
00083 ////////////////////////////////////////////////////////////////////
00084 //     Function: DCPackerCatalog::find_entry_by_field
00085 //       Access: Public
00086 //  Description: Returns the index number of the entry with the
00087 //               indicated field, or -1 if no entry has the indicated
00088 //               field.  The return value is suitable for passing to
00089 //               get_entry().
00090 ////////////////////////////////////////////////////////////////////
00091 int DCPackerCatalog::
00092 find_entry_by_field(const DCPackerInterface *field) const {
00093   EntriesByField::const_iterator ni;
00094   ni = _entries_by_field.find(field);
00095   if (ni != _entries_by_field.end()) {
00096     return (*ni).second;
00097   }
00098   return -1;
00099 }
00100 
00101 ////////////////////////////////////////////////////////////////////
00102 //     Function: DCPackerCatalog::get_live_catalog
00103 //       Access: Public
00104 //  Description: Returns a LiveCatalog object indicating the positions
00105 //               within the indicated data record of each field within
00106 //               the catalog.  If the catalog's fields are all
00107 //               fixed-width, this may return a statically-allocated
00108 //               LiveCatalog object that is the same for all data
00109 //               records; otherwise, it will allocate a new
00110 //               LiveCatalog object that must be freed with a later
00111 //               call to release_live_catalog().
00112 ////////////////////////////////////////////////////////////////////
00113 const DCPackerCatalog::LiveCatalog *DCPackerCatalog::
00114 get_live_catalog(const char *data, size_t length) const {
00115   if (_live_catalog != (LiveCatalog *)NULL) {
00116     // Return the previously-allocated live catalog; it will be the
00117     // same as this one since it's based on a fixed-length field.
00118     return _live_catalog;
00119   }
00120 
00121   LiveCatalog *live_catalog = new LiveCatalog;
00122   live_catalog->_catalog = this;
00123   live_catalog->_live_entries.reserve(_entries.size());
00124   LiveCatalogEntry zero_entry;
00125   zero_entry._begin = 0;
00126   zero_entry._end = 0;
00127   for (size_t i = 0; i < _entries.size(); i++) {
00128     live_catalog->_live_entries.push_back(zero_entry);
00129   }
00130   
00131   DCPacker packer;
00132   packer.set_unpack_data(data, length, false);
00133   packer.begin_unpack(_root);
00134   const DCSwitchParameter *last_switch = NULL;
00135   r_fill_live_catalog(live_catalog, packer, last_switch);
00136   bool okflag = packer.end_unpack();
00137 
00138   if (!okflag) {
00139     delete live_catalog;
00140     return NULL;
00141   }
00142 
00143   if (_root->has_fixed_structure()) {
00144     // If our root field has a fixed structure, then the live catalog
00145     // will always be the same every time, so we might as well keep
00146     // this one around as an optimization.
00147     ((DCPackerCatalog *)this)->_live_catalog = live_catalog;
00148   }
00149 
00150   return live_catalog;
00151 }
00152 
00153 ////////////////////////////////////////////////////////////////////
00154 //     Function: DCPackerCatalog::release_live_catalog
00155 //       Access: Public
00156 //  Description: Releases the LiveCatalog object that was returned by
00157 //               an earlier call to get_live_catalog().  If this
00158 //               represents a newly-allocated live catalog, it will
00159 //               free it; otherwise, it will do nothing.
00160 //
00161 //               It is therefore always correct (and necessary) to
00162 //               match a call to get_live_catalog() with a later call
00163 //               to release_live_catalog().
00164 ////////////////////////////////////////////////////////////////////
00165 void DCPackerCatalog::
00166 release_live_catalog(const DCPackerCatalog::LiveCatalog *live_catalog) const {
00167   if (live_catalog != _live_catalog) {
00168     delete (LiveCatalog *)live_catalog;
00169   }
00170 }
00171 
00172 ////////////////////////////////////////////////////////////////////
00173 //     Function: DCPackerCatalog::add_entry
00174 //       Access: Private
00175 //  Description: Called only by DCPackerInterface::r_fill_catalog(),
00176 //               this adds a new entry to the catalog.
00177 ////////////////////////////////////////////////////////////////////
00178 void DCPackerCatalog::
00179 add_entry(const string &name, const DCPackerInterface *field,
00180           const DCPackerInterface *parent, int field_index) {
00181   Entry entry;
00182   entry._name = name;
00183   entry._field = field;
00184   entry._parent = parent;
00185   entry._field_index = field_index;
00186 
00187   int entry_index = (int)_entries.size();
00188   _entries.push_back(entry);
00189   _entries_by_field.insert(EntriesByField::value_type(field, entry_index));
00190 
00191   // Add an entry for the fully-qualified field name
00192   // (e.g. dna.topTex).  If there was another entry for this name
00193   // previously, completely replace it--the fully-qualified name is
00194   // supposed to be unique and trumps the local field names (which are
00195   // not necessarily unique).
00196   _entries_by_name[name] = entry_index;
00197 
00198   // We'll also add an entry for the local field name, for the user's
00199   // convenience.  This won't override a fully-qualified name that
00200   // might already have been recorded, and a fully-qualified name
00201   // discovered later that conflicts with this name will replace it.
00202   string local_name = field->get_name();
00203   if (local_name != name) {
00204     _entries_by_name.insert(EntriesByName::value_type(local_name, entry_index));
00205   }
00206 }
00207 
00208 ////////////////////////////////////////////////////////////////////
00209 //     Function: DCPackerCatalog::r_fill_catalog
00210 //       Access: Private
00211 //  Description: Called by DCPackerInterface to recursively fill up a
00212 //               newly-allocated reference catalog.  Also called by
00213 //               update_switch_fields to append fields to a catalog
00214 //               after a DCSwitch node is selected.
00215 ////////////////////////////////////////////////////////////////////
00216 void DCPackerCatalog::
00217 r_fill_catalog(const string &name_prefix, const DCPackerInterface *field,
00218                const DCPackerInterface *parent, int field_index) {
00219   string next_name_prefix = name_prefix;
00220 
00221   if (parent != (const DCPackerInterface *)NULL && !field->get_name().empty()) {
00222     // Record this entry in the catalog.
00223     next_name_prefix += field->get_name();
00224     add_entry(next_name_prefix, field, parent, field_index);
00225 
00226     next_name_prefix += ".";
00227   }
00228 
00229   const DCSwitchParameter *switch_parameter = field->as_switch_parameter();
00230   if (switch_parameter != (DCSwitchParameter *)NULL) {
00231     // If we come upon a DCSwitch while building the catalog, save the
00232     // name_prefix at this point so we'll have it again when we later
00233     // encounter the switch while unpacking a live record (and so we
00234     // can return to this point in the recursion from
00235     // update_switch_fields).
00236     _switch_prefixes[switch_parameter] = next_name_prefix;
00237   }
00238 
00239   // Add any children.
00240   if (field->has_nested_fields()) {
00241     int num_nested = field->get_num_nested_fields();
00242     // It's ok if num_nested is -1.
00243     for (int i = 0; i < num_nested; i++) {
00244       DCPackerInterface *nested = field->get_nested_field(i);
00245       if (nested != (DCPackerInterface *)NULL) {
00246         r_fill_catalog(next_name_prefix, nested, field, i);
00247       }
00248     }
00249   }
00250 }
00251 
00252 ////////////////////////////////////////////////////////////////////
00253 //     Function: DCPackerCatalog::r_fill_live_catalog
00254 //       Access: Private
00255 //  Description: Recursively walks through all of the fields on the
00256 //               catalog and fills the live catalog with the
00257 //               appropriate offsets.
00258 ////////////////////////////////////////////////////////////////////
00259 void DCPackerCatalog::
00260 r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer,
00261                     const DCSwitchParameter *&last_switch) const {
00262   const DCPackerInterface *current_field = packer.get_current_field();
00263 
00264   int field_index = live_catalog->find_entry_by_field(current_field);
00265   if (field_index >= 0) {
00266     nassertv(field_index < (int)live_catalog->_live_entries.size());
00267     live_catalog->_live_entries[field_index]._begin = packer.get_num_unpacked_bytes();
00268   }
00269 
00270   if (packer.has_nested_fields() && 
00271       (packer.get_pack_type() != PT_string && packer.get_pack_type() != PT_blob)) {
00272     packer.push();
00273     while (packer.more_nested_fields()) {
00274       r_fill_live_catalog(live_catalog, packer, last_switch);
00275     }
00276     packer.pop();
00277 
00278   } else {
00279     packer.unpack_skip();
00280   }
00281 
00282   if (field_index >= 0) {
00283     live_catalog->_live_entries[field_index]._end = packer.get_num_unpacked_bytes();
00284   }
00285 
00286   if (last_switch != packer.get_last_switch()) {
00287     // We've just invoked a new DCSwitch.  That means we must add the
00288     // new fields revealed by the switch to the reference catalog.
00289     last_switch = packer.get_last_switch();
00290 
00291     const DCPackerInterface *switch_case = packer.get_current_parent();
00292     nassertv(switch_case != (DCPackerInterface *)NULL);
00293     const DCPackerCatalog *switch_catalog = 
00294       live_catalog->_catalog->update_switch_fields(last_switch, switch_case);
00295     nassertv(switch_catalog != (DCPackerCatalog *)NULL);
00296     live_catalog->_catalog = switch_catalog;
00297 
00298     // And we also have to expand the live catalog to hold the new
00299     // entries.
00300     LiveCatalogEntry zero_entry;
00301     zero_entry._begin = 0;
00302     zero_entry._end = 0;
00303     for (size_t i = live_catalog->_live_entries.size(); 
00304          i < switch_catalog->_entries.size(); 
00305          i++) {
00306       live_catalog->_live_entries.push_back(zero_entry);
00307     }
00308   }
00309 }
00310 
00311 ////////////////////////////////////////////////////////////////////
00312 //     Function: DCPackerCatalog::update_switch_fields
00313 //       Access: Private
00314 //  Description: Returns a new DCPackerCatalog that includes all of
00315 //               the fields in this object, with the addition of the
00316 //               fields named by switch_case.  
00317 //
00318 //               This is used to implement switches, which change the
00319 //               set of fields they make available according to the
00320 //               data in the record, and therefore present a different
00321 //               catalog under different circumstances.
00322 //
00323 //               This returned pointer is allocated one time for each
00324 //               different switch_case instance; if a given same
00325 //               switch_case is supplied twice, the same pointer is
00326 //               returned both times.  The ownership of the returned
00327 //               pointer is kept by this object.
00328 ////////////////////////////////////////////////////////////////////
00329 const DCPackerCatalog *DCPackerCatalog::
00330 update_switch_fields(const DCSwitchParameter *switch_parameter,
00331                      const DCPackerInterface *switch_case) const {
00332   SwitchCatalogs::const_iterator si = _switch_catalogs.find(switch_case);
00333   if (si != _switch_catalogs.end()) {
00334     return (*si).second;
00335   }
00336 
00337   // Look up the name_prefix will we use for all of the fields that
00338   // descend from this switch.  This should be stored in this record
00339   // because we must have come across the DCSwitch when building the
00340   // catalog the first time.
00341   SwitchPrefixes::const_iterator pi = _switch_prefixes.find(switch_parameter);
00342   if (pi == _switch_prefixes.end()) {
00343     // If it's not stored in the record, the switch must be hidden
00344     // within some non-seekable object, like an array; in this case,
00345     // never mind.
00346     return this;
00347   }
00348 
00349   string name_prefix = (*pi).second;
00350 
00351   // Start by creating a new DCPackerCatalog object that contains all
00352   // of the fields that this one contains.
00353   DCPackerCatalog *switch_catalog = new DCPackerCatalog(*this);
00354 
00355   // Now record all of the fields of the switch case in the new
00356   // catalog.  We start with the second field of the switch case,
00357   // since the first field will be the switch parameter itself, which
00358   // we would have already recorded the first time around.
00359   int num_nested = switch_case->get_num_nested_fields();
00360   for (int i = 1; i < num_nested; i++) {
00361     DCPackerInterface *nested = switch_case->get_nested_field(i);
00362     if (nested != (DCPackerInterface *)NULL) {
00363       switch_catalog->r_fill_catalog(name_prefix, nested, switch_case, i);
00364     }
00365   }
00366 
00367   // Store the newly-generated switch catalog in the record so the
00368   // same pointer can be returned in the future.
00369   ((DCPackerCatalog *)this)->_switch_catalogs[switch_case] = switch_catalog;
00370 
00371   return switch_catalog;
00372 }
 All Classes Functions Variables Enumerations