// $Id$ #include "tao/AnyTypeCode/Null_RefCount_Policy.h" #include "tao/AnyTypeCode/Alias_TypeCode_Static.h" #include "tao/AnyTypeCode/Value_TypeCode_Static.h" #include "tao/AnyTypeCode/TypeCode_Constants.h" #include "tao/Valuetype/ValueBase.h" #include "tao/Valuetype/ValueFactory.h" #include "tao/CDR.h" #include "tao/ORB.h" #include "tao/ORB_Core.h" #include "tao/debug.h" #include "ace/OS_NS_string.h" #include "ace/CORBA_macros.h" #if !defined (__ACE_INLINE__) # include "tao/Valuetype/ValueBase.inl" #endif /* ! __ACE_INLINE__ */ ACE_RCSID (Valuetype, ValueBase, "$Id$") TAO_BEGIN_VERSIONED_NAMESPACE_DECL // Static operations in namespace CORBA. void CORBA::add_ref (CORBA::ValueBase *val) { if (val) { val->_add_ref (); } } void CORBA::remove_ref (CORBA::ValueBase *val) { if (val) { val->_remove_ref (); } } // *********************************************************************** TAO_ChunkInfo::TAO_ChunkInfo (CORBA::Boolean do_chunking, CORBA::Long init_level) : chunking_(do_chunking), value_nesting_level_(init_level), chunk_size_pos_ (0), length_to_chunk_octets_pos_ (0), chunk_octets_end_pos_ (0) { } CORBA::ValueBase::ValueBase (void) : is_truncatable_(0), chunking_(0) { } CORBA::ValueBase::ValueBase (const ValueBase& val) : is_truncatable_ (val.is_truncatable_), chunking_ (val.chunking_) { } CORBA::ValueBase::~ValueBase (void) { } CORBA::ValueBase* CORBA::ValueBase::_downcast (CORBA::ValueBase *vt) { // Every vt is a CORBA::ValueBase :-). return vt; } void CORBA::ValueBase::_tao_any_destructor (void *x) { CORBA::ValueBase *tmp = static_cast (x); CORBA::remove_ref (tmp); } // OBV marshaling in principle: // _tao_marshal () is called from the CDR operator<< () // to marshal a valuetype. To marshal the state // it calls (virtual) _tao_marshal_v () (IDL generated) on itself // which 'jumps' to the most derived valuetype class. This function // further calls (inline) _tao_marshal_state, which is generated from // IDL too and does the marshaling of state members and base classes // (before, if any) actually. // Fragmentation (chunking) needs some cooperation with the CDR stream. // It needs to keep track of the state we're in: // (outside chunk, beginning of chunk - no data, inside chunk and // the nesting level of valuetypes. (The chunks itself are not nested.)) // (see CORBA 2.3 GIOP 15.3.4) // %! yet much to do ... look for +++ ! // 1. Is 'this' yet marshalled ? (->1a) // If not then mark 'this' as marshalled. (->2) +++ // Or is it null ? (write null_ref and return ok) // 1a. Put indirection and return successfull. // 2. if (chunking) and we are in a chunk (look in strm), // end the chunk by writing its length at its start. // This is the responsibility of the CDR stream. // But if nothing is writtern in this chunk yet, // we want to overwrite the place of the dummy blocksize-tag // with our . // Increase the nesting level of valuetypes. // 3. Build , which states if chunking is used // and if type information ((list of) repository id(s)) // is provided. The latter is necessary if the formal_type_id // is unequal the 'true derived' type of this object. // 4. Marshal type information. // 5. if (chunking) let room for a blocksize-tag. (i.e. write Long) // 6. Now marshal state members. (%! Problem when state is empty // and chunked encoding is used.) // 7. if (chunking) let strm overwrite the last blocksize tag // with its concrete value. // 8. if (chunking) write an end tag, or (optimization) let the CDR // care for collecting all end tags of nested values (e.g. linked // list), so that only one end tag at all must be written. CORBA::Boolean CORBA::ValueBase::_tao_marshal (TAO_OutputCDR &strm, const CORBA::ValueBase *this_, ptrdiff_t formal_type_id) { if ( ! _tao_write_special_value (strm, this_)) { return _tao_write_value (strm, this_, formal_type_id); } return true; } CORBA::Boolean CORBA::ValueBase::_tao_unmarshal (TAO_InputCDR &strm, CORBA::ValueBase *&new_object) { // This is for the special case only that one unmarshals in order // to assign the newly created object directly to a ValueBase pointer. // Implementation is like a specialized one (in TC.cpp, if T.idl is source). // basically do: // ValueBase::_tao_unmarshal_pre () // (Gets factory or possible a null or an existing object. // Then the job is done. On an existing (backreferenced) object // do a cast and a type check) // new_object = factory->create_for_unmarshal () // (with apropriate cast) // new_object->_tao_unmarshal_v () // new_object->_tao_unmarshal_post () CORBA::Boolean const retval = CORBA::ValueBase::_tao_unmarshal_pre (strm, new_object, 0); if (!retval) { return false; } if (new_object != 0) { if (!new_object->_tao_unmarshal_v (strm)) { return false; } } return retval; } CORBA::Boolean CORBA::ValueBase::_tao_unmarshal_pre (TAO_InputCDR &strm, CORBA::ValueBase *&valuetype, const char * const repo_id) { // Value factories are reference counted, when we get a new value factory // from the ORB, its reference count is increased. CORBA::ValueFactory_var factory; // %! yet much to do ... look for +++ ! // 1. Get the (else it may be or ). // Is object yet unmarshalled ( is set) ? (->1a) // Is given ? Set 0 and return ok. // 1a. Lookup the real address in memory, which should be aligned +++ // to CORBA::ValueBase. Its possible at this point that worse // type mismatch gets by undetected, if the CDR stream fakes. // So the type should be checked ... +++ // 2. Now at this point it must be a (error else). // if (chunking) check that any last chunk ends with matching // size. If not raise marshal exception. // Increase the nesting level of valuetypes. // 3. if (chunking) read and record the blocksize-tag. // 4. Unmarshal type information and lookup factory. // If no type information is given in the CDR encoding, as told // from the , then use the repository id parameter // (it _must_ be right). CORBA::Long valuetag; Repository_Id_List ids; if (! strm.read_long (valuetag)) { return false; } if (TAO_OBV_GIOP_Flags::is_indirection_tag (valuetag)) { //@@TODO: read indirection value. if (TAO_debug_level > 0) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("TAO does not currently ") ACE_TEXT ("support valuetype indirecton\n"))); } return false; } else if (TAO_OBV_GIOP_Flags::is_null_ref (valuetag)) { // null reference is unmarshalled. valuetype = 0; return true; } else if (TAO_OBV_GIOP_Flags::has_single_type_info (valuetag)) { if (! _tao_read_repository_id(strm, ids)) { return false; } } else if (TAO_OBV_GIOP_Flags::has_list_type_info (valuetag)) { if (! _tao_read_repository_id_list(strm, ids)) { return false; } } else if (TAO_OBV_GIOP_Flags::has_no_type_info (valuetag)) { ids.push_back (repo_id); } else { if (TAO_debug_level > 0) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("TAO (%P|%t) unknown value tag: %x\n"), valuetag)); } return false; } TAO_ORB_Core *orb_core = strm.orb_core (); if (orb_core == 0) { orb_core = TAO_ORB_Core_instance (); if (TAO_debug_level > 0) { ACE_DEBUG ((LM_WARNING, "TAO (%P|%t) WARNING: extracting valuetype using " "default ORB_Core\n")); } } CORBA::Boolean require_truncation = false; CORBA::Boolean const chunking = TAO_OBV_GIOP_Flags::is_chunked (valuetag); CORBA::ULong const num_ids = ids.size (); // Find the registered factory for this unmarshalling valuetype. If any // factory for the valuetype in its truncatable derivation hierarchy // is registered, the factory is used to create value for unmarshalling. for (CORBA::ULong i = 0; i < num_ids; ++i) { factory = orb_core->orb ()->lookup_value_factory (ids[i].c_str ()); if (factory.in() != 0) { if (i != 0 && chunking) { require_truncation = true; } break; } } if (factory.in() == 0) { if (TAO_debug_level > 0) { ACE_ERROR ((LM_ERROR, ACE_TEXT ("TAO (%P|%t) OBV factory ") ACE_TEXT ("is null, id = %s\n"), repo_id)); } ACE_THROW_RETURN (CORBA::MARSHAL (CORBA::OMGVMCID | 1, CORBA::COMPLETED_MAYBE), false); } valuetype = factory->create_for_unmarshal (); if (require_truncation) { valuetype->truncation_hook (); } if (valuetype == 0) { return false; // %! except.? } valuetype->chunking_ = chunking; return true; } CORBA::Boolean CORBA::ValueBase::_tao_unmarshal_post (TAO_InputCDR &) { // (... called from T::_tao_unmarshal) // 7. if (chunking) check the last blocksize tag for correct value. +++ // And if we're gonna to truncate, skip all the state of the more // derived classes. (But it might need to be accessed again, // if there are embedded objects which are referenced later // in this CDR encoding stream.) // 8. if (chunking) there must be some end tag. Let the CDR stream deal // with this (and decrease the nesting level of valuetypes). // Also the CDR stream must check for eventually outstanding end tags // at the end of the stream which have to cause a marshal // exception there. return true; } CORBA::Boolean CORBA::ValueBase::_tao_validate_box_type (TAO_InputCDR &strm, const char * const repo_id_expected, CORBA::Boolean & null_object) { CORBA::Long value_tag; // todo: no handling for indirection yet if (!strm.read_long (value_tag)) { return false; } if (TAO_OBV_GIOP_Flags::is_null_ref (value_tag)) { // ok, null reference unmarshaled null_object = true; return true; } null_object = false; if (!TAO_OBV_GIOP_Flags::is_value_tag (value_tag)) { ACE_DEBUG ((LM_DEBUG, ACE_TEXT ("!CORBA::ValueBase::_tao_validate_box_type ") ACE_TEXT ("not value_tag\n"))); return false; } if (TAO_OBV_GIOP_Flags::has_codebase_url (value_tag)) { // Demarshal the codebase url (but we won't be using it). CORBA::String_var codebase_url; if (!strm.read_string (codebase_url.inout ())) { return false; } } if (TAO_OBV_GIOP_Flags::has_no_type_info (value_tag)) { // No type information so assume it is the correct type. return true; } if (TAO_OBV_GIOP_Flags::has_single_type_info (value_tag)) { // Demarshal the repository id and check if it is the expected one. CORBA::String_var repo_id_stream; if (!strm.read_string (repo_id_stream.inout ())) { return false; } if (!ACE_OS::strcmp (repo_id_stream.in (), repo_id_expected)) { // Repository ids matched as expected return true; } } if (TAO_OBV_GIOP_Flags::has_list_type_info (value_tag)) { // Don't know how to handle a repository id list. It does not // make sense for a value box anyway. return false; } return false; } // =================== methods for chunking ==================== CORBA::Boolean CORBA::ValueBase::_tao_write_special_value (TAO_OutputCDR &strm, const CORBA::ValueBase *value) { // If the 'value' is null then write the null value to the stream. if (value == 0) { return strm.write_long (TAO_OBV_GIOP_Flags::Null_tag); } //@@TODO: Check if the value is already written to stream. If it is then // put indirection and return successful, otherwise does nothing // and returns false. else { // value not handled by this method - other code will write the value. return false; } } CORBA::Boolean CORBA::ValueBase::_tao_write_value (TAO_OutputCDR &strm, const CORBA::ValueBase * value, ptrdiff_t formal_type_id) { if (! value->_tao_write_value_header (strm, formal_type_id)) { return false; } if (! value->_tao_marshal_v (strm)) { return false; } return true; } CORBA::Boolean CORBA::ValueBase::_tao_write_value_header (TAO_OutputCDR &strm, ptrdiff_t formal_type_id) const { #ifdef TAO_HAS_OPTIMIZED_VALUETYPE_MARSHALING // this case allows TAO to avoid marshaling the typeID for values // where the actual type matches the formal type (ie not a derived // type). // // I would much prefer that there be a way to have a -ORB option to // control this behavior, but for now there is no reference to the // ORB Core available during marshaling (there is during unmarshaling) // and no other way to communicate such configuration values. CORBA::Boolean const is_formal_type = this->_tao_match_formal_type (formal_type_id); #else // Unfortunately, all versions of tao prior to TAO 1.5.2 did not // support unmarshaling of valuetypes that did not explicitly // marshal the type id. At least it is benign to always encode the // typecode value, even if it can be a little verbose. CORBA::Boolean const is_formal_type = false; ACE_UNUSED_ARG (formal_type_id); #endif /* TAO_HAS_OPTIMIZED_VALUETYPE_MARSHALING */ // Get the list of repository ids for this valuetype. Repository_Id_List repository_ids; this->_tao_obv_truncatable_repo_ids (repository_ids); CORBA::Long const num_ids = static_cast (repository_ids.size ()); // Build , which states if chunking is used // and if type information ((list of) repository id(s)) // is provided. The latter is necessary if the formal_type_id // is unequal the 'true derived' type of this object. CORBA::Long valuetag = TAO_OBV_GIOP_Flags::Value_tag_base; // Truncatable value type, must use chunking and list all repository // ids in its "truncatable" derivation hierarchy. if (this->is_truncatable_ || this->chunking_) valuetag |= TAO_OBV_GIOP_Flags::Chunking_tag_sigbits; if (!is_formal_type || this->is_truncatable_) valuetag |= TAO_OBV_GIOP_Flags::Type_info_single; if (num_ids > 1) { valuetag |= TAO_OBV_GIOP_Flags::Type_info_list; } if (! strm.write_long (valuetag) // Write . || (num_ids > 1 && !strm.write_long (num_ids))) // Write . { return false; } #ifndef TAO_HAS_OPTIMIMIZED_VALUETYPE_MARSHALING if (this->is_truncatable_ || !is_formal_type /* Always evaluates to true in the !TAO_HAS_OPTIMIMIZED_VALUETYPE_MARSHALING case */ || num_ids > 1) { #endif /* !TAO_HAS_OPTIMIMIZED_VALUETYPE_MARSHALING */ // Marshal type information. for (CORBA::Long i = 0; i < num_ids; ++i ) { if (! strm.write_string (repository_ids[i])) { return false; } } #ifndef TAO_HAS_OPTIMIMIZED_VALUETYPE_MARSHALING } #endif /* !TAO_HAS_OPTIMIMIZED_VALUETYPE_MARSHALING */ return true; } // this method is called by the IDL generated _tao_marshal_state() method. CORBA::Boolean TAO_ChunkInfo::start_chunk(TAO_OutputCDR &strm) { // If chunking, reserve the space for the chunk size of next chunk // and increase the nesting level. if (this->chunking_) { if (! reserve_chunk_size(strm)) { return false; } this->value_nesting_level_ ++; } return true; } // this method is called by the IDL generated _tao_marshal_state() method. CORBA::Boolean TAO_ChunkInfo::end_chunk(TAO_OutputCDR &strm) { if (this->chunking_) { // Write actual chunk size at the reserved chunk size place. if (! this->write_previous_chunk_size(strm)) { return false; } // Write an end tag which is negation of value_nesting_level_. if (! strm.write_long(- this->value_nesting_level_)) { return false; } // -- this->value_nesting_level_; if ( -- this->value_nesting_level_ == 0 ) { // ending chunk for outermost value this->chunking_ = false; } } return true; } CORBA::Boolean TAO_ChunkInfo::write_previous_chunk_size(TAO_OutputCDR &strm) { if (this->chunk_size_pos_ != 0) { // Calculate the chunk size. CORBA::Long const chunk_size = strm.total_length () - this->length_to_chunk_octets_pos_; // This should not happen since this is called in end_chunk() and // the idl generated code always have the matched start_chunk() and // end_chunk() pair. There is always data written to the stream between // the start_chunk() and end_chunk() calls. if (chunk_size == 0) { return false; } // Write the actual chunk size to the reserved chunk size position // in the stream. if (!strm.replace (chunk_size, this->chunk_size_pos_)) { return false; } // We finish writing the actual chunk size, now we need reset the state. this->chunk_size_pos_ = 0; this->length_to_chunk_octets_pos_ = 0; } return true; } CORBA::Boolean TAO_ChunkInfo::reserve_chunk_size(TAO_OutputCDR &strm) { // This is called in the start_chunk(). // Reserve the chunk size the first time the start_chunk () is called // if there are several start_chunk () called continuously without // calling end_chunk (). This could happen in the _tao_marshal_state() // in the most derived valuetype. if (this->chunk_size_pos_ == 0) { // Align the wr_ptr before we reserve the space for chunk size. strm.align_write_ptr (ACE_CDR::LONG_SIZE); // Remember begin of the chunk (at chunk size position) that is needed // when we write back actual chunk size to the stream. this->chunk_size_pos_ = strm.current ()->wr_ptr (); // Insert four bytes here as a place-holder, we need to go back // later and write the actual size. if (! strm.write_long (0)) { return false; } // Remember length before writing chunk data. This is used to calculate // the actual size of the chunk. this->length_to_chunk_octets_pos_ = strm.total_length (); } return true; } CORBA::Boolean TAO_ChunkInfo::handle_chunking (TAO_InputCDR &strm) { if (!this->chunking_) { return true; } char* the_rd_ptr = strm.start()->rd_ptr (); //This case could happen if a handle_chunking() reads a chunk size //and then calls the handle_chunking() again without reading the chunk data. //The handle_chunking() called continuously without reading the chunk data //only happens at the beginning of _tao_unmarshal_state() in a valuetype //that has parents. if (the_rd_ptr < this->chunk_octets_end_pos_) { this->value_nesting_level_ ++; return true; } //Safty check if reading is out of range of current chunk. if (this->chunk_octets_end_pos_ != 0 && the_rd_ptr > this->chunk_octets_end_pos_) { return false; } // Read a long value that might be an endtag, the chunk size or the value tag // of the nested valuetype. CORBA::Long tag; if (!strm.read_long (tag)) { return false; } if (tag < 0) { // tag is an end tag if (-tag > this->value_nesting_level_) { ACE_ERROR_RETURN ((LM_ERROR, ACE_TEXT ("%P|%t) received end tag ") ACE_TEXT ("%d > value_nesting_level %d\n"), -tag, this->value_nesting_level_), false); } this->value_nesting_level_ = - tag; this->value_nesting_level_--; this->chunk_octets_end_pos_ = 0; // Continue reading so that we can read the outmost endtag. This // would simplify the implementation in the derived valuetype. if (this->value_nesting_level_ > 0) { this->handle_chunking(strm); } } else if (tag < TAO_OBV_GIOP_Flags::Value_tag_base) { // Read the chunk size of another chunk. this->chunk_octets_end_pos_ = strm.rd_ptr () + tag; this->value_nesting_level_ ++; } else // (tag >= 0x7fffff00) { // This should not happen since the valuetag of the nested values // are always unmarshalled in the ValueBase::_tao_unmarshal_pre(). return false; } return true; } CORBA::Boolean TAO_ChunkInfo::skip_chunks (TAO_InputCDR &strm) { if (!this->chunking_) { return true; } // This function is called after reading data of the truncated parent and // skips the remaining chunks until the outmost endtag (-1). // The tag read here is suppoused to be an endtag. CORBA::Long tag; if (!strm.read_long(tag)) { return false; } // end of the whole valuetype. if (tag == -1) { return true; } else if (tag < 0) { // continue skip the chunk. return this->skip_chunks (strm); } else if (tag < TAO_OBV_GIOP_Flags::Value_tag_base) { // Read the chunk size and move forward to skip the data. ACE_Message_Block* current = const_cast(strm.start ()); current->rd_ptr (tag); return this->skip_chunks (strm); } else return false; } CORBA::Boolean CORBA::ValueBase::_tao_read_repository_id_list (ACE_InputCDR& strm, Repository_Id_List& ids) { CORBA::Long num_ids; if (!strm.read_long (num_ids)) { return false; } if (num_ids == TAO_OBV_GIOP_Flags::Indirection_tag) { //@@TODO: read indirection repository ids and return true. return false; } else { //@@TODO: map repository id for indirection for (CORBA::Long i = 0; i < num_ids; ++i) { if (!_tao_read_repository_id (strm,ids)) { return false; } } } return true; } CORBA::Boolean CORBA::ValueBase::_tao_read_repository_id (ACE_InputCDR& strm, Repository_Id_List& ids) { ACE_CString id; CORBA::ULong length = 0; CORBA::Long offset = 0; size_t buffer_size = strm.length(); if (!strm.read_ulong (length)) { return false; } // 'length' may not be the repo id length - it could be the // FFFFFFF indirection marker instead. If it is an indirection marker, we // get the offset following the indirection marker, otherwise we can follow // the same logic using the offset to simply rewind to the start of length // and re-read the length as part of the string if (TAO_OBV_GIOP_Flags::is_indirection_tag (length)) { // Read the negative byte offset if (!strm.read_long (offset) || offset >= 0) { return false; } buffer_size = -(offset) + sizeof (CORBA::Long); } // Cribbed from tc_demarshal_indirection in Typecode_CDR_Extraction.cpp TAO_InputCDR indir_stream (strm.rd_ptr () + offset - sizeof (CORBA::Long), buffer_size, strm.byte_order ()); if (!indir_stream.good_bit ()) { return false; } indir_stream.read_string (id); // Since the ID is always read from the indirection cdr we have to skip // the main CDR forward if we were in fact reading from the current // location and not rewinding back some offset. if (offset == 0) { strm.skip_bytes (length); } ids.push_back (id); return true; } void CORBA::ValueBase::truncation_hook () { throw ::CORBA::INTERNAL (); } // ================== Typecode initializations ================== namespace TAO { namespace TypeCode { char const tc_value_base_id[] = "IDL:omg.org/CORBA/ValueBase:1.0"; char const tc_value_base_name[] = "ValueBase"; Value const *, TAO::Null_RefCount_Policy> tc_ValueBase (CORBA::tk_value, tc_value_base_id, tc_value_base_name, CORBA::VM_NONE, &CORBA::_tc_null, 0, // Field array 0); // Field count char const tc_event_base_id[] = "IDL:omg.org/CORBA/EventBase:1.0"; char const tc_event_base_name[] = "EventBase"; Value const *, TAO::Null_RefCount_Policy> tc_EventBase (CORBA::tk_event, tc_event_base_id, tc_event_base_name, CORBA::VM_NONE, &CORBA::_tc_null, 0, // Field array 0); // Field count } } namespace CORBA { TypeCode_ptr const _tc_ValueBase = &TAO::TypeCode::tc_ValueBase; TypeCode_ptr const _tc_EventBase = &TAO::TypeCode::tc_EventBase; } // member functions for CORBA::DefaultValueRefCountBase ============ // destructor CORBA::DefaultValueRefCountBase::~DefaultValueRefCountBase (void) { } void CORBA::DefaultValueRefCountBase::_add_ref (void) { this->_tao_add_ref (); } void CORBA::DefaultValueRefCountBase::_remove_ref (void) { this->_tao_remove_ref (); } CORBA::ULong CORBA::DefaultValueRefCountBase::_refcount_value (void) { return this->_tao_refcount_value (); } // =========================================================== CORBA::DefaultValueRefCountBase::DefaultValueRefCountBase (void) : refcount_ (1) { } // Copy constructor CORBA::DefaultValueRefCountBase::DefaultValueRefCountBase (const DefaultValueRefCountBase& rhs) : ValueBase (rhs), refcount_ (1) { } void CORBA::DefaultValueRefCountBase::_tao_add_ref (void) { ++this->refcount_; } void CORBA::DefaultValueRefCountBase::_tao_remove_ref (void) { CORBA::ULong const new_count = --this->refcount_; if (new_count == 0) delete this; } CORBA::ULong CORBA::DefaultValueRefCountBase::_tao_refcount_value (void) { return this->refcount_.value (); } // =========================================================== CORBA::Boolean operator<< (TAO_OutputCDR &strm, CORBA::ValueBase *_tao_valuetype) { return CORBA::ValueBase::_tao_marshal ( strm, _tao_valuetype, reinterpret_cast (&CORBA::ValueBase::_downcast) ); } CORBA::Boolean operator>> (TAO_InputCDR &strm, CORBA::ValueBase *&_tao_valuetype) { return CORBA::ValueBase::_tao_unmarshal (strm, _tao_valuetype); } // =============== Template Specializations ===================== namespace TAO { void Value_Traits::add_ref ( CORBA::ValueBase *p) { CORBA::add_ref (p); } void Value_Traits::remove_ref ( CORBA::ValueBase * p) { CORBA::remove_ref (p); } void Value_Traits::release ( CORBA::ValueBase * p) { CORBA::remove_ref (p); } } TAO_END_VERSIONED_NAMESPACE_DECL