src/corelib/io/qfile.cpp
changeset 7 f7bc934e204c
parent 3 41300fa6a67c
--- a/src/corelib/io/qfile.cpp	Tue Feb 02 00:43:10 2010 +0200
+++ b/src/corelib/io/qfile.cpp	Wed Mar 31 11:06:36 2010 +0300
@@ -1,6 +1,6 @@
 /****************************************************************************
 **
-** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
 ** All rights reserved.
 ** Contact: Nokia Corporation (qt-info@nokia.com)
 **
@@ -62,21 +62,25 @@
 
 static QByteArray locale_encode(const QString &f)
 {
-#ifndef Q_OS_DARWIN
-    return f.toLocal8Bit();
-#else
+#if defined(Q_OS_DARWIN)
     // Mac always expects UTF-8... and decomposed...
     return f.normalized(QString::NormalizationForm_D).toUtf8();
+#elif defined(Q_OS_SYMBIAN)
+    return f.toUtf8();
+#else
+    return f.toLocal8Bit();
 #endif
 }
 
 static QString locale_decode(const QByteArray &f)
 {
-#ifndef Q_OS_DARWIN
-    return QString::fromLocal8Bit(f);
-#else
+#if defined(Q_OS_DARWIN)
     // Mac always gives us UTF-8 and decomposed, we want that composed...
     return QString::fromUtf8(f).normalized(QString::NormalizationForm_C);
+#elif defined(Q_OS_SYMBIAN)
+    return QString::fromUtf8(f);
+#else
+    return QString::fromLocal8Bit(f);
 #endif
 }
 
@@ -86,7 +90,8 @@
 
 QFilePrivate::QFilePrivate()
     : fileEngine(0), lastWasWrite(false),
-      writeBuffer(QFILE_WRITEBUFFER_SIZE), error(QFile::NoError)
+      writeBuffer(QFILE_WRITEBUFFER_SIZE), error(QFile::NoError),
+      cachedSize(0)
 {
 }
 
@@ -643,13 +648,14 @@
         qWarning("QFile::remove: Empty or null file name");
         return false;
     }
+    unsetError();
     close();
     if(error() == QFile::NoError) {
         if(fileEngine()->remove()) {
             unsetError();
             return true;
         }
-        d->setError(QFile::RemoveError, fileEngine()->errorString());
+        d->setError(QFile::RemoveError, d->fileEngine->errorString());
     }
     return false;
 }
@@ -703,7 +709,7 @@
         if (fileEngine()->rename(newName)) {
             unsetError();
             // engine was able to handle the new name so we just reset it
-            fileEngine()->setFileName(newName);
+            d->fileEngine->setFileName(newName);
             d->fileName = newName;
             return true;
         }
@@ -739,7 +745,7 @@
                 if (error) {
                     out.remove();
                 } else {
-                    fileEngine()->setFileName(newName);
+                    d->fileEngine->setFileName(newName);
                     setPermissions(permissions());
                     unsetError();
                     setFileName(newName);
@@ -804,7 +810,7 @@
         unsetError();
         return true;
     }
-    d->setError(QFile::RenameError, fileEngine()->errorString());
+    d->setError(QFile::RenameError, d->fileEngine->errorString());
     return false;
 }
 
@@ -968,9 +974,6 @@
     mode, if the relevant file does not already exist, this function
     will try to create a new file before opening it.
 
-    \note Because of limitations in the native API, QFile ignores the
-    Unbuffered flag on Windows.
-
     \sa QIODevice::OpenMode, setFileName()
 */
 bool QFile::open(OpenMode mode)
@@ -988,16 +991,18 @@
         qWarning("QIODevice::open: File access not specified");
         return false;
     }
-    if (fileEngine()->open(mode)) {
+
+    // QIODevice provides the buffering, so there's no need to request it from the file engine.
+    if (fileEngine()->open(mode | QIODevice::Unbuffered)) {
         QIODevice::open(mode);
         if (mode & Append)
             seek(size());
         return true;
     }
-    QFile::FileError err = fileEngine()->error();
+    QFile::FileError err = d->fileEngine->error();
     if(err == QFile::UnspecifiedError)
         err = QFile::OpenError;
-    d->setError(err, fileEngine()->errorString());
+    d->setError(err, d->fileEngine->errorString());
     return false;
 }
 
