KDECore
kfilterdev.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000, 2006 David Faure <faure@kde.org> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License version 2 as published by the Free Software Foundation. 00007 00008 This library is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 Library General Public License for more details. 00012 00013 You should have received a copy of the GNU Library General Public License 00014 along with this library; see the file COPYING.LIB. If not, write to 00015 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00016 Boston, MA 02110-1301, USA. 00017 */ 00018 00019 #include "kfilterdev.h" 00020 #include "kfilterbase.h" 00021 #include <kdebug.h> 00022 #include <stdio.h> // for EOF 00023 #include <stdlib.h> 00024 #include <assert.h> 00025 #include <QtCore/QFile> 00026 00027 #define BUFFER_SIZE 8*1024 00028 00029 class KFilterDev::Private 00030 { 00031 public: 00032 Private() : bNeedHeader(true), bSkipHeaders(false), 00033 autoDeleteFilterBase(false), bOpenedUnderlyingDevice(false), 00034 bIgnoreData(false){} 00035 bool bNeedHeader; 00036 bool bSkipHeaders; 00037 bool autoDeleteFilterBase; 00038 bool bOpenedUnderlyingDevice; 00039 bool bIgnoreData; 00040 QByteArray buffer; // Used as 'input buffer' when reading, as 'output buffer' when writing 00041 QByteArray origFileName; 00042 KFilterBase::Result result; 00043 KFilterBase *filter; 00044 }; 00045 00046 KFilterDev::KFilterDev( KFilterBase * _filter, bool autoDeleteFilterBase ) 00047 : d(new Private) 00048 { 00049 assert(_filter); 00050 d->filter = _filter; 00051 d->autoDeleteFilterBase = autoDeleteFilterBase; 00052 } 00053 00054 KFilterDev::~KFilterDev() 00055 { 00056 if ( isOpen() ) 00057 close(); 00058 if ( d->autoDeleteFilterBase ) 00059 delete d->filter; 00060 delete d; 00061 } 00062 00063 //static 00064 QIODevice * KFilterDev::deviceForFile( const QString & fileName, const QString & mimetype, 00065 bool forceFilter ) 00066 { 00067 QFile * f = new QFile( fileName ); 00068 KFilterBase * base = mimetype.isEmpty() ? KFilterBase::findFilterByFileName( fileName ) 00069 : KFilterBase::findFilterByMimeType( mimetype ); 00070 if ( base ) 00071 { 00072 base->setDevice(f, true); 00073 return new KFilterDev(base, true); 00074 } 00075 if(!forceFilter) 00076 return f; 00077 else 00078 { 00079 delete f; 00080 return 0L; 00081 } 00082 } 00083 00084 QIODevice * KFilterDev::device( QIODevice* inDevice, const QString & mimetype, bool autoDeleteInDevice ) 00085 { 00086 if (inDevice==0) 00087 return 0; 00088 KFilterBase * base = KFilterBase::findFilterByMimeType(mimetype); 00089 if ( base ) 00090 { 00091 base->setDevice(inDevice, autoDeleteInDevice); 00092 return new KFilterDev(base, true /* auto-delete "base" */); 00093 } 00094 return 0; 00095 } 00096 00097 bool KFilterDev::open( QIODevice::OpenMode mode ) 00098 { 00099 if (isOpen()) { 00100 kWarning(7005) << "already open"; 00101 return true; // QFile returns false, but well, the device -is- open... 00102 } 00103 //kDebug(7005) << mode; 00104 if ( mode == QIODevice::ReadOnly ) 00105 { 00106 d->buffer.resize(0); 00107 } 00108 else 00109 { 00110 d->buffer.resize( BUFFER_SIZE ); 00111 d->filter->setOutBuffer( d->buffer.data(), d->buffer.size() ); 00112 } 00113 d->bNeedHeader = !d->bSkipHeaders; 00114 d->filter->setFilterFlags(d->bSkipHeaders ? KFilterBase::NoHeaders : KFilterBase::WithHeaders); 00115 d->filter->init( mode ); 00116 d->bOpenedUnderlyingDevice = !d->filter->device()->isOpen(); 00117 bool ret = d->bOpenedUnderlyingDevice ? d->filter->device()->open( mode ) : true; 00118 d->result = KFilterBase::Ok; 00119 00120 if ( !ret ) 00121 kWarning(7005) << "KFilterDev::open: Couldn't open underlying device"; 00122 else 00123 setOpenMode( mode ); 00124 00125 return ret; 00126 } 00127 00128 void KFilterDev::close() 00129 { 00130 if ( !isOpen() ) 00131 return; 00132 if ( d->filter->mode() == QIODevice::WriteOnly ) 00133 write( 0L, 0 ); // finish writing 00134 //kDebug(7005) << "Calling terminate()."; 00135 00136 d->filter->terminate(); 00137 if ( d->bOpenedUnderlyingDevice ) 00138 d->filter->device()->close(); 00139 setOpenMode( QIODevice::NotOpen ); 00140 } 00141 00142 bool KFilterDev::seek( qint64 pos ) 00143 { 00144 qint64 ioIndex = this->pos(); // current position 00145 if ( ioIndex == pos ) 00146 return true; 00147 00148 //kDebug(7005) << "seek(" << pos << ") called"; 00149 00150 Q_ASSERT ( d->filter->mode() == QIODevice::ReadOnly ); 00151 00152 if ( pos == 0 ) 00153 { 00154 // We can forget about the cached data 00155 d->bNeedHeader = !d->bSkipHeaders; 00156 d->result = KFilterBase::Ok; 00157 d->filter->setInBuffer(0L,0); 00158 d->filter->reset(); 00159 QIODevice::seek(pos); 00160 return d->filter->device()->reset(); 00161 } 00162 00163 if ( ioIndex > pos ) // we can start from here 00164 pos = pos - ioIndex; 00165 else 00166 { 00167 // we have to start from 0 ! Ugly and slow, but better than the previous 00168 // solution (KTarGz was allocating everything into memory) 00169 if (!seek(0)) // recursive 00170 return false; 00171 } 00172 00173 //kDebug(7005) << "reading " << pos << " dummy bytes"; 00174 QByteArray dummy( qMin( pos, (qint64)3*BUFFER_SIZE ), 0 ); 00175 d->bIgnoreData = true; 00176 bool result = ( read( dummy.data(), pos ) == pos ); 00177 d->bIgnoreData = false; 00178 QIODevice::seek(pos); 00179 return result; 00180 } 00181 00182 bool KFilterDev::atEnd() const 00183 { 00184 return (d->result == KFilterBase::End) 00185 && QIODevice::atEnd() // take QIODevice's internal buffer into account 00186 && d->filter->device()->atEnd(); 00187 } 00188 00189 qint64 KFilterDev::readData( char *data, qint64 maxlen ) 00190 { 00191 Q_ASSERT ( d->filter->mode() == QIODevice::ReadOnly ); 00192 //kDebug(7005) << "maxlen=" << maxlen; 00193 KFilterBase* filter = d->filter; 00194 00195 uint dataReceived = 0; 00196 00197 // We came to the end of the stream 00198 if ( d->result == KFilterBase::End ) 00199 return dataReceived; 00200 00201 // If we had an error, return -1. 00202 if ( d->result != KFilterBase::Ok ) 00203 return -1; 00204 00205 00206 qint64 outBufferSize; 00207 if ( d->bIgnoreData ) 00208 { 00209 outBufferSize = qMin( maxlen, (qint64)3*BUFFER_SIZE ); 00210 } 00211 else 00212 { 00213 outBufferSize = maxlen; 00214 } 00215 outBufferSize -= dataReceived; 00216 qint64 availOut = outBufferSize; 00217 filter->setOutBuffer( data, outBufferSize ); 00218 00219 while ( dataReceived < maxlen ) 00220 { 00221 if (filter->inBufferEmpty()) 00222 { 00223 // Not sure about the best size to set there. 00224 // For sure, it should be bigger than the header size (see comment in readHeader) 00225 d->buffer.resize( BUFFER_SIZE ); 00226 // Request data from underlying device 00227 int size = filter->device()->read( d->buffer.data(), 00228 d->buffer.size() ); 00229 //kDebug(7005) << "got" << size << "bytes from device"; 00230 if (size) { 00231 filter->setInBuffer( d->buffer.data(), size ); 00232 } else { 00233 // Not enough data available in underlying device for now 00234 break; 00235 } 00236 } 00237 if (d->bNeedHeader) 00238 { 00239 (void) filter->readHeader(); 00240 d->bNeedHeader = false; 00241 } 00242 00243 d->result = filter->uncompress(); 00244 00245 if (d->result == KFilterBase::Error) 00246 { 00247 kWarning(7005) << "KFilterDev: Error when uncompressing data"; 00248 break; 00249 } 00250 00251 // We got that much data since the last time we went here 00252 uint outReceived = availOut - filter->outBufferAvailable(); 00253 //kDebug(7005) << "avail_out = " << filter->outBufferAvailable() << " result=" << d->result << " outReceived=" << outReceived; 00254 if( availOut < (uint)filter->outBufferAvailable() ) 00255 kWarning(7005) << " last availOut " << availOut << " smaller than new avail_out=" << filter->outBufferAvailable() << " !"; 00256 00257 dataReceived += outReceived; 00258 if ( !d->bIgnoreData ) // Move on in the output buffer 00259 { 00260 data += outReceived; 00261 availOut = maxlen - dataReceived; 00262 } 00263 else if ( maxlen - dataReceived < outBufferSize ) 00264 { 00265 availOut = maxlen - dataReceived; 00266 } 00267 if (d->result == KFilterBase::End) 00268 { 00269 //kDebug(7005) << "got END. dataReceived=" << dataReceived; 00270 break; // Finished. 00271 } 00272 filter->setOutBuffer( data, availOut ); 00273 } 00274 00275 return dataReceived; 00276 } 00277 00278 qint64 KFilterDev::writeData( const char *data /*0 to finish*/, qint64 len ) 00279 { 00280 KFilterBase* filter = d->filter; 00281 Q_ASSERT ( filter->mode() == QIODevice::WriteOnly ); 00282 // If we had an error, return 0. 00283 if ( d->result != KFilterBase::Ok ) 00284 return 0; 00285 00286 bool finish = (data == 0L); 00287 if (!finish) 00288 { 00289 filter->setInBuffer( data, len ); 00290 if (d->bNeedHeader) 00291 { 00292 (void)filter->writeHeader( d->origFileName ); 00293 d->bNeedHeader = false; 00294 } 00295 } 00296 00297 uint dataWritten = 0; 00298 uint availIn = len; 00299 while ( dataWritten < len || finish ) 00300 { 00301 00302 d->result = filter->compress( finish ); 00303 00304 if (d->result == KFilterBase::Error) 00305 { 00306 kWarning(7005) << "KFilterDev: Error when compressing data"; 00307 // What to do ? 00308 break; 00309 } 00310 00311 // Wrote everything ? 00312 if (filter->inBufferEmpty() || (d->result == KFilterBase::End)) 00313 { 00314 // We got that much data since the last time we went here 00315 uint wrote = availIn - filter->inBufferAvailable(); 00316 00317 //kDebug(7005) << " Wrote everything for now. avail_in=" << filter->inBufferAvailable() << "result=" << d->result << "wrote=" << wrote; 00318 00319 // Move on in the input buffer 00320 data += wrote; 00321 dataWritten += wrote; 00322 00323 availIn = len - dataWritten; 00324 //kDebug(7005) << " availIn=" << availIn << "dataWritten=" << dataWritten << "pos=" << pos(); 00325 if ( availIn > 0 ) 00326 filter->setInBuffer( data, availIn ); 00327 } 00328 00329 if (filter->outBufferFull() || (d->result == KFilterBase::End) || finish) 00330 { 00331 //kDebug(7005) << " writing to underlying. avail_out=" << filter->outBufferAvailable(); 00332 int towrite = d->buffer.size() - filter->outBufferAvailable(); 00333 if ( towrite > 0 ) 00334 { 00335 // Write compressed data to underlying device 00336 int size = filter->device()->write( d->buffer.data(), towrite ); 00337 if ( size != towrite ) { 00338 kWarning(7005) << "KFilterDev::write. Could only write " << size << " out of " << towrite << " bytes"; 00339 return 0; // indicate an error (happens on disk full) 00340 } 00341 //else 00342 //kDebug(7005) << " wrote " << size << " bytes"; 00343 } 00344 if (d->result == KFilterBase::End) 00345 { 00346 //kDebug(7005) << " END"; 00347 Q_ASSERT(finish); // hopefully we don't get end before finishing 00348 break; 00349 } 00350 d->buffer.resize(BUFFER_SIZE); 00351 filter->setOutBuffer( d->buffer.data(), d->buffer.size() ); 00352 } 00353 } 00354 00355 return dataWritten; 00356 } 00357 00358 void KFilterDev::setOrigFileName( const QByteArray & fileName ) 00359 { 00360 d->origFileName = fileName; 00361 } 00362 00363 void KFilterDev::setSkipHeaders() 00364 { 00365 d->bSkipHeaders = true; 00366 }
KDE 4.6 API Reference