<commit_before>//
// FileManager.cs
//
// Authors:
//   <NAME> <EMAIL>
//
// Copyright (C) 2006 <NAME>
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//



using MonoTorrent.Common;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Collections.Generic;
using MonoTorrent.Client.PeerMessages;
using System.Threading;

namespace MonoTorrent.Client
{
    /// <summary>
    /// This class manages writing and reading of pieces from the disk
    /// </summary>
    internal class FileManager : System.IDisposable
    {
        #region Private Members
        
        TorrentFile[] files;                                    // The files that are in the torrent that we have to downoad
        string baseDirectory;                                   // The base directory into which all the files will be put
        string savePath;                                        // The path where the base directory will be put
        private readonly int pieceLength;                       // The length of a piece in the torrent
        private long fileSize;                                  // The combined length of all the files
        private SHA1Managed hasher;                             // The SHA1 hasher used to calculate the hash of a piece
        private FileStream[] fileStreams;                       // The filestreams used to read/write to the files on disk
        private bool initialHashRequired;                       // Used to indicate whether we need to hashcheck the files or not
        private Thread ioThread;                                // The dedicated thread used for reading/writing
        private bool ioActive;                                  // Used to signal when the IO thread is running
        private ManualResetEvent threadWait;                    // Used to signal the IO thread when some data is ready for it to work on

        private object bufferedIoLock;                          // Used to synchronise access on the IO thread
        private Queue<BufferedFileWrite> bufferedWrites;        // A list of all the writes which are waiting to be performed
        private Queue<BufferedFileWrite> bufferedReads;         // A list of all the reads which are waiting to be performed
        
        #endregion


        #region Properties

        /// <summary>
        /// True if we need to hash the files (i.e. some were preexisting)
        /// </summary>
        internal bool InitialHashRequired
        {
            get { return this.initialHashRequired; }
            set { this.initialHashRequired = value; }
        }

        /// <summary>
        /// Returns the number of pieces which are currently queued in the write buffer
        /// </summary>
        internal int QueuedWrites
        {
            get { return this.bufferedWrites.Count; }
        }

        /// <summary>
        /// The length of a piece in bytes
        /// </summary>
        public int PieceLength
        {
            get { return this.pieceLength; }
        }

        /// <summary>
        /// Returns true if the write streams are open.
        /// </summary>
        public bool StreamsOpen
        {
            get { return this.fileStreams != null; }
        }

        #endregion


        #region Constructors

        /// <summary>
        /// Creates a new FileManager with read-only access
        /// </summary>
        /// <param name="file">The TorrentFile to open/create on disk</param>
        /// <param name="savePath">The directory the file should be contained in</param>
        /// <param name="pieceLength">The length of a "piece" for this file</param>
        public FileManager(TorrentFile file, string savePath, int pieceLength)
            : this(new TorrentFile[] { file }, string.Empty, savePath, pieceLength, FileAccess.Read)
        {
        }

        /// <summary>
        /// Creates a new FileManager with the supplied FileAccess
        /// </summary>
        /// <param name="file">The TorrentFile you want to create/open on the disk</param>
        /// <param name="savePath">The path to the directory that the file should be contained in</param>
        /// <param name="pieceLength">The length of a "piece" for this file</param>
        /// <param name="fileAccess">The access level for the file</param>
        public FileManager(TorrentFile file, string savePath, int pieceLength, FileAccess fileAccess)
            : this(new TorrentFile[] { file }, string.Empty, savePath, pieceLength, fileAccess)
        {
        }

        /// <summary>
        /// Creates a new FileManager with read-only access
        /// </summary>
        /// <param name="files">The TorrentFiles you want to create/open on the disk</param>
        /// <param name="baseDirectory">The name of the directory that the files are contained in</param>
        /// <param name="savePath">The path to the directory that contains the baseDirectory</param>
        /// <param name="pieceLength">The length of a "piece" for this file</param>
        public FileManager(TorrentFile[] files, string baseDirectory, string savePath, int pieceLength)
            : this(files, baseDirectory, savePath, pieceLength, FileAccess.Read)
        {
        }

