diff --git a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj
index a6eed56af1..9b2a4a80dd 100644
--- a/Builds/MacOSX/Juce.xcodeproj/project.pbxproj
+++ b/Builds/MacOSX/Juce.xcodeproj/project.pbxproj
@@ -898,6 +898,7 @@
4EEF0B0BB4C3D1956B04122B = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_FileOutputStream.h; path = ../../src/io/files/juce_FileOutputStream.h; sourceTree = SOURCE_ROOT; };
59B2FFF817679AEA84375E1B = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_FileSearchPath.cpp; path = ../../src/io/files/juce_FileSearchPath.cpp; sourceTree = SOURCE_ROOT; };
49BF2B02A6D7B4438FC24839 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_FileSearchPath.h; path = ../../src/io/files/juce_FileSearchPath.h; sourceTree = SOURCE_ROOT; };
+ 00B348FA35A7F691AEF84FBA = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_MemoryMappedFile.h; path = ../../src/io/files/juce_MemoryMappedFile.h; sourceTree = SOURCE_ROOT; };
D9B3C04F8EB4228DD59002E7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_NamedPipe.cpp; path = ../../src/io/files/juce_NamedPipe.cpp; sourceTree = SOURCE_ROOT; };
42DA88264F768BAACD0414A3 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_NamedPipe.h; path = ../../src/io/files/juce_NamedPipe.h; sourceTree = SOURCE_ROOT; };
17C3AF03FF7AE88AE0C73311 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_TemporaryFile.cpp; path = ../../src/io/files/juce_TemporaryFile.cpp; sourceTree = SOURCE_ROOT; };
@@ -1713,6 +1714,7 @@
4EEF0B0BB4C3D1956B04122B,
59B2FFF817679AEA84375E1B,
49BF2B02A6D7B4438FC24839,
+ 00B348FA35A7F691AEF84FBA,
D9B3C04F8EB4228DD59002E7,
42DA88264F768BAACD0414A3,
17C3AF03FF7AE88AE0C73311,
diff --git a/Builds/VisualStudio2005/Juce.vcproj b/Builds/VisualStudio2005/Juce.vcproj
index 9c420d7842..5df9a83f91 100644
--- a/Builds/VisualStudio2005/Juce.vcproj
+++ b/Builds/VisualStudio2005/Juce.vcproj
@@ -787,6 +787,7 @@
+
diff --git a/Builds/VisualStudio2008/Juce.vcproj b/Builds/VisualStudio2008/Juce.vcproj
index e92eeb8f0c..45910eb4bb 100644
--- a/Builds/VisualStudio2008/Juce.vcproj
+++ b/Builds/VisualStudio2008/Juce.vcproj
@@ -787,6 +787,7 @@
+
diff --git a/Builds/VisualStudio2008_DLL/Juce.vcproj b/Builds/VisualStudio2008_DLL/Juce.vcproj
index 6e57c750d7..9de9b7197c 100644
--- a/Builds/VisualStudio2008_DLL/Juce.vcproj
+++ b/Builds/VisualStudio2008_DLL/Juce.vcproj
@@ -789,6 +789,7 @@
+
diff --git a/Builds/VisualStudio2010/Juce.vcxproj b/Builds/VisualStudio2010/Juce.vcxproj
index cdd0142438..7e2a18216a 100644
--- a/Builds/VisualStudio2010/Juce.vcxproj
+++ b/Builds/VisualStudio2010/Juce.vcxproj
@@ -741,6 +741,7 @@
+
diff --git a/Builds/VisualStudio2010/Juce.vcxproj.filters b/Builds/VisualStudio2010/Juce.vcxproj.filters
index d0a9d13fbf..a948be7877 100644
--- a/Builds/VisualStudio2010/Juce.vcxproj.filters
+++ b/Builds/VisualStudio2010/Juce.vcxproj.filters
@@ -2154,6 +2154,9 @@
Juce\Source\io\files
+
+ Juce\Source\io\files
+
Juce\Source\io\files
diff --git a/Builds/iOS/Juce.xcodeproj/project.pbxproj b/Builds/iOS/Juce.xcodeproj/project.pbxproj
index fe270f87e6..ac113b91d4 100644
--- a/Builds/iOS/Juce.xcodeproj/project.pbxproj
+++ b/Builds/iOS/Juce.xcodeproj/project.pbxproj
@@ -898,6 +898,7 @@
4EEF0B0BB4C3D1956B04122B = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_FileOutputStream.h; path = ../../src/io/files/juce_FileOutputStream.h; sourceTree = SOURCE_ROOT; };
59B2FFF817679AEA84375E1B = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_FileSearchPath.cpp; path = ../../src/io/files/juce_FileSearchPath.cpp; sourceTree = SOURCE_ROOT; };
49BF2B02A6D7B4438FC24839 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_FileSearchPath.h; path = ../../src/io/files/juce_FileSearchPath.h; sourceTree = SOURCE_ROOT; };
+ 00B348FA35A7F691AEF84FBA = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_MemoryMappedFile.h; path = ../../src/io/files/juce_MemoryMappedFile.h; sourceTree = SOURCE_ROOT; };
D9B3C04F8EB4228DD59002E7 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_NamedPipe.cpp; path = ../../src/io/files/juce_NamedPipe.cpp; sourceTree = SOURCE_ROOT; };
42DA88264F768BAACD0414A3 = { isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = juce_NamedPipe.h; path = ../../src/io/files/juce_NamedPipe.h; sourceTree = SOURCE_ROOT; };
17C3AF03FF7AE88AE0C73311 = { isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; name = juce_TemporaryFile.cpp; path = ../../src/io/files/juce_TemporaryFile.cpp; sourceTree = SOURCE_ROOT; };
@@ -1713,6 +1714,7 @@
4EEF0B0BB4C3D1956B04122B,
59B2FFF817679AEA84375E1B,
49BF2B02A6D7B4438FC24839,
+ 00B348FA35A7F691AEF84FBA,
D9B3C04F8EB4228DD59002E7,
42DA88264F768BAACD0414A3,
17C3AF03FF7AE88AE0C73311,
diff --git a/Juce.jucer b/Juce.jucer
index 8db7892d99..1093348edc 100644
--- a/Juce.jucer
+++ b/Juce.jucer
@@ -1177,6 +1177,8 @@
file="src/io/files/juce_FileSearchPath.cpp"/>
+
#include
#include
+#include
#include
#include
#include
@@ -922,6 +923,7 @@ protected:
#include
#include
#include
+#include
#include
#include
#include
@@ -961,6 +963,7 @@ protected:
#include
#include
#include
+#include
#include
#include
#include
@@ -7857,53 +7860,48 @@ bool File::isAbsolutePath (const String& path)
const File File::getChildFile (String relativePath) const
{
if (isAbsolutePath (relativePath))
- {
- // the path is really absolute..
return File (relativePath);
- }
- else
+
+ String path (fullPath);
+
+ // It's relative, so remove any ../ or ./ bits at the start..
+ if (relativePath[0] == '.')
{
- // it's relative, so remove any ../ or ./ bits at the start.
- String path (fullPath);
+ #if JUCE_WINDOWS
+ relativePath = relativePath.replaceCharacter ('/', '\\').trimStart();
+ #else
+ relativePath = relativePath.trimStart();
+ #endif
- if (relativePath[0] == '.')
+ while (relativePath[0] == '.')
{
- #if JUCE_WINDOWS
- relativePath = relativePath.replaceCharacter ('/', '\\').trimStart();
- #else
- relativePath = relativePath.trimStart();
- #endif
-
- while (relativePath[0] == '.')
+ if (relativePath[1] == '.')
{
- if (relativePath[1] == '.')
+ if (relativePath [2] == 0 || relativePath[2] == separator)
{
- if (relativePath [2] == 0 || relativePath[2] == separator)
- {
- const int lastSlash = path.lastIndexOfChar (separator);
- if (lastSlash >= 0)
- path = path.substring (0, lastSlash);
+ const int lastSlash = path.lastIndexOfChar (separator);
+ if (lastSlash >= 0)
+ path = path.substring (0, lastSlash);
- relativePath = relativePath.substring (3);
- }
- else
- {
- break;
- }
- }
- else if (relativePath[1] == separator)
- {
- relativePath = relativePath.substring (2);
+ relativePath = relativePath.substring (3);
}
else
{
break;
}
}
+ else if (relativePath[1] == separator)
+ {
+ relativePath = relativePath.substring (2);
+ }
+ else
+ {
+ break;
+ }
}
-
- return File (addTrailingSeparator (path) + relativePath);
}
+
+ return File (addTrailingSeparator (path) + relativePath);
}
const File File::getSiblingFile (const String& fileName) const
@@ -8481,6 +8479,39 @@ public:
expect (mb[0] == '0');
}
+ beginTest ("Memory-mapped files");
+
+ {
+ MemoryMappedFile mmf (tempFile, MemoryMappedFile::readOnly);
+ expect (mmf.getSize() == 10);
+ expect (mmf.getData() != nullptr);
+ expect (memcmp (mmf.getData(), "0123456789", 10) == 0);
+ }
+
+ {
+ const File tempFile2 (tempFile.getNonexistentSibling (false));
+ expect (tempFile2.create());
+ expect (tempFile2.appendData ("xxxxxxxxxx", 10));
+
+ {
+ MemoryMappedFile mmf (tempFile2, MemoryMappedFile::readWrite);
+ expect (mmf.getSize() == 10);
+ expect (mmf.getData() != nullptr);
+ memcpy (mmf.getData(), "abcdefghij", 10);
+ }
+
+ {
+ MemoryMappedFile mmf (tempFile2, MemoryMappedFile::readWrite);
+ expect (mmf.getSize() == 10);
+ expect (mmf.getData() != nullptr);
+ expect (memcmp (mmf.getData(), "abcdefghij", 10) == 0);
+ }
+
+ expect (tempFile2.deleteFile());
+ }
+
+ beginTest ("More writing");
+
expect (tempFile.appendData ("abcdefghij", 10));
expect (tempFile.getSize() == 20);
expect (tempFile.replaceWithData ("abcdefghij", 10));
@@ -244800,6 +244831,55 @@ void FileOutputStream::flushInternal()
status = WindowsFileHelpers::getResultForLastError();
}
+MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode)
+ : address (nullptr),
+ internal (nullptr),
+ length (0)
+{
+ jassert (mode == readOnly || mode == readWrite);
+
+ DWORD accessMode = GENERIC_READ, shareMode = FILE_SHARE_READ, createType = OPEN_EXISTING;
+ DWORD protect = PAGE_READONLY, access = FILE_MAP_READ;
+
+ if (mode == readWrite)
+ {
+ accessMode = GENERIC_READ | GENERIC_WRITE;
+ createType = OPEN_ALWAYS;
+ protect = PAGE_READWRITE;
+ access = FILE_MAP_ALL_ACCESS;
+ }
+
+ HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), accessMode, FILE_SHARE_READ, 0,
+ createType, FILE_ATTRIBUTE_NORMAL, 0);
+
+ internal = (void*) h;
+
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ const int64 fileSize = file.getSize();
+
+ HANDLE mappingHandle = CreateFileMapping (h, 0, protect, (DWORD) (fileSize >> 32), (DWORD) fileSize, 0);
+ if (mappingHandle != 0)
+ {
+ address = MapViewOfFile (mappingHandle, access, 0, 0, (SIZE_T) fileSize);
+
+ if (address != nullptr)
+ length = (size_t) fileSize;
+
+ CloseHandle (mappingHandle);
+ }
+ }
+}
+
+MemoryMappedFile::~MemoryMappedFile()
+{
+ if (address != nullptr)
+ UnmapViewOfFile (address);
+
+ if (internal != nullptr)
+ CloseHandle ((HANDLE) internal);
+}
+
int64 File::getSize() const
{
WIN32_FILE_ATTRIBUTE_DATA attributes;
@@ -260588,6 +260668,46 @@ void FileOutputStream::flushInternal()
status = getResultForErrno();
}
+MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode)
+ : address (nullptr),
+ internal (nullptr),
+ length (0)
+{
+ jassert (mode == readOnly || mode == readWrite);
+
+ const int fd = open (file.getFullPathName().toUTF8(),
+ mode == readWrite ? (O_CREAT + O_RDWR) : O_RDONLY, 00644);
+
+ internal = reinterpret_cast (fd);
+
+ if (fd != -1)
+ {
+ const int64 fileSize = file.getSize();
+
+ address = mmap (0, (size_t) fileSize,
+ mode == readWrite ? (PROT_READ | PROT_WRITE) : PROT_READ,
+ MAP_SHARED, fd, 0);
+
+ if (address == MAP_FAILED)
+ address = nullptr;
+ else
+ length = (size_t) fileSize;
+ }
+}
+
+MemoryMappedFile::~MemoryMappedFile()
+{
+ const int fd = reinterpret_cast (internal);
+
+ if (fd != 0)
+ {
+ if (address != nullptr)
+ munmap (address, length);
+
+ close (fd);
+ }
+}
+
const File juce_getExecutableFile()
{
#if JUCE_ANDROID
@@ -270579,6 +270699,46 @@ void FileOutputStream::flushInternal()
status = getResultForErrno();
}
+MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode)
+ : address (nullptr),
+ internal (nullptr),
+ length (0)
+{
+ jassert (mode == readOnly || mode == readWrite);
+
+ const int fd = open (file.getFullPathName().toUTF8(),
+ mode == readWrite ? (O_CREAT + O_RDWR) : O_RDONLY, 00644);
+
+ internal = reinterpret_cast (fd);
+
+ if (fd != -1)
+ {
+ const int64 fileSize = file.getSize();
+
+ address = mmap (0, (size_t) fileSize,
+ mode == readWrite ? (PROT_READ | PROT_WRITE) : PROT_READ,
+ MAP_SHARED, fd, 0);
+
+ if (address == MAP_FAILED)
+ address = nullptr;
+ else
+ length = (size_t) fileSize;
+ }
+}
+
+MemoryMappedFile::~MemoryMappedFile()
+{
+ const int fd = reinterpret_cast (internal);
+
+ if (fd != 0)
+ {
+ if (address != nullptr)
+ munmap (address, length);
+
+ close (fd);
+ }
+}
+
const File juce_getExecutableFile()
{
#if JUCE_ANDROID
@@ -287465,6 +287625,46 @@ void FileOutputStream::flushInternal()
status = getResultForErrno();
}
+MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode)
+ : address (nullptr),
+ internal (nullptr),
+ length (0)
+{
+ jassert (mode == readOnly || mode == readWrite);
+
+ const int fd = open (file.getFullPathName().toUTF8(),
+ mode == readWrite ? (O_CREAT + O_RDWR) : O_RDONLY, 00644);
+
+ internal = reinterpret_cast (fd);
+
+ if (fd != -1)
+ {
+ const int64 fileSize = file.getSize();
+
+ address = mmap (0, (size_t) fileSize,
+ mode == readWrite ? (PROT_READ | PROT_WRITE) : PROT_READ,
+ MAP_SHARED, fd, 0);
+
+ if (address == MAP_FAILED)
+ address = nullptr;
+ else
+ length = (size_t) fileSize;
+ }
+}
+
+MemoryMappedFile::~MemoryMappedFile()
+{
+ const int fd = reinterpret_cast (internal);
+
+ if (fd != 0)
+ {
+ if (address != nullptr)
+ munmap (address, length);
+
+ close (fd);
+ }
+}
+
const File juce_getExecutableFile()
{
#if JUCE_ANDROID
diff --git a/juce_amalgamated.h b/juce_amalgamated.h
index c899f728a2..09f0ef004c 100644
--- a/juce_amalgamated.h
+++ b/juce_amalgamated.h
@@ -73,7 +73,7 @@ namespace JuceDummyNamespace {}
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 53
-#define JUCE_BUILDNUMBER 88
+#define JUCE_BUILDNUMBER 89
/** Current Juce version number.
@@ -19409,6 +19409,68 @@ private:
/*** End of inlined file: juce_FileSearchPath.h ***/
+#endif
+#ifndef __JUCE_MEMORYMAPPEDFILE_JUCEHEADER__
+
+/*** Start of inlined file: juce_MemoryMappedFile.h ***/
+#ifndef __JUCE_MEMORYMAPPEDFILE_JUCEHEADER__
+#define __JUCE_MEMORYMAPPEDFILE_JUCEHEADER__
+
+/**
+ Maps a file into virtual memory for easy reading and/or writing.
+*/
+class JUCE_API MemoryMappedFile
+{
+public:
+ /** The read/write flags used when opening a memory mapped file. */
+ enum AccessMode
+ {
+ readOnly, /**< Indicates that the memory can only be read. */
+ readWrite /**< Indicates that the memory can be read and written to - changes that are
+ made will be flushed back to disk at the whim of the OS. */
+ };
+
+ /** Opens a file and maps it to an area of virtual memory.
+
+ The file should already exist, and should already be the size that you want to work with
+ when you call this. If the file is resized after being opened, the behaviour is undefined.
+
+ If the file exists and the operation succeeds, the getData() and getSize() methods will
+ return the location and size of the data that can be read or written. Note that the entire
+ file is not read into memory immediately - the OS simply creates a virtual mapping, which
+ will lazily pull the data into memory when blocks are accessed.
+
+ If the file can't be opened for some reason, the getData() method will return a null pointer.
+ */
+ MemoryMappedFile (const File& file, AccessMode mode);
+
+ /** Destructor. */
+ ~MemoryMappedFile();
+
+ /** Returns the address at which this file has been mapped, or a null pointer if
+ the file couldn't be successfully mapped.
+ */
+ void* getData() const noexcept { return address; }
+
+ /** Returns the number of bytes of data that are available for reading or writing.
+ This will normally be the size of the file.
+ */
+ size_t getSize() const noexcept { return length; }
+
+private:
+
+ void* address;
+ void* internal;
+ size_t length;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedFile);
+};
+
+#endif // __JUCE_MEMORYMAPPEDFILE_JUCEHEADER__
+
+/*** End of inlined file: juce_MemoryMappedFile.h ***/
+
+
#endif
#ifndef __JUCE_NAMEDPIPE_JUCEHEADER__
diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h
index c377677522..7618dbada6 100644
--- a/src/core/juce_StandardHeader.h
+++ b/src/core/juce_StandardHeader.h
@@ -33,7 +33,7 @@
*/
#define JUCE_MAJOR_VERSION 1
#define JUCE_MINOR_VERSION 53
-#define JUCE_BUILDNUMBER 88
+#define JUCE_BUILDNUMBER 89
/** Current Juce version number.
diff --git a/src/io/files/juce_File.cpp b/src/io/files/juce_File.cpp
index 1506fa644e..763e06af94 100644
--- a/src/io/files/juce_File.cpp
+++ b/src/io/files/juce_File.cpp
@@ -380,53 +380,48 @@ bool File::isAbsolutePath (const String& path)
const File File::getChildFile (String relativePath) const
{
if (isAbsolutePath (relativePath))
- {
- // the path is really absolute..
return File (relativePath);
- }
- else
+
+ String path (fullPath);
+
+ // It's relative, so remove any ../ or ./ bits at the start..
+ if (relativePath[0] == '.')
{
- // it's relative, so remove any ../ or ./ bits at the start.
- String path (fullPath);
+ #if JUCE_WINDOWS
+ relativePath = relativePath.replaceCharacter ('/', '\\').trimStart();
+ #else
+ relativePath = relativePath.trimStart();
+ #endif
- if (relativePath[0] == '.')
+ while (relativePath[0] == '.')
{
- #if JUCE_WINDOWS
- relativePath = relativePath.replaceCharacter ('/', '\\').trimStart();
- #else
- relativePath = relativePath.trimStart();
- #endif
-
- while (relativePath[0] == '.')
+ if (relativePath[1] == '.')
{
- if (relativePath[1] == '.')
+ if (relativePath [2] == 0 || relativePath[2] == separator)
{
- if (relativePath [2] == 0 || relativePath[2] == separator)
- {
- const int lastSlash = path.lastIndexOfChar (separator);
- if (lastSlash >= 0)
- path = path.substring (0, lastSlash);
-
- relativePath = relativePath.substring (3);
- }
- else
- {
- break;
- }
- }
- else if (relativePath[1] == separator)
- {
- relativePath = relativePath.substring (2);
+ const int lastSlash = path.lastIndexOfChar (separator);
+ if (lastSlash >= 0)
+ path = path.substring (0, lastSlash);
+
+ relativePath = relativePath.substring (3);
}
else
{
break;
}
}
+ else if (relativePath[1] == separator)
+ {
+ relativePath = relativePath.substring (2);
+ }
+ else
+ {
+ break;
+ }
}
-
- return File (addTrailingSeparator (path) + relativePath);
}
+
+ return File (addTrailingSeparator (path) + relativePath);
}
const File File::getSiblingFile (const String& fileName) const
@@ -904,6 +899,8 @@ const File File::createTempFile (const String& fileNameEnding)
#include "../../utilities/juce_UnitTest.h"
#include "../../maths/juce_Random.h"
+#include "juce_MemoryMappedFile.h"
+
class FileTests : public UnitTest
{
@@ -1022,6 +1019,39 @@ public:
expect (mb[0] == '0');
}
+ beginTest ("Memory-mapped files");
+
+ {
+ MemoryMappedFile mmf (tempFile, MemoryMappedFile::readOnly);
+ expect (mmf.getSize() == 10);
+ expect (mmf.getData() != nullptr);
+ expect (memcmp (mmf.getData(), "0123456789", 10) == 0);
+ }
+
+ {
+ const File tempFile2 (tempFile.getNonexistentSibling (false));
+ expect (tempFile2.create());
+ expect (tempFile2.appendData ("xxxxxxxxxx", 10));
+
+ {
+ MemoryMappedFile mmf (tempFile2, MemoryMappedFile::readWrite);
+ expect (mmf.getSize() == 10);
+ expect (mmf.getData() != nullptr);
+ memcpy (mmf.getData(), "abcdefghij", 10);
+ }
+
+ {
+ MemoryMappedFile mmf (tempFile2, MemoryMappedFile::readWrite);
+ expect (mmf.getSize() == 10);
+ expect (mmf.getData() != nullptr);
+ expect (memcmp (mmf.getData(), "abcdefghij", 10) == 0);
+ }
+
+ expect (tempFile2.deleteFile());
+ }
+
+ beginTest ("More writing");
+
expect (tempFile.appendData ("abcdefghij", 10));
expect (tempFile.getSize() == 20);
expect (tempFile.replaceWithData ("abcdefghij", 10));
diff --git a/src/io/files/juce_MemoryMappedFile.h b/src/io/files/juce_MemoryMappedFile.h
new file mode 100644
index 0000000000..140bfb2ab3
--- /dev/null
+++ b/src/io/files/juce_MemoryMappedFile.h
@@ -0,0 +1,84 @@
+/*
+ ==============================================================================
+
+ This file is part of the JUCE library - "Jules' Utility Class Extensions"
+ Copyright 2004-11 by Raw Material Software Ltd.
+
+ ------------------------------------------------------------------------------
+
+ JUCE can be redistributed and/or modified under the terms of the GNU General
+ Public License (Version 2), as published by the Free Software Foundation.
+ A copy of the license is included in the JUCE distribution, or can be found
+ online at www.gnu.org/licenses.
+
+ JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ ------------------------------------------------------------------------------
+
+ To release a closed-source product which uses JUCE, commercial licenses are
+ available: visit www.rawmaterialsoftware.com/juce for more information.
+
+ ==============================================================================
+*/
+
+#ifndef __JUCE_MEMORYMAPPEDFILE_JUCEHEADER__
+#define __JUCE_MEMORYMAPPEDFILE_JUCEHEADER__
+
+#include "juce_File.h"
+
+//==============================================================================
+/**
+ Maps a file into virtual memory for easy reading and/or writing.
+*/
+class JUCE_API MemoryMappedFile
+{
+public:
+ /** The read/write flags used when opening a memory mapped file. */
+ enum AccessMode
+ {
+ readOnly, /**< Indicates that the memory can only be read. */
+ readWrite /**< Indicates that the memory can be read and written to - changes that are
+ made will be flushed back to disk at the whim of the OS. */
+ };
+
+ /** Opens a file and maps it to an area of virtual memory.
+
+ The file should already exist, and should already be the size that you want to work with
+ when you call this. If the file is resized after being opened, the behaviour is undefined.
+
+ If the file exists and the operation succeeds, the getData() and getSize() methods will
+ return the location and size of the data that can be read or written. Note that the entire
+ file is not read into memory immediately - the OS simply creates a virtual mapping, which
+ will lazily pull the data into memory when blocks are accessed.
+
+ If the file can't be opened for some reason, the getData() method will return a null pointer.
+ */
+ MemoryMappedFile (const File& file, AccessMode mode);
+
+ /** Destructor. */
+ ~MemoryMappedFile();
+
+ /** Returns the address at which this file has been mapped, or a null pointer if
+ the file couldn't be successfully mapped.
+ */
+ void* getData() const noexcept { return address; }
+
+ /** Returns the number of bytes of data that are available for reading or writing.
+ This will normally be the size of the file.
+ */
+ size_t getSize() const noexcept { return length; }
+
+
+private:
+ //==============================================================================
+ void* address;
+ void* internal;
+ size_t length;
+
+ JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedFile);
+};
+
+
+#endif // __JUCE_MEMORYMAPPEDFILE_JUCEHEADER__
diff --git a/src/juce_core_includes.h b/src/juce_core_includes.h
index d88894e9d4..a31ef92d13 100644
--- a/src/juce_core_includes.h
+++ b/src/juce_core_includes.h
@@ -146,6 +146,9 @@
#ifndef __JUCE_FILESEARCHPATH_JUCEHEADER__
#include "io/files/juce_FileSearchPath.h"
#endif
+#ifndef __JUCE_MEMORYMAPPEDFILE_JUCEHEADER__
+ #include "io/files/juce_MemoryMappedFile.h"
+#endif
#ifndef __JUCE_NAMEDPIPE_JUCEHEADER__
#include "io/files/juce_NamedPipe.h"
#endif
diff --git a/src/native/android/juce_android_NativeCode.cpp b/src/native/android/juce_android_NativeCode.cpp
index aae8ddc2ff..afcfc2988e 100644
--- a/src/native/android/juce_android_NativeCode.cpp
+++ b/src/native/android/juce_android_NativeCode.cpp
@@ -53,6 +53,7 @@ BEGIN_JUCE_NAMESPACE
#include "../../io/files/juce_FileOutputStream.h"
#include "../../io/files/juce_NamedPipe.h"
#include "../../io/files/juce_DirectoryIterator.h"
+#include "../../io/files/juce_MemoryMappedFile.h"
#include "../../io/network/juce_URL.h"
#include "../../io/network/juce_MACAddress.h"
#include "../../core/juce_PlatformUtilities.h"
diff --git a/src/native/android/juce_android_NativeIncludes.h b/src/native/android/juce_android_NativeIncludes.h
index 91092db5f7..6aeb86067f 100644
--- a/src/native/android/juce_android_NativeIncludes.h
+++ b/src/native/android/juce_android_NativeIncludes.h
@@ -43,6 +43,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/src/native/common/juce_posix_SharedCode.h b/src/native/common/juce_posix_SharedCode.h
index a4af1cdfa7..c9d4a9bda2 100644
--- a/src/native/common/juce_posix_SharedCode.h
+++ b/src/native/common/juce_posix_SharedCode.h
@@ -523,6 +523,47 @@ void FileOutputStream::flushInternal()
status = getResultForErrno();
}
+//==============================================================================
+MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode)
+ : address (nullptr),
+ internal (nullptr),
+ length (0)
+{
+ jassert (mode == readOnly || mode == readWrite);
+
+ const int fd = open (file.getFullPathName().toUTF8(),
+ mode == readWrite ? (O_CREAT + O_RDWR) : O_RDONLY, 00644);
+
+ internal = reinterpret_cast (fd);
+
+ if (fd != -1)
+ {
+ const int64 fileSize = file.getSize();
+
+ address = mmap (0, (size_t) fileSize,
+ mode == readWrite ? (PROT_READ | PROT_WRITE) : PROT_READ,
+ MAP_SHARED, fd, 0);
+
+ if (address == MAP_FAILED)
+ address = nullptr;
+ else
+ length = (size_t) fileSize;
+ }
+}
+
+MemoryMappedFile::~MemoryMappedFile()
+{
+ const int fd = reinterpret_cast (internal);
+
+ if (fd != 0)
+ {
+ if (address != nullptr)
+ munmap (address, length);
+
+ close (fd);
+ }
+}
+
//==============================================================================
const File juce_getExecutableFile()
{
diff --git a/src/native/linux/juce_linux_NativeCode.cpp b/src/native/linux/juce_linux_NativeCode.cpp
index 04b5211a83..5d1b1906ad 100644
--- a/src/native/linux/juce_linux_NativeCode.cpp
+++ b/src/native/linux/juce_linux_NativeCode.cpp
@@ -64,6 +64,7 @@ BEGIN_JUCE_NAMESPACE
#include "../../io/files/juce_FileInputStream.h"
#include "../../io/files/juce_FileOutputStream.h"
#include "../../io/files/juce_DirectoryIterator.h"
+#include "../../io/files/juce_MemoryMappedFile.h"
#include "../../io/network/juce_URL.h"
#include "../../io/network/juce_MACAddress.h"
#include "../../io/streams/juce_MemoryInputStream.h"
diff --git a/src/native/linux/juce_linux_NativeIncludes.h b/src/native/linux/juce_linux_NativeIncludes.h
index fa2dd34b33..693b0f4f94 100644
--- a/src/native/linux/juce_linux_NativeIncludes.h
+++ b/src/native/linux/juce_linux_NativeIncludes.h
@@ -42,6 +42,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/src/native/mac/juce_mac_NativeCode.mm b/src/native/mac/juce_mac_NativeCode.mm
index 442c501273..7c766288b7 100644
--- a/src/native/mac/juce_mac_NativeCode.mm
+++ b/src/native/mac/juce_mac_NativeCode.mm
@@ -53,6 +53,7 @@ BEGIN_JUCE_NAMESPACE
#include "../../io/files/juce_FileOutputStream.h"
#include "../../io/files/juce_NamedPipe.h"
#include "../../io/files/juce_DirectoryIterator.h"
+#include "../../io/files/juce_MemoryMappedFile.h"
#include "../../io/network/juce_URL.h"
#include "../../io/network/juce_MACAddress.h"
#include "../../io/streams/juce_MemoryInputStream.h"
diff --git a/src/native/mac/juce_mac_NativeIncludes.h b/src/native/mac/juce_mac_NativeIncludes.h
index 3f5e3e31ff..94c8e53df3 100644
--- a/src/native/mac/juce_mac_NativeIncludes.h
+++ b/src/native/mac/juce_mac_NativeIncludes.h
@@ -70,6 +70,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/src/native/windows/juce_win32_Files.cpp b/src/native/windows/juce_win32_Files.cpp
index cacc24c2d9..67637a50e8 100644
--- a/src/native/windows/juce_win32_Files.cpp
+++ b/src/native/windows/juce_win32_Files.cpp
@@ -299,6 +299,56 @@ void FileOutputStream::flushInternal()
status = WindowsFileHelpers::getResultForLastError();
}
+//==============================================================================
+MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode)
+ : address (nullptr),
+ internal (nullptr),
+ length (0)
+{
+ jassert (mode == readOnly || mode == readWrite);
+
+ DWORD accessMode = GENERIC_READ, shareMode = FILE_SHARE_READ, createType = OPEN_EXISTING;
+ DWORD protect = PAGE_READONLY, access = FILE_MAP_READ;
+
+ if (mode == readWrite)
+ {
+ accessMode = GENERIC_READ | GENERIC_WRITE;
+ createType = OPEN_ALWAYS;
+ protect = PAGE_READWRITE;
+ access = FILE_MAP_ALL_ACCESS;
+ }
+
+ HANDLE h = CreateFile (file.getFullPathName().toWideCharPointer(), accessMode, FILE_SHARE_READ, 0,
+ createType, FILE_ATTRIBUTE_NORMAL, 0);
+
+ internal = (void*) h;
+
+ if (h != INVALID_HANDLE_VALUE)
+ {
+ const int64 fileSize = file.getSize();
+
+ HANDLE mappingHandle = CreateFileMapping (h, 0, protect, (DWORD) (fileSize >> 32), (DWORD) fileSize, 0);
+ if (mappingHandle != 0)
+ {
+ address = MapViewOfFile (mappingHandle, access, 0, 0, (SIZE_T) fileSize);
+
+ if (address != nullptr)
+ length = (size_t) fileSize;
+
+ CloseHandle (mappingHandle);
+ }
+ }
+}
+
+MemoryMappedFile::~MemoryMappedFile()
+{
+ if (address != nullptr)
+ UnmapViewOfFile (address);
+
+ if (internal != nullptr)
+ CloseHandle ((HANDLE) internal);
+}
+
//==============================================================================
int64 File::getSize() const
{
diff --git a/src/native/windows/juce_win32_NativeCode.cpp b/src/native/windows/juce_win32_NativeCode.cpp
index 3929b7f765..04d0446455 100644
--- a/src/native/windows/juce_win32_NativeCode.cpp
+++ b/src/native/windows/juce_win32_NativeCode.cpp
@@ -54,6 +54,7 @@ BEGIN_JUCE_NAMESPACE
#include "../../io/streams/juce_MemoryOutputStream.h"
#include "../../io/files/juce_NamedPipe.h"
#include "../../io/files/juce_DirectoryIterator.h"
+#include "../../io/files/juce_MemoryMappedFile.h"
#include "../../io/network/juce_URL.h"
#include "../../io/network/juce_MACAddress.h"
#include "../../core/juce_PlatformUtilities.h"