File: System\Text\Encodings\Web\TextEncoderSettings.cs
Web Access
Project: src\src\libraries\System.Text.Encodings.Web\src\System.Text.Encodings.Web.csproj (System.Text.Encodings.Web)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Text.Unicode;
 
namespace System.Text.Encodings.Web
{
    /// <summary>
    /// Represents a filter which allows only certain Unicode code points through.
    /// </summary>
    public class TextEncoderSettings
    {
        private AllowedBmpCodePointsBitmap _allowedCodePointsBitmap;
 
        /// <summary>
        /// Instantiates an empty filter (allows no code points through by default).
        /// </summary>
        public TextEncoderSettings()
        {
        }
 
        /// <summary>
        /// Instantiates the filter by cloning the allow list of another <see cref="TextEncoderSettings"/>.
        /// </summary>
        public TextEncoderSettings(TextEncoderSettings other)
        {
            if (other is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.other);
            }
 
            _allowedCodePointsBitmap = other.GetAllowedCodePointsBitmap(); // copy byval
        }
 
        /// <summary>
        /// Instantiates the filter where only the character ranges specified by <paramref name="allowedRanges"/>
        /// are allowed by the filter.
        /// </summary>
        public TextEncoderSettings(params UnicodeRange[] allowedRanges)
        {
            if (allowedRanges is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.allowedRanges);
            }
 
            AllowRanges(allowedRanges);
        }
 
        /// <summary>
        /// Allows the character specified by <paramref name="character"/> through the filter.
        /// </summary>
        public virtual void AllowCharacter(char character)
        {
            _allowedCodePointsBitmap.AllowChar(character);
        }
 
        /// <summary>
        /// Allows all characters specified by <paramref name="characters"/> through the filter.
        /// </summary>
        public virtual void AllowCharacters(params char[] characters)
        {
            if (characters is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.characters);
            }
 
            for (int i = 0; i < characters.Length; i++)
            {
                _allowedCodePointsBitmap.AllowChar(characters[i]);
            }
        }
 
        /// <summary>
        /// Allows all code points specified by <paramref name="codePoints"/>.
        /// </summary>
        public virtual void AllowCodePoints(IEnumerable<int> codePoints)
        {
            if (codePoints is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.codePoints);
            }
 
            foreach (var allowedCodePoint in codePoints)
            {
                // If the code point can't be represented as a BMP character, skip it.
                if (UnicodeUtility.IsBmpCodePoint((uint)allowedCodePoint))
                {
                    _allowedCodePointsBitmap.AllowChar((char)allowedCodePoint);
                }
            }
        }
 
        /// <summary>
        /// Allows all characters specified by <paramref name="range"/> through the filter.
        /// </summary>
        public virtual void AllowRange(UnicodeRange range)
        {
            if (range is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.range);
            }
 
            int firstCodePoint = range.FirstCodePoint;
            int rangeSize = range.Length;
            for (int i = 0; i < rangeSize; i++)
            {
                int codePoint = firstCodePoint + i;
                UnicodeDebug.AssertIsBmpCodePoint((uint)codePoint); // UnicodeRange only supports BMP
                _allowedCodePointsBitmap.AllowChar((char)codePoint);
            }
        }
 
        /// <summary>
        /// Allows all characters specified by <paramref name="ranges"/> through the filter.
        /// </summary>
        public virtual void AllowRanges(params UnicodeRange[] ranges)
        {
            if (ranges is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ranges);
            }
 
            for (int i = 0; i < ranges.Length; i++)
            {
                AllowRange(ranges[i]);
            }
        }
 
        /// <summary>
        /// Resets this settings object by disallowing all characters.
        /// </summary>
        public virtual void Clear()
        {
            _allowedCodePointsBitmap = default;
        }
 
        /// <summary>
        /// Disallows the character <paramref name="character"/> through the filter.
        /// </summary>
        public virtual void ForbidCharacter(char character)
        {
            _allowedCodePointsBitmap.ForbidChar(character);
        }
 
        /// <summary>
        /// Disallows all characters specified by <paramref name="characters"/> through the filter.
        /// </summary>
        public virtual void ForbidCharacters(params char[] characters)
        {
            if (characters is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.characters);
            }
 
            for (int i = 0; i < characters.Length; i++)
            {
                _allowedCodePointsBitmap.ForbidChar(characters[i]);
            }
        }
 
        /// <summary>
        /// Disallows all characters specified by <paramref name="range"/> through the filter.
        /// </summary>
        public virtual void ForbidRange(UnicodeRange range)
        {
            if (range is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.range);
            }
 
            int firstCodePoint = range.FirstCodePoint;
            int rangeSize = range.Length;
            for (int i = 0; i < rangeSize; i++)
            {
                int codePoint = firstCodePoint + i;
                UnicodeDebug.AssertIsBmpCodePoint((uint)codePoint); // UnicodeRange only supports BMP
                _allowedCodePointsBitmap.ForbidChar((char)codePoint);
            }
        }
 
        /// <summary>
        /// Disallows all characters specified by <paramref name="ranges"/> through the filter.
        /// </summary>
        public virtual void ForbidRanges(params UnicodeRange[] ranges)
        {
            if (ranges is null)
            {
                ThrowHelper.ThrowArgumentNullException(ExceptionArgument.ranges);
            }
 
            for (int i = 0; i < ranges.Length; i++)
            {
                ForbidRange(ranges[i]);
            }
        }
 
        /// <summary>
        /// Gets an enumeration of all allowed code points.
        /// </summary>
        public virtual IEnumerable<int> GetAllowedCodePoints()
        {
            for (int i = 0; i <= char.MaxValue; i++)
            {
                if (_allowedCodePointsBitmap.IsCharAllowed((char)i))
                {
                    yield return i;
                }
            }
        }
 
        /// <summary>
        /// Retrieves the bitmap of allowed characters from this settings object.
        /// The data is returned readonly byref.
        /// </summary>
        internal ref readonly AllowedBmpCodePointsBitmap GetAllowedCodePointsBitmap()
        {
            if (GetType() == typeof(TextEncoderSettings))
            {
                return ref _allowedCodePointsBitmap;
            }
            else
            {
                // Somebody may have overridden GetAllowedCodePoints, and we need to honor that.
                // Fabricate a new bitmap and populate it from the virtual overrides.
                StrongBox<AllowedBmpCodePointsBitmap> newBitmap = new StrongBox<AllowedBmpCodePointsBitmap>();
                foreach (int allowedCodePoint in GetAllowedCodePoints())
                {
                    if ((uint)allowedCodePoint <= char.MaxValue)
                    {
                        newBitmap.Value.AllowChar((char)allowedCodePoint);
                    }
                }
                return ref newBitmap.Value;
            }
        }
    }
}