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"