#pragma once #include "coding/file_reader.hpp" #include "coding/file_writer.hpp" #include "base/assert.hpp" #include "base/macros.hpp" #include #include #include #include #include #include class FilesContainerBase { public: using Tag = std::string; /// Alignment of each new section that will be added to a file /// container, i.e. section's offset in bytes will be a multiple of /// this value. /// /// WARNING! Existing sections may not be properly aligned. static uint64_t const kSectionAlignment = 8; bool IsExist(Tag const & tag) const { return GetInfo(tag) != 0; } template void ForEachTag(ToDo && toDo) const { std::for_each(m_info.begin(), m_info.end(), std::forward(toDo)); } protected: struct Info { Tag m_tag; uint64_t m_offset; uint64_t m_size; Info() {} Info(Tag const & tag, uint64_t offset) : m_tag(tag), m_offset(offset) {} }; struct LessInfo { bool operator() (Info const & t1, Info const & t2) const { return (t1.m_tag < t2.m_tag); } bool operator() (Info const & t1, Tag const & t2) const { return (t1.m_tag < t2); } bool operator() (Tag const & t1, Info const & t2) const { return (t1 < t2.m_tag); } }; struct LessOffset { bool operator() (Info const & t1, Info const & t2) const { if (t1.m_offset == t2.m_offset) { // Element with nonzero size should be the last one, // for correct append writer mode (FilesContainerW::GetWriter). return (t1.m_size < t2.m_size); } else return (t1.m_offset < t2.m_offset); } bool operator() (Info const & t1, uint64_t const & t2) const { return (t1.m_offset < t2); } bool operator() (uint64_t const & t1, Info const & t2) const { return (t1 < t2.m_offset); } }; class EqualTag { public: EqualTag(Tag const & tag) : m_tag(tag) {} bool operator() (Info const & t) const { return (t.m_tag == m_tag); } private: Tag const & m_tag; }; friend std::string DebugPrint(Info const & info); Info const * GetInfo(Tag const & tag) const; template void ReadInfo(Reader & reader); using InfoContainer = std::vector; InfoContainer m_info; }; class FilesContainerR : public FilesContainerBase { public: using TReader = ModelReaderPtr; explicit FilesContainerR(std::string const & filePath, uint32_t logPageSize = 10, uint32_t logPageCount = 10); explicit FilesContainerR(TReader const & file); TReader GetReader(Tag const & tag) const; template void ForEachTag(F && f) const { for (size_t i = 0; i < m_info.size(); ++i) f(m_info[i].m_tag); } uint64_t GetFileSize() const { return m_source.Size(); } std::string const & GetFileName() const { return m_source.GetName(); } std::pair GetAbsoluteOffsetAndSize(Tag const & tag) const; private: TReader m_source; }; namespace detail { class MappedFile { public: MappedFile() = default; ~MappedFile() { Close(); } void Open(std::string const & fName); void Close(); class Handle { public: Handle() = default; Handle(char const * base, char const * alignBase, uint64_t size, uint64_t origSize) : m_base(base), m_origBase(alignBase), m_size(size), m_origSize(origSize) { } Handle(Handle && h) { Assign(std::move(h)); } ~Handle(); void Assign(Handle && h); void Unmap(); bool IsValid() const { return (m_base != 0); } uint64_t GetSize() const { return m_size; } template T const * GetData() const { ASSERT_EQUAL(m_size % sizeof(T), 0, ()); return reinterpret_cast(m_base); } template size_t GetDataCount() const { ASSERT_EQUAL(m_size % sizeof(T), 0, ()); return (m_size / sizeof(T)); } private: void Reset(); char const * m_base = nullptr; char const * m_origBase = nullptr; uint64_t m_size = 0; uint64_t m_origSize = 0; DISALLOW_COPY(Handle); }; Handle Map(uint64_t offset, uint64_t size, std::string const & tag) const; private: #ifdef OMIM_OS_WINDOWS void * m_hFile = (void *)-1; void * m_hMapping = (void *)-1; #else int m_fd = -1; #endif DISALLOW_COPY(MappedFile); }; } // namespace detail class FilesMappingContainer : public FilesContainerBase { public: using Handle = detail::MappedFile::Handle; /// Do nothing by default, call Open to attach to file. FilesMappingContainer() = default; explicit FilesMappingContainer(std::string const & fName); ~FilesMappingContainer(); void Open(std::string const & fName); void Close(); Handle Map(Tag const & tag) const; FileReader GetReader(Tag const & tag) const; std::string const & GetName() const { return m_name; } private: std::string m_name; detail::MappedFile m_file; }; class FilesContainerW : public FilesContainerBase { public: FilesContainerW(std::string const & fName, FileWriter::Op op = FileWriter::OP_WRITE_TRUNCATE); ~FilesContainerW(); FileWriter GetWriter(Tag const & tag); void Write(std::string const & fPath, Tag const & tag); void Write(ModelReaderPtr reader, Tag const & tag); void Write(std::vector const & buffer, Tag const & tag); void Write(std::vector const & buffer, Tag const & tag); void Finish(); /// Delete section with rewriting file. /// @precondition Container should be opened with FileWriter::OP_WRITE_EXISTING. void DeleteSection(Tag const & tag); std::string const & GetFileName() const { return m_name; } private: uint64_t SaveCurrentSize(); void Open(FileWriter::Op op); void StartNew(); std::string m_name; bool m_needRewrite; bool m_finished; };