// Filename: dcArrayParameter.cxx // Created by: drose (17Jun04) // //////////////////////////////////////////////////////////////////// // // 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 "dcArrayParameter.h" #include "dcSimpleParameter.h" #include "dcClassParameter.h" #include "hashGenerator.h" //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// DCArrayParameter:: DCArrayParameter(DCParameter *element_type, const DCUnsignedIntRange &size) : _element_type(element_type), _array_size_range(size) { set_name(_element_type->get_name()); _element_type->set_name(string()); _array_size = -1; if (_array_size_range.has_one_value()) { _array_size = _array_size_range.get_one_value(); } else { _has_range_limits = true; } 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 // variable size. _num_length_bytes = 2; } if (_element_type->has_range_limits()) { _has_range_limits = true; } if (_element_type->has_default_value()) { _has_default_value = true; } _has_nested_fields = true; _num_nested_fields = _array_size; _pack_type = PT_array; DCSimpleParameter *simple_type = _element_type->as_simple_parameter(); if (simple_type != (DCSimpleParameter *)NULL) { if (simple_type->get_type() == ST_char) { // We make a special case for char[] arrays: these we format as // a string. (It will still accept an array of ints packed into // it.) We don't make this special case for uint8[] or int8[] // arrays, although we will accept a string packed in for them. _pack_type = PT_string; } } } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::Copy Constructor // Access: Public // Description: //////////////////////////////////////////////////////////////////// DCArrayParameter:: DCArrayParameter(const DCArrayParameter ©) : DCParameter(copy), _element_type(copy._element_type->make_copy()), _array_size(copy._array_size), _array_size_range(copy._array_size_range) { } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::Destructor // Access: Public, Virtual // Description: //////////////////////////////////////////////////////////////////// DCArrayParameter:: ~DCArrayParameter() { delete _element_type; } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::as_array_parameter // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// DCArrayParameter *DCArrayParameter:: as_array_parameter() { return this; } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::as_array_parameter // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// const DCArrayParameter *DCArrayParameter:: as_array_parameter() const { return this; } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::make_copy // Access: Published, Virtual // Description: //////////////////////////////////////////////////////////////////// DCParameter *DCArrayParameter:: make_copy() const { return new DCArrayParameter(*this); } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::is_valid // Access: Published, Virtual // Description: Returns false if the type is an invalid type // (e.g. declared from an undefined typedef), true if // it is valid. //////////////////////////////////////////////////////////////////// bool DCArrayParameter:: is_valid() const { return _element_type->is_valid(); } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::get_element_type // Access: Published // Description: Returns the type of the individual elements of this // array. //////////////////////////////////////////////////////////////////// DCParameter *DCArrayParameter:: get_element_type() const { return _element_type; } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::get_array_size // Access: Published // Description: Returns the fixed number of elements in this array, // or -1 if the array may contain a variable number of // elements. //////////////////////////////////////////////////////////////////// int DCArrayParameter:: get_array_size() const { return _array_size; } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::append_array_specification // Access: Public, Virtual // Description: Returns the type represented by this_type[size]. // // In the case of a DCArrayParameter, this means it // modifies the current type to append the array // specification on the innermost type, and returns this // same pointer again. //////////////////////////////////////////////////////////////////// DCParameter *DCArrayParameter:: append_array_specification(const DCUnsignedIntRange &size) { if (get_typedef() != (DCTypedef *)NULL) { // If this was a typedef, wrap it directly. return new DCArrayParameter(this, size); } // Otherwise, the brackets get applied to the inner type. _element_type = _element_type->append_array_specification(size); return this; } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::calc_num_nested_fields // Access: Public, Virtual // Description: This flavor of get_num_nested_fields is used during // unpacking. It returns the number of nested fields to // expect, given a certain length in bytes (as read from // the get_num_length_bytes() stored in the stream on the // pack). This will only be called if // get_num_length_bytes() returns nonzero. //////////////////////////////////////////////////////////////////// int DCArrayParameter:: calc_num_nested_fields(size_t length_bytes) const { if (_element_type->has_fixed_byte_size()) { return length_bytes / _element_type->get_fixed_byte_size(); } return -1; } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::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 *DCArrayParameter:: get_nested_field(int) const { return _element_type; } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::validate_num_nested_fields // Access: Public, Virtual // Description: After a number of fields have been packed via push() // .. pack_*() .. pop(), this is called to confirm that // the number of nested fields that were added is valid // for this type. This is primarily useful for array // types with dynamic ranges that can't validate the // number of fields any other way. //////////////////////////////////////////////////////////////////// bool DCArrayParameter:: validate_num_nested_fields(int num_nested_fields) const { bool range_error = false; _array_size_range.validate(num_nested_fields, range_error); return !range_error; } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::output_instance // Access: Public, Virtual // Description: Formats the parameter in the C++-like dc syntax as a // typename and identifier. //////////////////////////////////////////////////////////////////// void DCArrayParameter:: output_instance(ostream &out, bool brief, const string &prename, const string &name, const string &postname) const { if (get_typedef() != (DCTypedef *)NULL) { output_typedef_name(out, brief, prename, name, postname); } else { ostringstream strm; strm << "["; _array_size_range.output(strm); strm << "]"; _element_type->output_instance(out, brief, prename, name, postname + strm.str()); } } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::generate_hash // Access: Public, Virtual // Description: Accumulates the properties of this type into the // hash. //////////////////////////////////////////////////////////////////// void DCArrayParameter:: generate_hash(HashGenerator &hashgen) const { DCParameter::generate_hash(hashgen); _element_type->generate_hash(hashgen); _array_size_range.generate_hash(hashgen); } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::pack_string // Access: Published, Virtual // Description: Packs the indicated numeric or string value into the // stream. //////////////////////////////////////////////////////////////////// void DCArrayParameter:: pack_string(DCPackData &pack_data, const string &value, bool &pack_error, bool &range_error) const { // We can only pack a string if the array element type is char or // int8. DCSimpleParameter *simple_type = _element_type->as_simple_parameter(); if (simple_type == (DCSimpleParameter *)NULL) { pack_error = true; return; } size_t string_length = value.length(); switch (simple_type->get_type()) { case ST_char: case ST_uint8: case ST_int8: _array_size_range.validate(string_length, range_error); if (_num_length_bytes != 0) { nassertv(_num_length_bytes == 2); do_pack_uint16(pack_data.get_write_pointer(2), string_length); } pack_data.append_data(value.data(), string_length); break; default: pack_error = true; } } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::pack_default_value // Access: Public, Virtual // Description: Packs the arrayParameter's specified default value (or a // sensible default if no value is specified) into the // stream. Returns true if the default value is packed, // false if the arrayParameter doesn't know how to pack its // default value. //////////////////////////////////////////////////////////////////// bool DCArrayParameter:: pack_default_value(DCPackData &pack_data, bool &pack_error) const { // We only want to call up if the DCField can pack the value // immediately--we don't trust the DCField to generate the default // value (since it doesn't know how large the minimum length array // is). if (_has_default_value && !_default_value_stale) { return DCField::pack_default_value(pack_data, pack_error); } // If a default value is not specified for a variable-length array, // the default is the minimum array. unsigned int minimum_length = 0; if (!_array_size_range.is_empty()) { minimum_length = _array_size_range.get_min(0); } DCPacker packer; packer.begin_pack(this); packer.push(); for (unsigned int i = 0; i < minimum_length; i++) { packer.pack_default_value(); } packer.pop(); if (!packer.end_pack()) { pack_error = true; } else { pack_data.append_data(packer.get_data(), packer.get_length()); } return true; } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::unpack_string // Access: Public, Virtual // Description: Unpacks the current numeric or string value from the // stream. //////////////////////////////////////////////////////////////////// void DCArrayParameter:: unpack_string(const char *data, size_t length, size_t &p, string &value, bool &pack_error, bool &range_error) const { // We can only unpack a string if the array element type is char or // int8. DCSimpleParameter *simple_type = _element_type->as_simple_parameter(); if (simple_type == (DCSimpleParameter *)NULL) { pack_error = true; return; } size_t string_length; switch (simple_type->get_type()) { case ST_char: case ST_uint8: case ST_int8: if (_num_length_bytes != 0) { string_length = do_unpack_uint16(data + p); p += 2; } else { nassertv(_array_size >= 0); string_length = _array_size; } if (p + string_length > length) { pack_error = true; return; } value.assign(data + p, string_length); p += string_length; break; default: pack_error = true; } } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::do_check_match // Access: Protected, Virtual // Description: Returns true if the other interface is bitwise the // same as this one--that is, a uint32 only matches a // uint32, etc. Names of components, and range limits, // are not compared. //////////////////////////////////////////////////////////////////// bool DCArrayParameter:: do_check_match(const DCPackerInterface *other) const { return other->do_check_match_array_parameter(this); } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::do_check_match_simple_parameter // Access: Protected, Virtual // Description: Returns true if this field matches the indicated // simple parameter, false otherwise. //////////////////////////////////////////////////////////////////// bool DCArrayParameter:: do_check_match_simple_parameter(const DCSimpleParameter *other) const { return ((const DCPackerInterface *)other)->do_check_match_array_parameter(this); } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::do_check_match_class_parameter // Access: Protected, Virtual // Description: Returns true if this field matches the indicated // class parameter, false otherwise. //////////////////////////////////////////////////////////////////// bool DCArrayParameter:: do_check_match_class_parameter(const DCClassParameter *other) const { return ((const DCPackerInterface *)other)->do_check_match_array_parameter(this); } //////////////////////////////////////////////////////////////////// // Function: DCArrayParameter::do_check_match_array_parameter // Access: Protected, Virtual // Description: Returns true if this field matches the indicated // array parameter, false otherwise. //////////////////////////////////////////////////////////////////// bool DCArrayParameter:: do_check_match_array_parameter(const DCArrayParameter *other) const { if (_array_size != other->_array_size) { return false; } return _element_type->check_match(other->_element_type); }