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  */
78 find_entry_by_field(const DCPackerInterface *field) const {
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  */
144 release_live_catalog(const DCPackerCatalog::LiveCatalog *live_catalog) const {
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 }
This object contains the names of all of the nested fields available within a particular field.
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...
void release_live_catalog(const LiveCatalog *live_catalog) const
Releases the LiveCatalog object that was returned by an earlier call to get_live_catalog().
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 ...
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...
This defines the internal interface for packing values into a DCField.
bool has_fixed_structure() const
Returns true if this field type always has the same structure regardless of the data in the stream,...
const std::string & get_name() const
Returns the name of this field, or empty string if the field is unnamed.
bool has_nested_fields() const
Returns true if this field type has any nested fields (and thus expects a push() .
int get_num_nested_fields() const
Returns the number of nested fields required by this field type.
virtual DCPackerInterface * get_nested_field(int n) const
Returns the DCPackerInterface object that represents the nth nested field.
This class can be used for packing a series of numeric and string data into a binary stream,...
Definition: dcPacker.h:34
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
void push()
Marks the beginning of a nested series of fields.
Definition: dcPacker.cxx:411
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 begin_unpack(const DCPackerInterface *root)
Begins an unpacking session.
Definition: dcPacker.cxx:153
DCPackType get_pack_type() const
Returns the type of value expected by the current field.
Definition: dcPacker.I:114
bool end_unpack()
Finishes the unpacking session.
Definition: dcPacker.cxx:179
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
bool has_nested_fields() const
Returns true if the current field has any nested fields (and thus expects a push() .
Definition: dcPacker.I:37
void pop()
Marks the end of a nested series of fields.
Definition: dcPacker.cxx:495
void unpack_skip()
Skips the current field without unpacking it and advances to the next field.
Definition: dcPacker.cxx:604
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 std::vector< unsigned char > &data)
Sets up the unpack_data pointer.
Definition: dcPacker.cxx:117
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
This represents a switch object used as a parameter itself, which packs the appropriate fields of the...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.