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