Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System / misc / ClientUtils.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="ClientUtils.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 /*
8  */
9 #if Microsoft_NAMESPACE 
10     namespace System.Windows.Forms
11 #elif DRAWING_NAMESPACE
12     namespace System.Drawing
13 #elif Microsoft_PUBLIC_GRAPHICS_LIBRARY 
14     namespace System.Internal
15 #elif SYSTEM_NAMESPACE
16     namespace System
17 #else
18    namespace System.Windows.Forms 
19 #endif
20 {
21     using System;
22     using System.Reflection;
23     using System.Diagnostics.CodeAnalysis;
24     using System.Globalization;
25     using System.Collections;
26     using System.Diagnostics;
27     using System.Security.Permissions;    
28
29     // Miscellaneous utilities
30     static internal class ClientUtils {
31
32 // ExecutionEngineException is obsolete and shouldn't be used (to catch, throw or reference) anymore.
33 // Pragma added to prevent converting the "type is obsolete" warning into build error.
34 // File owner should fix this.
35 #pragma warning disable 618
36         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
37         public static bool IsCriticalException(Exception ex) {
38             return ex is NullReferenceException
39                     || ex is StackOverflowException
40                     || ex is OutOfMemoryException
41                     || ex is System.Threading.ThreadAbortException
42                     || ex is ExecutionEngineException
43                     || ex is IndexOutOfRangeException
44                     || ex is AccessViolationException;
45         }
46 #pragma warning restore 618
47
48         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
49         public static bool IsSecurityOrCriticalException(Exception ex) {
50             return (ex is System.Security.SecurityException) || IsCriticalException(ex);
51         }
52
53         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
54         public static int GetBitCount(uint x) {
55           int count = 0;
56           while (x > 0){
57               x &= x - 1;
58               count++;
59           }
60           return count;
61         }
62
63       
64         // Sequential version
65         // assumes sequential enum members 0,1,2,3,4 -etc.            
66         // 
67         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
68         public static bool IsEnumValid(Enum enumValue, int value, int minValue, int maxValue)
69         {               
70             bool valid = (value >= minValue) && (value <= maxValue);
71 #if DEBUG            
72             Debug_SequentialEnumIsDefinedCheck(enumValue, minValue, maxValue);
73 #endif
74             return valid;
75
76         }
77
78         // Useful for sequential enum values which only use powers of two 0,1,2,4,8 etc: IsEnumValid(val, min, max, 1)
79         // Valid example: TextImageRelation 0,1,2,4,8 - only one bit can ever be on, and the value is between 0 and 8.
80         //
81         //   ClientUtils.IsEnumValid((int)(relation), /*min*/(int)TextImageRelation.None, (int)TextImageRelation.TextBeforeImage,1);
82         //
83         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
84         [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
85         public static bool IsEnumValid(Enum enumValue, int value, int minValue, int maxValue, int maxNumberOfBitsOn) {
86             System.Diagnostics.Debug.Assert(maxNumberOfBitsOn >=0 && maxNumberOfBitsOn<32, "expect this to be greater than zero and less than 32");
87
88             bool valid = (value >= minValue) && (value <= maxValue);
89             //Note: if it's 0, it'll have no bits on.  If it's a power of 2, it'll have 1.
90             valid =  (valid && GetBitCount((uint)value) <= maxNumberOfBitsOn);
91 #if DEBUG
92             Debug_NonSequentialEnumIsDefinedCheck(enumValue, minValue, maxValue, maxNumberOfBitsOn, valid);
93 #endif
94                         return valid;
95         }
96
97         // Useful for enums that are a subset of a bitmask
98         // Valid example: EdgeEffects  0, 0x800 (FillInterior), 0x1000 (Flat), 0x4000(Soft), 0x8000(Mono)
99         //
100         //   ClientUtils.IsEnumValid((int)(effects), /*mask*/ FillInterior | Flat | Soft | Mono,
101         //          ,2);
102         //
103         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
104         [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
105         public static bool IsEnumValid_Masked(Enum enumValue, int value, UInt32 mask) {
106             bool valid = ((value & mask) == value);
107
108 #if DEBUG
109             Debug_ValidateMask(enumValue, mask);
110 #endif
111
112             return valid;
113         }
114
115  
116
117
118
119         // Useful for cases where you have discontiguous members of the enum.
120         // Valid example: AutoComplete source.
121         // if (!ClientUtils.IsEnumValid(value, AutoCompleteSource.None, 
122         //                                            AutoCompleteSource.AllSystemSources
123         //                                            AutoCompleteSource.AllUrl,
124         //                                            AutoCompleteSource.CustomSource,
125         //                                            AutoCompleteSource.FileSystem,
126         //                                            AutoCompleteSource.FileSystemDirectories,
127         //                                            AutoCompleteSource.HistoryList,
128         //                                            AutoCompleteSource.ListItems,
129         //                                            AutoCompleteSource.RecentlyUsedList))
130         //
131         // PERF tip: put the default value in the enum towards the front of the argument list.
132         [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
133         public static bool IsEnumValid_NotSequential(System.Enum enumValue, int value, params int[] enumValues) {
134              System.Diagnostics.Debug.Assert(Enum.GetValues(enumValue.GetType()).Length == enumValues.Length, "Not all the enum members were passed in.");
135              for (int i = 0; i < enumValues.Length; i++){
136                  if (enumValues[i] == value){
137                      return true;
138                  }
139              }
140              return false;
141         }
142
143 #if DEBUG      
144         [ThreadStatic]
145         private static Hashtable enumValueInfo;
146         public const int MAXCACHE = 300;  // we think we're going to get O(100) of these, put in a tripwire if it gets larger.
147       
148         [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")]
149         private class SequentialEnumInfo {
150             [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
151             public SequentialEnumInfo(Type t) {
152                 int actualMinimum = Int32.MaxValue;
153                 int actualMaximum = Int32.MinValue;
154                 int countEnumVals = 0;
155    
156                 foreach (int iVal in Enum.GetValues(t)){
157                     actualMinimum = Math.Min(actualMinimum, iVal);
158                     actualMaximum = Math.Max(actualMaximum, iVal);
159                     countEnumVals++;
160                 }
161                 
162                 if (countEnumVals -1 != (actualMaximum - actualMinimum)) {
163                     Debug.Fail("this enum cannot be sequential.");
164                 }
165                 MinValue = actualMinimum;
166                 MaxValue = actualMaximum;
167                
168             }
169             public int MinValue;
170             public int MaxValue;
171         }
172
173
174         [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
175         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
176         [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")]
177         private static void Debug_SequentialEnumIsDefinedCheck(System.Enum value, int minVal, int maxVal) {
178             Type t = value.GetType();
179
180             if (enumValueInfo == null) {
181                 enumValueInfo = new Hashtable();
182             }
183
184             SequentialEnumInfo sequentialEnumInfo = null;
185
186             if (enumValueInfo.ContainsKey(t)) {
187                 sequentialEnumInfo = enumValueInfo[t] as SequentialEnumInfo;
188             }
189             if (sequentialEnumInfo == null) {
190                 sequentialEnumInfo = new SequentialEnumInfo(t);
191
192                 if (enumValueInfo.Count > MAXCACHE) {
193                     // see comment next to MAXCACHE declaration.
194                     Debug.Fail("cache is too bloated, clearing out, we need to revisit this.");
195                     enumValueInfo.Clear();
196                 }
197                 enumValueInfo[t] = sequentialEnumInfo;
198                
199             }
200             if (minVal != sequentialEnumInfo.MinValue) {
201                 // put string allocation in the IF block so the common case doesnt build up the string.
202                 System.Diagnostics.Debug.Fail("Minimum passed in is not the actual minimum for the enum.  Consider changing the parameters or using a different function.");
203             }
204             if (maxVal != sequentialEnumInfo.MaxValue) {
205                 // put string allocation in the IF block so the common case doesnt build up the string.
206                 Debug.Fail("Maximum passed in is not the actual maximum for the enum.  Consider changing the parameters or using a different function.");
207             }
208
209         }
210
211      
212
213         [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
214         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
215         private static void Debug_ValidateMask(System.Enum value, UInt32 mask) {
216             Type t = value.GetType();
217             UInt32 newmask = 0;
218             foreach (int iVal in Enum.GetValues(t)){
219                 newmask = newmask | (UInt32)iVal;
220             }
221             System.Diagnostics.Debug.Assert(newmask == mask, "Mask not valid in IsEnumValid!");
222         }
223
224         [SuppressMessage("Microsoft.Globalization", "CA1305:SpecifyIFormatProvider")]
225         [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
226         [SuppressMessage("Microsoft.Performance", "CA1808:AvoidCallsThatBoxValueTypes")]
227         private static void Debug_NonSequentialEnumIsDefinedCheck(System.Enum value, int minVal, int maxVal, int maxBitsOn, bool isValid) {
228                Type t = value.GetType();
229                int actualMinimum = Int32.MaxValue;
230                int actualMaximum = Int32.MinValue;
231                int checkedValue = Convert.ToInt32(value, CultureInfo.InvariantCulture);
232                int maxBitsFound = 0;
233                bool foundValue = false;
234                foreach (int iVal in Enum.GetValues(t)){
235                    actualMinimum = Math.Min(actualMinimum, iVal);
236                    actualMaximum = Math.Max(actualMaximum, iVal);
237                    maxBitsFound = Math.Max(maxBitsFound, GetBitCount((uint)iVal));
238                    if (checkedValue == iVal) {
239                        foundValue = true;
240                    }
241                }
242                if (minVal != actualMinimum) {                
243                     // put string allocation in the IF block so the common case doesnt build up the string.
244                    System.Diagnostics.Debug.Fail( "Minimum passed in is not the actual minimum for the enum.  Consider changing the parameters or using a different function.");
245                }
246                if (maxVal != actualMaximum) {                
247                     // put string allocation in the IF block so the common case doesnt build up the string.
248                    System.Diagnostics.Debug.Fail("Maximum passed in is not the actual maximum for the enum.  Consider changing the parameters or using a different function.");
249                }
250
251                if (maxBitsFound != maxBitsOn) {
252                    System.Diagnostics.Debug.Fail("Incorrect usage of IsEnumValid function. The bits set to 1 in this enum was found to be: " + maxBitsFound.ToString(CultureInfo.InvariantCulture) + "this does not match what's passed in: " + maxBitsOn.ToString(CultureInfo.InvariantCulture));
253                }
254                if (foundValue != isValid) {
255                     System.Diagnostics.Debug.Fail(String.Format(CultureInfo.InvariantCulture, "Returning {0} but we actually {1} found the value in the enum! Consider using a different overload to IsValidEnum.", isValid, ((foundValue) ? "have" : "have not")));            
256                }
257
258            }
259         #endif
260
261         /// <devdoc>
262         ///   WeakRefCollection - a collection that holds onto weak references
263         ///
264         ///   Essentially you pass in the object as it is, and under the covers
265         ///   we only hold a weak reference to the object.
266         ///
267         ///   -----------------------------------------------------------------
268         ///   !!!IMPORTANT USAGE NOTE!!!        
269         ///   Users of this class should set the RefCheckThreshold property 
270         ///   explicitly or call ScavengeReferences every once in a while to 
271         ///   remove dead references.
272         ///   Also avoid calling Remove(item).  Instead call RemoveByHashCode(item)
273         ///   to make sure dead refs are removed.
274         ///   -----------------------------------------------------------------
275         ///
276         /// </devdoc>        
277 #if Microsoft_NAMESPACE || Microsoft_PUBLIC_GRAPHICS_LIBRARY || DRAWING_NAMESPACE
278         internal class WeakRefCollection : IList {
279             private int refCheckThreshold = Int32.MaxValue; // this means this is disabled by default.
280             private ArrayList _innerList;
281
282             internal WeakRefCollection() {
283                 _innerList = new ArrayList(4);
284             }
285
286             internal WeakRefCollection(int size) {
287                 _innerList = new ArrayList(size);
288             }
289
290             internal ArrayList InnerList {
291                 get { return _innerList; }
292             }
293
294             /// <summary>
295             ///     Indicates the value where the collection should check its items to remove dead weakref left over.
296             ///     Note: When GC collects weak refs from this collection the WeakRefObject identity changes since its 
297             ///           Target becomes null.  This makes the item unrecognizable by the collection and cannot be
298             ///           removed - Remove(item) and Contains(item) will not find it anymore.
299             ///           
300             /// </summary>
301             public int RefCheckThreshold {
302                 get{
303                     return this.refCheckThreshold;
304                 }
305                 set {
306                     this.refCheckThreshold = value;
307                 }
308             }
309
310             public object this[int index] {
311                 get {
312                     WeakRefObject weakRef = InnerList[index] as WeakRefObject;
313
314                     if ((weakRef != null) && (weakRef.IsAlive)) {
315                         return weakRef.Target;
316                     }
317
318                     return null;
319                 }
320                 set {
321                     InnerList[index] = CreateWeakRefObject(value);
322                 }
323             }
324
325             public void ScavengeReferences() {
326                 int currentIndex = 0;
327                 int currentCount = Count;
328                 for (int i = 0; i < currentCount; i++) {
329                     object item = this[currentIndex];
330
331                     if (item == null) {
332                         InnerList.RemoveAt(currentIndex);
333                     }
334                     else {   // only incriment if we have not removed the item
335                         currentIndex++;
336                     }
337                 }
338             }
339
340             public override bool Equals(object obj) {
341                 WeakRefCollection other = obj as WeakRefCollection;
342
343                 if (other == this) {
344                     return true;
345                 }
346
347                 if (other == null || Count != other.Count) {
348                     return false;
349                 }
350
351                 for (int i = 0; i < Count; i++) {
352                     if( this.InnerList[i] != other.InnerList[i] ) {
353                         if( this.InnerList[i] == null || !this.InnerList[i].Equals(other.InnerList[i])) {
354                             return false;
355                         }
356                     }
357                 }
358
359                 return true;
360             }
361
362             public override int GetHashCode() { 
363                 return base.GetHashCode(); 
364             }
365
366             private WeakRefObject CreateWeakRefObject(object value) {
367                 if (value == null) {
368                     return null;
369                 }
370                 return new WeakRefObject(value);
371             }
372
373             private static void Copy(WeakRefCollection sourceList, int sourceIndex, WeakRefCollection destinationList, int destinationIndex, int length) {
374                 if (sourceIndex < destinationIndex) {
375                     // We need to copy from the back forward to prevent overwrite if source and
376                     // destination lists are the same, so we need to flip the source/dest indices
377                     // to point at the end of the spans to be copied.
378                     sourceIndex = sourceIndex + length;
379                     destinationIndex = destinationIndex + length;
380                     for (; length > 0; length--) {
381                         destinationList.InnerList[--destinationIndex] = sourceList.InnerList[--sourceIndex];
382                     }
383                 }
384                 else {
385                     for (; length > 0; length--) {
386                         destinationList.InnerList[destinationIndex++] = sourceList.InnerList[sourceIndex++];
387                     }
388                 }
389             }
390
391             /// <summary>
392             ///     Removes the value using its hash code as its identity.  
393             ///     This is needed because the underlying item in the collection may have already been collected
394             ///     changing the identity of the WeakRefObject making it impossible for the collection to identify
395             ///     it.  See WeakRefObject for more info.
396             /// </summary>
397             [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
398             public void RemoveByHashCode(object value) {
399                 if( value == null ) {
400                     return;
401                 }
402
403                 int hash = value.GetHashCode();
404
405                 for( int idx = 0; idx < this.InnerList.Count; idx++ ) {
406                     if(this.InnerList[idx] != null && this.InnerList[idx].GetHashCode() == hash ) {
407                         this.RemoveAt(idx);
408                         return;
409                     }
410                 }
411             }
412
413             #region IList Members
414             public void Clear() { InnerList.Clear(); }
415             public bool IsFixedSize { get { return InnerList.IsFixedSize; } }
416             public bool Contains(object value) { return InnerList.Contains(CreateWeakRefObject(value)); }
417             public void RemoveAt(int index) { InnerList.RemoveAt(index); }
418             public void Remove(object value) { InnerList.Remove(CreateWeakRefObject(value)); }
419             public int IndexOf(object value) { return InnerList.IndexOf(CreateWeakRefObject(value)); }
420             public void Insert(int index, object value) { InnerList.Insert(index, CreateWeakRefObject(value)); }
421             public int Add(object value) {
422                 if (this.Count > RefCheckThreshold) {
423                     ScavengeReferences();
424                 } 
425                 return InnerList.Add(CreateWeakRefObject(value));
426             }
427         #endregion
428
429         #region ICollection Members
430             /// <include file='doc\ArrangedElementCollection.uex' path='docs/doc[@for="ArrangedElementCollection.Count"]/*' />
431             public int Count { get { return InnerList.Count; } }
432             object ICollection.SyncRoot { get { return InnerList.SyncRoot; } }
433             public bool IsReadOnly { get { return InnerList.IsReadOnly; } }
434             public void CopyTo(Array array, int index) { InnerList.CopyTo(array, index); }
435             bool ICollection.IsSynchronized { get { return InnerList.IsSynchronized; } }
436         #endregion
437
438         #region IEnumerable Members
439             public IEnumerator GetEnumerator() { 
440                 return InnerList.GetEnumerator(); 
441             }
442         #endregion
443
444             /// <summary>
445             ///     Wraps a weak ref object.
446             ///     WARNING: Use this class carefully!  
447             ///     When the weak ref is collected, this object looses its identity. This is bad when the object
448             ///     has been added to a collection since Contains(WeakRef(item)) and Remove(WeakRef(item)) would 
449             ///     not be able to identify the item.
450             /// </summary>
451             internal class WeakRefObject {
452                 int hash;
453                 WeakReference weakHolder;
454
455                 internal WeakRefObject(object obj) {
456                     Debug.Assert(obj != null, "Unexpected null object!");
457                     weakHolder = new WeakReference(obj);
458                     hash = obj.GetHashCode();
459                 }
460
461                 internal bool IsAlive {
462                     get { return weakHolder.IsAlive; }
463                 }
464
465                 internal object Target {
466                     get {
467                         return weakHolder.Target;
468                     }
469                 }
470
471                 public override int GetHashCode() {
472                     return hash;
473                 }
474
475                 public override bool Equals(object obj) {
476                     WeakRefObject other = obj as WeakRefObject;
477
478                     if( other == this ) {
479                         return true;
480                     }
481
482                     if (other == null ){
483                         return false;
484                     }
485
486                     if( other.Target != this.Target ) {
487                         if( this.Target == null || !this.Target.Equals(other.Target) ) {
488                             return false;
489                         }
490                     }
491
492                     return true;
493                 }
494             }
495         }
496 #endif
497
498     }
499
500 }