Panda3D
dcPackerCatalog.cxx
1 // Filename: dcPackerCatalog.cxx
2 // Created by: drose (21Jun04)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "dcPackerCatalog.h"
16 #include "dcPackerInterface.h"
17 #include "dcPacker.h"
18 #include "dcSwitchParameter.h"
19 
20 ////////////////////////////////////////////////////////////////////
21 // Function: DCPackerCatalog::Constructor
22 // Access: Private
23 // Description: The catalog is created only by
24 // DCPackerInterface::get_catalog().
25 ////////////////////////////////////////////////////////////////////
26 DCPackerCatalog::
27 DCPackerCatalog(const DCPackerInterface *root) : _root(root) {
28  _live_catalog = NULL;
29 }
30 
31 ////////////////////////////////////////////////////////////////////
32 // Function: DCPackerCatalog::Copy Constructor
33 // Access: Private
34 // Description: The copy constructor is used only internally, in
35 // update_switch_fields().
36 ////////////////////////////////////////////////////////////////////
37 DCPackerCatalog::
38 DCPackerCatalog(const DCPackerCatalog &copy) :
39  _root(copy._root),
40  _entries(copy._entries),
41  _entries_by_name(copy._entries_by_name),
42  _entries_by_field(copy._entries_by_field)
43 {
44  _live_catalog = NULL;
45 }
46 
47 ////////////////////////////////////////////////////////////////////
48 // Function: DCPackerCatalog::Destructor
49 // Access: Private
50 // Description: The catalog is destroyed only by
51 // ~DCPackerInterface().
52 ////////////////////////////////////////////////////////////////////
53 DCPackerCatalog::
54 ~DCPackerCatalog() {
55  if (_live_catalog != (LiveCatalog *)NULL) {
56  delete _live_catalog;
57  }
58 
59  SwitchCatalogs::iterator si;
60  for (si = _switch_catalogs.begin(); si != _switch_catalogs.end(); ++si) {
61  delete (*si).second;
62  }
63 }
64 
65 ////////////////////////////////////////////////////////////////////
66 // Function: DCPackerCatalog::find_entry_by_name
67 // Access: Public
68 // Description: Returns the index number of the entry with the
69 // indicated name, or -1 if no entry has the indicated
70 // name. The return value is suitable for passing to
71 // get_entry().
72 ////////////////////////////////////////////////////////////////////
74 find_entry_by_name(const string &name) const {
75  EntriesByName::const_iterator ni;
76  ni = _entries_by_name.find(name);
77  if (ni != _entries_by_name.end()) {
78  return (*ni).second;
79  }
80  return -1;
81 }
82 
83 ////////////////////////////////////////////////////////////////////
84 // Function: DCPackerCatalog::find_entry_by_field
85 // Access: Public
86 // Description: Returns the index number of the entry with the
87 // indicated field, or -1 if no entry has the indicated
88 // field. The return value is suitable for passing to
89 // get_entry().
90 ////////////////////////////////////////////////////////////////////
93  EntriesByField::const_iterator ni;
94  ni = _entries_by_field.find(field);
95  if (ni != _entries_by_field.end()) {
96  return (*ni).second;
97  }
98  return -1;
99 }
100 
101 ////////////////////////////////////////////////////////////////////
102 // Function: DCPackerCatalog::get_live_catalog
103 // Access: Public
104 // Description: Returns a LiveCatalog object indicating the positions
105 // within the indicated data record of each field within
106 // the catalog. If the catalog's fields are all
107 // fixed-width, this may return a statically-allocated
108 // LiveCatalog object that is the same for all data
109 // records; otherwise, it will allocate a new
110 // LiveCatalog object that must be freed with a later
111 // call to release_live_catalog().
112 ////////////////////////////////////////////////////////////////////
114 get_live_catalog(const char *data, size_t length) const {
115  if (_live_catalog != (LiveCatalog *)NULL) {
116  // Return the previously-allocated live catalog; it will be the
117  // same as this one since it's based on a fixed-length field.
118  return _live_catalog;
119  }
120 
121  LiveCatalog *live_catalog = new LiveCatalog;
122  live_catalog->_catalog = this;
123  live_catalog->_live_entries.reserve(_entries.size());
124  LiveCatalogEntry zero_entry;
125  zero_entry._begin = 0;
126  zero_entry._end = 0;
127  for (size_t i = 0; i < _entries.size(); i++) {
128  live_catalog->_live_entries.push_back(zero_entry);
129  }
130 
131  DCPacker packer;
132  packer.set_unpack_data(data, length, false);
133  packer.begin_unpack(_root);
134  const DCSwitchParameter *last_switch = NULL;
135  r_fill_live_catalog(live_catalog, packer, last_switch);
136  bool okflag = packer.end_unpack();
137 
138  if (!okflag) {
139  delete live_catalog;
140  return NULL;
141  }
142 
143  if (_root->has_fixed_structure()) {
144  // If our root field has a fixed structure, then the live catalog
145  // will always be the same every time, so we might as well keep
146  // this one around as an optimization.
147  ((DCPackerCatalog *)this)->_live_catalog = live_catalog;
148  }
149 
150  return live_catalog;
151 }
152 
153 ////////////////////////////////////////////////////////////////////
154 // Function: DCPackerCatalog::release_live_catalog
155 // Access: Public
156 // Description: Releases the LiveCatalog object that was returned by
157 // an earlier call to get_live_catalog(). If this
158 // represents a newly-allocated live catalog, it will
159 // free it; otherwise, it will do nothing.
160 //
161 // It is therefore always correct (and necessary) to
162 // match a call to get_live_catalog() with a later call
163 // to release_live_catalog().
164 ////////////////////////////////////////////////////////////////////
167  if (live_catalog != _live_catalog) {
168  delete (LiveCatalog *)live_catalog;
169  }
170 }
171 
172 ////////////////////////////////////////////////////////////////////
173 // Function: DCPackerCatalog::add_entry
174 // Access: Private
175 // Description: Called only by DCPackerInterface::r_fill_catalog(),
176 // this adds a new entry to the catalog.
177 ////////////////////////////////////////////////////////////////////
178 void DCPackerCatalog::
179 add_entry(const string &name, const DCPackerInterface *field,
180  const DCPackerInterface *parent, int field_index) {
181  Entry entry;
182  entry._name = name;
183  entry._field = field;
184  entry._parent = parent;
185  entry._field_index = field_index;
186 
187  int entry_index = (int)_entries.size();
188  _entries.push_back(entry);
189  _entries_by_field.insert(EntriesByField::value_type(field, entry_index));
190 
191  // Add an entry for the fully-qualified field name
192  // (e.g. dna.topTex). If there was another entry for this name
193  // previously, completely replace it--the fully-qualified name is
194  // supposed to be unique and trumps the local field names (which are
195  // not necessarily unique).
196  _entries_by_name[name] = entry_index;
197 
198  // We'll also add an entry for the local field name, for the user's
199  // convenience. This won't override a fully-qualified name that
200  // might already have been recorded, and a fully-qualified name
201  // discovered later that conflicts with this name will replace it.
202  string local_name = field->get_name();
203  if (local_name != name) {
204  _entries_by_name.insert(EntriesByName::value_type(local_name, entry_index));
205  }
206 }
207 
208 ////////////////////////////////////////////////////////////////////
209 // Function: DCPackerCatalog::r_fill_catalog
210 // Access: Private
211 // Description: Called by DCPackerInterface to recursively fill up a
212 // newly-allocated reference catalog. Also called by
213 // update_switch_fields to append fields to a catalog
214 // after a DCSwitch node is selected.
215 ////////////////////////////////////////////////////////////////////
216 void DCPackerCatalog::
217 r_fill_catalog(const string &name_prefix, const DCPackerInterface *field,
218  const DCPackerInterface *parent, int field_index) {
219  string next_name_prefix = name_prefix;
220 
221  if (parent != (const DCPackerInterface *)NULL && !field->get_name().empty()) {
222  // Record this entry in the catalog.
223  next_name_prefix += field->get_name();
224  add_entry(next_name_prefix, field, parent, field_index);
225 
226  next_name_prefix += ".";
227  }
228 
229  const DCSwitchParameter *switch_parameter = field->as_switch_parameter();
230  if (switch_parameter != (DCSwitchParameter *)NULL) {
231  // If we come upon a DCSwitch while building the catalog, save the
232  // name_prefix at this point so we'll have it again when we later
233  // encounter the switch while unpacking a live record (and so we
234  // can return to this point in the recursion from
235  // update_switch_fields).
236  _switch_prefixes[switch_parameter] = next_name_prefix;
237  }
238 
239  // Add any children.
240  if (field->has_nested_fields()) {
241  int num_nested = field->get_num_nested_fields();
242  // It's ok if num_nested is -1.
243  for (int i = 0; i < num_nested; i++) {
244  DCPackerInterface *nested = field->get_nested_field(i);
245  if (nested != (DCPackerInterface *)NULL) {
246  r_fill_catalog(next_name_prefix, nested, field, i);
247  }
248  }
249  }
250 }
251 
252 ////////////////////////////////////////////////////////////////////
253 // Function: DCPackerCatalog::r_fill_live_catalog
254 // Access: Private
255 // Description: Recursively walks through all of the fields on the
256 // catalog and fills the live catalog with the
257 // appropriate offsets.
258 ////////////////////////////////////////////////////////////////////
259 void DCPackerCatalog::
260 r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer,
261  const DCSwitchParameter *&last_switch) const {
262  const DCPackerInterface *current_field = packer.get_current_field();
263 
264  int field_index = live_catalog->find_entry_by_field(current_field);
265  if (field_index >= 0) {
266  nassertv(field_index < (int)live_catalog->_live_entries.size());
267  live_catalog->_live_entries[field_index]._begin = packer.get_num_unpacked_bytes();
268  }
269 
270  if (packer.has_nested_fields() &&
271  (packer.get_pack_type() != PT_string && packer.get_pack_type() != PT_blob)) {
272  packer.push();
273  while (packer.more_nested_fields()) {
274  r_fill_live_catalog(live_catalog, packer, last_switch);
275  }
276  packer.pop();
277 
278  } else {
279  packer.unpack_skip();
280  }
281 
282  if (field_index >= 0) {
283  live_catalog->_live_entries[field_index]._end = packer.get_num_unpacked_bytes();
284  }
285 
286  if (last_switch != packer.get_last_switch()) {
287  // We've just invoked a new DCSwitch. That means we must add the
288  // new fields revealed by the switch to the reference catalog.
289  last_switch = packer.get_last_switch();
290 
291  const DCPackerInterface *switch_case = packer.get_current_parent();
292  nassertv(switch_case != (DCPackerInterface *)NULL);
293  const DCPackerCatalog *switch_catalog =
294  live_catalog->_catalog->update_switch_fields(last_switch, switch_case);
295  nassertv(switch_catalog != (DCPackerCatalog *)NULL);
296  live_catalog->_catalog = switch_catalog;
297 
298  // And we also have to expand the live catalog to hold the new
299  // entries.
300  LiveCatalogEntry zero_entry;
301  zero_entry._begin = 0;
302  zero_entry._end = 0;
303  for (size_t i = live_catalog->_live_entries.size();
304  i < switch_catalog->_entries.size();
305  i++) {
306  live_catalog->_live_entries.push_back(zero_entry);
307  }
308  }
309 }
310 
311 ////////////////////////////////////////////////////////////////////
312 // Function: DCPackerCatalog::update_switch_fields
313 // Access: Private
314 // Description: Returns a new DCPackerCatalog that includes all of
315 // the fields in this object, with the addition of the
316 // fields named by switch_case.
317 //
318 // This is used to implement switches, which change the
319 // set of fields they make available according to the
320 // data in the record, and therefore present a different
321 // catalog under different circumstances.
322 //
323 // This returned pointer is allocated one time for each
324 // different switch_case instance; if a given same
325 // switch_case is supplied twice, the same pointer is
326 // returned both times. The ownership of the returned
327 // pointer is kept by this object.
328 ////////////////////////////////////////////////////////////////////
329 const DCPackerCatalog *DCPackerCatalog::
330 update_switch_fields(const DCSwitchParameter *switch_parameter,
331  const DCPackerInterface *switch_case) const {
332  SwitchCatalogs::const_iterator si = _switch_catalogs.find(switch_case);
333  if (si != _switch_catalogs.end()) {
334  return (*si).second;
335  }
336 
337  // Look up the name_prefix will we use for all of the fields that
338  // descend from this switch. This should be stored in this record
339  // because we must have come across the DCSwitch when building the
340  // catalog the first time.
341  SwitchPrefixes::const_iterator pi = _switch_prefixes.find(switch_parameter);
342  if (pi == _switch_prefixes.end()) {
343  // If it's not stored in the record, the switch must be hidden
344  // within some non-seekable object, like an array; in this case,
345  // never mind.
346  return this;
347  }
348 
349  string name_prefix = (*pi).second;
350 
351  // Start by creating a new DCPackerCatalog object that contains all
352  // of the fields that this one contains.
353  DCPackerCatalog *switch_catalog = new DCPackerCatalog(*this);
354 
355  // Now record all of the fields of the switch case in the new
356  // catalog. We start with the second field of the switch case,
357  // since the first field will be the switch parameter itself, which
358  // we would have already recorded the first time around.
359  int num_nested = switch_case->get_num_nested_fields();
360  for (int i = 1; i < num_nested; i++) {
361  DCPackerInterface *nested = switch_case->get_nested_field(i);
362  if (nested != (DCPackerInterface *)NULL) {
363  switch_catalog->r_fill_catalog(name_prefix, nested, switch_case, i);
364  }
365  }
366 
367  // Store the newly-generated switch catalog in the record so the
368  // same pointer can be returned in the future.
369  ((DCPackerCatalog *)this)->_switch_catalogs[switch_case] = switch_catalog;
370 
371  return switch_catalog;
372 }
DCPackType get_pack_type() const
Returns the type of value expected by the current field.
Definition: dcPacker.I:145
void release_live_catalog(const LiveCatalog *live_catalog) const
Releases the LiveCatalog object that was returned by an earlier call to get_live_catalog().
bool has_fixed_structure() const
Returns true if this field type always has the same structure regardless of the data in the stream...
bool has_nested_fields() const
Returns true if this field type has any nested fields (and thus expects a push() .
size_t get_num_unpacked_bytes() const
Returns the number of bytes that have been unpacked so far, or after unpack_end(), the total number of bytes that were unpacked at all.
Definition: dcPacker.I:640
const DCPackerInterface * get_current_field() const
Returns the field that will be referenced by the next call to pack_*() or unpack_*().
Definition: dcPacker.I:109
const string & get_name() const
Returns the name of this field, or empty string if the field is unnamed.
int get_num_nested_fields() const
Returns the number of nested fields required by this field type.
This represents a switch object used as a parameter itself, which packs the appropriate fields of the...
virtual DCPackerInterface * get_nested_field(int n) const
Returns the DCPackerInterface object that represents the nth nested field.
int find_entry_by_field(const DCPackerInterface *field) const
Returns the index number of the entry with the indicated field, or -1 if no entry has the indicated f...
int find_entry_by_field(const DCPackerInterface *field) const
Returns the index number of the entry with the indicated field, or -1 if no entry has the indicated f...
bool has_nested_fields() const
Returns true if the current field has any nested fields (and thus expects a push() ...
Definition: dcPacker.I:45
const DCPackerInterface * get_current_parent() const
Returns the field that we left in our last call to push(): the owner of the current level of fields...
Definition: dcPacker.I:96
bool more_nested_fields() const
Returns true if there are more nested fields to pack or unpack in the current push sequence...
Definition: dcPacker.I:83
void push()
Marks the beginning of a nested series of fields.
Definition: dcPacker.cxx:448
const DCSwitchParameter * get_last_switch() const
Returns a pointer to the last DCSwitch instance that we have passed by and selected one case of durin...
Definition: dcPacker.I:127
This class can be used for packing a series of numeric and string data into a binary stream...
Definition: dcPacker.h:38
int find_entry_by_name(const string &name) const
Returns the index number of the entry with the indicated name, or -1 if no entry has the indicated na...
void unpack_skip()
Skips the current field without unpacking it and advances to the next field.
Definition: dcPacker.cxx:655
This object contains the names of all of the nested fields available within a particular field...
void set_unpack_data(const string &data)
Sets up the unpack_data pointer.
Definition: dcPacker.cxx:125
void pop()
Marks the end of a nested series of fields.
Definition: dcPacker.cxx:537
const LiveCatalog * get_live_catalog(const char *data, size_t length) const
Returns a LiveCatalog object indicating the positions within the indicated data record of each field ...
This defines the internal interface for packing values into a DCField.
void begin_unpack(const DCPackerInterface *root)
Begins an unpacking session.
Definition: dcPacker.cxx:168
bool end_unpack()
Finishes the unpacking session.
Definition: dcPacker.cxx:197