| @@ -881,6 +881,19 @@ File File::createTempFile (const String& fileNameEnding) | |||
| return tempFile; | |||
| } | |||
| //============================================================================== | |||
| MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode) | |||
| : address (nullptr), range (0, file.getSize()), fileHandle (0) | |||
| { | |||
| openInternal (file, mode); | |||
| } | |||
| MemoryMappedFile::MemoryMappedFile (const File& file, const Range<int64>& fileRange, AccessMode mode) | |||
| : address (nullptr), range (fileRange.getIntersectionWith (Range<int64> (0, file.getSize()))), fileHandle (0) | |||
| { | |||
| openInternal (file, mode); | |||
| } | |||
| //============================================================================== | |||
| #if JUCE_UNIT_TESTS | |||
| @@ -57,6 +57,26 @@ public: | |||
| */ | |||
| MemoryMappedFile (const File& file, AccessMode mode); | |||
| /** Opens a section of 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. | |||
| NOTE: the start of the actual range used may be rounded-down to a multiple of the OS's page-size, | |||
| so do not assume that the mapped memory will begin at exactly the position you requested - always | |||
| use getRange() to check the actual range that is being used. | |||
| */ | |||
| MemoryMappedFile (const File& file, | |||
| const Range<int64>& fileRange, | |||
| AccessMode mode); | |||
| /** Destructor. */ | |||
| ~MemoryMappedFile(); | |||
| @@ -68,13 +88,15 @@ public: | |||
| /** 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; } | |||
| size_t getSize() const noexcept { return (size_t) range.getLength(); } | |||
| /** Returns the section of the file at which the mapped memory represents. */ | |||
| Range<int64> getRange() const noexcept { return range; } | |||
| private: | |||
| //============================================================================== | |||
| void* address; | |||
| size_t length; | |||
| Range<int64> range; | |||
| #if JUCE_WINDOWS | |||
| void* fileHandle; | |||
| @@ -82,6 +104,8 @@ private: | |||
| int fileHandle; | |||
| #endif | |||
| void openInternal (const File&, AccessMode); | |||
| JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MemoryMappedFile) | |||
| }; | |||
| @@ -546,36 +546,37 @@ String SystemStats::getEnvironmentVariable (const String& name, const String& de | |||
| } | |||
| //============================================================================== | |||
| MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode) | |||
| : address (nullptr), | |||
| length (0), | |||
| fileHandle (0) | |||
| void MemoryMappedFile::openInternal (const File& file, AccessMode mode) | |||
| { | |||
| jassert (mode == readOnly || mode == readWrite); | |||
| if (range.getStart() > 0) | |||
| { | |||
| const long pageSize = sysconf (_SC_PAGE_SIZE); | |||
| range.setStart (range.getStart() - (range.getStart() % pageSize)); | |||
| } | |||
| fileHandle = open (file.getFullPathName().toUTF8(), | |||
| mode == readWrite ? (O_CREAT + O_RDWR) : O_RDONLY, 00644); | |||
| if (fileHandle != -1) | |||
| { | |||
| const int64 fileSize = file.getSize(); | |||
| void* m = mmap (0, (size_t) fileSize, | |||
| void* m = mmap (0, (size_t) range.getLength(), | |||
| mode == readWrite ? (PROT_READ | PROT_WRITE) : PROT_READ, | |||
| MAP_SHARED, fileHandle, 0); | |||
| MAP_SHARED, fileHandle, | |||
| (off_t) range.getStart()); | |||
| if (m != MAP_FAILED) | |||
| { | |||
| address = m; | |||
| length = (size_t) fileSize; | |||
| } | |||
| else | |||
| range = Range<int64>(); | |||
| } | |||
| } | |||
| MemoryMappedFile::~MemoryMappedFile() | |||
| { | |||
| if (address != nullptr) | |||
| munmap (address, length); | |||
| munmap (address, range.getLength()); | |||
| if (fileHandle != 0) | |||
| close (fileHandle); | |||
| @@ -310,13 +310,18 @@ Result FileOutputStream::truncate() | |||
| } | |||
| //============================================================================== | |||
| MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMode mode) | |||
| : address (nullptr), | |||
| length (0), | |||
| fileHandle (nullptr) | |||
| void MemoryMappedFile::openInternal (const File& file, AccessMode mode) | |||
| { | |||
| jassert (mode == readOnly || mode == readWrite); | |||
| if (range.getStart() > 0) | |||
| { | |||
| SYSTEM_INFO systemInfo; | |||
| GetNativeSystemInfo (&systemInfo); | |||
| range.setStart (range.getStart() - (range.getStart() % systemInfo.dwAllocationGranularity)); | |||
| } | |||
| DWORD accessMode = GENERIC_READ, createType = OPEN_EXISTING; | |||
| DWORD protect = PAGE_READONLY, access = FILE_MAP_READ; | |||
| @@ -334,15 +339,16 @@ MemoryMappedFile::MemoryMappedFile (const File& file, MemoryMappedFile::AccessMo | |||
| if (h != INVALID_HANDLE_VALUE) | |||
| { | |||
| fileHandle = (void*) h; | |||
| const int64 fileSize = file.getSize(); | |||
| HANDLE mappingHandle = CreateFileMapping (h, 0, protect, (DWORD) (fileSize >> 32), (DWORD) fileSize, 0); | |||
| HANDLE mappingHandle = CreateFileMapping (h, 0, protect, (DWORD) (range.getEnd() >> 32), (DWORD) range.getEnd(), 0); | |||
| if (mappingHandle != 0) | |||
| { | |||
| address = MapViewOfFile (mappingHandle, access, 0, 0, (SIZE_T) fileSize); | |||
| address = MapViewOfFile (mappingHandle, access, (DWORD) (range.getStart() >> 32), | |||
| (DWORD) range.getStart(), (SIZE_T) range.getLength()); | |||
| if (address != nullptr) | |||
| length = (size_t) fileSize; | |||
| if (address == nullptr) | |||
| range = Range<int64>(); | |||
| CloseHandle (mappingHandle); | |||
| } | |||