@@ -1152,12 +1157,11 @@
 int
 QFile::handle() const
 {
-    if (!isOpen())
+    Q_D(const QFile);
+    if (!isOpen() || !d->fileEngine)
         return -1;
 
-    if (QAbstractFileEngine *engine = fileEngine())
-        return engine->handle();
-    return -1;
+    return d->fileEngine->handle();
 }
 
 /*!
@@ -1189,13 +1193,12 @@
 uchar *QFile::map(qint64 offset, qint64 size, MemoryMapFlags flags)
 {
     Q_D(QFile);
-    QAbstractFileEngine *engine = fileEngine();
-    if (engine
-        && engine->supportsExtension(QAbstractFileEngine::MapExtension)) {
+    if (fileEngine()
+            && d->fileEngine->supportsExtension(QAbstractFileEngine::MapExtension)) {
         unsetError();
-        uchar *address = engine->map(offset, size, flags);
+        uchar *address = d->fileEngine->map(offset, size, flags);
         if (address == 0)
-            d->setError(engine->error(), engine->errorString());
+            d->setError(d->fileEngine->error(), d->fileEngine->errorString());
         return address;
     }
     return 0;
@@ -1212,13 +1215,12 @@
 bool QFile::unmap(uchar *address)
 {
     Q_D(QFile);
-    QAbstractFileEngine *engine = fileEngine();
-    if (engine
-        && engine->supportsExtension(QAbstractFileEngine::UnMapExtension)) {
+    if (fileEngine()
+        && d->fileEngine->supportsExtension(QAbstractFileEngine::UnMapExtension)) {
         unsetError();
-        bool success = engine->unmap(address);
+        bool success = d->fileEngine->unmap(address);
         if (!success)
-            d->setError(engine->error(), engine->errorString());
+            d->setError(d->fileEngine->error(), d->fileEngine->errorString());
         return success;
     }
     return false;
@@ -1251,13 +1253,16 @@
     Q_D(QFile);
     if (!d->ensureFlushed())
         return false;
-    if (isOpen() && fileEngine()->pos() > sz)
+    fileEngine();
+    if (isOpen() && d->fileEngine->pos() > sz)
         seek(sz);
-    if(fileEngine()->setSize(sz)) {
+    if(d->fileEngine->setSize(sz)) {
         unsetError();
+        d->cachedSize = sz;
         return true;
     }
-    d->setError(QFile::ResizeError, fileEngine()->errorString());
+    d->cachedSize = 0;
+    d->setError(QFile::ResizeError, d->fileEngine->errorString());
     return false;
 }
 
@@ -1321,7 +1326,7 @@
         unsetError();
         return true;
     }
-    d->setError(QFile::PermissionsError, fileEngine()->errorString());
+    d->setError(QFile::PermissionsError, d->fileEngine->errorString());
     return false;
 }
 
@@ -1354,23 +1359,27 @@
 QFile::flush()
 {
     Q_D(QFile);
+    if (!d->fileEngine) {
+        qWarning("QFile::flush: No file engine. Is IODevice open?");
+        return false;
+    }
+
     if (!d->writeBuffer.isEmpty()) {
         qint64 size = d->writeBuffer.size();
-        if (_qfile_writeData(d->fileEngine ? d->fileEngine : fileEngine(),
-                             &d->writeBuffer) != size) {
-            QFile::FileError err = fileEngine()->error();
+        if (_qfile_writeData(d->fileEngine, &d->writeBuffer) != size) {
+            QFile::FileError err = d->fileEngine->error();
             if(err == QFile::UnspecifiedError)
                 err = QFile::WriteError;
-            d->setError(err, fileEngine()->errorString());
+            d->setError(err, d->fileEngine->errorString());
             return false;
         }
     }
 
-    if (!fileEngine()->flush()) {
-        QFile::FileError err = fileEngine()->error();
+    if (!d->fileEngine->flush()) {
+        QFile::FileError err = d->fileEngine->error();
         if(err == QFile::UnspecifiedError)
             err = QFile::WriteError;
-        d->setError(err, fileEngine()->errorString());
+        d->setError(err, d->fileEngine->errorString());
         return false;
     }
     return true;
@@ -1387,12 +1396,18 @@
     Q_D(QFile);
     if(!isOpen())
         return;
-    flush();
+    bool flushed = flush();
     QIODevice::close();
 
-    unsetError();
-    if(!fileEngine()->close())
-        d->setError(fileEngine()->error(), fileEngine()->errorString());
+    // reset write buffer
+    d->lastWasWrite = false;
+    d->writeBuffer.clear();
+
+    // keep earlier error from flush
+    if (d->fileEngine->close() && flushed)
+        unsetError();
+    else if (flushed)
+        d->setError(d->fileEngine->error(), d->fileEngine->errorString());
 }
 
 /*!
@@ -1408,7 +1423,8 @@
     Q_D(const QFile);
     if (!d->ensureFlushed())
         return 0;
-    return fileEngine()->size();
+    d->cachedSize = fileEngine()->size();
+    return d->cachedSize;
 }
 
 /*!
@@ -1434,23 +1450,28 @@
 {
     Q_D(const QFile);
 
+    // If there's buffered data left, we're not at the end.
+    if (!d->buffer.isEmpty())
+        return false;
+
     if (!isOpen())
         return true;
 
     if (!d->ensureFlushed())
         return false;
 
-    // If there's buffered data left, we're not at the end.
-    if (!d->buffer.isEmpty())
-        return false;
-
     // If the file engine knows best, say what it says.
-    if (fileEngine()->supportsExtension(QAbstractFileEngine::AtEndExtension)) {
+    if (d->fileEngine->supportsExtension(QAbstractFileEngine::AtEndExtension)) {
         // Check if the file engine supports AtEndExtension, and if it does,
         // check if the file engine claims to be at the end.
-        return fileEngine()->atEnd();
+        return d->fileEngine->atEnd();
     }
 
+    // if it looks like we are at the end, or if size is not cached,
+    // fall through to bytesAvailable() to make sure.
+    if (pos() < d->cachedSize)
+        return false;
+
     // Fall back to checking how much is available (will stat files).
     return bytesAvailable() == 0;
 }
@@ -1470,11 +1491,11 @@
     if (!d->ensureFlushed())
         return false;
 
-    if (!fileEngine()->seek(off) || !QIODevice::seek(off)) {
-        QFile::FileError err = fileEngine()->error();
+    if (!d->fileEngine->seek(off) || !QIODevice::seek(off)) {
+        QFile::FileError err = d->fileEngine->error();
         if(err == QFile::UnspecifiedError)
             err = QFile::PositionError;
-        d->setError(err, fileEngine()->errorString());
+        d->setError(err, d->fileEngine->errorString());
         return false;
     }
     unsetError();
@@ -1490,12 +1511,21 @@
     if (!d->ensureFlushed())
         return -1;
 
-    if (fileEngine()->supportsExtension(QAbstractFileEngine::FastReadLineExtension))
-        return fileEngine()->readLine(data, maxlen);
+    qint64 read;
+    if (d->fileEngine->supportsExtension(QAbstractFileEngine::FastReadLineExtension)) {
+        read = d->fileEngine->readLine(data, maxlen);
+    } else {
+        // Fall back to QIODevice's readLine implementation if the engine
+        // cannot do it faster.
+        read = QIODevice::readLineData(data, maxlen);
+    }
 
-    // Fall back to QIODevice's readLine implementation if the engine
-    // cannot do it faster.
-    return QIODevice::readLineData(data, maxlen);
+    if (read < maxlen) {
+        // failed to read all requested, may be at the end of file, stop caching size so that it's rechecked
+        d->cachedSize = 0;
+    }
+
+    return read;
 }
 
 /*!
@@ -1509,18 +1539,20 @@
     if (!d->ensureFlushed())
         return -1;
 
-    qint64 ret = -1;
-    qint64 read = fileEngine()->read(data, len);
-    if (read != -1)
-        ret = read;
-
-    if(ret < 0) {
-        QFile::FileError err = fileEngine()->error();
+    qint64 read = d->fileEngine->read(data, len);
+    if(read < 0) {
+        QFile::FileError err = d->fileEngine->error();
         if(err == QFile::UnspecifiedError)
             err = QFile::ReadError;
-        d->setError(err, fileEngine()->errorString());
+        d->setError(err, d->fileEngine->errorString());
     }
-    return ret;
+
+    if (read < len) {
+        // failed to read all requested, may be at the end of file, stop caching size so that it's rechecked
+        d->cachedSize = 0;
+    }
+
+    return read;
 }
 
 /*!
@@ -1600,13 +1632,12 @@
     // Write directly to the engine if the block size is larger than
     // the write buffer size.
     if (!buffered || len > QFILE_WRITEBUFFER_SIZE) {
-        QAbstractFileEngine *fe = d->fileEngine ? d->fileEngine : fileEngine();
-        qint64 ret = fe->write(data, len);
+        qint64 ret = d->fileEngine->write(data, len);
         if(ret < 0) {
-            QFile::FileError err = fileEngine()->error();
+            QFile::FileError err = d->fileEngine->error();
             if(err == QFile::UnspecifiedError)
                 err = QFile::WriteError;
-            d->setError(err, fileEngine()->errorString());
+            d->setError(err, d->fileEngine->errorString());
         }
         return ret;
     }