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