<commit_before>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.FlowAnalysis.SymbolUsageAnalysis
{
    internal static partial class SymbolUsageAnalysis
    {
        /// <summary>
        /// Analysis data for a particular <see cref="BasicBlock"/> for <see cref="ControlFlowGraph"/>
        /// based dataflow analysis OR for the entire executable code block for high level operation
        /// tree based analysis.
        /// </summary>
        private sealed class BasicBlockAnalysisData : IEquatable<BasicBlockAnalysisData>
        {
            private static readonly ObjectPool<BasicBlockAnalysisData> s_pool =
                new ObjectPool<BasicBlockAnalysisData>(() => new BasicBlockAnalysisData());

            /// <summary>
            /// Map from each symbol to possible set of reachable write operations that are live at current program point.
            /// A write is live if there is no intermediate write operation that overwrites it.
            /// </summary>
            private readonly Dictionary<ISymbol, PooledHashSet<IOperation>> _reachingWrites;

            private BasicBlockAnalysisData()
            {
                _reachingWrites = new Dictionary<ISymbol, PooledHashSet<IOperation>>();
            }

            public static BasicBlockAnalysisData GetInstance() => s_pool.Allocate();

            public void Free()
            {
                FreeAndClearValues();
                s_pool.Free(this);
            }

            private void FreeAndClearValues()
            {
                foreach (var value in _reachingWrites.Values)
                {
                    value.Free();
                }

                _reachingWrites.Clear();
            }

            public void SetAnalysisDataFrom(BasicBlockAnalysisData other)
            {
                if (ReferenceEquals(this, other))
                {
                    return;
                }

                FreeAndClearValues();
                AddEntries(_reachingWrites, other);
            }

            /// <summary>
            /// Gets the currently reachable writes for the given symbol.
            /// </summary>
            public IEnumerable<IOperation> GetCurrentWrites(ISymbol symbol)
            {
                if (_reachingWrites.TryGetValue(symbol, out var values))
                {
                    foreach (var value in values)
                    {
                        yield return value;
                    }
                }
            }

            /// <summary>
            /// Marks the given symbol write as a new unread write operation,
            /// potentially clearing out the prior write operations if <paramref name="maybeWritten"/> is <code>false</code>.
            /// </summary>
            public void OnWriteReferenceFound(ISymbol symbol, IOperation operation, bool maybeWritten)
            {
                if (!_reachingWrites.TryGetValue(symbol, out var values))
                {
                    values = PooledHashSet<IOperation>.GetInstance();
                    _reachingWrites.Add(symbol, values);
                }
                else if (!maybeWritten)
                {
                    values.Clear();
                }

                values.Add(operation);
            }

            public bool Equals(BasicBlockAnalysisData other)
                => this.GetHashCode() == (other?.GetHashCode() ?? 0);

            public override bool Equals(object obj)
            {
                return Equals(obj as BasicBlockAnalysisData);
            }

            public override int GetHashCode()
            {
                var hashCode = _reachingWrites.Count;
                foreach ((int keyHash, int valueHash) in _reachingWrites.Select(GetKeyValueHashCodeTuple).OrderBy(hashTuple => hashTuple.keyHash))
                {
                    hashCode = Hash.Combine(Hash.Combine(keyHash, valueHash), hashCode);
                }

                return hashCode;

                // Local functions.
                (int keyHash, int valueHash) GetKeyValueHashCodeTuple(KeyValuePair<ISymbol, PooledHashSet<IOperation>> keyValuePair)
                    => (keyValuePair.Key.GetHashCode(), Hash.Combine(keyValuePair.Value.Count, Hash.CombineValues(keyValuePair.Value)));
            }
<commit_msg>Refactor BasicBlockData equality<commit_after>﻿// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.FlowAnalysis.SymbolUsageAnalysis
{
    internal static partial class SymbolUsageAnalysis
    {
        /// <summary>
        /// Analysis data for a particular <see cref="BasicBlock"/> for <see cref="ControlFlowGraph"/>
        /// based dataflow analysis OR for the entire executable code block for high level operation
        /// tree based analysis.
        /// </summary>
        private sealed class BasicBlockAnalysisData
        {
            private static readonly ObjectPool<BasicBlockAnalysisData> s_pool =
                new ObjectPool<BasicBlockAnalysisData>(() => new BasicBlockAnalysisData());

            /// <summary>
            /// Map from each symbol to possible set of reachable write operations that are live at current program point.
            /// A write is live if there is no intermediate write operation that overwrites it.
            /// </summary>
            private readonly Dictionary<ISymbol, PooledHashSet<IOperation>> _reachingWrites;

            private BasicBlockAnalysisData()
            {
                _reachingWrites = new Dictionary<ISymbol, PooledHashSet<IOperation>>();
            }

            public static BasicBlockAnalysisData GetInstance() => s_pool.Allocate();

            public void Free()
            {
                FreeAndClearValues();
                s_pool.Free(this);
            }

            private void FreeAndClearValues()
            {
                foreach (var value in _reachingWrites.Values)
                {
                    value.Free();
                }

                _reachingWrites.Clear();
            }

            public void SetAnalysisDataFrom(BasicBlockAnalysisData other)
            {
                if (ReferenceEquals(this, other))
                {
                    return;
                }

                FreeAndClearValues();
                AddEntries(_reachingWrites, other);
            }

            /// <summary>
            /// Gets the currently reachable writes for the given symbol.
            /// </summary>
            public IEnumerable<IOperation> GetCurrentWrites(ISymbol symbol)
            {
                if (_reachingWrites.TryGetValue(symbol, out var values))
                {
                    foreach (var value in values)
                    {
                        yield return value;
                    }
                }
            }

            /// <summary>
            /// Marks the given symbol write as a new unread write operation,
            /// potentially clearing out the prior write operations if <paramref name="maybeWritten"/> is <code>false</code>.
            /// </summary>
            public void OnWriteReferenceFound(ISymbol symbol, IOperation operation, bool maybeWritten)
            {
                if (!_reachingWrites.TryGetValue(symbol, out var values))
                {
                    values = PooledHashSet<IOperation>.GetInstance();
                    _reachingWrites.Add(symbol, values);
                }
                else if (!maybeWritten)
                {
                    values.Clear();
                }

                values.Add(operation);
            }

            public bool Equals(BasicBlockAnalysisData other)
            {
                // Check if both _reachingWrites maps have same key-value pair count.
                if (other == null ||
                    other._reachingWrites.Count != _reachingWrites.Count)
                {
                    return false;
                }

                var uniqueSymbols = PooledHashSet<ISymbol>.GetInstance();
                try
                {
                    // Check if both _reachingWrites maps have same set of keys.
                    uniqueSymbols.AddRange(_reachingWrites.Keys);
                    uniqueSymbols.AddRange(other._reachingWrites.Keys);
                    if (uniqueSymbols.Count != _reachingWrites.Count)
                    {
                        return false;
                    }

                    // Check if both _reachingWrites maps have same set of write
                    // operations for each tracked symbol.
                    foreach (var symbol in uniqueSymbols)
                    {
                        var writes1 = _reachingWrites[symbol];
                        var writes2 = other._reachingWrites[symbol];
                        if (!writes1.SetEquals(writes2))
                        {
                            return false;
                        }
                    }

                    return true;
                }
                finally
                {
                    uniqueSymbols.Free();
                }
            }

{'fraction_non_alphanumeric': 0.0761227853316852, 'fraction_numerical': 0.000927070457354759, 'mean_word_length': 1.4567307692307692, 'pattern_counts': {'":': 0, '<': 49, '<?xml version=': 0, '>': 56, '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': '5665670', 'n_tokens_mistral': 2252, 'n_tokens_neox': 2089, 'n_words': 598}