/*** Copyright (C) 2024 Jamie Reece Wilson (a/k/a "Reece"). All rights reserved. File: FSMemoryMappedFile.cpp Date: 2024-03-05 Author: Reece ***/ #include #include "FSMemoryMappedFile.hpp" #include namespace Aurora::IO::FS { IIOHandle * MemoryMappedFile::ToHandle() { return this->pHandleShared ? this->pHandleShared.get() : this->handle.AsPointer(); } AuUInt MemoryMappedFile::GetBaseAddress() { if (this->pView) { return (AuUInt)this->pView->GetBasePointer(); } else { return 0; } } AuUInt MemoryMappedFile::GetEndAddress() { if (this->pView) { return (AuUInt)this->pView->GetBasePointer() + this->GetLength(); } else { return 0; } } AuUInt MemoryMappedFile::GetLength() { return this->uFileMapLength; } AuSPtr MemoryMappedFile::NewStreamReader() { return AuMakeShared(this->ToReadView(), this->pView); } AuSPtr MemoryMappedFile::NewStreamWriter() { return AuMakeShared(this->ToWriteView(), this->pView); } void MemoryMappedFile::InitTransactions() { if (AuThreading::InitOnceLocker::TryLock(&this->initOnce)) { bool bOk { true }; if (this->eMode == EFileOpenMode::eReadWrite || this->eMode == EFileOpenMode::eRead) { auto pReader = AuMakeShared(this->ToReadViewAlt(), this->pView); if (pReader) { auto iBaseOffset = 0 - AuInt64(this->uFileMapOffset) + this->iAdjustStreamOffset; pReader->GetBaseOffset() = iBaseOffset; } else { bOk = false; } this->pStreamReader = pReader; } if (this->eMode == EFileOpenMode::eReadWrite || this->eMode == EFileOpenMode::eWrite) { auto pWriter = AuMakeShared(this->ToWriteViewAlt(), this->pView); if (pWriter) { auto iBaseOffset = 0 - AuInt64(this->uFileMapOffset) + this->iAdjustStreamOffset; pWriter->GetBaseOffset() = iBaseOffset; } else { bOk = false; } this->pStreamWriter = pWriter; } AuThreading::InitOnceLocker::Finish(&this->initOnce, !bOk); } else { this->initOnce.Wait(); } } AuSPtr MemoryMappedFile::NewAsyncReadTransaction() { this->InitTransactions(); SysCheckNotNullMemory(this->pStreamReader, {}); auto pTransaction = Adapters::NewAsyncTransactionFromStreamSeekingReader(this->pStreamReader, {}); SysCheckNotNullMemory(pTransaction, {}); return pTransaction; } AuSPtr MemoryMappedFile::NewAsyncWriteTransaction() { this->InitTransactions(); SysCheckNotNullMemory(this->pStreamWriter, {}); auto pTransaction = Adapters::NewAsyncTransactionFromStreamSeekingWriter(this->pStreamWriter, {}); SysCheckNotNullMemory(pTransaction, {}); return pTransaction; } AuSPtr MemoryMappedFile::NewAsyncAnyTransaction() { this->InitTransactions(); SysCheckNotNullMemory(this->pStreamWriter, {}); SysCheckNotNullMemory(this->pStreamReader, {}); auto pTransaction = Adapters::NewAsyncTransactionFromStreamSeekingPair(this->pStreamReader, this->pStreamWriter, {}); SysCheckNotNullMemory(pTransaction, {}); return pTransaction; } ISeekingReader *MemoryMappedFile::ToStreamSeekingReader() { if (this->optSeekableReader) { return &this->optSeekableReader.Value(); } else { return nullptr; } } ISeekingWriter *MemoryMappedFile::ToStreamSeekingWriter() { if (this->optSeekableWriter) { return &this->optSeekableWriter.Value(); } else { return nullptr; } } AuMemoryViewRead MemoryMappedFile::ToReadView() { auto pBasePointer = this->pView->GetBasePointer(); auto iBaseOffset = 0 - AuInt64(this->uFileMapOffset) + this->iAdjustStreamOffset; if (iBaseOffset < 0) { return {}; } auto pHeadPointer = pBasePointer + iBaseOffset; return { pHeadPointer, this->GetEndAddress() - AuUInt(pHeadPointer) }; } AuMemoryViewWrite MemoryMappedFile::ToWriteView() { auto pBasePointer = this->pView->GetBasePointer(); auto iBaseOffset = 0 - AuInt64(this->uFileMapOffset) + this->iAdjustStreamOffset; if (iBaseOffset < 0) { return {}; } auto pHeadPointer = pBasePointer + iBaseOffset; return { pHeadPointer, this->GetEndAddress() - AuUInt(pHeadPointer) }; } AuMemoryViewRead MemoryMappedFile::ToReadViewAlt() { auto pBasePointer = this->pView->GetBasePointer(); return { pBasePointer, this->GetEndAddress() - AuUInt(pBasePointer) }; } AuMemoryViewWrite MemoryMappedFile::ToWriteViewAlt() { auto pBasePointer = this->pView->GetBasePointer(); return { pBasePointer, this->GetEndAddress() - AuUInt(pBasePointer) }; } void MemoryMappedFile::Flush(AuUInt uOffset, AuUInt uLength) { auto iOffset = 0 - AuSInt(this->uFileMapOffset) + AuSInt(iAdjustStreamOffset) + AuSInt(uOffset); if (iOffset < 0) { return; } auto uStartAddress = iOffset; auto uEndAddress = AuMin(this->GetLength(), uStartAddress + uLength); auto uSecondLength = uEndAddress - uStartAddress; this->pView->Flush(iOffset, uSecondLength); } void MemoryMappedFile::Prefetch(AuUInt uOffset, AuUInt uLength) { AU_DEBUG_MEMCRUNCH; auto iOffset = 0 - AuSInt(this->uFileMapOffset) + AuSInt(iAdjustStreamOffset) + AuSInt(uOffset); if (iOffset < 0) { return; } auto uStartAddress = this->GetBaseAddress() + iOffset; auto uEndAddress = AuMin(this->GetEndAddress(), uStartAddress + uLength); auto uSecondLength = uEndAddress - uStartAddress; AuMemory::Cache::OptimizeAddressRangeOnCore({ { uStartAddress, uSecondLength } }); } IAsyncFileStream *MemoryMappedFile::ToAsyncFile() { return this; } AuSPtr MemoryMappedFile::NewTransaction() { return this->NewAsyncAnyTransaction(); } bool MemoryMappedFile::BlockingTruncate(AuUInt64 length) { return false; } bool MemoryMappedFile::BlockingRead(AuUInt64 offset, const Memory::MemoryViewStreamWrite ¶meters) { this->InitTransactions(); SysCheckNotNullMemory(this->pStreamReader, {}); return this->pStreamReader->ArbitraryRead(offset, parameters) == EStreamError::eErrorNone; } bool MemoryMappedFile::BlockingWrite(AuUInt64 offset, const Memory::MemoryViewStreamRead ¶meters) { this->InitTransactions(); SysCheckNotNullMemory(this->pStreamWriter, {}); return this->pStreamWriter->ArbitraryWrite(offset, parameters) == EStreamError::eErrorNone; } bool MemoryMappedFile::Init(EFileOpenMode eMode, AuUInt64 uFileMapOffset, AuUInt64 uFileMapLength, AuInt64 iAdjustStreamOffset, EFileAdvisoryLockLevel eLockLevel) { auto pHandle = this->ToHandle(); if (!uFileMapLength) { uFileMapLength = pHandle->GetFileLength(); } this->eMode = eMode; this->uFileMapLength = uFileMapLength; this->uFileMapOffset = uFileMapOffset; this->iAdjustStreamOffset = iAdjustStreamOffset; auto pAddressSpace = AuProcess::GetGlobalProcessSpace(); this->pView = pAddressSpace->MapFileByObject(AuUnsafeRaiiToShared(this->ToHandle()), uFileMapOffset, uFileMapLength, eMode, eLockLevel); if (!this->pView) { SysPushErrorNested(); return false; } { auto iBaseOffset = 0 - AuInt64(this->uFileMapOffset) + this->iAdjustStreamOffset; IO::Buffered::ViewSeekableReader reader(this->ToReadViewAlt(), this->pView); IO::Buffered::ViewSeekableWriter writer(this->ToWriteViewAlt(), this->pView); writer.GetBaseOffset() = iBaseOffset; reader.GetBaseOffset() = iBaseOffset; this->optSeekableReader = AuMove(reader); this->optSeekableWriter = AuMove(writer); } return true; } IMemoryMappedFile *OpenMapNew(const AuString &path, AuOptional opteMode, AuOptional optuFileMapOffset, AuOptional optuFileMapLength, AuOptional optiAdjustStreamOffset, AuOptional opteAdvisoryLevel, AuOptional optbLockEntireFile) { auto eMode = opteMode.ValueOr(EFileOpenMode::eRead); auto uFileMapOffset = optuFileMapOffset.ValueOr(0); auto uFileLength = optuFileMapLength.ValueOr(0); auto iAdjustStreamOffset = optiAdjustStreamOffset.ValueOr(uFileMapOffset); auto bLockEntireFile = optbLockEntireFile.ValueOr(true); auto eAdvisoryLevel = opteAdvisoryLevel.ValueOr(EFileAdvisoryLockLevel::eBlockReadWrite); auto pReturn = _new MemoryMappedFile(); SysCheckNotNullMemory(pReturn, {}); auto createRequest = AuIO::IIOHandle::HandleCreate::Create(path); createRequest.eMode = eMode; createRequest.eAdvisoryLevel = bLockEntireFile ? eAdvisoryLevel : EFileAdvisoryLockLevel::eNoSafety; createRequest.bDirectIOMode = false; if (!pReturn->handle->InitFromPath(createRequest)) { delete pReturn; return nullptr; } if (!pReturn->Init(eMode, uFileMapOffset, uFileLength, iAdjustStreamOffset, !bLockEntireFile ? eAdvisoryLevel : EFileAdvisoryLockLevel::eNoSafety)) { delete pReturn; return nullptr; } return pReturn; } void OpenMapRelease(IMemoryMappedFile *pReturn) { AuSafeDelete(pReturn); } IMemoryMappedFile *OpenMapFromSharedHandleNew(const AuSPtr &pIOHandle, AuOptional optuFileMapOffset, AuOptional optuFileMapLength, AuOptional optiAdjustStreamOffset, AuOptional opteAdvisoryLevel) { auto uFileMapOffset = optuFileMapOffset.ValueOr(0); auto uFileLength = optuFileMapLength.ValueOr(0); auto iAdjustStreamOffset = optiAdjustStreamOffset.ValueOr(uFileMapOffset); auto eAdvisoryLevel = opteAdvisoryLevel.ValueOr(EFileAdvisoryLockLevel::eNoSafety); SysCheckArgNotNull(pIOHandle, {}); auto pReturn = _new MemoryMappedFile(); SysCheckNotNullMemory(pReturn, {}); pReturn->pHandleShared = pIOHandle; if (!pReturn->Init(pIOHandle->GetOSWriteHandleSafe() ? EFileOpenMode::eReadWrite : EFileOpenMode::eRead, uFileMapOffset, uFileLength, iAdjustStreamOffset, eAdvisoryLevel)) { delete pReturn; return nullptr; } return pReturn; } void OpenMapFromSharedHandleRelease(IMemoryMappedFile *pReturn) { AuSafeDelete(pReturn); } }