#pragma once #include "file.h" #include "align.h" #include "yassert.h" #include #include #include #include #include #include #include #include namespace NPrivate { // NB: use TFileMap::Precharge() and TFileMappedArray::Prechage() void Precharge(const void* data, size_t dataSize, size_t offset, size_t size); } // namespace NPrivate struct TMemoryMapCommon { struct TMapResult { inline size_t MappedSize() const noexcept { return Size - Head; } inline void* MappedData() const noexcept { return Ptr ? (void*)((char*)Ptr + Head) : nullptr; } inline bool IsMapped() const noexcept { return Ptr != nullptr; } inline void Reset() noexcept { Ptr = nullptr; Size = 0; Head = 0; } void* Ptr; size_t Size; i32 Head; TMapResult(void) noexcept { Reset(); } }; enum EOpenModeFlag { oRdOnly = 1, oRdWr = 2, oCopyOnWr = 4, oAccessMask = 7, oNotGreedy = 8, oPrecharge = 16, oPopulate = 32, // Populate page table entries (see mmap's MAP_POPULATE) }; Y_DECLARE_FLAGS(EOpenMode, EOpenModeFlag); /** * Name that will be printed in exceptions if not specified. * Overridden by name obtained from `TFile` if it's not empty. */ static const TString& UnknownFileName(); }; Y_DECLARE_OPERATORS_FOR_FLAGS(TMemoryMapCommon::EOpenMode); class TMemoryMap: public TMemoryMapCommon { public: explicit TMemoryMap(const TString& name); explicit TMemoryMap(const TString& name, EOpenMode om); TMemoryMap(const TString& name, i64 length, EOpenMode om); TMemoryMap(FILE* f, TString dbgName = UnknownFileName()); TMemoryMap(FILE* f, EOpenMode om, TString dbgName = UnknownFileName()); TMemoryMap(const TFile& file, const TString& dbgName = UnknownFileName()); TMemoryMap(const TFile& file, EOpenMode om, const TString& dbgName = UnknownFileName()); ~TMemoryMap(); TMapResult Map(i64 offset, size_t size); bool Unmap(TMapResult region); void ResizeAndReset(i64 size); TMapResult ResizeAndRemap(i64 offset, size_t size); i64 Length() const noexcept; bool IsOpen() const noexcept; bool IsWritable() const noexcept; EOpenMode GetMode() const noexcept; TFile GetFile() const noexcept; void SetSequential(); void Evict(void* ptr, size_t len); void Evict(); /* * deprecated */ bool Unmap(void* ptr, size_t size); private: class TImpl; TSimpleIntrusivePtr Impl_; }; class TFileMap: public TMemoryMapCommon { public: TFileMap(const TMemoryMap& map) noexcept; TFileMap(const TString& name); TFileMap(const TString& name, EOpenMode om); TFileMap(const TString& name, i64 length, EOpenMode om); TFileMap(FILE* f, EOpenMode om = oRdOnly, TString dbgName = UnknownFileName()); TFileMap(const TFile& file, EOpenMode om = oRdOnly, const TString& dbgName = UnknownFileName()); TFileMap(const TFileMap& fm) noexcept; ~TFileMap(); TMapResult Map(i64 offset, size_t size); TMapResult ResizeAndRemap(i64 offset, size_t size); void Unmap(); void Flush(void* ptr, size_t size) { Flush(ptr, size, true); } void Flush() { Flush(Ptr(), MappedSize()); } void FlushAsync(void* ptr, size_t size) { Flush(ptr, size, false); } void FlushAsync() { FlushAsync(Ptr(), MappedSize()); } inline i64 Length() const noexcept { return Map_.Length(); } inline bool IsOpen() const noexcept { return Map_.IsOpen(); } inline bool IsWritable() const noexcept { return Map_.IsWritable(); } EOpenMode GetMode() const noexcept { return Map_.GetMode(); } inline void* Ptr() const noexcept { return Region_.MappedData(); } inline size_t MappedSize() const noexcept { return Region_.MappedSize(); } TFile GetFile() const noexcept { return Map_.GetFile(); } void Precharge(size_t pos = 0, size_t size = (size_t)-1) const; void SetSequential() { Map_.SetSequential(); } void Evict() { Map_.Evict(); } private: void Flush(void* ptr, size_t size, bool sync); TMemoryMap Map_; TMapResult Region_; }; template class TFileMappedArray { private: const T* Ptr_; const T* End_; size_t Size_; char DummyData_[sizeof(T) + PLATFORM_DATA_ALIGN]; mutable THolder Dummy_; THolder DataHolder_; public: TFileMappedArray() : Ptr_(nullptr) , End_(nullptr) , Size_(0) { } ~TFileMappedArray() { Ptr_ = nullptr; End_ = nullptr; } void Init(const char* name) { DataHolder_.Reset(new TFileMap(name)); DoInit(name); } void Init(const TFileMap& fileMap) { DataHolder_.Reset(new TFileMap(fileMap)); DoInit(fileMap.GetFile().GetName()); } void Term() { DataHolder_.Destroy(); Ptr_ = nullptr; Size_ = 0; End_ = nullptr; } void Precharge() { DataHolder_->Precharge(); } const T& operator[](size_t pos) const { Y_ASSERT(pos < size()); return Ptr_[pos]; } /// for STL compatibility only, Size() usage is recommended size_t size() const { return Size_; } size_t Size() const { return Size_; } const T& GetAt(size_t pos) const { if (pos < Size_) return Ptr_[pos]; return Dummy(); } void SetDummy(const T& n_Dummy) { Dummy_.Destroy(); Dummy_.Reset(new (DummyData()) T(n_Dummy)); } inline char* DummyData() const noexcept { return AlignUp((char*)DummyData_); } inline const T& Dummy() const { if (!Dummy_) { Dummy_.Reset(new (DummyData()) T()); } return *Dummy_; } /// for STL compatibility only, Empty() usage is recommended Y_PURE_FUNCTION bool empty() const noexcept { return Empty(); } Y_PURE_FUNCTION bool Empty() const noexcept { return 0 == Size_; } /// for STL compatibility only, Begin() usage is recommended const T* begin() const noexcept { return Begin(); } const T* Begin() const noexcept { return Ptr_; } /// for STL compatibility only, End() usage is recommended const T* end() const noexcept { return End_; } const T* End() const noexcept { return End_; } private: void DoInit(const TString& fileName) { DataHolder_->Map(0, DataHolder_->Length()); if (DataHolder_->Length() % sizeof(T)) { Term(); ythrow yexception() << "Incorrect size of file " << fileName.Quote(); } Ptr_ = (const T*)DataHolder_->Ptr(); Size_ = DataHolder_->Length() / sizeof(T); End_ = Ptr_ + Size_; } }; class TMappedAllocation: TMoveOnly { public: TMappedAllocation(size_t size = 0, bool shared = false, void* addr = nullptr); ~TMappedAllocation() { Dealloc(); } TMappedAllocation(TMappedAllocation&& other) noexcept { this->swap(other); } TMappedAllocation& operator=(TMappedAllocation&& other) noexcept { this->swap(other); return *this; } void* Alloc(size_t size, void* addr = nullptr); void Dealloc(); void* Ptr() const { return Ptr_; } char* Data(ui32 pos = 0) const { return (char*)(Ptr_ ? ((char*)Ptr_ + pos) : nullptr); } char* Begin() const noexcept { return (char*)Ptr(); } char* End() const noexcept { return Begin() + MappedSize(); } size_t MappedSize() const { return Size_; } void swap(TMappedAllocation& with) noexcept; private: void* Ptr_ = nullptr; size_t Size_ = 0; bool Shared_ = false; #ifdef _win_ void* Mapping_ = nullptr; #endif }; template class TMappedArray: private TMappedAllocation { public: TMappedArray(size_t siz = 0) : TMappedAllocation(0) { if (siz) Create(siz); } ~TMappedArray() { Destroy(); } T* Create(size_t siz) { Y_ASSERT(MappedSize() == 0 && Ptr() == nullptr); T* arr = (T*)Alloc((sizeof(T) * siz)); if (!arr) return nullptr; Y_ASSERT(MappedSize() == sizeof(T) * siz); for (size_t n = 0; n < siz; n++) new (&arr[n]) T(); return arr; } void Destroy() { T* arr = (T*)Ptr(); if (arr) { for (size_t n = 0; n < size(); n++) arr[n].~T(); Dealloc(); } } T& operator[](size_t pos) { Y_ASSERT(pos < size()); return ((T*)Ptr())[pos]; } const T& operator[](size_t pos) const { Y_ASSERT(pos < size()); return ((T*)Ptr())[pos]; } T* begin() { return (T*)Ptr(); } T* end() { return (T*)((char*)Ptr() + MappedSize()); } size_t size() const { return MappedSize() / sizeof(T); } void swap(TMappedArray& with) { TMappedAllocation::swap(with); } };