/*

Copyright (c) 2016, 2018-2022, Arvid Norberg
Copyright (c) 2019, Steven Siloti
All rights reserved.

You may use, distribute and modify this code under the terms of the BSD license,
see LICENSE file.
*/

#ifndef TORRENT_MMAP_HPP
#define TORRENT_MMAP_HPP

#include "libtorrent/config.hpp"

#if TORRENT_HAVE_MMAP || TORRENT_HAVE_MAP_VIEW_OF_FILE

#include "libtorrent/disk_interface.hpp" // for open_file_state
#include "libtorrent/aux_/open_mode.hpp"
#include "libtorrent/aux_/file.hpp" // for file_handle

#if TORRENT_HAVE_MAP_VIEW_OF_FILE

#include "libtorrent/aux_/windows.hpp"
#include <mutex>

#endif // TORRENT_HAVE_MAP_VIEW_OF_FILE

namespace libtorrent {

// for now
using byte = char;

namespace aux {

	// files smaller than this will not be mapped into memory, they will just
	// have a file descriptor to be used with regular pread/pwrite calls
	std::int64_t const mapped_file_cutoff = 1024 * 1024;

	using namespace flags;

#if TORRENT_HAVE_MAP_VIEW_OF_FILE
	struct TORRENT_EXTRA_EXPORT file_mapping_handle
	{
		file_mapping_handle(file_handle file, open_mode_t mode, std::int64_t size);
		~file_mapping_handle();
		file_mapping_handle(file_mapping_handle const&) = delete;
		file_mapping_handle& operator=(file_mapping_handle const&) = delete;

		file_mapping_handle(file_mapping_handle&& fm) noexcept;
		file_mapping_handle& operator=(file_mapping_handle&& fm) & noexcept;

		HANDLE handle() const { return m_mapping; }
		handle_type fd() const { return m_file.fd(); }
	private:
		void close();
		file_handle m_file;
		HANDLE m_mapping;
	};
#endif

	struct TORRENT_EXTRA_EXPORT file_mapping : std::enable_shared_from_this<file_mapping>
	{
		file_mapping(file_handle file, open_mode_t mode, std::int64_t file_size
#if TORRENT_HAVE_MAP_VIEW_OF_FILE
			, std::shared_ptr<std::mutex> open_unmap_lock
#endif
			);

#if TORRENT_HAVE_MAP_VIEW_OF_FILE
		void flush();
#endif

		// non-copyable
		file_mapping(file_mapping const&) = delete;
		file_mapping& operator=(file_mapping const&) = delete;

		file_mapping(file_mapping&& rhs) noexcept;
		file_mapping& operator=(file_mapping&& rhs) & noexcept;
		~file_mapping();

		handle_type fd() const { return m_file.fd(); }

		bool has_memory_map() const { return m_mapping != nullptr; }

		// the memory range this file has been mapped into
		span<byte> range()
		{
			TORRENT_ASSERT(m_mapping);
			return { static_cast<byte*>(m_mapping), static_cast<std::ptrdiff_t>(m_size) };
		}

		// hint the kernel that we probably won't need this part of the file
		// anytime soon
		void dont_need(span<byte const> range);

		// hint the kernel that the given (dirty) range of pages should be
		// flushed to disk
		void page_out(span<byte const> range);

	private:

		void close();

		std::int64_t m_size;
#if TORRENT_HAVE_MAP_VIEW_OF_FILE
		file_mapping_handle m_file;
		std::shared_ptr<std::mutex> m_open_unmap_lock;
#else
		file_handle m_file;
#endif
		void* m_mapping;
	};
} // aux
} // libtorrent

#endif // HAVE_MMAP || HAVE_MAP_VIEW_OF_FILE

#endif

