add switches

This commit is contained in:
David Rose 2004-06-24 07:43:28 +00:00
parent be06c52586
commit 803712f986
26 changed files with 1107 additions and 137 deletions

View File

@ -26,6 +26,7 @@
dcPackerInterface.h dcPackerInterface.I \
dcParameter.h dcClassParameter.h dcArrayParameter.h dcSimpleParameter.h \
dcNumericRange.h dcNumericRange.I \
dcSwitch.h \
dcTypedef.h \
dcPython.h \
dcbase.h dcindent.h hashGenerator.h \
@ -42,6 +43,7 @@
dcPackerInterface.cxx \
dcParameter.cxx dcClassParameter.cxx \
dcArrayParameter.cxx dcSimpleParameter.cxx \
dcSwitch.cxx \
dcTypedef.cxx \
dcindent.cxx \
hashGenerator.cxx primeNumberGenerator.cxx

View File

@ -40,6 +40,7 @@ DCArrayParameter(DCParameter *element_type, const DCUnsignedIntRange &size) :
if (_array_size >= 0 && _element_type->has_fixed_byte_size()) {
_has_fixed_byte_size = true;
_fixed_byte_size = _array_size * _element_type->get_fixed_byte_size();
_has_fixed_structure = true;
} else {
// We only need to store the length bytes if the array has a

View File

@ -317,7 +317,7 @@ write(ostream &out, bool brief, int indent_level) const {
}
out << ";";
if (!brief) {
if (!brief && _number >= 0) {
out << " // field " << _number;
}
out << "\n";
@ -372,6 +372,9 @@ add_element(const DCAtomicField::ElementType &element) {
_has_fixed_byte_size = element._param->has_fixed_byte_size();
_fixed_byte_size += element._param->get_fixed_byte_size();
}
if (_has_fixed_structure) {
_has_fixed_structure = element._param->has_fixed_structure();
}
}
////////////////////////////////////////////////////////////////////

View File

@ -22,6 +22,41 @@
#include "dcindent.h"
#include "dcmsgtypes.h"
////////////////////////////////////////////////////////////////////
// Function: DCClass::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
DCClass::
DCClass(const string &name, bool is_struct, bool bogus_class) :
_name(name),
_is_struct(is_struct),
_bogus_class(bogus_class)
{
_number = -1;
#ifdef HAVE_PYTHON
_class_def = NULL;
#endif
}
////////////////////////////////////////////////////////////////////
// Function: DCClass::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
DCClass::
~DCClass() {
Fields::iterator fi;
for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
delete (*fi);
}
#ifdef HAVE_PYTHON
Py_XDECREF(_class_def);
#endif
}
////////////////////////////////////////////////////////////////////
// Function: DCClass::get_name
// Access: Published
@ -540,39 +575,6 @@ ai_format_generate(PyObject *distobj, int do_id,
}
#endif // HAVE_PYTHON
////////////////////////////////////////////////////////////////////
// Function: DCClass::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
DCClass::
DCClass(const string &name, bool is_struct, bool bogus_class) :
_name(name),
_is_struct(is_struct),
_bogus_class(bogus_class)
{
#ifdef HAVE_PYTHON
_class_def = NULL;
#endif
}
////////////////////////////////////////////////////////////////////
// Function: DCClass::Destructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
DCClass::
~DCClass() {
Fields::iterator fi;
for (fi = _fields.begin(); fi != _fields.end(); ++fi) {
delete (*fi);
}
#ifdef HAVE_PYTHON
Py_XDECREF(_class_def);
#endif
}
////////////////////////////////////////////////////////////////////
// Function: DCClass::write
// Access: Public, Virtual
@ -599,7 +601,7 @@ write(ostream &out, bool brief, int indent_level) const {
}
out << " {";
if (!brief) {
if (!brief && _number >= 0) {
out << " // index " << _number;
}
out << "\n";

View File

@ -33,6 +33,10 @@ class DCParameter;
// input .dc file.
////////////////////////////////////////////////////////////////////
class EXPCL_DIRECT DCClass : public DCDeclaration {
public:
DCClass(const string &name, bool is_struct, bool bogus_class);
~DCClass();
PUBLISHED:
const string &get_name() const;
int get_number() const;
@ -76,9 +80,6 @@ PUBLISHED:
#endif
public:
DCClass(const string &name, bool is_struct, bool bogus_class);
~DCClass();
virtual void write(ostream &out, bool brief, int indent_level) const;
void generate_hash(HashGenerator &hashgen) const;

View File

@ -40,10 +40,12 @@ DCClassParameter(DCClass *dclass) :
// fields).
_has_fixed_byte_size = true;
_fixed_byte_size = 0;
_has_fixed_structure = true;
for (int i = 0; i < _num_nested_fields && _has_fixed_byte_size; i++) {
DCPackerInterface *field = get_nested_field(i);
_has_fixed_byte_size = field->has_fixed_byte_size();
_has_fixed_byte_size = _has_fixed_byte_size && field->has_fixed_byte_size();
_fixed_byte_size += field->get_fixed_byte_size();
_has_fixed_structure = _has_fixed_structure && field->has_fixed_structure();
}
}

View File

@ -28,13 +28,15 @@
////////////////////////////////////////////////////////////////////
DCField::
DCField(const string &name) : DCPackerInterface(name) {
_number = 0;
_number = -1;
_has_nested_fields = true;
_num_nested_fields = 0;
_pack_type = PT_field;
_has_fixed_byte_size = true;
_fixed_byte_size = 0;
_has_fixed_structure = true;
}
////////////////////////////////////////////////////////////////////
@ -92,6 +94,16 @@ as_parameter() {
return (DCParameter *)NULL;
}
////////////////////////////////////////////////////////////////////
// Function: DCField::as_switch
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
DCSwitch *DCField::
as_switch() {
return (DCSwitch *)NULL;
}
////////////////////////////////////////////////////////////////////
// Function: DCField::format_data
// Access: Published

View File

@ -26,6 +26,7 @@
class DCAtomicField;
class DCMolecularField;
class DCParameter;
class DCSwitch;
class HashGenerator;
////////////////////////////////////////////////////////////////////
@ -44,6 +45,7 @@ PUBLISHED:
virtual DCAtomicField *as_atomic_field();
virtual DCMolecularField *as_molecular_field();
virtual DCParameter *as_parameter();
virtual DCSwitch *as_switch();
string format_data(const string &packed_data);
string parse_string(const string &formatted_string);

View File

@ -370,6 +370,16 @@ REALNUM ([+-]?(([0-9]+[.])|([0-9]*[.][0-9]+))([eE][+-]?[0-9]+)?)
return KW_TYPEDEF;
}
"switch" {
accept();
return KW_SWITCH;
}
"case" {
accept();
return KW_CASE;
}
"int8" {
accept();
return KW_INT8;

View File

@ -96,6 +96,9 @@ add_atomic(DCAtomicField *atomic) {
_has_fixed_byte_size = atomic->has_fixed_byte_size();
_fixed_byte_size += atomic->get_fixed_byte_size();
}
if (_has_fixed_structure) {
_has_fixed_structure = atomic->has_fixed_structure();
}
}
////////////////////////////////////////////////////////////////////