        /// <summary>
        /// Creates a new FileManager with the supplied FileAccess
        /// </summary>
        /// <param name="files">The TorrentFiles you want to create/open on the disk</param>
        /// <param name="baseDirectory">The name of the directory that the files are contained in</param>
        /// <param name="savePath">The path to the directory that contains the baseDirectory</param>
        /// <param name="pieceLength">The length of a "piece" for this file</param>
        /// <param name="fileAccess">The access level for the files</param>
        public FileManager(TorrentFile[] files, string baseDirectory, string savePath, int pieceLength, FileAccess fileAccess)
        {
            if (files.Length == 1)
                this.baseDirectory = string.Empty;
            else
                this.baseDirectory = baseDirectory;

            this.bufferedIoLock = new object();
            this.bufferedReads = new Queue<BufferedFileWrite>();
            this.bufferedWrites = new Queue<BufferedFileWrite>();
            this.files = files;
            this.hasher = new SHA1Managed();
            this.initialHashRequired = false;
            this.ioActive = true;
            this.pieceLength = pieceLength;
            this.savePath = savePath;
            this.threadWait = new ManualResetEvent(false);
        }

        #endregion


        #region Methods

        /// <summary>
        /// Closes all the filestreams
        /// </summary>
        internal void CloseFileStreams()
        {
            for (int i = 0; i < this.fileStreams.Length; i++)
                this.fileStreams[i].Dispose();

            this.fileStreams = null;

            // Setting this boolean true allows the IO thread to terminate gracefully
            this.ioActive = false;

            // Allow the IO thread to run.
            SetHandleState(true);
			this.ioThread.Join(150);
        }


        /// <summary>
        /// Disposes all necessary objects
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
        }


        /// <summary>
        /// Disposes all necessary objects
        /// </summary>
        public void Dispose(bool disposing)
        {
            hasher.Clear();
            if (this.StreamsOpen)
                CloseFileStreams();
        }


