File: system\security\util\tokenbasedset.cs
Project: ndp\clr\src\bcl\mscorlib.csproj (mscorlib)
// ==++==
// 
//   Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// ==--==
// <OWNER>Microsoft</OWNER>
// 
 
//
// TokenBasedSet.cs
//
 
namespace System.Security.Util
{
    using System;
    using System.Collections;
    using System.Security.Permissions;
    using System.Runtime.Serialization;
    using System.Threading;
    using System.Diagnostics.Contracts;
    using System.Diagnostics.CodeAnalysis;
 
    [Serializable]
    internal class TokenBasedSet
    {
 
 
        // Following 3 fields are used only for serialization compat purposes: DO NOT USE THESE EVER!
#pragma warning disable 414        
        private int      m_initSize = 24;
        private int      m_increment = 8;
#pragma warning restore 414        
        private Object[] m_objSet;
        //  END -> Serialization only fields
 
        [OptionalField(VersionAdded = 2)]        
        private volatile Object m_Obj;
        [OptionalField(VersionAdded = 2)]        
        private volatile Object[] m_Set;
        
        private int m_cElt;
        private volatile int m_maxIndex;
 
 
        [OnDeserialized]
        private void OnDeserialized(StreamingContext ctx)
        {
            OnDeserializedInternal();
        }
        private void OnDeserializedInternal()
        {
            if (m_objSet != null) //v1.x case 
            {
                if (m_cElt == 1)
                    m_Obj = m_objSet[m_maxIndex];
                else
                    m_Set = m_objSet;
                m_objSet = null;
            }
            // Nothing to do for the v2.0 and beyond case
        }
 
        [OnSerializing]
        private void OnSerializing(StreamingContext ctx)
        {
 
            if ((ctx.State & ~(StreamingContextStates.Clone|StreamingContextStates.CrossAppDomain)) != 0)
            {
                //Nothing special for the v2 and beyond case
                
                // for the v1.x case, we need to create m_objSet if necessary
                if (m_cElt == 1)
                {
                    m_objSet = new Object[m_maxIndex+1];
                    m_objSet[m_maxIndex] = m_Obj;
                }
                else if (m_cElt > 0)
                {
                    // Array case:
                    m_objSet = m_Set;
                }
                
            }
        }   
        [OnSerialized]
        private void OnSerialized(StreamingContext ctx)
        {
            if ((ctx.State & ~(StreamingContextStates.Clone|StreamingContextStates.CrossAppDomain)) != 0)
            {
                m_objSet = null;
                
            }
        }
 
 
        internal bool MoveNext(ref TokenBasedSetEnumerator e)
        {
            switch (m_cElt)
            {
            case 0:
                return false;
 
            case 1:
                if (e.Index == -1)
                {
                    e.Index = m_maxIndex;
                    e.Current = m_Obj;
                    return true;
                }
                else
                {
                    e.Index = (short)(m_maxIndex+1);
                    e.Current = null;
                    return false;
                }
 
            default:
                while (++e.Index <= m_maxIndex)
                {
                    e.Current = Volatile.Read(ref m_Set[e.Index]);
                    
                    if (e.Current != null)
                        return true;
                }
 
                e.Current = null;
                return false;
            }
        }
 
        internal TokenBasedSet()
        {
            Reset();
        }
 
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread safety")]
        internal TokenBasedSet(TokenBasedSet tbSet)
        {
            if (tbSet == null)
            {
                Reset();
                return;
            }
 
            if (tbSet.m_cElt > 1)
            {
                Object[] aObj = tbSet.m_Set;
                int aLen = aObj.Length;
                
                Object[] aNew = new Object[aLen];
                System.Array.Copy(aObj, 0, aNew, 0, aLen);
                
                m_Set = aNew;
            }
            else
            {
                m_Obj = tbSet.m_Obj;
            }
 
            m_cElt      = tbSet.m_cElt;
            m_maxIndex  = tbSet.m_maxIndex;
        }
 
        internal void Reset()
        {
            m_Obj = null;
            m_Set = null;
            m_cElt = 0;
            m_maxIndex = -1;
        }
 
