File: ModelBinding\Validation\ValidationStack.cs
Web Access
Project: src\src\Mvc\Mvc.Core\src\Microsoft.AspNetCore.Mvc.Core.csproj (Microsoft.AspNetCore.Mvc.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#nullable enable
 
using System.Diagnostics;
 
namespace Microsoft.AspNetCore.Mvc.ModelBinding.Validation;
 
#pragma warning disable CA1852 // Seal internal types
internal class ValidationStack
#pragma warning restore CA1852 // Seal internal types
{
    public int Count => HashSet?.Count ?? List.Count;
 
    // We tested the performance of a list at size 15 and found it still better than hashset, but to avoid a costly
    // O(n) search at larger n we set the cutoff to 20. If someone finds the point where they intersect feel free to change this number.
    internal const int CutOff = 20;
 
    internal List<object> List { get; } = new List<object>();
 
    internal HashSet<object>? HashSet { get; set; }
 
    public bool Push(object model)
    {
        if (HashSet != null)
        {
            return HashSet.Add(model);
        }
 
        if (ListContains(model))
        {
            return false;
        }
 
        List.Add(model);
 
        if (HashSet == null && List.Count > CutOff)
        {
            HashSet = new HashSet<object>(List, ReferenceEqualityComparer.Instance);
        }
 
        return true;
    }
 
    public void Pop(object? model)
    {
        if (HashSet != null)
        {
            HashSet.Remove(model!);
        }
        else
        {
            if (model != null)
            {
                Debug.Assert(ReferenceEquals(List[List.Count - 1], model));
                List.RemoveAt(List.Count - 1);
            }
        }
    }
 
    private bool ListContains(object model)
    {
        for (var i = 0; i < List.Count; i++)
        {
            if (ReferenceEquals(model, List[i]))
            {
                return true;
            }
        }
 
        return false;
    }
}