Add QFileDevice::MemoryMapFlags::MapPrivateOption flag.

Passing this flag to QFileDevice::map() will allow writes to the mapped memory
without modifying the file that was mapped. These writes will be lost when the
memory is unmapped.

Change-Id: I7d46b044fc370585de8c06fdb4059f1f1be12d7d
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@digia.com>
bb10
Simon Sasburg 2014-07-21 19:28:43 +01:00
parent f713bd3e19
commit 3905c6f00d
5 changed files with 51 additions and 14 deletions

View File

@ -680,6 +680,13 @@ bool QFileDevice::setPermissions(Permissions permissions)
function.
\value NoOptions No options.
\value MapPrivateOption The mapped memory will be private, so any
modifications will not be visible to other processes and will not
be written to disk. Any such modifications will be lost when the
memory is unmapped. It is unspecified whether modifications made
to the file made after the mapping is created will be visible through
the mapped memory. This flag is not supported on Windows CE.
This enum value was introduced in Qt 5.4.
*/
/*!
@ -689,6 +696,10 @@ bool QFileDevice::setPermissions(Permissions permissions)
or a new file is opened with this object, any maps that have not been
unmapped will automatically be unmapped.
The mapping will have the same open mode as the file (read and/or write),
except when using MapPrivateOption, in which case it is always possible
to write to the mapped memory.
Any mapping options can be passed through \a flags.
Returns a pointer to the memory or 0 if there is an error.

View File

@ -112,8 +112,10 @@ public:
virtual Permissions permissions() const;
virtual bool setPermissions(Permissions permissionSpec);
// ### Qt 6: rename to MemoryMapFlag & make it a QFlags
enum MemoryMapFlags {
NoOptions = 0
NoOptions = 0,
MapPrivateOption = 0x0001
};
uchar *map(qint64 offset, qint64 size, MemoryMapFlags flags = NoOptions);

View File

@ -713,6 +713,12 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla
if (openMode & QIODevice::ReadOnly) access |= PROT_READ;
if (openMode & QIODevice::WriteOnly) access |= PROT_WRITE;
int sharemode = MAP_SHARED;
if (flags & QFileDevice::MapPrivateOption) {
sharemode = MAP_PRIVATE;
access |= PROT_WRITE;
}
#if defined(Q_OS_INTEGRITY)
int pageSize = sysconf(_SC_PAGESIZE);
#else
@ -730,7 +736,7 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size, QFile::MemoryMapFla
realOffset &= ~(QT_OFF_T(pageSize - 1));
void *mapAddress = QT_MMAP((void*)0, realSize,
access, MAP_SHARED, nativeHandle(), realOffset);
access, sharemode, nativeHandle(), realOffset);
if (MAP_FAILED != mapAddress) {
uchar *address = extra + static_cast<uchar*>(mapAddress);
maps[address] = QPair<int,size_t>(extra, realSize);

View File

@ -965,6 +965,21 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
return 0;
}
// check/setup args to map
DWORD access = 0;
if (flags & QFileDevice::MapPrivateOption) {
#ifdef FILE_MAP_COPY
access = FILE_MAP_COPY;
#else
q->setError(QFile::UnspecifiedError, "MapPrivateOption unsupported");
return 0;
#endif
} else if (openMode & QIODevice::WriteOnly) {
access = FILE_MAP_WRITE;
} else if (openMode & QIODevice::ReadOnly) {
access = FILE_MAP_READ;
}
if (mapHandle == NULL) {
// get handle to the file
HANDLE handle = fileHandle;
@ -1011,11 +1026,6 @@ uchar *QFSFileEnginePrivate::map(qint64 offset, qint64 size,
}
}
// setup args to map
DWORD access = 0;
if (openMode & QIODevice::ReadOnly) access = FILE_MAP_READ;
if (openMode & QIODevice::WriteOnly) access = FILE_MAP_WRITE;
DWORD offsetHi = offset >> 32;
DWORD offsetLo = offset & Q_UINT64_C(0xffffffff);
SYSTEM_INFO sysinfo;

View File

@ -3028,17 +3028,21 @@ void tst_QFile::mapResource()
void tst_QFile::mapOpenMode_data()
{
QTest::addColumn<int>("openMode");
QTest::addColumn<int>("flags");
QTest::newRow("ReadOnly") << int(QIODevice::ReadOnly);
QTest::newRow("ReadOnly") << int(QIODevice::ReadOnly) << int(QFileDevice::NoOptions);
//QTest::newRow("WriteOnly") << int(QIODevice::WriteOnly); // this doesn't make sense
QTest::newRow("ReadWrite") << int(QIODevice::ReadWrite);
QTest::newRow("ReadOnly,Unbuffered") << int(QIODevice::ReadOnly | QIODevice::Unbuffered);
QTest::newRow("ReadWrite,Unbuffered") << int(QIODevice::ReadWrite | QIODevice::Unbuffered);
QTest::newRow("ReadWrite") << int(QIODevice::ReadWrite) << int(QFileDevice::NoOptions);
QTest::newRow("ReadOnly,Unbuffered") << int(QIODevice::ReadOnly | QIODevice::Unbuffered) << int(QFileDevice::NoOptions);
QTest::newRow("ReadWrite,Unbuffered") << int(QIODevice::ReadWrite | QIODevice::Unbuffered) << int(QFileDevice::NoOptions);
QTest::newRow("ReadOnly + MapPrivate") << int(QIODevice::ReadOnly) << int(QFileDevice::MapPrivateOption);
QTest::newRow("ReadWrite + MapPrivate") << int(QIODevice::ReadWrite) << int(QFileDevice::MapPrivateOption);
}
void tst_QFile::mapOpenMode()
{
QFETCH(int, openMode);
QFETCH(int, flags);
static const qint64 fileSize = 4096;
QByteArray pattern(fileSize, 'A');
@ -3060,11 +3064,15 @@ void tst_QFile::mapOpenMode()
// open according to our mode
QVERIFY(file.open(QIODevice::OpenMode(openMode)));
uchar *memory = file.map(0, fileSize);
uchar *memory = file.map(0, fileSize, QFileDevice::MemoryMapFlags(flags));
#if defined(Q_OS_WINCE)
QEXPECT_FAIL("ReadOnly + MapPrivate" , "Windows CE does not support MapPrivateOption.", Abort);
QEXPECT_FAIL("ReadWrite + MapPrivate", "Windows CE does not support MapPrivateOption.", Abort);
#endif
QVERIFY(memory);
QVERIFY(memcmp(memory, pattern, fileSize) == 0);
if (openMode & QIODevice::WriteOnly) {
if ((openMode & QIODevice::WriteOnly) || (flags & QFileDevice::MapPrivateOption)) {
// try to write to the file
*memory = 'a';
file.unmap(memory);
@ -3073,7 +3081,7 @@ void tst_QFile::mapOpenMode()
file.seek(0);
char c;
QVERIFY(file.getChar(&c));
QCOMPARE(c, 'a');
QCOMPARE(c, (flags & QFileDevice::MapPrivateOption) ? 'A' : 'a');
}
file.close();