00001 /* 00002 * Copyright (C) 2003-2009 Olivier Boudeville 00003 * 00004 * This file is part of the Ceylan library. 00005 * 00006 * The Ceylan library is free software: you can redistribute it and/or modify 00007 * it under the terms of either the GNU Lesser General Public License or 00008 * the GNU General Public License, as they are published by the Free Software 00009 * Foundation, either version 3 of these Licenses, or (at your option) 00010 * any later version. 00011 * 00012 * The Ceylan library is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00015 * GNU Lesser General Public License and the GNU General Public License 00016 * for more details. 00017 * 00018 * You should have received a copy of the GNU Lesser General Public 00019 * License and the GNU General Public License along with the Ceylan library. 00020 * If not, see <http://www.gnu.org/licenses/>. 00021 * 00022 * Author: Olivier Boudeville (olivier.boudeville@esperide.com) 00023 * 00024 */ 00025 00026 00027 #include "CeylanMarshaller.h" 00028 00029 #include "CeylanLogPlug.h" // for LogPlug 00030 #include "CeylanOperators.h" // for toString 00031 #include "CeylanInputOutputStream.h" // for InputOutputStream 00032 #include "CeylanMemoryStream.h" // for MemoryStream 00033 00034 00035 00036 using std::string ; 00037 00038 using namespace Ceylan ; 00039 using namespace Ceylan::Log ; 00040 using namespace Ceylan::System ; 00041 using namespace Ceylan::Middleware ; 00042 00043 00044 00045 #ifdef CEYLAN_USES_CONFIG_H 00046 #include "CeylanConfig.h" // for configure-time settings 00047 #endif // CEYLAN_USES_CONFIG_H 00048 00049 00050 00051 MarshallException::MarshallException( const string & message ) : 00052 MiddlewareException( message ) 00053 { 00054 00055 } 00056 00057 00058 00059 MarshallException::~MarshallException() throw() 00060 { 00061 00062 } 00063 00064 00065 00066 00067 DecodeException::DecodeException( const string & message ) : 00068 MarshallException( message ) 00069 { 00070 00071 } 00072 00073 00074 00075 DecodeException::~DecodeException() throw() 00076 { 00077 00078 } 00079 00080 00081 00082 00083 EncodeException::EncodeException( const string & message ) : 00084 MarshallException( message ) 00085 { 00086 00087 } 00088 00089 00090 00091 EncodeException::~EncodeException() throw() 00092 { 00093 00094 } 00095 00096 00097 00098 00099 Marshaller::Marshaller( System::InputOutputStream & lowerLevelStream, 00100 System::Size bufferedSize ) : 00101 TextDisplayable(), 00102 _lowerLevelStream( & lowerLevelStream ), 00103 _bufferStream( 0 ) 00104 { 00105 00106 if ( bufferedSize != 0 ) 00107 _bufferStream = new MemoryStream( bufferedSize /* bytes */ ) ; 00108 00109 } 00110 00111 00112 00113 Marshaller::~Marshaller() throw() 00114 { 00115 00116 // _lowerLevelStream not owned. 00117 00118 if ( isBuffered() ) 00119 delete _bufferStream ; 00120 00121 } 00122 00123 00124 00125 System::Size Marshaller::retrieveData( System::Size requestedSize ) 00126 { 00127 00128 00129 #if CEYLAN_DEBUG_MARSHALLERS 00130 LogPlug::trace( "Marshaller::retrieveData: " 00131 + Ceylan::toString( 00132 static_cast<Ceylan::Uint32>( requestedSize ) ) 00133 + " byte(s) requested." ) ; 00134 #endif // CEYLAN_DEBUG_MARSHALLERS 00135 00136 if ( ! isBuffered() ) 00137 throw DecodeException( "Marshaller::retrieveData: " 00138 "no buffer available for this operation." ) ; 00139 00140 if ( requestedSize > _bufferStream->getSize() ) 00141 throw DecodeException( "Marshaller::retrieveData: " 00142 "buffer too small, requesting " 00143 + Ceylan::toString( 00144 static_cast<Ceylan::Uint32>( requestedSize ) ) 00145 + " bytes whereas size of buffer is " 00146 + Ceylan::toString( 00147 static_cast<Ceylan::Uint32>( _bufferStream->getSize() ) ) ) ; 00148 00149 /* 00150 * Enough total size, but does requested size currently fit in buffer ? 00151 * 00152 */ 00153 00154 if ( requestedSize > 00155 ( _bufferStream->getSize() - _bufferStream->getBlockLength() ) ) 00156 throw DecodeException( "Marshaller::retrieveData: " 00157 "no enough space left in buffer, requesting " 00158 + Ceylan::toString( static_cast<Ceylan::Uint32>( requestedSize ) ) 00159 + " bytes whereas free space in buffer is " 00160 + Ceylan::toString( 00161 static_cast<Ceylan::Uint32>( 00162 _bufferStream->getSize() 00163 - _bufferStream->getBlockLength() ) ) ) ; 00164 00165 /* 00166 * Here we know there is enough free space, we have to make sure that 00167 * the requested size is available in one chunk, not split by the end of 00168 * buffer: 00169 * 00170 * (we always request the full free size, even if requestedSize is not zero, 00171 * as it cannot lead to asking for fewer bytes than requested) 00172 * 00173 */ 00174 00175 Size targetFreeSize = _bufferStream->getSizeOfNextFreeChunk() ; 00176 00177 if ( requestedSize > targetFreeSize ) 00178 { 00179 00180 /* 00181 * We have here to move first the block of already read data to the 00182 * beginning of buffer: 00183 * 00184 */ 00185 _bufferStream->moveFilledBlockToBufferStart() ; 00186 00187 } 00188 00189 00190 Size readSize ; 00191 00192 try 00193 { 00194 00195 #if CEYLAN_DEBUG_MARSHALLERS 00196 LogPlug::debug( "Marshaller::retrieveData: will try to read " 00197 + Ceylan::toString( 00198 static_cast<Ceylan::Uint32>( targetFreeSize ) ) 00199 + " actual byte(s)." ) ; 00200 #endif // CEYLAN_DEBUG_MARSHALLERS 00201 00202 // Avoid useless buffer copy thanks to in-place writing: 00203 readSize = _lowerLevelStream->read( 00204 _bufferStream->getAddressOfNextFreeChunk(), targetFreeSize ) ; 00205 00206 #if CEYLAN_DEBUG_MARSHALLERS 00207 LogPlug::debug( "Marshaller::retrieveData: read " 00208 + Ceylan::toString( 00209 static_cast<Ceylan::Uint32>( readSize ) ) 00210 + " actual byte(s)." ) ; 00211 #endif // CEYLAN_DEBUG_MARSHALLERS 00212 00213 } 00214 catch( const InputStream::ReadFailedException & e ) 00215 { 00216 throw DecodeException( "Marshaller::retrieveData: " + e.toString() ) ; 00217 } 00218 00219 00220 if ( readSize == 0 ) 00221 LogPlug::error( "Marshaller::retrieveData: read zero byte " 00222 "from lower-level stream (abnormal, as should be selected)." ) ; 00223 00224 try 00225 { 00226 00227 _bufferStream->increaseFilledBlockOf( readSize ) ; 00228 00229 } 00230 catch( const MemoryStream::MemoryStreamException & e ) 00231 { 00232 throw DecodeException( "Marshaller::retrieveData has a bug: " 00233 + e.toString() ) ; 00234 } 00235 00236 00237 #if CEYLAN_DEBUG_MARSHALLERS 00238 LogPlug::trace( "Marshaller::retrieveData: " 00239 + Ceylan::toString( static_cast<Ceylan::Uint32>( readSize ) ) 00240 + " byte(s) read, " + Ceylan::toString( 00241 static_cast<Ceylan::Uint32>( _bufferStream->getBlockLength() ) ) 00242 + " byte(s) available now." ) ; 00243 #endif // CEYLAN_DEBUG_MARSHALLERS 00244 00245 return _bufferStream->getBlockLength() ; 00246 00247 } 00248 00249 00250 00251 const string Marshaller::toString( Ceylan::VerbosityLevels level ) const 00252 { 00253 00254 string res = "Marshaller using as underlying lower-level stream " 00255 + _lowerLevelStream->toString( level ) ; 00256 00257 if ( _bufferStream != 0 ) 00258 res += ". This marshaller has following internal buffered stream: " 00259 + _bufferStream->toString( level ) ; 00260 else 00261 res += ". This marshaller has no internal buffered stream" ; 00262 00263 return res ; 00264 00265 } 00266