<commit_before>﻿// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.IO.Enumeration;
using System.Runtime.InteropServices;
using System.Threading;

namespace System.IO.FileSystem
{
    /// <summary>
    /// PollingFileSystemWatcher can be used to monitor changes to a file system directory
    /// </summary>
    /// <remarks>
    /// This type is similar to FileSystemWatcher, but unlike FileSystemWatcher it is fully reliable,
    /// at the cost of some performance overhead. 
    /// Instead of relying on Win32 file notification APIs, it periodically scans the watched directory to discover changes.
    /// This means that sooner or later it will discover every change.
    /// FileSystemWatcher's Win32 APIs can drop some events in rare circumstances, which is often an acceptable compromise.
    /// In scenarios where events cannot be missed, PollingFileSystemWatcher should be used.
    /// Note: When a watched file is renamed, one or two notifications will be made.
    /// Note: When no changes are detected, PollingFileSystemWatcher will not allocate memory on the GC heap.
    /// </remarks>
    public class PollingFileSystemWatcher : IDisposable
    {
        private Timer _timer;
        private PathToFileStateHashtable _state; // stores state of the directory
        private byte _version; // this is used to keep track of removals. // TODO: describe the algorithm
        private bool _disposed = false;

        /// <summary>
        /// Creates an instance of a watcher
        /// </summary>
        /// <param name="path">The path to watch.</param>
        /// <param name="filter">The type of files to watch. For example, "*.txt" watches for changes to all text files.</param>
        public PollingFileSystemWatcher(string path, string filter = "*.*", EnumerationOptions options = null)
        {
            if (!Directory.Exists(path))
                throw new ArgumentException("Path not found.", nameof(path));

            _state = new PathToFileStateHashtable();
            Path = path;
            Filter = filter;
            EnumerationOptions = null ?? new EnumerationOptions();
            _timer = new Timer(new TimerCallback(TimerHandler));
        }

        public EnumerationOptions EnumerationOptions { get; set; } = new EnumerationOptions();
        public string Filter { get; set; } = "*.*";
        public string Path { get; set; }
        public int PollingIntervalInMilliseconds { get; set; } = 1000;

        public void Start()
        {
            if (_disposed) throw new ObjectDisposedException(nameof(PollingFileSystemWatcher));

            ComputeChangesAndUpdateState(); // captures the initial state
            _timer.Change(PollingIntervalInMilliseconds, Timeout.Infinite);
        }

        // This function walks all watched files, collects changes, and updates state
        private FileChangeList ComputeChangesAndUpdateState()
        {
            _version++;

            var enumerator = new FileSystemChangeEnumerator(this, Path, EnumerationOptions);
            while (enumerator.MoveNext())
            {
                // Ignore `.Current`
            }
            var changes = enumerator.Changes;<commit_msg>Conflict fix<commit_after>﻿// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.IO.Enumeration;
using System.Runtime.InteropServices;
using System.Threading;

namespace System.IO.FileSystem
{
    /// <summary>
    /// PollingFileSystemWatcher can be used to monitor changes to a file system directory
    /// </summary>
    /// <remarks>
    /// This type is similar to FileSystemWatcher, but unlike FileSystemWatcher it is fully reliable,
    /// at the cost of some performance overhead. 
    /// Instead of relying on Win32 file notification APIs, it periodically scans the watched directory to discover changes.
    /// This means that sooner or later it will discover every change.
    /// FileSystemWatcher's Win32 APIs can drop some events in rare circumstances, which is often an acceptable compromise.
    /// In scenarios where events cannot be missed, PollingFileSystemWatcher should be used.
    /// Note: When a watched file is renamed, one or two notifications will be made.
    /// Note: When no changes are detected, PollingFileSystemWatcher will not allocate memory on the GC heap.
    /// </remarks>
    public class PollingFileSystemWatcher : IDisposable
    {
        private Timer _timer;
        private PathToFileStateHashtable _state; // stores state of the directory
        private byte _version; // this is used to keep track of removals. // TODO: describe the algorithm
        private bool _disposed = false;

        /// <summary>
        /// Creates an instance of a watcher
        /// </summary>
        /// <param name="path">The path to watch.</param>
        /// <param name="filter">The type of files to watch. For example, "*.txt" watches for changes to all text files.</param>
        public PollingFileSystemWatcher(string path, string filter = "*.*", EnumerationOptions options = null)
        {
            if (!Directory.Exists(path))
                throw new ArgumentException("Path not found.", nameof(path));

            _state = new PathToFileStateHashtable();
            Path = path;
            Filter = filter;
            EnumerationOptions = options ?? new EnumerationOptions();
            _timer = new Timer(new TimerCallback(TimerHandler));
        }

        public EnumerationOptions EnumerationOptions { get; set; } = new EnumerationOptions();
        public string Filter { get; set; } = "*.*";
        public string Path { get; set; }
        public int PollingIntervalInMilliseconds { get; set; } = 1000;

        public void Start()
        {
            if (_disposed) throw new ObjectDisposedException(nameof(PollingFileSystemWatcher));

            ComputeChangesAndUpdateState(); // captures the initial state
            _timer.Change(PollingIntervalInMilliseconds, Timeout.Infinite);
        }

        // This function walks all watched files, collects changes, and updates state
        private FileChangeList ComputeChangesAndUpdateState()
        {
            _version++;

            var enumerator = new FileSystemChangeEnumerator(this, Path, EnumerationOptions);
            while (enumerator.MoveNext())
            {
                // Ignore `.Current`
            }
            var changes = enumerator.Changes;
{'fraction_non_alphanumeric': 0.08472012102874432, 'fraction_numerical': 0.002420574886535552, 'mean_word_length': 2.531517094017094, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1796125', 'n_tokens_mistral': 1639, 'n_tokens_neox': 1560, 'n_words': 626}