        internal void SetItem(int index, Object item)
        {
            Object[] aObj = null;
 
            if (item == null)
            {
                RemoveItem(index);
                return;
            }
 
            switch (m_cElt)
            {
            case 0:
                // on the first item, we don't create an array, we merely remember it's index and value
                // this this the 99% case
                m_cElt = 1;
                m_maxIndex = (short)index;
                m_Obj = item;
                break;
 
            case 1:
                // we have to decide if a 2nd item has indeed been added and create the array
                // if it has
                if (index == m_maxIndex)
                {
                    // replacing the one existing item
                    m_Obj = item;
                 }
                else
                {
                    // adding a second distinct permission
                    Object objSaved = m_Obj;
                    int iMax = Math.Max(m_maxIndex, index);
                    
                    aObj = new Object[iMax+1];
                    aObj[m_maxIndex] = objSaved;
                    aObj[index] = item;
                    m_maxIndex = (short)iMax;
                    m_cElt = 2;
                    m_Set = aObj;
                    m_Obj = null;
                }
                break;
 
            default:
                // this is the general case code for when there is really an array
 
                aObj = m_Set;
 
                // we are now adding an item, check if we need to grow
 
                if (index >= aObj.Length)
                {
                    Object[] newset = new Object[index+1];
                    System.Array.Copy(aObj, 0, newset, 0, m_maxIndex+1);
                    m_maxIndex = (short)index;
                    newset[index] = item;
                    m_Set = newset;
                    m_cElt++;
                }
                else
                {
                    if (aObj[index] == null)
                        m_cElt++;
 
                    aObj[index] = item;
 
                    if (index > m_maxIndex)
                        m_maxIndex = (short)index;
                }
                break;
            }
        }
 
        [SuppressMessage("Microsoft.Concurrency", "CA8001", Justification = "Reviewed for thread-safety")]
        internal Object GetItem(int index)
        {
            switch (m_cElt)
            {
            case 0:
                return null;
 
            case 1:
                if (index == m_maxIndex)
                    return m_Obj;
                else
                    return null;
            default:
                if (index < m_Set.Length)
                    return Volatile.Read(ref m_Set[index]);
                else
                    return null;
            }
        }
 
        internal Object RemoveItem(int index)
        {
            Object ret = null;
 
            switch (m_cElt)
            {
            case 0:
                ret = null;
                break;
 
            case 1:
                if (index != m_maxIndex)
                {
                    // removing a permission we don't have ignore it
                    ret = null;
                }
                else 
                {
                    // removing the permission we have at the moment
                    ret = m_Obj;
                    Reset();
                }
                break;
 
            default:
                // this is the general case code for when there is really an array
 
                // we are removing an item                
                if (index < m_Set.Length && (ret = Volatile.Read(ref m_Set[index])) != null)
                {
                    // ok we really deleted something at this point
 
                    Volatile.Write(ref m_Set[index], null);
                    m_cElt--;
 
                    if (index == m_maxIndex)
                        ResetMaxIndex(m_Set);
 
                    // collapse the array
                    if (m_cElt == 1)
                    {
                        m_Obj = Volatile.Read(ref m_Set[m_maxIndex]);
                        m_Set = null;
                    }
                }
                break;
            }
 
            return ret;
        }
 
        private void ResetMaxIndex(Object[] aObj)
        {
            int i;
 
            // Start at the end of the array, and
            // scan backwards for the first non-null
            // slot. That is the new maxIndex.
            for (i = aObj.Length - 1; i >= 0; i--)
            {
                if (aObj[i] != null)
                {
                    m_maxIndex = (short)i;
                    return;
                }
            }
 
            m_maxIndex = -1;
        }
        internal int GetStartingIndex()
        {
            if (m_cElt <= 1)
                return m_maxIndex;
            return 0;
        }
        internal int GetCount()
        {
            return m_cElt;
        }
 
        internal int GetMaxUsedIndex()
        {
            return m_maxIndex;
        }
 
        internal bool FastIsEmpty()
        {
            return m_cElt == 0;
        }
 
