Panda3D
Loading...
Searching...
No Matches
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
19using std::string;
20
21/**
22 * The catalog is created only by DCPackerInterface::get_catalog().
23 */
24DCPackerCatalog::
25DCPackerCatalog(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 */
32DCPackerCatalog::
33DCPackerCatalog(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 */
45DCPackerCatalog::
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 */
63find_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 */
78find_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 */
96get_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 */
144release_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 */
154void DCPackerCatalog::
155add_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 */
188void DCPackerCatalog::
189r_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 */
227void DCPackerCatalog::
228r_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 */
291const DCPackerCatalog *DCPackerCatalog::
292update_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.