1 // ==++==
2 //
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 //
5 // ==--==
6 /*============================================================
7 **
8 ** Class:  StringBuilderCache
9 **
10 ** Purpose: provide a cached reusable instance of stringbuilder
11 **          per thread  it's an optimisation that reduces the
12 **          number of instances constructed and collected.
13 **
14 **  Acquire - is used to get a string builder to use of a
15 **            particular size.  It can be called any number of
16 **            times, if a stringbuilder is in the cache then
17 **            it will be returned and the cache emptied.
18 **            subsequent calls will return a new stringbuilder.
19 **
20 **            A StringBuilder instance is cached in
21 **            Thread Local Storage and so there is one per thread
22 **
23 **  Release - Place the specified builder in the cache if it is
24 **            not too big.
25 **            The stringbuilder should not be used after it has
26 **            been released.
27 **            Unbalanced Releases are perfectly acceptable.  It
28 **            will merely cause the runtime to create a new
29 **            stringbuilder next time Acquire is called.
30 **
31 **  GetStringAndRelease
32 **          - ToString() the stringbuilder, Release it to the
33 **            cache and return the resulting string
34 **
35 ===========================================================*/
36 using System.Threading;
37 
38 namespace System.Text
39 {
40     internal static class StringBuilderCache
41     {
42         // The value 360 was chosen in discussion with performance experts as a compromise between using
43         // as litle memory (per thread) as possible and still covering a large part of short-lived
44         // StringBuilder creations on the startup path of VS designers.
45         private const int MAX_BUILDER_SIZE = 360;
46 
47         [ThreadStatic]
48         private static StringBuilder 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.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.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.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