File: AliasGenerator.cs

package info (click to toggle)
mono 6.14.1%2Bds2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,282,732 kB
  • sloc: cs: 11,182,461; xml: 2,850,281; ansic: 699,123; cpp: 122,919; perl: 58,604; javascript: 30,841; asm: 21,845; makefile: 19,602; sh: 10,973; python: 4,772; pascal: 925; sql: 859; sed: 16; php: 1
file content (131 lines) | stat: -rw-r--r-- 6,187 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//---------------------------------------------------------------------
// <copyright file="AliasGenerator.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;

namespace System.Data.Common.Utils
{
    /// <summary>
    /// 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
    /// </summary>
    internal sealed class AliasGenerator
    {
        // beyond this size - we recycle the cache and regenerate names
        // this recycling is in place because CTreeGenerator.GenerateNameForVar has prefix of "columnName" which could be unbounded
        private const int MaxPrefixCount = 500;

        // beyond this size per prefix, we don't cache the names (really large queries)
        private const int CacheSize = 250;

        // this caches integer->string so that happens less fequently
        private readonly static string[] _counterNames = new string[CacheSize];

        // We are using a copy-on-write instead of lock-on-read because dictionary is not multi-reader/single-writer safe.
        // safe for frequent multi-thread reading by creating new instances (copy of previous instance) for uncommon writes.
        private static Dictionary<string, string[]> _prefixCounter;

        private int _counter;
        private readonly string _prefix;
        private string[] _cache;

        /// <summary>
        /// Constructs a new AliasGenerator with the specified prefix string
        /// </summary>
        /// <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>
        internal AliasGenerator(string prefix) : this(prefix, CacheSize) { }

        [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>)")]
        internal AliasGenerator(string prefix, int cacheSize)
        {
            _prefix = prefix ?? String.Empty;

            // don't cache all alias, some are truely unique like CommandTree.BindingAliases
            if (0 < cacheSize) 
            {
                string[] cache = null;
                Dictionary<string, string[]> updatedCache;
                Dictionary<string, string[]> prefixCounter;
                while ((null == (prefixCounter = _prefixCounter)) || !prefixCounter.TryGetValue(prefix, out _cache))
                {
                    if (null == cache)
                    {   // we need to create an instance, but it a different thread may win
                        cache = new string[cacheSize];
                    }

                    // grow the cache for prefixes
                    // We are using a copy-on-write instead of lock-on-read because dictionary is not multi-reader/single-writer safe.
                    //     a)Create a larger dictionary
                    //     b) Copy references from previous dictionary
                    //     c) If previous dictionary changed references, repeat from (a)
                    //     d) We now know the individual cache
                    int capacity = 1 + ((null != prefixCounter) ? prefixCounter.Count : 0);
                    updatedCache = new Dictionary<string, string[]>(capacity, StringComparer.InvariantCultureIgnoreCase);
                    if ((null != prefixCounter) && (capacity < MaxPrefixCount))
                    {
                        foreach (KeyValuePair<string, string[]> entry in prefixCounter)
                        {
                            updatedCache.Add(entry.Key, entry.Value);
                        }
                    }
                    updatedCache.Add(prefix, cache);
                    System.Threading.Interlocked.CompareExchange(ref _prefixCounter, updatedCache, prefixCounter);
                }
            }
        }

        /// <summary>
        /// Generates the next alias and increments the Counter.
        /// </summary>
        /// <returns>The generated alias</returns>
        internal string Next()
        {
            _counter = Math.Max(unchecked(1+_counter), 0);
            return GetName(_counter);
        }

        /// <summary>
        /// Generates the alias for the index.
        /// </summary>
        /// <param name="index">index to generate the alias for</param>
        /// <returns>The generated alias</returns>
        internal string GetName(int index)
        {
            string name;
            if ((null == _cache) || unchecked((uint)_cache.Length <= (uint)index))
            {   // names are not cached beyond a particlar size
                name = String.Concat(_prefix, index.ToString(CultureInfo.InvariantCulture));
            }
            else if (null == (name = _cache[index]))
            {   // name has not been generated and cached yet
                if (unchecked((uint)_counterNames.Length <= (uint)index))
                {   // integer->string are not cached beyond a particular size
                    name = index.ToString(CultureInfo.InvariantCulture);
                }
                else if (null == (name = _counterNames[index]))
                {   // generate and cache the integer->string
                    _counterNames[index] = name = index.ToString(CultureInfo.InvariantCulture);
                }
                // generate and cache the prefix+integer
                _cache[index] = name = String.Concat(_prefix, name);
            }
            return name;
        }

#if DEBUG
        internal string Prefix
        {
            get { return _prefix; }
        }
#endif
    }
}