File: src\runtime\src\libraries\System.Private.CoreLib\src\System\Text\EncodingProvider.cs
Web Access
Project: System.Private.CoreLib.csproj (System.Private.CoreLib)
// 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.Threading;

namespace System.Text
{
    public abstract class EncodingProvider
    {
        private static EncodingProvider[]? s_providers;

        public EncodingProvider() { }
        public abstract Encoding? GetEncoding(string name);
        public abstract Encoding? GetEncoding(int codepage);

        // GetEncoding should return either valid encoding or null. shouldn't throw any exception except on null name
        public virtual Encoding? GetEncoding(string name, EncoderFallback encoderFallback, DecoderFallback decoderFallback)
        {
            Encoding? enc = GetEncoding(name);
            if (enc != null)
            {
                enc = (Encoding)enc.Clone();
                enc.EncoderFallback = encoderFallback;
                enc.DecoderFallback = decoderFallback;
            }

            return enc;
        }

        public virtual Encoding? GetEncoding(int codepage, EncoderFallback encoderFallback, DecoderFallback decoderFallback)
        {
            Encoding? enc = GetEncoding(codepage);
            if (enc != null)
            {
                enc = (Encoding)enc.Clone();
                enc.EncoderFallback = encoderFallback;
                enc.DecoderFallback = decoderFallback;
            }

            return enc;
        }

        public virtual IEnumerable<EncodingInfo> GetEncodings() => [];

        internal static void AddProvider(EncodingProvider provider)
        {
            ArgumentNullException.ThrowIfNull(provider);

            // Few providers are added in a typical app (typically just CodePagesEncodingProvider.Instance), and when they are,
            // they're generally not added concurrently.  So use an optimistic concurrency scheme rather than paying for a lock
            // object allocation on the startup path.

            if (s_providers is null &&
                Interlocked.CompareExchange(ref s_providers, [provider], null) is null)
            {
                return;
            }

            while (true)
            {
                EncodingProvider[] providers = s_providers;

                if (Array.IndexOf(providers, provider) >= 0)
                {
                    return;
                }

                var newProviders = new EncodingProvider[providers.Length + 1];
                Array.Copy(providers, newProviders, providers.Length);
                newProviders[^1] = provider;

                if (Interlocked.CompareExchange(ref s_providers, newProviders, providers) == providers)
                {
                    return;
                }
            }
        }

        internal static Encoding? GetEncodingFromProvider(int codepage)
        {
            EncodingProvider[]? providers = s_providers;
            if (providers == null)
                return null;

            foreach (EncodingProvider provider in providers)
            {
                Encoding? enc = provider.GetEncoding(codepage);
                if (enc != null)
                    return enc;
            }

            return null;
        }

        internal static Dictionary<int, EncodingInfo>? GetEncodingListFromProviders()
        {
            EncodingProvider[]? providers = s_providers;
            if (providers == null)
                return null;

            Dictionary<int, EncodingInfo> result = new Dictionary<int, EncodingInfo>();

            foreach (EncodingProvider provider in providers)
            {
                IEnumerable<EncodingInfo>? encodingInfoList = provider.GetEncodings();
                if (encodingInfoList != null)
                {
                    foreach (EncodingInfo ei in encodingInfoList)
                    {
                        result.TryAdd(ei.CodePage, ei);
                    }
                }
            }

            return result;
        }

        internal static Encoding? GetEncodingFromProvider(string encodingName)
        {
            EncodingProvider[]? providers = s_providers;
            if (providers == null)
                return null;

            foreach (EncodingProvider provider in providers)
            {
                Encoding? enc = provider.GetEncoding(encodingName);
                if (enc != null)
                    return enc;
            }

            return null;
        }

        internal static Encoding? GetEncodingFromProvider(int codepage, EncoderFallback enc, DecoderFallback dec)
        {
            EncodingProvider[]? providers = s_providers;
            if (providers == null)
                return null;

            foreach (EncodingProvider provider in providers)
            {
                Encoding? encoding = provider.GetEncoding(codepage, enc, dec);
                if (encoding != null)
                    return encoding;
            }

            return null;
        }

        internal static Encoding? GetEncodingFromProvider(string encodingName, EncoderFallback enc, DecoderFallback dec)
        {
            EncodingProvider[]? providers = s_providers;
            if (providers == null)
                return null;

            foreach (EncodingProvider provider in providers)
            {
                Encoding? encoding = provider.GetEncoding(encodingName, enc, dec);
                if (encoding != null)
                    return encoding;
            }

            return null;
        }
    }
}