View File

@ -45,6 +45,12 @@ has_nested_fields() const {
// The return value may be -1 to indicate that a
// variable number of nested fields are accepted by this
// field type (e.g. a variable-length array).
//
// Note that this method is unreliable to determine how
// many fields you must traverse before you can call
// pop(), since particularly in the presence of a
// DCSwitch, it may change during traversal. Use
// more_nested_fields() instead.
////////////////////////////////////////////////////////////////////
INLINE int DCPacker::
get_num_nested_fields() const {
@ -63,6 +69,19 @@ more_nested_fields() const {
return (_current_field != (DCPackerInterface *)NULL);
}
////////////////////////////////////////////////////////////////////
// Function: DCPacker::get_current_parent
// Access: Published
// Description: Returns the field that we left in our last call to
// push(): the owner of the current level of fields.
// This may be NULL at the beginning of the pack
// operation.
////////////////////////////////////////////////////////////////////
INLINE const DCPackerInterface *DCPacker::
get_current_parent() const {
return _current_parent;
}
////////////////////////////////////////////////////////////////////
// Function: DCPacker::get_current_field
// Access: Published
@ -76,6 +95,24 @@ get_current_field() const {
return _current_field;
}
////////////////////////////////////////////////////////////////////
// Function: DCPacker::get_last_switch
// Access: Published
// Description: Returns a pointer to the last DCSwitch instance that
// we have passed by and selected one case of during the
// pack/unpack process. Each time we encounter a new
// DCSwitch and select a case, this will change state.
//
// This may be used to detect when a DCSwitch has been
// selected. At the moment this changes state,
// get_current_parent() will contain the particular
// SwitchCase that was selected by the switch.
////////////////////////////////////////////////////////////////////
INLINE const DCSwitch *DCPacker::
get_last_switch() const {
return _last_switch;
}
////////////////////////////////////////////////////////////////////
// Function: DCPacker::get_pack_type
// Access: Published
@ -470,8 +507,16 @@ advance() {
// call pop().
_current_field = NULL;
} else if (_mode == M_unpack && _push_marker != 0 &&
_unpack_p >= _push_marker) {
// But if the parent is a switch record, we make a special case so
// we can get the alternate fields.
if (_current_parent != (DCPackerInterface *)NULL) {
const DCSwitch *dswitch = ((DCPackerInterface *)_current_parent)->as_switch();
if (dswitch != (DCSwitch *)NULL) {
handle_switch(dswitch);
}
}
} else if (_pop_marker != 0 && _unpack_p >= _pop_marker) {
// Done with all the fields on this parent. The caller must now
// call pop().
_current_field = NULL;

View File

@ -17,6 +17,7 @@
////////////////////////////////////////////////////////////////////
#include "dcPacker.h"
#include "dcSwitch.h"
#include "dcParserDefs.h"
#include "dcLexerDefs.h"
@ -32,15 +33,11 @@ DCPacker() {
_unpack_length = 0;
_owns_unpack_data = false;
_unpack_p = 0;
_root = NULL;
_catalog = NULL;
_live_catalog = NULL;
_current_field = NULL;
_current_parent = NULL;
_current_field_index = 0;
_num_nested_fields = 0;
_pack_error = false;
_range_error = false;
clear();
}
////////////////////////////////////////////////////////////////////
@ -272,14 +269,14 @@ seek(const string &field_name) {
}
nassertr(_catalog != (DCPackerCatalog *)NULL, false);
int entry_index = _catalog->find_entry_by_name(field_name);
int entry_index = _live_catalog->find_entry_by_name(field_name);
if (entry_index < 0) {
// The field was not known.
_pack_error = true;
return false;
}
const DCPackerCatalog::Entry &entry = _catalog->get_entry(entry_index);
const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(entry_index);
// If we are seeking, we don't need to remember our current stack
// position.
@ -290,6 +287,13 @@ seek(const string &field_name) {
_num_nested_fields = _current_parent->get_num_nested_fields();
_unpack_p = _live_catalog->get_begin(entry_index);
// We don't really need _push_marker and _pop_marker now, except
// that we should set _push_marker in case we have just seeked to
// a switch parameter, and we should set _pop_marker to 0 just so
// it won't get in the way.
_push_marker = _unpack_p;
_pop_marker = 0;
return true;
} else if (_mode == M_repack) {
@ -302,14 +306,24 @@ seek(const string &field_name) {
return false;
}
int entry_index = _catalog->find_entry_by_name(field_name);
int entry_index = _live_catalog->find_entry_by_name(field_name);
if (entry_index < 0) {
// The field was not known.
_pack_error = true;
return false;
}
const DCPackerCatalog::Entry &entry = _catalog->get_entry(entry_index);
const DCPackerCatalog::Entry &entry = _live_catalog->get_entry(entry_index);
if (((DCPackerInterface *)entry._parent)->as_switch() != (DCSwitch *)NULL) {
// If the parent is a DCSwitch, that can only mean that the
// seeked field is a switch parameter. We can't support seeking
// to a switch parameter and modifying it directly--what would
// happen to all of the related fields? Instead, you'll have to
// seek to the switch itself and repack the whole entity.
_pack_error = true;
return false;
}
size_t begin = _live_catalog->get_begin(entry_index);
if (begin < _unpack_p) {
@ -337,9 +351,14 @@ seek(const string &field_name) {
_current_field = entry._field;
_current_parent = entry._parent;
_current_field_index = entry._field_index;
_num_nested_fields = 0; // this makes advance() stop after this field.
_num_nested_fields = 1;
_unpack_p = _live_catalog->get_end(entry_index);
// Set up push_marker and pop_marker so we won't try to advance
// beyond this field.
_push_marker = begin;
_pop_marker = _live_catalog->get_end(entry_index);
return true;
}
@ -370,6 +389,7 @@ push() {
element._current_parent = _current_parent;
element._current_field_index = _current_field_index;
element._push_marker = _push_marker;
element._pop_marker = _pop_marker;
_stack.push_back(element);
_current_parent = _current_field;
@ -383,12 +403,14 @@ push() {
// Reserve length_bytes for when we figure out what the length
// is.
_push_marker = _pack_data.get_length();
_pop_marker = 0;
_pack_data.append_junk(length_bytes);
} else if (_mode == M_unpack) {
// Read length_bytes to determine the end of this nested
// sequence.
_push_marker = 0;
_push_marker = _unpack_p;
_pop_marker = 0;
if (length_bytes != 0) {
if (_unpack_p + length_bytes > _unpack_length) {
@ -400,13 +422,13 @@ push() {
length = DCPackerInterface::do_unpack_uint32
(_unpack_data + _unpack_p);
_unpack_p += 4;
_push_marker = _unpack_p + length;
_pop_marker = _unpack_p + length;
} else {
length = DCPackerInterface::do_unpack_uint16
(_unpack_data + _unpack_p);
_unpack_p += 2;
}
_push_marker = _unpack_p + length;
_pop_marker = _unpack_p + length;
// The explicit length trumps the number of nested fields
// reported by get_num_nested_fields().
@ -452,8 +474,8 @@ pop() {
// Oops, didn't pack or unpack enough values.
_pack_error = true;
} else if (_mode == M_unpack && _push_marker != 0 &&
_unpack_p != _push_marker) {
} else if (_mode == M_unpack && _pop_marker != 0 &&
_unpack_p != _pop_marker) {
// Didn't unpack the right number of values.
_pack_error = true;
}
@ -488,6 +510,7 @@ pop() {
_current_parent = _stack.back()._current_parent;
_current_field_index = _stack.back()._current_field_index;
_push_marker = _stack.back()._push_marker;
_pop_marker = _stack.back()._pop_marker;
_num_nested_fields = (_current_parent == NULL) ? 0 : _current_parent->get_num_nested_fields();
_stack.pop_back();
}
@ -791,6 +814,7 @@ unpack_and_format(ostream &out) {
break;
case PT_field:
case PT_switch:
out << '(';
break;
@ -816,6 +840,7 @@ unpack_and_format(ostream &out) {
break;
case PT_field:
case PT_switch:
out << ')';
break;
@ -829,6 +854,54 @@ unpack_and_format(ostream &out) {
}
}
////////////////////////////////////////////////////////////////////
// Function: DCPacker::handle_switch
// Access: Private
// Description: When we advance past the key field on a switch
// record, we suddenly have more fields available--all
// the appropriate alternate fields in the switch.
//
// This function is called when we detect this
// condition; it switches the _current_parent to the
// appropriate case of the switch record.
////////////////////////////////////////////////////////////////////
void DCPacker::
handle_switch(const DCSwitch *dswitch) {
// First, get the value from the key. This is either found in the
// unpack or the pack data, depending on what mode we're in.
const DCPackerInterface *new_parent = NULL;
if (_mode == M_pack || _mode == M_repack) {
const char *data = _pack_data.get_data();
new_parent = dswitch->apply_switch(data + _push_marker,
_pack_data.get_length() - _push_marker);
} else if (_mode == M_unpack) {
new_parent = dswitch->apply_switch(_unpack_data + _push_marker,
_unpack_p - _push_marker);
}
if (new_parent == (DCPackerInterface *)NULL) {
// This means an invalid value was packed for the key.
_pack_error = true;
return;
}
_last_switch = dswitch;
// Now substitute in the switch case for the previous parent (which
// replaces the switch node itself). This will suddenly make a slew
// of new fields appear.
_current_parent = new_parent;
_num_nested_fields = _current_parent->get_num_nested_fields();
if (_num_nested_fields < 0 ||
_current_field_index < _num_nested_fields) {
_current_field = _current_parent->get_nested_field(_current_field_index);
}
}
////////////////////////////////////////////////////////////////////
// Function: DCPacker::clear
// Access: Private
@ -842,6 +915,9 @@ clear() {
_current_parent = NULL;
_current_field_index = 0;
_num_nested_fields = 0;
_push_marker = 0;
_pop_marker = 0;
_last_switch = NULL;
if (_live_catalog != (DCPackerCatalog::LiveCatalog *)NULL) {
_catalog->release_live_catalog(_live_catalog);

View File

@ -26,6 +26,8 @@
#include "dcPackerCatalog.h"
#include "dcPython.h"
class DCSwitch;
////////////////////////////////////////////////////////////////////
// Class : DCPacker
// Description : This class can be used for packing a series of
@ -64,7 +66,9 @@ PUBLISHED:
INLINE int get_num_nested_fields() const;
INLINE bool more_nested_fields() const;
INLINE const DCPackerInterface *get_current_parent() const;
INLINE const DCPackerInterface *get_current_field() const;
INLINE const DCSwitch *get_last_switch() const;
INLINE DCPackType get_pack_type() const;
void push();
@ -109,6 +113,7 @@ public:
private:
INLINE void advance();
void handle_switch(const DCSwitch *dswitch);
void clear();
void set_unpack_data(const char *unpack_data, size_t unpack_length,
bool owns_unpack_data);
@ -138,6 +143,7 @@ private:
const DCPackerInterface *_current_parent;
int _current_field_index;
size_t _push_marker;
size_t _pop_marker;
};
typedef pvector<StackElement> Stack;
@ -146,12 +152,17 @@ private:
const DCPackerInterface *_current_parent;
int _current_field_index;
// In pack mode, _push_marker marks the beginning of the push record
// (so we can go back and write in the length later). In unpack
// mode, it marks the end of the push record (so we know when we've
// reached the end).
// _push_marker marks the beginning of the push record (so we can go
// back and write in the length later, or figure out the switch
// parameter).
size_t _push_marker;
// _pop_marker is used in unpack mode with certain data structures
// (like dynamic arrays) to mark the end of the push record (so we
// know when we've reached the end). It is zero when it is not in
// use.
size_t _pop_marker;
int _num_nested_fields;
const DCSwitch *_last_switch;
bool _pack_error;
bool _range_error;

View File

@ -42,6 +42,52 @@ get_end(int n) const {
return _live_entries[n]._end;
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerCatalog::LiveCatalog::get_num_entries
// Access: Public
// Description: Returns the number of entries in the catalog.
////////////////////////////////////////////////////////////////////
INLINE int DCPackerCatalog::LiveCatalog::
get_num_entries() const {
return _catalog->get_num_entries();
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerCatalog::LiveCatalog::get_entry
// Access: Public
// Description: Returns the nth entry in the catalog.
////////////////////////////////////////////////////////////////////
INLINE const DCPackerCatalog::Entry &DCPackerCatalog::LiveCatalog::
get_entry(int n) const {
return _catalog->get_entry(n);
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerCatalog::LiveCatalog::find_entry_by_name
// Access: Public
// Description: Returns the index number of the entry with the
// indicated name, or -1 if no entry has the indicated
// name. The return value is suitable for passing to
// get_entry().
////////////////////////////////////////////////////////////////////
int DCPackerCatalog::LiveCatalog::
find_entry_by_name(const string &name) const {
return _catalog->find_entry_by_name(name);
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerCatalog::LiveCatalog::find_entry_by_field
// Access: Public
// Description: Returns the index number of the entry with the
// indicated field, or -1 if no entry has the indicated
// field. The return value is suitable for passing to
// get_entry().
////////////////////////////////////////////////////////////////////
int DCPackerCatalog::LiveCatalog::
find_entry_by_field(const DCPackerInterface *field) const {
return _catalog->find_entry_by_field(field);
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerCatalog::get_num_entries
// Access: Public

View File

@ -19,6 +19,7 @@
#include "dcPackerCatalog.h"
#include "dcPackerInterface.h"
#include "dcPacker.h"
#include "dcSwitch.h"
////////////////////////////////////////////////////////////////////
// Function: DCPackerCatalog::Constructor
@ -31,6 +32,22 @@ DCPackerCatalog(const DCPackerInterface *root) : _root(root) {
_live_catalog = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerCatalog::Copy Constructor
// Access: Private
// Description: The copy constructor is used only internally, in
// update_switch_fields().
////////////////////////////////////////////////////////////////////
DCPackerCatalog::
DCPackerCatalog(const DCPackerCatalog &copy) :
_root(copy._root),
_entries(copy._entries),
_entries_by_name(copy._entries_by_name),
_entries_by_field(copy._entries_by_field)
{
_live_catalog = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerCatalog::Destructor
// Access: Private
@ -42,6 +59,11 @@ DCPackerCatalog::
if (_live_catalog != (LiveCatalog *)NULL) {
delete _live_catalog;
}
SwitchCatalogs::iterator si;
for (si = _switch_catalogs.begin(); si != _switch_catalogs.end(); ++si) {
delete (*si).second;
}
}
////////////////////////////////////////////////////////////////////
@ -95,11 +117,13 @@ find_entry_by_field(const DCPackerInterface *field) const {
const DCPackerCatalog::LiveCatalog *DCPackerCatalog::
get_live_catalog(const char *data, size_t length) const {
if (_live_catalog != (LiveCatalog *)NULL) {
// Return the previously-allocated static catalog.
// Return the previously-allocated live catalog; it will be the
// same as this one since it's based on a fixed-length field.
return _live_catalog;
}
LiveCatalog *live_catalog = new LiveCatalog;
live_catalog->_catalog = this;
live_catalog->_live_entries.reserve(_entries.size());
LiveCatalogEntry zero_entry;
zero_entry._begin = 0;
@ -110,13 +134,14 @@ get_live_catalog(const char *data, size_t length) const {
DCPacker packer;
packer.begin_unpack(data, length, _root);
r_fill_live_catalog(live_catalog, packer);
const DCSwitch *last_switch = NULL;
r_fill_live_catalog(live_catalog, packer, last_switch);
bool okflag = packer.end_unpack();
nassertr(okflag, live_catalog);
if (_root->has_fixed_byte_size()) {
// If our root field has a fixed byte size, then the live catalog
if (_root->has_fixed_structure()) {
// If our root field has a fixed structure, then the live catalog
// will always be the same every time, so we might as well keep
// this one around as an optimization.
((DCPackerCatalog *)this)->_live_catalog = live_catalog;
@ -165,6 +190,50 @@ add_entry(const string &name, const DCPackerInterface *field,
_entries_by_field.insert(EntriesByField::value_type(field, entry_index));
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerCatalog::r_fill_catalog
// Access: Private
// Description: Called by DCPackerInterface to recursively fill up a
// newly-allocated reference catalog. Also called by
// update_switch_fields to append fields to a catalog
// after a DCSwitch node is selected.
////////////////////////////////////////////////////////////////////
void DCPackerCatalog::
r_fill_catalog(const string &name_prefix, const DCPackerInterface *field,
const DCPackerInterface *parent, int field_index) {
string next_name_prefix = name_prefix;
if (!field->get_name().empty()) {
// Record this entry in the catalog.
next_name_prefix += field->get_name();
add_entry(next_name_prefix, field, parent, field_index);
next_name_prefix += ".";
}
const DCSwitch *dswitch = ((DCPackerInterface *)field)->as_switch();
if (dswitch != (DCSwitch *)NULL) {
// If we come upon a DCSwitch while building the catalog, save the
// name_prefix at this point so we'll have it again when we later
// encounter the switch while unpacking a live record (and so we
// can return to this point in the recursion from
// update_switch_fields).
_switch_prefixes[dswitch] = next_name_prefix;
}
// Add any children.
if (field->has_nested_fields()) {
int num_nested = field->get_num_nested_fields();
// It's ok if num_nested is -1.
for (int i = 0; i < num_nested; i++) {
DCPackerInterface *nested = field->get_nested_field(i);
if (nested != (DCPackerInterface *)NULL) {
r_fill_catalog(next_name_prefix, nested, field, i);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerCatalog::r_fill_live_catalog
// Access: Private
@ -173,18 +242,20 @@ add_entry(const string &name, const DCPackerInterface *field,
// appropriate offsets.
////////////////////////////////////////////////////////////////////
void DCPackerCatalog::
r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer) const {
r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer,
const DCSwitch *&last_switch) const {
const DCPackerInterface *current_field = packer.get_current_field();
int field_index = find_entry_by_field(current_field);
int field_index = live_catalog->find_entry_by_field(current_field);
if (field_index >= 0) {
nassertv(field_index < (int)live_catalog->_live_entries.size());
live_catalog->_live_entries[field_index]._begin = packer.get_num_unpacked_bytes();
}
if (packer.has_nested_fields() && packer.get_pack_type() != PT_string) {
packer.push();
while (packer.more_nested_fields()) {
r_fill_live_catalog(live_catalog, packer);
r_fill_live_catalog(live_catalog, packer, last_switch);
}
packer.pop();
@ -195,4 +266,84 @@ r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer) const {
if (field_index >= 0) {
live_catalog->_live_entries[field_index]._end = packer.get_num_unpacked_bytes();
}
if (last_switch != packer.get_last_switch()) {
// We've just invoked a new DCSwitch. That means we must add the
// new fields revealed by the switch to the reference catalog.
last_switch = packer.get_last_switch();
const DCPackerInterface *switch_case = packer.get_current_parent();
nassertv(switch_case != (DCPackerInterface *)NULL);
const DCPackerCatalog *switch_catalog =
live_catalog->_catalog->update_switch_fields(last_switch, switch_case);
live_catalog->_catalog = switch_catalog;
// And we also have to expand the live catalog to hold the new
// entries.
LiveCatalogEntry zero_entry;
zero_entry._begin = 0;
zero_entry._end = 0;
for (size_t i = live_catalog->_live_entries.size();
i < switch_catalog->_entries.size();
i++) {
live_catalog->_live_entries.push_back(zero_entry);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerCatalog::update_switch_fields
// Access: Private
// Description: Returns a new DCPackerCatalog that includes all of
// the fields in this object, with the addition of the
// fields named by switch_case.
//
// This is used to implement switches, which change the
// set of fields they make available according to the
// data in the record, and therefore present a different
// catalog under different circumstances.
//
// This returned pointer is allocated one time for each
// different switch_case instance; if a given same
// switch_case is supplied twice, the same pointer is
// returned both times. The ownership of the returned
// pointer is kept by this object.
////////////////////////////////////////////////////////////////////
const DCPackerCatalog *DCPackerCatalog::
update_switch_fields(const DCSwitch *dswitch,
const DCPackerInterface *switch_case) const {
SwitchCatalogs::const_iterator si = _switch_catalogs.find(switch_case);
if (si != _switch_catalogs.end()) {
return (*si).second;
}
// Look up the name_prefix will we use for all of the fields that
// descend from this switch. This should be stored in this record
// because we must have come across the DCSwitch when building the
// catalog the first time.
SwitchPrefixes::const_iterator pi = _switch_prefixes.find(dswitch);
nassertr(pi != _switch_prefixes.end(), NULL);
string name_prefix = (*pi).second;
// Start by creating a new DCPackerCatalog object that contains all
// of the fields that this one contains.
DCPackerCatalog *switch_catalog = new DCPackerCatalog(*this);
// Now record all of the fields of the switch case in the new
// catalog. We start with the second field of the switch case,
// since the first field will be the switch parameter itself, which
// we would have already recorded the first time around.
int num_nested = switch_case->get_num_nested_fields();
for (int i = 1; i < num_nested; i++) {
DCPackerInterface *nested = switch_case->get_nested_field(i);
if (nested != (DCPackerInterface *)NULL) {
switch_catalog->r_fill_catalog(name_prefix, nested, switch_case, i);
}
}
// Store the newly-generated switch catalog in the record so the
// same pointer can be returned in the future.
((DCPackerCatalog *)this)->_switch_catalogs[switch_case] = switch_catalog;
return switch_catalog;
}

View File

@ -23,6 +23,7 @@
class DCPackerInterface;
class DCPacker;
class DCSwitch;
////////////////////////////////////////////////////////////////////
// Class : DCPackerCatalog
@ -35,6 +36,7 @@ class DCPacker;
class EXPCL_DIRECT DCPackerCatalog {
private:
DCPackerCatalog(const DCPackerInterface *root);
DCPackerCatalog(const DCPackerCatalog &copy);
~DCPackerCatalog();
public:
@ -62,9 +64,16 @@ public:
INLINE size_t get_begin(int n) const;
INLINE size_t get_end(int n) const;
INLINE int get_num_entries() const;
INLINE const Entry &get_entry(int n) const;
INLINE int find_entry_by_name(const string &name) const;
INLINE int find_entry_by_field(const DCPackerInterface *field) const;
private:
typedef pvector<LiveCatalogEntry> LiveEntries;
LiveEntries _live_entries;
const DCPackerCatalog *_catalog;
friend class DCPackerCatalog;
};
@ -75,11 +84,19 @@ public:
const LiveCatalog *get_live_catalog(const char *data, size_t length) const;
void release_live_catalog(const LiveCatalog *live_catalog) const;
private:
void add_entry(const string &name, const DCPackerInterface *field,
const DCPackerInterface *parent, int field_index);
void r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer) const;
void r_fill_catalog(const string &name_prefix, const DCPackerInterface *field,
const DCPackerInterface *parent, int field_index);
void r_fill_live_catalog(LiveCatalog *live_catalog, DCPacker &packer,
const DCSwitch *&last_switch) const;
const DCPackerCatalog *update_switch_fields(const DCSwitch *dswitch,
const DCPackerInterface *switch_case) const;
const DCPackerInterface *_root;
LiveCatalog *_live_catalog;
@ -93,6 +110,12 @@ private:
typedef pmap<const DCPackerInterface *, int> EntriesByField;
EntriesByField _entries_by_field;
typedef pmap<const DCPackerInterface *, DCPackerCatalog *> SwitchCatalogs;
SwitchCatalogs _switch_catalogs;
typedef pmap<const DCSwitch *, string> SwitchPrefixes;
SwitchPrefixes _switch_prefixes;
friend class DCPackerInterface;
};

View File

@ -60,6 +60,24 @@ get_fixed_byte_size() const {
return _fixed_byte_size;
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerInterface::has_fixed_structure
// Access: Public
// Description: Returns true if this field type always has the same
// structure regardless of the data in the stream, or
// false if its structure may vary. This is almost, but
// not quite, the same thing as has_fixed_byte_size.
// The difference is that a DCSwitch may have multiple
// cases all with the same byte size, but they will
// still (presumably) have different structures, in the
// sense that the actual list of fields varies according
// to the live data.
////////////////////////////////////////////////////////////////////
INLINE bool DCPackerInterface::
has_fixed_structure() const {
return _has_fixed_structure;
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerInterface::get_num_length_bytes
// Access: Public

View File

@ -30,6 +30,7 @@ DCPackerInterface(const string &name) :
{
_has_fixed_byte_size = false;
_fixed_byte_size = 0;
_has_fixed_structure = false;
_num_length_bytes = 0;
_has_nested_fields = false;
_num_nested_fields = -1;
@ -47,6 +48,7 @@ DCPackerInterface(const DCPackerInterface &copy) :
_name(copy._name),
_has_fixed_byte_size(copy._has_fixed_byte_size),
_fixed_byte_size(copy._fixed_byte_size),
_has_fixed_structure(copy._has_fixed_structure),
_num_length_bytes(copy._num_length_bytes),
_has_nested_fields(copy._has_nested_fields),
_num_nested_fields(copy._num_nested_fields),
@ -67,6 +69,16 @@ DCPackerInterface::
}
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerInterface::as_switch
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
DCSwitch *DCPackerInterface::
as_switch() {
return (DCSwitch *)NULL;
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerInterface::calc_num_nested_fields
// Access: Public, Virtual
@ -303,39 +315,7 @@ make_catalog() {
for (int i = 0; i < num_nested; i++) {
DCPackerInterface *nested = get_nested_field(i);
if (nested != (DCPackerInterface *)NULL) {
nested->r_fill_catalog(_catalog, "", this, i);
}
}
}
}
////////////////////////////////////////////////////////////////////
// Function: DCPackerInterface::r_fill_catalog
// Access: Private
// Description: Called internally to recursively fill up the new
// DCPackerCatalog object.
////////////////////////////////////////////////////////////////////
void DCPackerInterface::
r_fill_catalog(DCPackerCatalog *catalog, const string &name_prefix,
DCPackerInterface *parent, int field_index) {
string next_name_prefix = name_prefix;
if (!get_name().empty()) {
// Record this entry in the catalog.
next_name_prefix += get_name();
catalog->add_entry(next_name_prefix, this, parent, field_index);
next_name_prefix += ".";
}
// Add any children.
if (has_nested_fields()) {
int num_nested = get_num_nested_fields();
// As above, it's ok if num_nested is -1.
for (int i = 0; i < num_nested; i++) {
DCPackerInterface *nested = get_nested_field(i);
if (nested != (DCPackerInterface *)NULL) {
nested->r_fill_catalog(catalog, next_name_prefix, this, i);
_catalog->r_fill_catalog("", nested, this, i);
}
}
}

View File

@ -22,6 +22,7 @@
#include "dcbase.h"
#include "dcSubatomicType.h"
class DCSwitch;
class DCPackData;
class DCPackerCatalog;
@ -48,6 +49,7 @@ enum DCPackType {
PT_array,
PT_field,
PT_class,
PT_switch,
};
END_PUBLISH
@ -70,10 +72,13 @@ public:
PUBLISHED:
INLINE const string &get_name() const;
virtual DCSwitch *as_switch();
public:
INLINE void set_name(const string &name);
INLINE bool has_fixed_byte_size() const;
INLINE size_t get_fixed_byte_size() const;
INLINE bool has_fixed_structure() const;
INLINE size_t get_num_length_bytes() const;
INLINE bool has_nested_fields() const;
@ -152,6 +157,7 @@ protected:
string _name;
bool _has_fixed_byte_size;
size_t _fixed_byte_size;
bool _has_fixed_structure;
size_t _num_length_bytes;
bool _has_nested_fields;
int _num_nested_fields;
@ -159,9 +165,6 @@ protected:
private:
void make_catalog();
void r_fill_catalog(DCPackerCatalog *catalog, const string &name_prefix,
DCPackerInterface *parent, int field_index);
DCPackerCatalog *_catalog;
};

View File

@ -31,6 +31,7 @@ DCParameter::
DCParameter() {
_typedef = NULL;
_has_fixed_byte_size = false;
_has_fixed_structure = false;
_num_nested_fields = -1;
}
@ -139,9 +140,12 @@ output(ostream &out, bool brief) const {
// Description:
////////////////////////////////////////////////////////////////////
void DCParameter::
write(ostream &out, bool brief, int indent_level) const {
write(ostream &out, bool, int indent_level) const {
// we must always output the name when the parameter occurs by
// itself within a class, so we ignore brief and pass false up to
// output().
indent(out, indent_level);
output(out, brief);
output(out, false);
out << ";\n";
}
@ -168,4 +172,6 @@ output_typedef_name(ostream &out, const string &prename, const string &name,
////////////////////////////////////////////////////////////////////
void DCParameter::
generate_hash(HashGenerator &) const {
// We specifically don't call up to DCField::generate_hash(), since
// the parameter name is not actually significant to the hash.
}

View File

@ -8,6 +8,7 @@
#include "dcParserDefs.h"
#include "dcFile.h"
#include "dcClass.h"
#include "dcSwitch.h"
#include "dcAtomicField.h"
#include "dcMolecularField.h"
#include "dcClassParameter.h"
@ -27,6 +28,7 @@
static DCFile *dc_file = (DCFile *)NULL;
static DCClass *current_class = (DCClass *)NULL;
static DCSwitch *current_switch = (DCSwitch *)NULL;
static DCAtomicField *current_atomic = (DCAtomicField *)NULL;
static DCMolecularField *current_molecular = (DCMolecularField *)NULL;
static DCAtomicField::ElementType atomic_element(new DCSimpleParameter(ST_invalid));
@ -71,6 +73,8 @@ dc_cleanup_parser() {
%token KW_FROM
%token KW_IMPORT
%token KW_TYPEDEF
%token KW_SWITCH
%token KW_CASE
%token KW_INT8
%token KW_INT16
@ -111,6 +115,11 @@ dc_cleanup_parser() {
%type <u.flag> kw_struct_or_kw_dclass
%type <u.dclass> dclass_name
%type <u.atomic> atomic_name
%type <u.field> dclass_field
%type <u.field> switch_field
%type <u.field> atomic_field
%type <u.field> molecular_field
%type <u.field> switch
%type <u.subatomic> type_token
%type <u.parameter> type_name
%type <u.parameter> type_definition
@ -120,6 +129,7 @@ dc_cleanup_parser() {
%type <u.parameter> parameter_definition
%type <str> import_identifier
%type <str> slash_identifier
%type <str> optional_name
%type <u.real> number
%%
@ -263,13 +273,7 @@ base_list:
dclass_fields:
empty
| dclass_fields ';'
| dclass_fields atomic_field
| dclass_fields molecular_field
| dclass_fields unnamed_parameter ';'
{
current_class->add_field($2);
}
| dclass_fields named_parameter
| dclass_fields dclass_field
{
if (!current_class->add_field($2)) {
yyerror("Duplicate field name: " + $2->get_name());
@ -277,32 +281,30 @@ dclass_fields:
}
;
dclass_field:
atomic_field
| molecular_field
| switch
| unnamed_parameter ';'
{
$$ = $1;
}
| named_parameter
{
$$ = $1;
}
;
atomic_field:
IDENTIFIER '('
{
current_atomic = new DCAtomicField($1);
if (!current_class->add_field(current_atomic)) {
yyerror("Duplicate field name: " + current_atomic->get_name());
}
}
parameter_list ')' atomic_flags
;
atomic_name:
IDENTIFIER
{
DCField *field = current_class->get_field_by_name($1);
$$ = (DCAtomicField *)NULL;
if (field == (DCField *)NULL) {
yyerror("Unknown field: " + $1);
} else {
$$ = field->as_atomic_field();
if ($$ == (DCAtomicField *)NULL) {
yyerror("Not an atomic field: " + $1);
}
}
$$ = current_atomic;
}
;
;
parameter_list:
empty
@ -794,13 +796,29 @@ molecular_field:
IDENTIFIER ':'
{
current_molecular = new DCMolecularField($1);
if (!current_class->add_field(current_molecular)) {
yyerror("Duplicate field name: " + current_molecular->get_name());
}
}
molecular_atom_list
{
$$ = current_molecular;
}
;
atomic_name:
IDENTIFIER
{
DCField *field = current_class->get_field_by_name($1);
$$ = (DCAtomicField *)NULL;
if (field == (DCField *)NULL) {
yyerror("Unknown field: " + $1);
} else {
$$ = field->as_atomic_field();
if ($$ == (DCAtomicField *)NULL) {
yyerror("Not an atomic field: " + $1);
}
}
}
;
molecular_atom_list:
atomic_name
{
@ -821,6 +839,70 @@ molecular_atom_list:
}
;
optional_name:
empty
{
$$ = "";
}
| IDENTIFIER
;
switch:
KW_SWITCH optional_name '(' parameter ')' '{'
{
$$ = current_switch;
current_switch = new DCSwitch($2, $4);
}
switch_fields '}'
{
$$ = current_switch;
current_switch = (DCSwitch *)$<u.parameter>7;
}
;
switch_fields:
empty
| switch_fields ';'
| switch_fields switch_case
| switch_fields switch_field
{
if (current_switch->get_num_cases() == 0) {
yyerror("case declaration required before first element");
} else if (!current_switch->add_field($2)) {
yyerror("Duplicate field name: " + $2->get_name());
}
}
;
switch_case:
KW_CASE
{
current_packer = &default_packer;
current_packer->begin_pack(current_switch->get_switch_parameter());
}
parameter_value ':'
{
if (!current_packer->end_pack()) {
yyerror("Invalid value for switch parameter");
} else {
current_switch->add_case(current_packer->get_string());
}
}
;
switch_field:
switch
| unnamed_parameter ';'
{
$$ = $1;
}
| named_parameter
{
$$ = $1;
}
;
empty:
;

View File

@ -24,6 +24,7 @@
class DCFile;
class DCClass;
class DCField;
class DCAtomicField;
class DCParameter;
class DCPacker;
@ -48,6 +49,7 @@ public:
double real;
bool flag;
DCClass *dclass;
DCField *field;
DCAtomicField *atomic;
DCSubatomicType subatomic;
DCParameter *parameter;

View File

@ -166,6 +166,7 @@ DCSimpleParameter(DCSubatomicType type, int divisor) :
case ST_invalid:
break;
}
_has_fixed_structure = _has_fixed_byte_size;
set_divisor(divisor);
@ -412,9 +413,11 @@ set_range(const DCDoubleRange &range) {
_num_length_bytes = 0;
_has_fixed_byte_size = true;
_fixed_byte_size = _uint_range.get_one_value();
_has_fixed_structure = true;
} else {
_num_length_bytes = 2;
_has_fixed_byte_size = false;
_has_fixed_structure = false;
}
break;
@ -431,9 +434,11 @@ set_range(const DCDoubleRange &range) {
_num_length_bytes = 0;
_has_fixed_byte_size = true;
_fixed_byte_size = _uint_range.get_one_value();
_has_fixed_structure = true;
} else {
_num_length_bytes = 4;
_has_fixed_byte_size = false;
_has_fixed_structure = false;
}
break;

View File

@ -0,0 +1,397 @@
// Filename: dcSwitch.cxx
// Created by: drose (23Jun04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#include "dcSwitch.h"
#include "dcField.h"
#include "dcindent.h"
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::Constructor
// Access: Public
// Description: The switch_parameter must be recently allocated via
// new; it will be deleted via delete when the switch
// destructs.
////////////////////////////////////////////////////////////////////
DCSwitch::
DCSwitch(const string &name, DCParameter *switch_parameter) :
DCField(name),
_switch_parameter(switch_parameter)
{
_has_fixed_byte_size = _switch_parameter->has_fixed_byte_size();
_fixed_byte_size = _switch_parameter->get_fixed_byte_size();
_has_fixed_structure = false;
// The DCSwitch presents just one nested field initially, which is
// the switch parameter. When we pack or unpack that, the DCPacker
// calls apply_switch(), which returns a new record that presents
// the remaining nested fields.
_has_nested_fields = true;
_num_nested_fields = 1;
_pack_type = PT_switch;
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::Destructor
// Access: Public, Virtual
// Description:
////////////////////////////////////////////////////////////////////
DCSwitch::
~DCSwitch() {
delete _switch_parameter;
Cases::iterator ci;
for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
const SwitchCase *dcase = (*ci);
delete dcase;
}
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::as_switch
// Access: Published, Virtual
// Description:
////////////////////////////////////////////////////////////////////
DCSwitch *DCSwitch::
as_switch() {
return this;
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::get_switch_parameter
// Access: Published
// Description: Returns the key parameter on which the switch is
// based. The value of this parameter in the record
// determines which one of the several cases within the
// switch will be used.
////////////////////////////////////////////////////////////////////
DCParameter *DCSwitch::
get_switch_parameter() const {
return _switch_parameter;
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::get_num_cases
// Access: Published
// Description: Returns the number of different cases within the
// switch. The legal values for case_index range from 0
// to get_num_cases() - 1.
////////////////////////////////////////////////////////////////////
int DCSwitch::
get_num_cases() const {
return _cases.size();
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::get_case_by_value
// Access: Published
// Description: Returns the index number of the case with the
// indicated packed value, or -1 if no case has this
// value.
////////////////////////////////////////////////////////////////////
int DCSwitch::
get_case_by_value(const string &case_value) const {
CasesByValue::const_iterator vi;
vi = _cases_by_value.find(case_value);
if (vi != _cases_by_value.end()) {
return (*vi).second;
}
return -1;
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::get_value
// Access: Published
// Description: Returns the packed value associated with the
// indicated case.
////////////////////////////////////////////////////////////////////
string DCSwitch::
get_value(int case_index) const {
nassertr(case_index >= 0 && case_index < (int)_cases.size(), string());
return _cases[case_index]->_value;
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::get_num_fields
// Access: Published
// Description: Returns the number of fields in the indicated case.
////////////////////////////////////////////////////////////////////
int DCSwitch::
get_num_fields(int case_index) const {
nassertr(case_index >= 0 && case_index < (int)_cases.size(), 0);
return _cases[case_index]->_fields.size();
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::get_num_fields
// Access: Published
// Description: Returns the nth field in the indicated case.
////////////////////////////////////////////////////////////////////
DCField *DCSwitch::
get_field(int case_index, int n) const {
nassertr(case_index >= 0 && case_index < (int)_cases.size(), NULL);
nassertr(n >= 0 && n < (int)_cases[case_index]->_fields.size(), NULL);
return _cases[case_index]->_fields[n];
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::get_field_by_name
// Access: Published
// Description: Returns the field with the given name from the
// indicated case, or NULL if no field has this name.
////////////////////////////////////////////////////////////////////
DCField *DCSwitch::
get_field_by_name(int case_index, const string &name) const {
nassertr(case_index >= 0 && case_index < (int)_cases.size(), NULL);
const FieldsByName &fields_by_name = _cases[case_index]->_fields_by_name;
FieldsByName::const_iterator ni;
ni = fields_by_name.find(name);
if (ni != fields_by_name.end()) {
return (*ni).second;
}
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::get_nested_field
// Access: Public, Virtual
// Description: Returns the DCPackerInterface object that represents
// the nth nested field. This may return NULL if there
// is no such field (but it shouldn't do this if n is in
// the range 0 <= n < get_num_nested_fields()).
////////////////////////////////////////////////////////////////////
DCPackerInterface *DCSwitch::
get_nested_field(int) const {
// The DCSwitch presents just one nested field initially, which is
// the switch parameter. When we pack or unpack that, the DCPacker
// calls apply_switch(), which returns a new record that presents
// the remaining nested fields.
return _switch_parameter;
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::add_case
// Access: Public
// Description: Adds a new case to the switch with the indicated
// value, and returns the new case_index. If the value
// has already been used for another case, returns -1.
// This is normally called only by the parser.
////////////////////////////////////////////////////////////////////
int DCSwitch::
add_case(const string &value) {
int case_index = (int)_cases.size();
if (!_cases_by_value.insert(CasesByValue::value_type(value, case_index)).second) {
return -1;
}
SwitchCase *dcase = new SwitchCase(_name, value);
dcase->add_field(_switch_parameter);
_cases.push_back(dcase);
return case_index;
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::add_field
// Access: Public
// Description: Adds a field to the case most recently added via
// add_case(). Returns true if successful, false if the
// field duplicates a field already named within this
// case. It is an error to call this before calling
// add_case(). This is normally called only by the
// parser.
////////////////////////////////////////////////////////////////////
bool DCSwitch::
add_field(DCField *field) {
nassertr(!_cases.empty(), false);
if (!_cases.back()->add_field(field)) {
return false;
}
// See if we now have a fixed byte size for the overall switch.
// This will be true only if all of the individual cases have the
// same fixed byte size.
_fixed_byte_size = _cases.back()->get_fixed_byte_size();
Cases::const_iterator ci;
for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
const SwitchCase *dcase = (*ci);
if (!dcase->has_fixed_byte_size() ||
dcase->get_fixed_byte_size() != _fixed_byte_size) {
// Nope, we have a variable byte size.
_has_fixed_byte_size = false;
return true;
}
}
// Sonofagun, we do have a fixed byte size.
_has_fixed_byte_size = true;
return true;
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::apply_switch
// Access: Public
// Description: Returns the DCPackerInterface that presents the
// alternative fields for the case indicated by the
// given packed value string, or NULL if the value
// string does not match one of the expected cases.
////////////////////////////////////////////////////////////////////
const DCPackerInterface *DCSwitch::
apply_switch(const char *value_data, size_t length) const {
CasesByValue::const_iterator vi;
vi = _cases_by_value.find(string(value_data, length));
if (vi != _cases_by_value.end()) {
return _cases[(*vi).second];
}
// Invalid value.
return NULL;
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::write
// Access: Public, Virtual
// Description: Generates a parseable description of the object to
// the indicated output stream.
////////////////////////////////////////////////////////////////////
void DCSwitch::
write(ostream &out, bool brief, int indent_level) const {
indent(out, indent_level)
<< "switch";
if (!_name.empty()) {
out << " " << _name;
}
out << " (";
_switch_parameter->output(out, brief);
out << ") {\n";
Cases::const_iterator ci;
for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
const SwitchCase *dcase = (*ci);
indent(out, indent_level)
<< "case " << _switch_parameter->format_data(dcase->_value) << ":\n";
Fields::const_iterator fi;
if (!dcase->_fields.empty()) {
fi = dcase->_fields.begin();
++fi;
while (fi != dcase->_fields.end()) {
(*fi)->write(out, brief, indent_level + 2);
++fi;
}
}
}
indent(out, indent_level)
<< "};\n";
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::generate_hash
// Access: Public, Virtual
// Description: Accumulates the properties of this switch into the
// hash.
////////////////////////////////////////////////////////////////////
void DCSwitch::
generate_hash(HashGenerator &hashgen) const {
DCField::generate_hash(hashgen);
_switch_parameter->generate_hash(hashgen);
hashgen.add_int(_cases.size());
Cases::const_iterator ci;
for (ci = _cases.begin(); ci != _cases.end(); ++ci) {
const SwitchCase *dcase = (*ci);
hashgen.add_string(dcase->_value);
hashgen.add_int(dcase->_fields.size());
Fields::const_iterator fi;
for (fi = dcase->_fields.begin(); fi != dcase->_fields.end(); ++fi) {
(*fi)->generate_hash(hashgen);
}
}
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::SwitchCase::Constructor
// Access: Public
// Description:
////////////////////////////////////////////////////////////////////
DCSwitch::SwitchCase::
SwitchCase(const string &name, const string &value) :
DCPackerInterface(name),
_value(value)
{
_has_nested_fields = true;
_num_nested_fields = 0;
_pack_type = PT_switch;
_has_fixed_byte_size = true;
_fixed_byte_size = 0;
_has_fixed_structure = true;
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::SwitchCase::get_nested_field
// Access: Public, Virtual
// Description: Returns the DCPackerInterface object that represents
// the nth nested field. This may return NULL if there
// is no such field (but it shouldn't do this if n is in
// the range 0 <= n < get_num_nested_fields()).
////////////////////////////////////////////////////////////////////
DCPackerInterface *DCSwitch::SwitchCase::
get_nested_field(int n) const {
nassertr(n >= 0 && n < (int)_fields.size(), NULL);
return _fields[n];
}
////////////////////////////////////////////////////////////////////
// Function: DCSwitch::SwitchCase::add_field
// Access: Public
// Description: Adds a field to this case. Returns true if
// successful, false if the field duplicates a field
// already named within this case. This is normally
// called only by the parser.
////////////////////////////////////////////////////////////////////
bool DCSwitch::SwitchCase::
add_field(DCField *field) {
bool inserted = _fields_by_name.insert
(FieldsByName::value_type(field->get_name(), field)).second;
if (!inserted) {
return false;
}
_fields.push_back(field);
_num_nested_fields = (int)_fields.size();
// See if we still have a fixed byte size.
if (_has_fixed_byte_size) {
_has_fixed_byte_size = field->has_fixed_byte_size();
_fixed_byte_size += field->get_fixed_byte_size();
}
if (_has_fixed_structure) {
_has_fixed_structure = field->has_fixed_structure();
}
return true;
}

View File

@ -0,0 +1,86 @@
// Filename: dcSwitch.h
// Created by: drose (23Jun04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license. You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////
#ifndef DCSWITCH_H
#define DCSWITCH_H
#include "dcbase.h"
#include "dcField.h"
////////////////////////////////////////////////////////////////////
// Class : DCSwitch
// Description : This represents a switch statement, which can appear
// inside a class body and represents two or more
// alternative unpacking schemes based on the first
// field read.
////////////////////////////////////////////////////////////////////
class EXPCL_DIRECT DCSwitch : public DCField {
public:
DCSwitch(const string &name, DCParameter *switch_parameter);
virtual ~DCSwitch();
PUBLISHED:
virtual DCSwitch *as_switch();
DCParameter *get_switch_parameter() const;
int get_num_cases() const;
int get_case_by_value(const string &case_value) const;
string get_value(int case_index) const;
int get_num_fields(int case_index) const;
DCField *get_field(int case_index, int n) const;
DCField *get_field_by_name(int case_index, const string &name) const;
public:
virtual DCPackerInterface *get_nested_field(int n) const;
int add_case(const string &value);
bool add_field(DCField *field);
const DCPackerInterface *apply_switch(const char *value_data, size_t length) const;
virtual void write(ostream &out, bool brief, int indent_level) const;
virtual void generate_hash(HashGenerator &hashgen) const;
private:
DCParameter *_switch_parameter;
typedef pvector<DCField *> Fields;
typedef pmap<string, DCField *> FieldsByName;
class SwitchCase : public DCPackerInterface {
public:
SwitchCase(const string &name, const string &value);
virtual DCPackerInterface *get_nested_field(int n) const;
bool add_field(DCField *field);
string _value;
Fields _fields;
FieldsByName _fields_by_name;
};
typedef pvector<SwitchCase *> Cases;
Cases _cases;
typedef pmap<string, int> CasesByValue;
CasesByValue _cases_by_value;
};
#endif

View File

@ -7,4 +7,5 @@
#include "dcFile.cxx"
#include "dcMolecularField.cxx"
#include "dcSubatomicType.cxx"
#include "dcSwitch.cxx"
#include "dcTypedef.cxx"