1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 /*============================================================
6 **
7 **
8 ** Purpose: provide a cached reusable instance of stringbuilder
9 **          per thread  it's an optimisation that reduces the
10 **          number of instances constructed and collected.
11 **
12 **  Acquire - is used to get a string builder to use of a
13 **            particular size.  It can be called any number of
14 **            times, if a stringbuilder is in the cache then
15 **            it will be returned and the cache emptied.
16 **            subsequent calls will return a new stringbuilder.
17 **
18 **            A StringBuilder instance is cached in
19 **            Thread Local Storage and so there is one per thread
20 **
21 **  Release - Place the specified builder in the cache if it is
22 **            not too big.
23 **            The stringbuilder should not be used after it has
24 **            been released.
25 **            Unbalanced Releases are perfectly acceptable.  It
26 **            will merely cause the runtime to create a new
27 **            stringbuilder next time Acquire is called.
28 **
29 **  GetStringAndRelease
30 **          - ToString() the stringbuilder, Release it to the
31 **            cache and return the resulting string
32 **
33 ===========================================================*/
34 
35 using System.Threading;
36 
37 namespace System.Text
38 {
39     internal static class StringBuilderCache
40     {
41         private const int MAX_BUILDER_SIZE = 260;
42 
43         // WARNING: We allow diagnostic tools to directly inspect this member (t_cachedInstance).
44         // See https://github.com/dotnet/corert/blob/master/Documentation/design-docs/diagnostics/diagnostics-tools-contract.md for more details.
45         // Please do not change the type, the name, or the semantic usage of this member without understanding the implication for tools.
46         // Get in touch with the diagnostics team if you have questions.
47         [ThreadStatic]
48         private static StringBuilder t_cachedInstance;
49 
Acquire(int capacity = StringBuilder.DefaultCapacity)50         public static StringBuilder Acquire(int capacity = StringBuilder.DefaultCapacity)
51         {
52             if (capacity <= MAX_BUILDER_SIZE)
53             {
54                 StringBuilder sb = StringBuilderCache.t_cachedInstance;
55                 if (sb != null)
56                 {
57                     // Avoid stringbuilder block fragmentation by getting a new StringBuilder
58                     // when the requested size is larger than the current capacity
59                     if (capacity <= sb.Capacity)
60                     {
61                         StringBuilderCache.t_cachedInstance = null;
62                         sb.Clear();
63                         return sb;
64                     }
65                 }
66             }
67             return new StringBuilder(capacity);
68         }
69 
Release(StringBuilder sb)70         public static void Release(StringBuilder sb)
71         {
72             if (sb.Capacity <= MAX_BUILDER_SIZE)
73             {
74                 StringBuilderCache.t_cachedInstance = sb;
75             }
76         }
77 
GetStringAndRelease(StringBuilder sb)78         public static string GetStringAndRelease(StringBuilder sb)
79         {
80             string result = sb.ToString();
81             Release(sb);
82             return result;
83         }
84     }
85 }
86