        /// <summary>
        /// Flushes all data in the FileStreams to disk
        /// </summary>
        internal void FlushAll()
        {<commit_msg>Re-ordered events to avoid disposing a filestream while being in the middle of writing some data.<commit_after>//
// FileManager.cs
//
// Authors:
//   <NAME> <EMAIL>
//
// Copyright (C) 2006 <NAME>
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//



using MonoTorrent.Common;
using System;
using System.IO;
using System.Security.Cryptography;
using System.Collections.Generic;
using MonoTorrent.Client.PeerMessages;
using System.Threading;

namespace MonoTorrent.Client
{
    /// <summary>
    /// This class manages writing and reading of pieces from the disk
    /// </summary>
    internal class FileManager : System.IDisposable
    {
        #region Private Members
        
        TorrentFile[] files;                                    // The files that are in the torrent that we have to downoad
        string baseDirectory;                                   // The base directory into which all the files will be put
        string savePath;                                        // The path where the base directory will be put
        private readonly int pieceLength;                       // The length of a piece in the torrent
        private long fileSize;                                  // The combined length of all the files
        private SHA1Managed hasher;                             // The SHA1 hasher used to calculate the hash of a piece
        private FileStream[] fileStreams;                       // The filestreams used to read/write to the files on disk
        private bool initialHashRequired;                       // Used to indicate whether we need to hashcheck the files or not
        private Thread ioThread;                                // The dedicated thread used for reading/writing
        private bool ioActive;                                  // Used to signal when the IO thread is running
        private ManualResetEvent threadWait;                    // Used to signal the IO thread when some data is ready for it to work on

        private object bufferedIoLock;                          // Used to synchronise access on the IO thread
        private Queue<BufferedFileWrite> bufferedWrites;        // A list of all the writes which are waiting to be performed
        private Queue<BufferedFileWrite> bufferedReads;         // A list of all the reads which are waiting to be performed
        
        #endregion


        #region Properties

        /// <summary>
        /// True if we need to hash the files (i.e. some were preexisting)
        /// </summary>
        internal bool InitialHashRequired
        {
            get { return this.initialHashRequired; }
            set { this.initialHashRequired = value; }
        }

        /// <summary>
        /// Returns the number of pieces which are currently queued in the write buffer
        /// </summary>
        internal int QueuedWrites
        {
            get { return this.bufferedWrites.Count; }
        }

        /// <summary>
        /// The length of a piece in bytes
        /// </summary>
        public int PieceLength
        {
            get { return this.pieceLength; }
        }

        /// <summary>
        /// Returns true if the write streams are open.
        /// </summary>
        public bool StreamsOpen
        {
            get { return this.fileStreams != null; }
        }

        #endregion


        #region Constructors

        /// <summary>
        /// Creates a new FileManager with read-only access
        /// </summary>
        /// <param name="file">The TorrentFile to open/create on disk</param>
        /// <param name="savePath">The directory the file should be contained in</param>
        /// <param name="pieceLength">The length of a "piece" for this file</param>
        public FileManager(TorrentFile file, string savePath, int pieceLength)
            : this(new TorrentFile[] { file }, string.Empty, savePath, pieceLength, FileAccess.Read)
        {
        }

        /// <summary>
        /// Creates a new FileManager with the supplied FileAccess
        /// </summary>
        /// <param name="file">The TorrentFile you want to create/open on the disk</param>
        /// <param name="savePath">The path to the directory that the file should be contained in</param>
        /// <param name="pieceLength">The length of a "piece" for this file</param>
        /// <param name="fileAccess">The access level for the file</param>
        public FileManager(TorrentFile file, string savePath, int pieceLength, FileAccess fileAccess)
            : this(new TorrentFile[] { file }, string.Empty, savePath, pieceLength, fileAccess)
        {
        }

        /// <summary>
        /// Creates a new FileManager with read-only access
        /// </summary>
        /// <param name="files">The TorrentFiles you want to create/open on the disk</param>
        /// <param name="baseDirectory">The name of the directory that the files are contained in</param>
        /// <param name="savePath">The path to the directory that contains the baseDirectory</param>
        /// <param name="pieceLength">The length of a "piece" for this file</param>
        public FileManager(TorrentFile[] files, string baseDirectory, string savePath, int pieceLength)
            : this(files, baseDirectory, savePath, pieceLength, FileAccess.Read)
        {
        }

        /// <summary>
        /// Creates a new FileManager with the supplied FileAccess
        /// </summary>
        /// <param name="files">The TorrentFiles you want to create/open on the disk</param>
        /// <param name="baseDirectory">The name of the directory that the files are contained in</param>
        /// <param name="savePath">The path to the directory that contains the baseDirectory</param>
        /// <param name="pieceLength">The length of a "piece" for this file</param>
        /// <param name="fileAccess">The access level for the files</param>
        public FileManager(TorrentFile[] files, string baseDirectory, string savePath, int pieceLength, FileAccess fileAccess)
        {
            if (files.Length == 1)
                this.baseDirectory = string.Empty;
            else
                this.baseDirectory = baseDirectory;

            this.bufferedIoLock = new object();
            this.bufferedReads = new Queue<BufferedFileWrite>();
            this.bufferedWrites = new Queue<BufferedFileWrite>();
            this.files = files;
            this.hasher = new SHA1Managed();
            this.initialHashRequired = false;
            this.ioActive = true;
            this.pieceLength = pieceLength;
            this.savePath = savePath;
            this.threadWait = new ManualResetEvent(false);
        }

        #endregion


        #region Methods

        /// <summary>
        /// Closes all the filestreams
        /// </summary>
        internal void CloseFileStreams()
        {
            // Setting this boolean true allows the IO thread to terminate gracefully
            this.ioActive = false;

            // Allow the IO thread to run.
            SetHandleState(true);
			this.ioThread.Join(150);

            for (int i = 0; i < this.fileStreams.Length; i++)
                this.fileStreams[i].Dispose();

            this.fileStreams = null;
        }


        /// <summary>
        /// Disposes all necessary objects
        /// </summary>
        public void Dispose()
        {
            this.Dispose(true);
        }


        /// <summary>
        /// Disposes all necessary objects
        /// </summary>
        public void Dispose(bool disposing)
        {
            hasher.Clear();
            if (this.StreamsOpen)
                CloseFileStreams();
        }


        /// <summary>
        /// Flushes all data in the FileStreams to disk
        /// </summary>
        internal void FlushAll()
        {
{'fraction_non_alphanumeric': 0.08817179865924327, 'fraction_numerical': 0.0013634814225656176, 'mean_word_length': 1.8838466579292268, 'pattern_counts': {'":': 0, '<': 135, '<?xml version=': 0, '>': 133, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 18, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3512556', 'n_tokens_mistral': 4303, 'n_tokens_neox': 4132, 'n_words': 1677}