File: Utilities\ValueSetFactory.NintValueSet.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    using static BinaryOperatorKind;
 
    internal static partial class ValueSetFactory
    {
        private sealed class NintValueSet : IValueSet<int>, IValueSet
        {
            public static readonly NintValueSet AllValues = new NintValueSet(hasSmall: true, values: NumericValueSet<int>.AllValues(IntTC.DefaultInstance), hasLarge: true);
 
            public static readonly NintValueSet NoValues = new NintValueSet(hasSmall: false, values: NumericValueSet<int>.NoValues(IntTC.DefaultInstance), hasLarge: false);
 
            private readonly IValueSet<int> _values;
 
            /// <summary>
            /// A value of type nint may, in a 64-bit runtime, take on values less than <see cref="System.Int32.MinValue"/>.
            /// A value set representing values of type nint groups them all together, so that it is not possible to
            /// distinguish one such value from another.  The flag <see cref="_hasSmall"/> is true when the set is considered
            /// to contain all values less than <see cref="System.Int32.MinValue"/> (if any).
            /// </summary>
            private readonly bool _hasSmall;
 
            /// <summary>
            /// A value of type nint may, in a 64-bit runtime, take on values greater than <see cref="System.Int32.MaxValue"/>.
            /// A value set representing values of type nint groups them all together, so that it is not possible to
            /// distinguish one such value from another.  The flag <see cref="_hasLarge"/> is true when the set is considered
            /// to contain all values greater than <see cref="System.Int32.MaxValue"/> (if any).
            /// </summary>
            private readonly bool _hasLarge;
 
            internal NintValueSet(bool hasSmall, IValueSet<int> values, bool hasLarge)
            {
                _hasSmall = hasSmall;
                _values = values;
                _hasLarge = hasLarge;
            }
 
            public bool IsEmpty => !_hasSmall && !_hasLarge && _values.IsEmpty;
 
            ConstantValue? IValueSet.Sample
            {
                get
                {
                    if (IsEmpty)
                        throw new ArgumentException();
 
                    if (!_values.IsEmpty)
                        return _values.Sample;
 
                    // We do not support sampling from a nint value set without a specific value. The caller
                    // must arrange another way to get a sample, since we can return no specific value.  This
                    // occurs when the value set was constructed from a pattern like `> (nint)int.MaxValue`.
                    return null;
                }
            }
 
            public bool All(BinaryOperatorKind relation, int value)
            {
                if (_hasLarge && relation switch { LessThan => true, LessThanOrEqual => true, _ => false })
                    return false;
                if (_hasSmall && relation switch { GreaterThan => true, GreaterThanOrEqual => true, _ => false })
                    return false;
                return _values.All(relation, value);
            }
 
            bool IValueSet.All(BinaryOperatorKind relation, ConstantValue value) => value.IsBad || All(relation, value.Int32Value);
 
            public bool Any(BinaryOperatorKind relation, int value)
            {
                if (_hasSmall && relation switch { LessThan => true, LessThanOrEqual => true, _ => false })
                    return true;
                if (_hasLarge && relation switch { GreaterThan => true, GreaterThanOrEqual => true, _ => false })
                    return true;
                return _values.Any(relation, value);
            }
 
            bool IValueSet.Any(BinaryOperatorKind relation, ConstantValue value) => value.IsBad || Any(relation, value.Int32Value);
 
            public IValueSet<int> Complement()
            {
                return new NintValueSet(
                    hasSmall: !this._hasSmall,
                    values: this._values.Complement(),
                    hasLarge: !this._hasLarge
                    );
            }
 
            IValueSet IValueSet.Complement() => this.Complement();
 
            public IValueSet<int> Intersect(IValueSet<int> o)
            {
                var other = (NintValueSet)o;
                return new NintValueSet(
                    hasSmall: this._hasSmall && other._hasSmall,
                    values: this._values.Intersect(other._values),
                    hasLarge: this._hasLarge && other._hasLarge
                    );
            }
 
            IValueSet IValueSet.Intersect(IValueSet other) => this.Intersect((NintValueSet)other);
 
            public IValueSet<int> Union(IValueSet<int> o)
            {
                var other = (NintValueSet)o;
                return new NintValueSet(
                    hasSmall: this._hasSmall || other._hasSmall,
                    values: this._values.Union(other._values),
                    hasLarge: this._hasLarge || other._hasLarge
                    );
            }
 
            IValueSet IValueSet.Union(IValueSet other) => this.Union((NintValueSet)other);
 
            public override bool Equals(object? obj) => obj is NintValueSet other &&
                this._hasSmall == other._hasSmall &&
                this._hasLarge == other._hasLarge &&
                this._values.Equals(other._values);
 
            public override int GetHashCode() =>
                Hash.Combine(this._hasSmall.GetHashCode(), Hash.Combine(this._hasLarge.GetHashCode(), this._values.GetHashCode()));
 
            public override string ToString()
            {
                var psb = PooledStringBuilder.GetInstance();
                var builder = psb.Builder;
                if (_hasSmall)
                    builder.Append("Small");
                if (_hasSmall && !_values.IsEmpty)
                    builder.Append(',');
                builder.Append(_values.ToString());
                if (_hasLarge && builder.Length > 0)
                    builder.Append(',');
                if (_hasLarge)
                    builder.Append("Large");
                return psb.ToStringAndFree();
            }
        }
    }
}