File: SharedStringBuilder.cs
Web Access
Project: src\src\nuget-client\src\NuGet.Core\NuGet.Versioning\NuGet.Versioning.csproj (NuGet.Versioning)
// Copyright (c) .NET Foundation. 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.Text;

namespace NuGet
{
    /// <summary>
    /// Provides a resource pool that enables reusing instances of <see cref="StringBuilder" /> instances.
    /// </summary>
    /// <remarks>
    /// <para>
    /// Renting and returning buffers with an <see cref="StringBuilderPool" /> can increase performance
    /// in situations where <see cref="StringBuilder" /> instances are created and destroyed frequently,
    /// resulting in significant memory pressure on the garbage collector.
    /// </para>
    /// <para>
    /// This class is thread-safe.  All members may be used by multiple threads concurrently.  The string
    /// builder must be returned, even if an exception is thrown while it is in use.  Failure to do results
    /// in other callers not being able to rent the string builder.
    /// </para>
    /// </remarks>
    internal class SharedStringBuilder
    {
        public static readonly SharedStringBuilder Instance = new SharedStringBuilder();

        private const int MaxSize = 256;

        [ThreadStatic]
        private static StringBuilder? BuilderInstance = null;

        [ThreadStatic]
        private static bool InUse = false;

        private SharedStringBuilder()
        {
        }

        /// <summary>
        /// Retrieves a <see cref="StringBuilder" /> that is at least the requested length.
        /// </summary>
        /// <param name="minimumCapacity">The minimum capacity of the <see cref="StringBuilder" /> needed.</param>
        /// <returns>
        /// A <see cref="StringBuilder" /> that is at least <paramref name="minimumCapacity" /> in length.
        /// </returns>
        /// <remarks>
        /// This buffer is loaned to the caller and should be returned to the same pool via
        /// <see cref="ToStringAndReturn" /> so that it may be reused in subsequent usage of <see cref="Rent" />.
        /// It is not a fatal error to not return a rented string builder, but failure to do so may lead to
        /// decreased application performance, as the pool may need to create a new instance to replace
        /// the one lost.
        /// </remarks>
#pragma warning disable CA1822 // Mark members as static.  Callers are supposed to access this method via SharedStringBuilder.Instance
        public StringBuilder Rent(int minimumCapacity)
#pragma warning restore CA1822 // Mark members as static
        {
            if (!InUse)
            {
                if (minimumCapacity <= MaxSize)
                {
                    InUse = true;

                    if (BuilderInstance is null)
                    {
                        BuilderInstance = new StringBuilder(MaxSize);
                    }

                    return BuilderInstance;
                }
            }

            return new StringBuilder(minimumCapacity);
        }

        /// <summary>
        /// Returns to the pool an array that was previously obtained via <see cref="Rent" /> on the same
        /// <see cref="StringBuilderPool" /> instance, returning the built string.
        /// </summary>
        /// <param name="builder">
        /// The <see cref="StringBuilder" /> previously obtained from <see cref="Rent" /> to return to the pool.
        /// </param>
        /// <remarks>
        /// Once a <see cref="StringBuilder" /> has been returned to the pool, the caller gives up all ownership
        /// of the instance and must not use it. The reference returned from a given call to <see cref="Rent" />
        /// must only be returned via <see cref="ToStringAndReturn" /> once.  The default <see cref="StringBuilderPool" />
        /// may hold onto the returned instance in order to rent it again, or it may release the returned instance
        /// if it's determined that the pool already has enough instances stored.
        /// </remarks>
        /// <returns>The string, built from <paramref name="builder" />.</returns>
#pragma warning disable CA1822 // Mark members as static.  Callers are supposed to access this method via SharedStringBuilder.Instance
        public string ToStringAndReturn(StringBuilder builder)
#pragma warning restore CA1822 // Mark members as static
        {
            string result = builder.ToString();

            if (ReferenceEquals(BuilderInstance, builder))
            {
                InUse = false;
                builder.Clear();
            }

            return result;
        }
    }
}