        // Used to merge two distinct TokenBasedSets (used currently only in PermissionSet Deserialization)
        internal TokenBasedSet SpecialUnion(TokenBasedSet other)
        {
            // This gets called from PermissionSet.OnDeserialized and it's possible that the TokenBasedSets have 
            // not been subjected to VTS callbacks yet
            OnDeserializedInternal();
            TokenBasedSet unionSet = new TokenBasedSet();
            int maxMax;
            if (other != null)
            {
                other.OnDeserializedInternal();
                maxMax = this.GetMaxUsedIndex() > other.GetMaxUsedIndex() ? this.GetMaxUsedIndex() : other.GetMaxUsedIndex();
            }
            else
                maxMax = this.GetMaxUsedIndex();
        
            for (int i = 0; i <= maxMax; ++i)
            {
                Object thisObj = this.GetItem( i );
                IPermission thisPerm = thisObj as IPermission;
#if FEATURE_CAS_POLICY
                ISecurityElementFactory thisElem = thisObj as ISecurityElementFactory;
#endif // FEATURE_CAS_POLICY
 
                Object otherObj = (other != null)?other.GetItem( i ):null;
                IPermission otherPerm = otherObj as IPermission;
#if FEATURE_CAS_POLICY
                ISecurityElementFactory otherElem = otherObj as ISecurityElementFactory;
#endif // FEATURE_CAS_POLICY
 
                if (thisObj == null && otherObj == null)
                    continue;
        
             
                if (thisObj == null)
                {
#if FEATURE_CAS_POLICY
                    if (otherElem != null)
                    {
                        otherPerm = PermissionSet.CreatePerm(otherElem, false);
                    }
#endif // FEATURE_CAS_POLICY
 
                    PermissionToken token = PermissionToken.GetToken(otherPerm);
                    
                    if (token == null)
                    {
                        throw new SerializationException(Environment.GetResourceString("Serialization_InsufficientState"));
                    }
                    
                    unionSet.SetItem(token.m_index, otherPerm);
                }
                else if (otherObj == null)
                {
#if FEATURE_CAS_POLICY
                    if (thisElem != null)
                    {
                        thisPerm = PermissionSet.CreatePerm(thisElem, false);
                    }
#endif // FEATURE_CAS_POLICY
 
                    PermissionToken token = PermissionToken.GetToken(thisPerm);
                    if (token == null)
                    {
                        throw new SerializationException(Environment.GetResourceString("Serialization_InsufficientState"));
                    }
                    unionSet.SetItem( token.m_index, thisPerm);
                }
                else
                {
                    Contract.Assert( (thisObj == null || otherObj == null), "Permission cannot be in both TokenBasedSets" );
                }
            }
            return unionSet;
        }
 
        internal void SpecialSplit(ref TokenBasedSet unrestrictedPermSet, ref TokenBasedSet normalPermSet, bool ignoreTypeLoadFailures)
        {
           int maxIndex = GetMaxUsedIndex();
 
            for (int i = GetStartingIndex(); i <= maxIndex; ++i)
            {
                Object obj = GetItem( i );
                if (obj != null)
                {
                    IPermission perm = obj as IPermission;
#if FEATURE_CAS_POLICY
                    if (perm == null)
                        perm = PermissionSet.CreatePerm(obj, ignoreTypeLoadFailures);
#endif // FEATURE_CAS_POLICY
                    PermissionToken token = PermissionToken.GetToken(perm);
 
                    if (perm == null || token == null)
                        continue;
 
                    if (perm is IUnrestrictedPermission)
                    {
                        // Add to unrestrictedPermSet
                        if (unrestrictedPermSet == null)
                            unrestrictedPermSet = new TokenBasedSet();
                        unrestrictedPermSet.SetItem(token.m_index, perm);
                    }
                    else
                    {
                        // Add to normalPermSet
                        if (normalPermSet == null)
                            normalPermSet = new TokenBasedSet();
                        normalPermSet.SetItem(token.m_index, perm);
                    }
 
                }
 
            }
            
        }
    }
}