Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Common / Utils / AliasGenerator.cs
1 //---------------------------------------------------------------------
2 // <copyright file="AliasGenerator.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 using System;
11 using System.Collections.Generic;
12 using System.Diagnostics;
13 using System.Globalization;
14 using System.Text;
15
16 namespace System.Data.Common.Utils
17 {
18     /// <summary>
19     /// Generates monotonically increasing names of the form PrefixCounter, where Prefix is an optional prefix string and Counter is the string representation of a monotonically increasing int value that wraps to zero at int.MaxValue
20     /// </summary>
21     internal sealed class AliasGenerator
22     {
23         // beyond this size - we recycle the cache and regenerate names
24         // this recycling is in place because CTreeGenerator.GenerateNameForVar has prefix of "columnName" which could be unbounded
25         private const int MaxPrefixCount = 500;
26
27         // beyond this size per prefix, we don't cache the names (really large queries)
28         private const int CacheSize = 250;
29
30         // this caches integer->string so that happens less fequently
31         private readonly static string[] _counterNames = new string[CacheSize];
32
33         // We are using a copy-on-write instead of lock-on-read because dictionary is not multi-reader/single-writer safe.
34         // safe for frequent multi-thread reading by creating new instances (copy of previous instance) for uncommon writes.
35         private static Dictionary<string, string[]> _prefixCounter;
36
37         private int _counter;
38         private readonly string _prefix;
39         private string[] _cache;
40
41         /// <summary>
42         /// Constructs a new AliasGenerator with the specified prefix string
43         /// </summary>
44         /// <param name="prefix">The prefix string that will appear as the first part of all aliases generated by this AliasGenerator. May be null to indicate that no prefix should be used</param>
45         internal AliasGenerator(string prefix) : this(prefix, CacheSize) { }
46
47         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1309:UseOrdinalStringComparison", MessageId = "System.Collections.Generic.Dictionary`2<System.String,System.String[]>.#ctor(System.Int32,System.Collections.Generic.IEqualityComparer`1<System.String>)")]
48         internal AliasGenerator(string prefix, int cacheSize)
49         {
50             _prefix = prefix ?? String.Empty;
51
52             // don't cache all alias, some are truely unique like CommandTree.BindingAliases
53             if (0 < cacheSize) 
54             {
55                 string[] cache = null;
56                 Dictionary<string, string[]> updatedCache;
57                 Dictionary<string, string[]> prefixCounter;
58                 while ((null == (prefixCounter = _prefixCounter)) || !prefixCounter.TryGetValue(prefix, out _cache))
59                 {
60                     if (null == cache)
61                     {   // we need to create an instance, but it a different thread may win
62                         cache = new string[cacheSize];
63                     }
64
65                     // grow the cache for prefixes
66                     // We are using a copy-on-write instead of lock-on-read because dictionary is not multi-reader/single-writer safe.
67                     //     a)Create a larger dictionary
68                     //     b) Copy references from previous dictionary
69                     //     c) If previous dictionary changed references, repeat from (a)
70                     //     d) We now know the individual cache
71                     int capacity = 1 + ((null != prefixCounter) ? prefixCounter.Count : 0);
72                     updatedCache = new Dictionary<string, string[]>(capacity, StringComparer.InvariantCultureIgnoreCase);
73                     if ((null != prefixCounter) && (capacity < MaxPrefixCount))
74                     {
75                         foreach (KeyValuePair<string, string[]> entry in prefixCounter)
76                         {
77                             updatedCache.Add(entry.Key, entry.Value);
78                         }
79                     }
80                     updatedCache.Add(prefix, cache);
81                     System.Threading.Interlocked.CompareExchange(ref _prefixCounter, updatedCache, prefixCounter);
82                 }
83             }
84         }
85
86         /// <summary>
87         /// Generates the next alias and increments the Counter.
88         /// </summary>
89         /// <returns>The generated alias</returns>
90         internal string Next()
91         {
92             _counter = Math.Max(unchecked(1+_counter), 0);
93             return GetName(_counter);
94         }
95
96         /// <summary>
97         /// Generates the alias for the index.
98         /// </summary>
99         /// <param name="index">index to generate the alias for</param>
100         /// <returns>The generated alias</returns>
101         internal string GetName(int index)
102         {
103             string name;
104             if ((null == _cache) || unchecked((uint)_cache.Length <= (uint)index))
105             {   // names are not cached beyond a particlar size
106                 name = String.Concat(_prefix, index.ToString(CultureInfo.InvariantCulture));
107             }
108             else if (null == (name = _cache[index]))
109             {   // name has not been generated and cached yet
110                 if (unchecked((uint)_counterNames.Length <= (uint)index))
111                 {   // integer->string are not cached beyond a particular size
112                     name = index.ToString(CultureInfo.InvariantCulture);
113                 }
114                 else if (null == (name = _counterNames[index]))
115                 {   // generate and cache the integer->string
116                     _counterNames[index] = name = index.ToString(CultureInfo.InvariantCulture);
117                 }
118                 // generate and cache the prefix+integer
119                 _cache[index] = name = String.Concat(_prefix, name);
120             }
121             return name;
122         }
123
124 #if DEBUG
125         internal string Prefix
126         {
127             get { return _prefix; }
128         }
129 #endif
130     }
131 }